diff --git a/.env.dev b/.env.dev
index 6640842..a74195b 100644
--- a/.env.dev
+++ b/.env.dev
@@ -12,10 +12,11 @@ DOTENV_PUBLIC_KEY_DEV="02a9d0f6522086b04ac7763e008a6ad0273c58258715e613c977fd6c8
DOTENV_PUBLIC_KEY="02a9d0f6522086b04ac7763e008a6ad0273c58258715e613c977fd6c82a2030d92"
# .env
-COM_ERMNVLDMR_STATIC_S3_BUCKET="encrypted:BL1vJLB9uPSvsUvrJah4UegRU8uPdFjE7sya+Fae3rwOmVzWsK5+TP4z4YzigTAuwit4FANvANvhRV46se+ZgprYplU8Cw1tZ5mHKO4rWAjif2sJCnsGXUKp8GZ2J7PrMH35MY0YrXX4NVqe18b2NEv2B9K7"
-COM_ERMNVLDMR_STATIC_S3_REGION="encrypted:BAj3iuo7tWWNR2V4yQ+LHnFCB4JcZp7TTfUtvv+2FPntNhwc/fV95IeFWz6S0z6aPRkDG+UQzPXNwdMz7y8ZbTHbduDDrgydHu1fST1NfQd2s1Bbpf+yJTG7dPBSc5RSNKZkkpB4v55qg/UC"
-COM_ERMNVLDMR_STATIC_S3_ENDPOINT_URL="encrypted:BNkQrgsrxtz544vuJbgwWUh5cPjHG5wbmHNcxTUWrzbFJJsdncaAitviCvIrS7Q41w+FiWkssv3D05Tu8Inn2P62E6G3PU4gNxO2oLdmGEdrzUqB/xvxOvLvos42j54QOZf7lOqMPKH6SL2/rheCAo10gSCicCqkfh4xsCAbYpyL"
-COM_ERMNVLDMR_STATIC_AWS_ACCESS_KEY_ID="encrypted:BCsp8p4bp3cB+9grxHo634isRUaFGU3MOc1lr8ClSFRYLjqjqukyrbJCTZWV2Xdo8iQ33EylmbzUltbwzTWMQcjJSvIskesVLyV93q60o/Mu9L9jg/apwHuBgHv4zVuHstJ0zkHWgapbNTIlWD3RCm0ujeX/H1gBH3U="
-COM_ERMNVLDMR_STATIC_AWS_SECRET_ACCESS_KEY="encrypted:BM7aPliOyiPrqFPZMfqpn534Nu+Y0Nbh/YRoxv2KY6XAi2dNO0LSEf97DqSUQYdsnu9iibZmVNktF2K/D6mtHr9+gumF6KsmaLH65KE7c9ly7NdFQHL9I26wxRq1Sr3Zgrfs954J6oURSlBRKqJFdcTsBltuuupGuqJHyLS9+DE0jD+bl0Qg0lA="
+COM_ERMNVLDMR_STATIC_S3_BUCKET="encrypted:BE65d5I9m1jrHGVNKDmfGL3Cjb7LjhB4tVWyNzSPU1Zu5Y00+R8NXgqSBrzBnXVj3vVinibjihVt3fStegyiqI52GtSycX1NlFSVJlRFWadUJ6SVK+SYfB7sFKSJ2jKGfwRaXKMzaKCdPWY+ni685oPXU9Rp"
+COM_ERMNVLDMR_STATIC_S3_REGION="encrypted:BPQ2+ms76A+PLFUeKL1Cnz8bdOcsJou03e8AyGwzdhozpcAwhP5yWXSqhTK8ZccAvmfjJcM4NK+x2a2r4g0ALd/UaQDa9xy2eZ6QnXwkoHXu2ce+F86/HuFnzX0qWqYTWi4q+N4="
+COM_ERMNVLDMR_STATIC_S3_ENDPOINT_URL="encrypted:BCcK2ih1QrABMImpUj31KIQYlbp9zhnafzrXINAwvyCaCjbOst6UGnhhi5M3LtQ+Fhgpx52ZKGjmke1oVxnvU6BBGz/lAbSD5euhHNbJ6EIoSqc25XpcmgucIQGx1cfiJxsL4sDR95FyR3fNaS9KGr005DwQaIRKGg86gALUCPhaLekq/Sd6Nn+TDoqmigLJPozdxp33AFpmWF+EwayYae2u"
+COM_ERMNVLDMR_STATIC_AWS_ACCESS_KEY_ID="encrypted:BPaBvzNo1eH3Z2ro+NFq4zLi4krZQT48h20r0etBfVFLDNS01gh5Uosy70Ip29rC8sa1JoP4Q8isJ2Xz0pr5Vxxa62vVFOJ9wJ6LkXbYF2bUm+UNyjfsIztJsMyiVZfPEEPEEKcniXZ3faqePwgLwCZSTa/Qry6znNmMV5FshSsP"
+COM_ERMNVLDMR_STATIC_AWS_SECRET_ACCESS_KEY="encrypted:BFiOxOo/TNvSIMOjyeOswLcl+9AVdqp4axW+I46c1CL0ckSucQS+7CLpO8fNfakmW2bSibqru+xfWUbQT7FDH1YB3CPXUc0qPm1PsG0FiLixDefQuDrC/JHuA0Z3/dOAkn/e94BBuPtyJXxcLRP1v517MijPa0MuNqhI9BS3jFHKjWivqc9AVPs2ybglo14b/nEO1GqlM0TYflPoDhHoWjM="
SOPS_AGE_KEY="encrypted:BKiJVxKYhiadu0kNCw8wVLYdaEWZreAVu2PaujxhvcXeCowCGjivEuQD/uhduAy/CwwDWyQkgQJ2FJMEKA47e7QDjS9TMdPCXO19/teR7LThgqTf6AbSM3sebln5zalgyplay4A/7cVytPXuOvVkaYyoh9CgzTVsPJKXRq5yMaDEx/9/91Yo8zUMF3z1vq3/preI5baK4/1xXewR3OwfZqnNGyd7LXkdqyT5"
COM_ERMNVLDMR_DEV_ROOT_CA_KEY="encrypted:BP+GhCwutIMF2hSxIs92VNSoEAvfdhYRFSi5MUpFfCs+pNxJkZ7tFOAadMqSlPiLBcm9+A3yIn/04/oEtRNjXk/jlZQ2G21mt30sd89NARjJO+94ui6XbllW65E5/T+d8fDmRhz4c4OcNoKk70aXcdJqd1CJT7IlYQn++zQvB2el9cp9e7E22rvGtvum40iISEt/pVU84uHyQg6ZeCn+1czM+nLhPiWOAgveTz3EAgExlJW2de1ilDIuBCFXJy5+KvnCj3WNwRTAbIYgCwFnZMOUE5jYlhP6OZ2DQ3sWv7noG/Xaa/P+EomP9dwcamlAU32tRruyA8ZSp/ah9E8hFfmBqgKfSbRlWq3qGkq/F935DZfU/C+PjRcepS/eLZQ0mfD23B21VX2CjOhEkp5V+9I5L8XkXDjDg56JDRGvF/umpdqF/KpmtimqcgB0yDVY1Z3yT1U5IZ6Yf4WlqzhchdJXRWfo0bqK8Tk0b442/6AQgRP3n6DIrCjcpmr4jx+RJiejOr3xv3QtHGafDdkF2gO7DrYztdd6XpRjij0orBUiyYECgPsLHuwd+/2jXnxLVTavVTQ0Ddz0LLNjaYxqr8KK/B+0MeS/iuLXmACs8XCKwQmV8hPYQ0C/JXGiL4GOH0V70r6ghBxCDDMpJjPasCbiCBxSAMLkQ7ZmLbO0WVPPUTMAsZ8aKUy2MLe466zizXN640B4H6bkI7x/KC29sroEJWpUufD7f+t2tnVx/KLeAxAC6afuUCGi4ibFiwLHZjfimYKaYpuMcSZHQ64iONixyfUDIRNIOWQSiX1V5EBJgBDE0JVDnExmQYAIG5isYQEorC/7z6IJeZHc4NuiKDWuvd+jwwj8tN8PVAcACJKpQHpyUU20kvp4dYgBEeiQZpBfXWrnmOJZ0HgDHbvVxPW+SHTL/uoi/Tl9xdKe+z3Q/MoT8iYXggMPcFQIqVemQUTw9Tu0oNsSj3xueR9awJc2Le9aYbKTg54MAtcBnfKXN+3cYQkNVbqC5RfiAe2D3gfbT1OgApTUNz8n8v1dgAzTa+EB0i+w8nTsb3N/N4yAXMNSKdGeUl2rFRuQO7qlWorKc3pE+nBC2ex0sOMEUFYUXcyULaxsKM3ddakCAv9/jesnkrZl2gX7YDp8iqiFzh7l8jMAFHtFCMp3CSXlBAOpfQlGLY80qTSi1e8GrnV9Ow9t0DdS1wP7J6ES9DiNuEep5atHD+0MC6u0Dw0GtmsNnNRp77my/KI8ILhF4VBVBFQ5BiJDfdpAA3RNlANo2gK5ZCKPw4tXCYUknjYdwkqvUaIQCJcr2n+WkE2eCUCqjXTFr/KMvRByY6Fb1vAwcGoCWjP2g0Tliz3hcyh/72+umn05NO3cJSAfDEjfcYBUQZ/LCcy86G0EsHXP3NabgeHNXf/Z/VG7zWqQAPX0KaVrF9XC6I2FLkg1aNGWZfJGv4uohdIny7rWU7FWPaxy9GMk6x7xBh/Hk/5/rrGHCNGs+SegLglDs2+v4P56316k4kg//dmVl/vh3h0KC2P/1xiJZ10I91k7HqZLb2qdXToa4yOHB5Euu49UV/Qtv+cZU03fuT81sEv3YT819OdmwUZgkq0PT/Lrck+pbNDaUOakpgkGAbXy6SCvjM87jYN9Ia7druulWWO0pPi4T8daQ26ZQmZp57gdkpuh/He4kC5m1aWpT2s5STSbapChd9tFNjPhjCG1DZb7h0X8jg1VkxqutDDu17jOXrhElT1ejvYqYy9lIG1b1r7Jmjxo7LgGQuPMX6CIo/BLTsuNJyC5SfSCxEm56LvJgoNk2Vaeq6mLDOaKm2PNwPuYPnJw9xWBM0EetJBnsYMutPDMXhrB7x/8DKy5/hCsKICTfhlsboUHmbSfsikvGPYx7YOaOo8xFh4yAQ73T8hFX7kn2DBfRzGkJm8SQJRPj0Hjsf5mEFUt/OrieK/Jw10HKD6hDoEFmUDv5svC3nUY0FX0fTyi0jRAri+burrCQ0HXk/qj4Xs+WLiJgfQbprV2TwTIlzOdLb/jD8PeiElNzBxmOonRj76QKcbkOnPkCWCMFKapOJskxe0ASJVjYt142D0ndTa6F7oFu72ue5U+hyoToKeb7ONZilhF0+yL7akNfFKURUa/X+cEoewuSM7r8whWwZyG+M2cwGUTRMJiAC9wK7yE8MqdrEuQtILnCA0yRTPOh1Z9ifUY0asL1Tw9cbdGRW8svHH/oVHkWe3P+yepTNrn7xQlX/O1/p4FXRm0gSE3Fdnbli8VZdwVn2SET3WkPH9rJT2F7/hovfhx+lQzBQq9ZBpNLrWRdhXzCpiaC04o7q/eef+8dZwU62A1pd8uZ+0dI5T7lOVXoo03uF863YSOKX8ad2CiYUJ8gMvMA8FSWnsUbxiDaiXPj8kni5TLsvrkH/CKY/okG5SdRCF/XCcRcXP5dxdsS90tIWdd0xi/2wbalmn3CPQ2eVeDI3dP/jCRBkfiNtD70flhxVngZMZW+Rr1c0Cpo//fMCiV5ksDIBGin5AfOPmvSnPYWV5zcU3QEfl2F5fLXgDCbpaYjaOYjyRt9ZTqabSf1Ar4L9yQFBr4N9cNg4jKskvbha4BDmUL8J7xz/F/OXHFnEDV2YkMcpBfGM2XhKDevceOQcjrPkF5/e5LdwQeYhqgzzm6ur42OwHB2HBU7ulXn/LCk2eYCFa2D1f+2F3ExbwAvnDMhymdQdUniF/NzOfG/1L3yplFsqi270T/2gT1LdOQ/Fgp3FA81k/sMLbICcF/qCp/nS3x+iMZn9B+WLjUQvJy2PehXNcpXEvXWM/e1X4PWDUhwQi7+wjJwERX2W5SB1XjQa7n/osZqpOUG2ShssPNpS3mecaV+MTpRPVYTPFiDTIgkG7F/DSND0c2PDWsBLFus0h4jG70L94uDxVNiMJb/xq/7d2+7FMNPh9UJSK75XcKDhrRYJo3iU7mfl1U89vJB179ulGrdMpWd8v5DHWEDrn3NRBa0KWVYKpqeQ0195XXH1YlZjzK7615K/5ooOGUL42PlX2Hw1zxOJubsIl6LZt8V6wa6zJgywuVLsZb+kpjtHTHmVZyhY8IUnAn6Uj8erYXapvZ8rlhUpjvVuosn8gB9JM6vNct5dVn4e8Wbt6KnRcogHYUJT32QRSWsHCaZNEqwbX+dYaLx1FQZ5daYbXSWwiHhtAJRuYutbmWwWCRTVzyNNMh3sMAI1EcmMoOIX0DEhXYNe7daErR6vMyizQ+h5YKUYK4nUu270M0x/YKRHlxwK8WkEqWv8vfhZqzV0YGud1Hwd5JwWhIEZQ7cc1uPeYJJ5kAaIFqCffmwecS2gzVvOgDkNC5eENXSk7SzDpVgm8IcfZV47L17ni75tyJCS/kreODjap6VdpDNTyS1YJaaO2TMX/s4kWiEZ22Ma4CEv2eQq1uGazGamli+ZwExAtehw=="
+COM_ERMNVLDMR_STATIC_PUBLIC_URL="encrypted:BEZbNy1WbNKhgUWmKO648nvuVNyMNXrMnq64WkjsScTOcE/HFtMX3nQktbIvEYiTSNHvoceUeW10E9i4n+C5ZEHKIBi9VUk3nuqVIcwAr6xTfqKjM1cJlRNKFQzwhHbBTYcImLoxFvM56KU4L/9Ei/XuQ/QRz0KTabhvGMM="
diff --git a/.github/workflows/ci-next-build-golden-image.yml b/.github/workflows/ci-next-build-golden-image.yml
index 1a06454..484d037 100644
--- a/.github/workflows/ci-next-build-golden-image.yml
+++ b/.github/workflows/ci-next-build-golden-image.yml
@@ -116,7 +116,7 @@ jobs:
env:
AWS_ACCESS_KEY_ID: ${{ secrets.STATIC_AWS_ACCESS_KEY_ID || secrets.COM_ERMNVLDMR_STATIC_AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.STATIC_AWS_SECRET_ACCESS_KEY || secrets.COM_ERMNVLDMR_STATIC_AWS_SECRET_ACCESS_KEY }}
- AWS_DEFAULT_REGION: ${{ secrets.STATIC_AWS_REGION || secrets.COM_ERMNVLDMR_STATIC_S3_REGION || 'ru-central1' }}
+ AWS_DEFAULT_REGION: ${{ secrets.STATIC_AWS_REGION || secrets.COM_ERMNVLDMR_STATIC_S3_REGION || 'auto' }}
S3_ENDPOINT: ${{ secrets.STATIC_S3_ENDPOINT_URL || secrets.COM_ERMNVLDMR_STATIC_S3_ENDPOINT_URL }}
S3_BUCKET: ${{ secrets.STATIC_S3_BUCKET || secrets.COM_ERMNVLDMR_STATIC_S3_BUCKET }}
IMAGE_NAME: ${{ needs.build-image.outputs.image_name }}
diff --git a/ci/scripts/ensure-dev-certs.mjs b/ci/scripts/ensure-dev-certs.mjs
index eefa942..2092501 100644
--- a/ci/scripts/ensure-dev-certs.mjs
+++ b/ci/scripts/ensure-dev-certs.mjs
@@ -162,7 +162,7 @@ export function loadDevRootCaKey(workspaceRoot = findWorkspaceRoot()) {
* Remote public URL for the Ermnvldmr development root CA certificate.
*/
export const REMOTE_ROOT_CA_URL =
- 'https://static.ermnvldmr.com/ssl/certs/ErmnvldmrDevelopmentRootCA.pem';
+ 'https://static.ermnvldmr.com/dev/ssl/certs/ErmnvldmrDevelopmentRootCA.pem';
/**
* Ensures valid development certificates exist for localhost.ermnvldmr.com.
diff --git a/packages/cli/src/lib/runners/s3SyncRunner.test.ts b/packages/cli/src/lib/runners/s3SyncRunner.test.ts
index 8ff196f..0d9a37b 100644
--- a/packages/cli/src/lib/runners/s3SyncRunner.test.ts
+++ b/packages/cli/src/lib/runners/s3SyncRunner.test.ts
@@ -376,4 +376,33 @@ describe('runS3Sync', () => {
const content = fs.readFileSync(srcPath, 'utf-8');
expect(content).toContain('https://cdn.example.com/www/img.webp');
});
+
+ it('should fallback to COM_ERMNVLDMR_STATIC_PUBLIC_URL when publicUrl is not explicitly configured', async () => {
+ process.env.COM_ERMNVLDMR_STATIC_PUBLIC_URL = 'https://static.ermnvldmr.com';
+
+ const staticDir = path.join(tempDir, 'static');
+ fs.mkdirSync(staticDir, { recursive: true });
+ fs.writeFileSync(path.join(staticDir, 'asset.png'), 'png-content');
+
+ const srcDir = path.join(tempDir, 'src');
+ fs.mkdirSync(srcDir, { recursive: true });
+ const srcPath = path.join(srcDir, 'index.ts');
+ fs.writeFileSync(srcPath, 'const img = "/static/asset.png";');
+
+ const cmd: S3SyncCommandConfig = {
+ name: 'sync',
+ description: 'desc',
+ bucket: 'com-ermnvldmr-static',
+ targetPrefix: 'www',
+ sourceDir: './static',
+ type: 's3-sync',
+ };
+
+ await runS3Sync(cmd, {});
+
+ const content = fs.readFileSync(srcPath, 'utf-8');
+ expect(content).toContain('https://static.ermnvldmr.com/www/asset.png');
+
+ delete process.env.COM_ERMNVLDMR_STATIC_PUBLIC_URL;
+ });
});
diff --git a/packages/cli/src/lib/runners/s3SyncRunner.ts b/packages/cli/src/lib/runners/s3SyncRunner.ts
index 99339e0..8c36ebe 100644
--- a/packages/cli/src/lib/runners/s3SyncRunner.ts
+++ b/packages/cli/src/lib/runners/s3SyncRunner.ts
@@ -53,6 +53,7 @@ export async function runS3Sync(
const bucket = cmd.bucket ?? process.env.S3_BUCKET;
const region = cmd.region ?? process.env.AWS_REGION ?? process.env.S3_REGION;
const endpoint = cmd.endpoint ?? process.env.S3_ENDPOINT_URL ?? process.env.S3_ENDPOINT;
+ const publicUrl = cmd.publicUrl ?? process.env.COM_ERMNVLDMR_STATIC_PUBLIC_URL ?? process.env.S3_PUBLIC_URL;
const accessKey = cmd.accessKeyId ?? process.env.AWS_ACCESS_KEY_ID ?? process.env.S3_ACCESS_KEY_ID;
const secretKey = cmd.secretAccessKey ?? process.env.AWS_SECRET_ACCESS_KEY ?? process.env.S3_SECRET_ACCESS_KEY;
@@ -149,8 +150,8 @@ export async function runS3Sync(
*/
const getS3Url = (finalName: string): string => {
const normalizedKey = normalizeKey(cmd.targetPrefix, finalName);
- if (cmd.publicUrl) {
- const cleanPublicUrl = cmd.publicUrl.replace(/\/+$/, '');
+ if (publicUrl) {
+ const cleanPublicUrl = publicUrl.replace(/\/+$/, '');
return `${cleanPublicUrl}/${normalizedKey}`;
}
if (bucket.includes('.')) {
diff --git a/packages/rsbuild-config/src/lib/config/server.ts b/packages/rsbuild-config/src/lib/config/server.ts
index 143f575..1aa34a8 100644
--- a/packages/rsbuild-config/src/lib/config/server.ts
+++ b/packages/rsbuild-config/src/lib/config/server.ts
@@ -214,7 +214,7 @@ export function loadDevRootCaKey(
* Remote public URL for the Ermnvldmr development root CA certificate.
*/
export const REMOTE_ROOT_CA_URL =
- 'https://static.ermnvldmr.com/ssl/certs/ErmnvldmrDevelopmentRootCA.pem';
+ 'https://static.ermnvldmr.com/dev/ssl/certs/ErmnvldmrDevelopmentRootCA.pem';
/**
* Ensures that valid TLS development certificates exist for localhost.ermnvldmr.com.
diff --git a/packages/ssg/package.json b/packages/ssg/package.json
index b239817..ca4fc18 100644
--- a/packages/ssg/package.json
+++ b/packages/ssg/package.json
@@ -24,6 +24,7 @@
"build": "tsc",
"lint": "eslint src --max-warnings 0",
"typecheck": "tsc --noEmit",
+ "test": "vitest run",
"clean": "rm -rf dist"
},
"dependencies": {
@@ -39,6 +40,8 @@
"@rsbuild/core": "^1.7.3",
"@rsbuild/plugin-react": "^1.1.1",
"@rsbuild/plugin-type-check": "^1.1.1",
+ "@testing-library/jest-dom": "^6.9.1",
+ "@testing-library/react": "^16.3.1",
"@types/node": "*",
"@types/react": "^19.1.2",
"@types/react-dom": "^19.1.2",
@@ -46,7 +49,8 @@
"fast-glob": "^3.3.3",
"jsdom": "^28.0.0",
"rsbuild-plugin-dts": "^0.19.5",
- "typescript": "^5.8.2"
+ "typescript": "^5.8.2",
+ "vitest": "^4.1.11"
},
"peerDependencies": {
"react": "^19.1.0",
diff --git a/packages/ssg/src/lib/config/ssg.ts b/packages/ssg/src/lib/config/ssg.ts
index 600154f..daaa941 100644
--- a/packages/ssg/src/lib/config/ssg.ts
+++ b/packages/ssg/src/lib/config/ssg.ts
@@ -5,14 +5,36 @@ import path from 'node:path';
import React from 'react';
+import { getPageOptions } from '../core/createPage.js';
+
import type { RsbuildPlugin } from '@rsbuild/core';
+/**
+ * Escapes HTML special characters for safe injection into markup.
+ *
+ * @param str - The raw string to escape.
+ * @returns The escaped HTML string.
+ * @example
+ * ```ts
+ * escapeHtml('');
+ * ```
+ */
+function escapeHtml(str: string): string {
+ return str
+ .replace(/&/g, '&')
+ .replace(//g, '>')
+ .replace(/"/g, '"')
+ .replace(/'/g, ''');
+}
+
const SSR_RUNNER_SCRIPT = `
import { createRsbuild } from '@rsbuild/core';
async function main() {
const entries = JSON.parse(process.argv[1]);
const ssrDistPath = process.argv[2];
+ const locale = process.argv[3] || process.env.LOCALE || 'en';
const plugins = [];
try {
@@ -31,10 +53,35 @@ async function main() {
// MDX plugins not installed or needed in this service
}
+ if (locale !== 'en') {
+ plugins.push({
+ name: 'ermnvldmr:ssr-locale-mdx-replacement',
+ setup(api) {
+ api.modifyRspackConfig((config, { rspack }) => {
+ config.plugins = [
+ ...(config.plugins ?? []),
+ new rspack.NormalModuleReplacementPlugin(
+ /\\.en\\.mdx$/,
+ (resource) => {
+ resource.request = resource.request.replace(/\\.en\\.mdx$/, '.' + locale + '.mdx');
+ }
+ ),
+ ];
+ return config;
+ });
+ },
+ });
+ }
+
const ssrBuild = await createRsbuild({
rsbuildConfig: {
plugins,
- source: { entry: entries },
+ source: {
+ entry: entries,
+ define: {
+ 'process.env.LOCALE': JSON.stringify(locale),
+ },
+ },
output: {
target: 'node',
distPath: { root: ssrDistPath, js: '.' },
@@ -119,11 +166,16 @@ export function ssgPlugin(): RsbuildPlugin {
);
const ssrDistPath = path.join(path.dirname(webDistPath), 'dist-ssr');
+ const locale = process.env.LOCALE ?? 'en';
+
console.log('[ssg-plugin] Building SSR bundles...');
execFileSync(
process.execPath,
- ['--input-type=module', '-e', SSR_RUNNER_SCRIPT, JSON.stringify(entries), ssrDistPath],
- { stdio: 'inherit' }
+ ['--input-type=module', '-e', SSR_RUNNER_SCRIPT, JSON.stringify(entries), ssrDistPath, locale],
+ {
+ stdio: 'inherit',
+ env: { ...process.env, LOCALE: locale },
+ }
);
console.log('[ssg-plugin] SSR bundles ready.');
@@ -175,8 +227,10 @@ export function ssgPlugin(): RsbuildPlugin {
React.createElement(React.StrictMode, null, React.createElement(Component))
);
+ const pageOptions = getPageOptions(Component);
+
const source = await fs.readFile(webHtmlPath, 'utf8');
- const injected = source.replace(
+ let injected = source.replace(
/
]*>\s*<\/div>/,
`
${renderedHtml}
`
);
@@ -187,9 +241,29 @@ export function ssgPlugin(): RsbuildPlugin {
);
}
+ if (pageOptions?.title) {
+ injected = injected.replace(
+ /
[^<]*<\/title>/i,
+ `${escapeHtml(pageOptions.title)}`
+ );
+ }
+
+ if (pageOptions?.description) {
+ if (/]*name=["']description["'][^>]*>/i.test(injected)) {
+ injected = injected.replace(
+ /]*name=["']description["'][^>]*>/i,
+ ``
+ );
+ } else {
+ injected = injected.replace(
+ /<\/head>/i,
+ ` \n`
+ );
+ }
+ }
+
const processed = await critters.process(injected);
- const locale = process.env.LOCALE ?? 'en';
const localized = processed.replace(
/]*)\slang="[^"]*"/,
` {
+ beforeEach(() => {
+ document.title = 'Default Title';
+ const existingMeta = document.querySelector('meta[name="description"]');
+ if (existingMeta) {
+ existingMeta.remove();
+ }
+ });
+
+ it('attaches metadata options to component and retrieves them via getPageOptions', () => {
+ function SamplePage(): React.JSX.Element {
+ return Sample
;
+ }
+
+ createPage(SamplePage, {
+ title: 'Sample Title',
+ description: 'Sample Description',
+ });
+
+ const options = getPageOptions(SamplePage);
+ expect(options).toEqual({
+ title: 'Sample Title',
+ description: 'Sample Description',
+ });
+ });
+
+ it('returns undefined for entities without metadata options', () => {
+ function PlainComponent(): React.JSX.Element {
+ return Plain
;
+ }
+
+ expect(getPageOptions(PlainComponent)).toBeUndefined();
+ expect(getPageOptions(null)).toBeUndefined();
+ expect(getPageOptions(undefined)).toBeUndefined();
+ expect(getPageOptions('string')).toBeUndefined();
+ expect(getPageOptions(123)).toBeUndefined();
+ expect(getPageOptions({})).toBeUndefined();
+ });
+
+ it('updates document.title and creates meta description in DOM', () => {
+ function MetaPage(): React.JSX.Element {
+ return Meta
;
+ }
+
+ createPage(MetaPage, {
+ title: 'Custom Title',
+ description: 'Custom Description',
+ });
+
+ expect(document.title).toBe('Custom Title');
+ const meta = document.querySelector('meta[name="description"]');
+ expect(meta).not.toBeNull();
+ expect(meta?.getAttribute('content')).toBe('Custom Description');
+ });
+
+ it('updates existing meta description if already present in DOM', () => {
+ const existingMeta = document.createElement('meta');
+ existingMeta.setAttribute('name', 'description');
+ existingMeta.setAttribute('content', 'Old Description');
+ document.head.appendChild(existingMeta);
+
+ function AnotherMetaPage(): React.JSX.Element {
+ return Another Meta
;
+ }
+
+ createPage(AnotherMetaPage, {
+ description: 'Updated Description',
+ });
+
+ const meta = document.querySelector('meta[name="description"]');
+ expect(meta?.getAttribute('content')).toBe('Updated Description');
+ });
+});
diff --git a/packages/ssg/src/lib/core/createPage.ts b/packages/ssg/src/lib/core/createPage.ts
new file mode 100644
index 0000000..39708a2
--- /dev/null
+++ b/packages/ssg/src/lib/core/createPage.ts
@@ -0,0 +1,121 @@
+import React from 'react';
+
+/**
+ * Options for page creation.
+ */
+export interface PageOptions {
+ /** Document title to set on mount. */
+ title?: string;
+ /** Content of the meta description tag. */
+ description?: string;
+}
+
+/**
+ * Internal interface representing a component with attached page options.
+ */
+interface HasPageOptions {
+ /** The attached page options. */
+ __pageOptions?: PageOptions;
+}
+
+/**
+ * Type predicate checking if a value is an object or function with attached page options.
+ *
+ * @param value - The value to inspect.
+ * @returns True if value has __pageOptions.
+ * @example
+ * ```ts
+ * if (hasPageOptions(Component)) {
+ * console.log(Component.__pageOptions);
+ * }
+ * ```
+ */
+function hasPageOptions(value: unknown): value is HasPageOptions {
+ return (
+ (typeof value === 'function' || (typeof value === 'object' && value !== null)) &&
+ '__pageOptions' in value
+ );
+}
+
+/**
+ * Retrieves the page metadata options attached to a component via createPage.
+ *
+ * @param component - The component to inspect.
+ * @returns The associated PageOptions, or undefined if none are set.
+ * @example
+ * ```ts
+ * const options = getPageOptions(MyPageComponent);
+ * ```
+ */
+export function getPageOptions(component: unknown): PageOptions | undefined {
+ if (hasPageOptions(component)) {
+ return component.__pageOptions;
+ }
+ return undefined;
+}
+
+/**
+ * Mounts or hydrates a React component into the `#root` element.
+ *
+ * Attaches metadata options for SSG extraction. Skips DOM mounting during the SSG
+ * server-side render pass (`typeof window === 'undefined'`). On the client, hydrates
+ * asynchronously during idle time to unblock First Contentful Paint.
+ *
+ * @param Component - The React component to render.
+ * @param options - Optional page metadata (title, description).
+ *
+ * @example
+ * ```tsx
+ * createPage(App, { title: 'My Page' });
+ * ```
+ */
+export function createPage(Component: React.ComponentType, options: PageOptions = {}): void {
+ Object.assign(Component, { __pageOptions: options });
+
+ if (typeof window === 'undefined') return; // SSG build context: skip DOM mounting
+ const rootElement = document.getElementById('root');
+
+ if (options.title && document.title !== options.title) {
+ document.title = options.title;
+ }
+
+ if (options.description) {
+ let metaDescription = document.querySelector('meta[name="description"]');
+ if (!metaDescription) {
+ metaDescription = document.createElement('meta');
+ metaDescription.setAttribute('name', 'description');
+ document.head.appendChild(metaDescription);
+ }
+ if (metaDescription.getAttribute('content') !== options.description) {
+ metaDescription.setAttribute('content', options.description);
+ }
+ }
+
+ if (!rootElement) return;
+
+ const mountOrHydrate = () => {
+ void import('react-dom/client')
+ .then(({ createRoot, hydrateRoot }) => {
+ const node = React.createElement(
+ React.StrictMode,
+ null,
+ React.createElement(Component)
+ );
+
+ if (rootElement.hasChildNodes()) {
+ hydrateRoot(rootElement, node);
+ } else {
+ createRoot(rootElement).render(node);
+ }
+ })
+ .catch((err: unknown) => {
+ console.error('[createPage] Client hydration failed:', err);
+ });
+ };
+
+ if ('requestIdleCallback' in window) {
+ window.requestIdleCallback(mountOrHydrate);
+ } else {
+ setTimeout(mountOrHydrate, 1);
+ }
+}
diff --git a/packages/ssg/src/lib/core/createPage.tsx b/packages/ssg/src/lib/core/createPage.tsx
deleted file mode 100644
index b114568..0000000
--- a/packages/ssg/src/lib/core/createPage.tsx
+++ /dev/null
@@ -1,73 +0,0 @@
-import React from 'react';
-
-/**
- * Options for page creation.
- */
-export interface PageOptions {
- /** Document title to set on mount. */
- title?: string;
- /** Content of the meta description tag. */
- description?: string;
-}
-
-/**
- * Mounts or hydrates a React component into the `#root` element.
- *
- * Skips DOM mounting during the SSG server-side render pass
- * (`typeof window === 'undefined'`). On the client, hydrates asynchronously
- * during idle time to unblock First Contentful Paint.
- *
- * @param Component - The React component to render.
- * @param options - Optional page metadata (title, description).
- *
- * @example
- * ```tsx
- * createPage(App, { title: 'My Page' });
- * ```
- */
-export function createPage(Component: React.ComponentType, options: PageOptions = {}): void {
- if (typeof window === 'undefined') return; // SSG build context: skip DOM mounting
- const rootElement = document.getElementById('root');
-
- if (options.title) {
- document.title = options.title;
- }
-
- if (options.description) {
- let metaDescription = document.querySelector('meta[name="description"]');
- if (!metaDescription) {
- metaDescription = document.createElement('meta');
- metaDescription.setAttribute('name', 'description');
- document.head.appendChild(metaDescription);
- }
- metaDescription.setAttribute('content', options.description);
- }
-
- if (!rootElement) return;
-
- const mountOrHydrate = () => {
- void import('react-dom/client')
- .then(({ createRoot, hydrateRoot }) => {
- const node = (
-
-
-
- );
-
- if (rootElement.hasChildNodes()) {
- hydrateRoot(rootElement, node);
- } else {
- createRoot(rootElement).render(node);
- }
- })
- .catch((err: unknown) => {
- console.error('[createPage] Client hydration failed:', err);
- });
- };
-
- if ('requestIdleCallback' in window) {
- window.requestIdleCallback(mountOrHydrate);
- } else {
- setTimeout(mountOrHydrate, 1);
- }
-}
diff --git a/packages/ssg/vitest.config.ts b/packages/ssg/vitest.config.ts
new file mode 100644
index 0000000..962c6ca
--- /dev/null
+++ b/packages/ssg/vitest.config.ts
@@ -0,0 +1,9 @@
+import { baseVitestConfig } from '@ermnvldmr/rsbuild-config/dev';
+import { mergeConfig } from 'vitest/config';
+
+export default mergeConfig(baseVitestConfig, {
+ test: {
+ environment: 'jsdom',
+ exclude: ['dist/**', 'node_modules/**'],
+ },
+});
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index baba032..f495565 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -69,7 +69,7 @@ importers:
version: 5.8.3
vitest:
specifier: ^4.1.11
- version: 4.1.11(@types/node@22.20.0)(jsdom@28.0.0)(vite@8.1.6(@types/node@22.20.0)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.51.2)(yaml@2.8.2))
+ version: 4.1.11(@types/node@22.20.0)(@vitest/coverage-v8@4.0.18)(jsdom@28.0.0)(vite@8.1.6(@types/node@22.20.0)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.51.2)(yaml@2.8.2))
packages/eslint-config:
dependencies:
@@ -176,7 +176,7 @@ importers:
version: 8.6.15(storybook@8.6.17(prettier@3.9.10))
'@storybook/addon-coverage':
specifier: ^3.0.0
- version: 3.0.0(vite@8.1.6(@types/node@22.20.0)(esbuild@0.25.12)(jiti@2.6.1)(terser@5.51.2)(yaml@2.8.2))
+ version: 3.0.0(vite@8.1.6(@types/node@22.20.0)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.51.2)(yaml@2.8.2))
'@storybook/addon-essentials':
specifier: ^8.6.15
version: 8.6.15(@types/react@19.2.9)(storybook@8.6.17(prettier@3.9.10))
@@ -236,13 +236,13 @@ importers:
version: 8.6.17(prettier@3.9.10)
storybook-react-rsbuild:
specifier: ^1.0.3
- version: 1.0.3(@rsbuild/core@1.7.3)(@storybook/test@8.6.15(storybook@8.6.17(prettier@3.9.10)))(@types/react@19.2.9)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(rollup@4.60.1)(storybook@8.6.17(prettier@3.9.10))(typescript@5.8.3)(webpack@5.105.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.12))
+ version: 1.0.3(@rsbuild/core@1.7.3)(@storybook/test@8.6.15(storybook@8.6.17(prettier@3.9.10)))(@types/react@19.2.9)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(rollup@4.60.1)(storybook@8.6.17(prettier@3.9.10))(typescript@5.8.3)(webpack@5.105.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.27.7))
typescript:
specifier: ^5.8.3
version: 5.8.3
vitest:
specifier: ^4.1.11
- version: 4.1.11(@types/node@22.20.0)(@vitest/coverage-v8@4.0.18)(jsdom@28.0.0)(vite@8.1.6(@types/node@22.20.0)(esbuild@0.25.12)(jiti@2.6.1)(terser@5.51.2)(yaml@2.8.2))
+ version: 4.1.11(@types/node@22.20.0)(@vitest/coverage-v8@4.0.18)(jsdom@28.0.0)(vite@8.1.6(@types/node@22.20.0)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.51.2)(yaml@2.8.2))
wait-on:
specifier: ^8.0.3
version: 8.0.5
@@ -300,7 +300,7 @@ importers:
dependencies:
'@rsbuild/plugin-mdx':
specifier: ^1.1.1
- version: 1.1.1(@rsbuild/core@1.7.3)(webpack@5.105.1)
+ version: 1.1.1(@rsbuild/core@1.7.3)(webpack@5.105.1(esbuild@0.27.7))
react:
specifier: ^19.1.0
version: 19.2.3
@@ -329,6 +329,12 @@ importers:
'@rsbuild/plugin-type-check':
specifier: ^1.1.1
version: 1.3.3(@rsbuild/core@1.7.3)(@rspack/core@1.7.6(@swc/helpers@0.5.18))(tslib@2.8.1)(typescript@5.8.3)
+ '@testing-library/jest-dom':
+ specifier: ^6.9.1
+ version: 6.9.1
+ '@testing-library/react':
+ specifier: ^16.3.1
+ version: 16.3.2(@testing-library/dom@10.4.0)(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
'@types/node':
specifier: '*'
version: 22.20.0
@@ -353,6 +359,9 @@ importers:
typescript:
specifier: ^5.8.2
version: 5.8.3
+ vitest:
+ specifier: ^4.1.11
+ version: 4.1.11(@types/node@22.20.0)(@vitest/coverage-v8@4.0.18)(jsdom@28.0.0)(vite@8.1.6(@types/node@22.20.0)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.51.2)(yaml@2.8.2))
packages/stl:
dependencies:
@@ -444,7 +453,7 @@ importers:
version: 7.0.1
storybook-react-rsbuild:
specifier: ^1.0.3
- version: 1.0.3(@rsbuild/core@1.7.3)(@storybook/test@8.6.15(storybook@8.6.17(prettier@3.9.10)))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(rollup@4.60.1)(storybook@8.6.17(prettier@3.9.10))(typescript@5.8.3)(webpack@5.105.1(esbuild@0.27.7))
+ version: 1.0.3(@rsbuild/core@1.7.3)(@storybook/test@8.6.15(storybook@8.6.17(prettier@3.9.10)))(@types/react@19.2.9)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(rollup@4.60.1)(storybook@8.6.17(prettier@3.9.10))(typescript@5.8.3)(webpack@5.105.1(esbuild@0.27.7))
typescript:
specifier: ^5.8.3
version: 5.8.3
@@ -523,7 +532,7 @@ importers:
version: 8.6.15(storybook@8.6.17(prettier@3.9.10))
'@storybook/addon-coverage':
specifier: ^3.0.0
- version: 3.0.0(vite@8.1.6(@types/node@25.6.2)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.51.2)(yaml@2.8.2))
+ version: 3.0.0(vite@8.1.6(@types/node@22.20.0)(esbuild@0.25.12)(jiti@2.6.1)(terser@5.51.2)(yaml@2.8.2))
'@storybook/addon-essentials':
specifier: ^8.6.15
version: 8.6.15(@types/react@19.2.9)(storybook@8.6.17(prettier@3.9.10))
@@ -541,7 +550,7 @@ importers:
version: 8.6.15(storybook@8.6.17(prettier@3.9.10))
'@storybook/test-runner':
specifier: ^0.19.1
- version: 0.19.1(@swc/helpers@0.5.18)(@types/node@25.6.2)(storybook@8.6.17(prettier@3.9.10))(ts-node@10.9.2(@swc/core@1.15.10(@swc/helpers@0.5.18))(@types/node@25.6.2)(typescript@5.8.3))
+ version: 0.19.1(@swc/helpers@0.5.18)(@types/node@22.20.0)(storybook@8.6.17(prettier@3.9.10))(ts-node@10.9.2(@swc/core@1.15.10(@swc/helpers@0.5.18))(@types/node@22.20.0)(typescript@5.8.3))
'@tailwindcss/postcss':
specifier: ^4.1.18
version: 4.1.18
@@ -589,13 +598,13 @@ importers:
version: 8.6.17(prettier@3.9.10)
storybook-react-rsbuild:
specifier: ^1.0.3
- version: 1.0.3(@rsbuild/core@1.7.3)(@storybook/test@8.6.15(storybook@8.6.17(prettier@3.9.10)))(@types/react@19.2.9)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(rollup@4.60.1)(storybook@8.6.17(prettier@3.9.10))(typescript@5.8.3)(webpack@5.105.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.27.7)(postcss@8.5.31))
+ version: 1.0.3(@rsbuild/core@1.7.3)(@storybook/test@8.6.15(storybook@8.6.17(prettier@3.9.10)))(@types/react@19.2.9)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(rollup@4.60.1)(storybook@8.6.17(prettier@3.9.10))(typescript@5.8.3)(webpack@5.105.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.12)(postcss@8.5.31))
typescript:
specifier: ^5.8.3
version: 5.8.3
vitest:
specifier: ^4.1.11
- version: 4.1.11(@types/node@25.6.2)(@vitest/coverage-v8@4.0.18)(jsdom@28.0.0)(vite@8.1.6(@types/node@25.6.2)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.51.2)(yaml@2.8.2))
+ version: 4.1.11(@types/node@22.20.0)(@vitest/coverage-v8@4.0.18)(jsdom@28.0.0)(vite@8.1.6(@types/node@22.20.0)(esbuild@0.25.12)(jiti@2.6.1)(terser@5.51.2)(yaml@2.8.2))
wait-on:
specifier: ^8.0.3
version: 8.0.5
@@ -10259,41 +10268,6 @@ snapshots:
- supports-color
- ts-node
- '@jest/core@29.7.0(ts-node@10.9.2(@swc/core@1.15.10(@swc/helpers@0.5.18))(@types/node@25.6.2)(typescript@5.8.3))':
- dependencies:
- '@jest/console': 29.7.0
- '@jest/reporters': 29.7.0
- '@jest/test-result': 29.7.0
- '@jest/transform': 29.7.0
- '@jest/types': 29.6.3
- '@types/node': 22.20.0
- ansi-escapes: 4.3.2
- chalk: 4.1.2
- ci-info: 3.9.0
- exit: 0.1.2
- graceful-fs: 4.2.11
- jest-changed-files: 29.7.0
- jest-config: 29.7.0(@types/node@22.20.0)(ts-node@10.9.2(@swc/core@1.15.10(@swc/helpers@0.5.18))(@types/node@25.6.2)(typescript@5.8.3))
- jest-haste-map: 29.7.0
- jest-message-util: 29.7.0
- jest-regex-util: 29.6.3
- jest-resolve: 29.7.0
- jest-resolve-dependencies: 29.7.0
- jest-runner: 29.7.0
- jest-runtime: 29.7.0
- jest-snapshot: 29.7.0
- jest-util: 29.7.0
- jest-validate: 29.7.0
- jest-watcher: 29.7.0
- micromatch: 4.0.8
- pretty-format: 29.7.0
- slash: 3.0.0
- strip-ansi: 6.0.1
- transitivePeerDependencies:
- - babel-plugin-macros
- - supports-color
- - ts-node
-
'@jest/create-cache-key-function@30.2.0':
dependencies:
'@jest/types': 30.2.0
@@ -10628,12 +10602,12 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@mdx-js/loader@3.1.1(webpack@5.105.1)':
+ '@mdx-js/loader@3.1.1(webpack@5.105.1(esbuild@0.27.7))':
dependencies:
'@mdx-js/mdx': 3.1.1
source-map: 0.7.6
optionalDependencies:
- webpack: 5.105.1
+ webpack: 5.105.1(esbuild@0.27.7)
transitivePeerDependencies:
- supports-color
@@ -12563,9 +12537,9 @@ snapshots:
- supports-color
- webpack
- '@rsbuild/plugin-mdx@1.1.1(@rsbuild/core@1.7.3)(webpack@5.105.1)':
+ '@rsbuild/plugin-mdx@1.1.1(@rsbuild/core@1.7.3)(webpack@5.105.1(esbuild@0.27.7))':
dependencies:
- '@mdx-js/loader': 3.1.1(webpack@5.105.1)
+ '@mdx-js/loader': 3.1.1(webpack@5.105.1(esbuild@0.27.7))
optionalDependencies:
'@rsbuild/core': 1.7.3
transitivePeerDependencies:
@@ -13083,6 +13057,20 @@ snapshots:
- supports-color
- vite
+ '@storybook/addon-coverage@3.0.0(vite@8.1.6(@types/node@22.20.0)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.51.2)(yaml@2.8.2))':
+ dependencies:
+ '@istanbuljs/load-nyc-config': 1.1.0
+ '@jsdevtools/coverage-istanbul-loader': 3.0.5
+ '@types/istanbul-lib-coverage': 2.0.6
+ convert-source-map: 2.0.0
+ espree: 9.6.1
+ istanbul-lib-instrument: 6.0.3
+ test-exclude: 6.0.0
+ vite-plugin-istanbul: 6.0.2(vite@8.1.6(@types/node@22.20.0)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.51.2)(yaml@2.8.2))
+ transitivePeerDependencies:
+ - supports-color
+ - vite
+
'@storybook/addon-coverage@3.0.0(vite@8.1.6(@types/node@25.0.10)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.51.2)(yaml@2.8.2))':
dependencies:
'@istanbuljs/load-nyc-config': 1.1.0
@@ -13408,7 +13396,7 @@ snapshots:
dependencies:
storybook: 8.6.17(prettier@3.9.10)
- '@storybook/react-docgen-typescript-plugin@1.0.1(typescript@5.8.3)(webpack@5.105.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.12))':
+ '@storybook/react-docgen-typescript-plugin@1.0.1(typescript@5.8.3)(webpack@5.105.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.12)(postcss@8.5.31))':
dependencies:
debug: 4.4.3
endent: 2.1.0
@@ -13418,7 +13406,7 @@ snapshots:
react-docgen-typescript: 2.4.0(typescript@5.8.3)
tslib: 2.8.1
typescript: 5.8.3
- webpack: 5.105.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.12)
+ webpack: 5.105.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.12)(postcss@8.5.31)
transitivePeerDependencies:
- supports-color
@@ -13436,6 +13424,20 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ '@storybook/react-docgen-typescript-plugin@1.0.1(typescript@5.8.3)(webpack@5.105.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.27.7))':
+ dependencies:
+ debug: 4.4.3
+ endent: 2.1.0
+ find-cache-dir: 3.3.2
+ flat-cache: 3.2.0
+ micromatch: 4.0.8
+ react-docgen-typescript: 2.4.0(typescript@5.8.3)
+ tslib: 2.8.1
+ typescript: 5.8.3
+ webpack: 5.105.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.27.7)
+ transitivePeerDependencies:
+ - supports-color
+
'@storybook/react-docgen-typescript-plugin@1.0.1(typescript@5.8.3)(webpack@5.105.1(esbuild@0.27.7))':
dependencies:
debug: 4.4.3
@@ -13604,40 +13606,6 @@ snapshots:
- supports-color
- ts-node
- '@storybook/test-runner@0.19.1(@swc/helpers@0.5.18)(@types/node@25.6.2)(storybook@8.6.17(prettier@3.9.10))(ts-node@10.9.2(@swc/core@1.15.10(@swc/helpers@0.5.18))(@types/node@25.6.2)(typescript@5.8.3))':
- dependencies:
- '@babel/core': 7.28.6
- '@babel/generator': 7.28.6
- '@babel/template': 7.28.6
- '@babel/types': 7.28.6
- '@jest/types': 29.6.3
- '@storybook/core-common': 8.6.14(storybook@8.6.17(prettier@3.9.10))
- '@storybook/csf': 0.1.13
- '@storybook/csf-tools': 8.6.14(storybook@8.6.17(prettier@3.9.10))
- '@storybook/preview-api': 8.6.15(storybook@8.6.17(prettier@3.9.10))
- '@swc/core': 1.15.10(@swc/helpers@0.5.18)
- '@swc/jest': 0.2.39(@swc/core@1.15.10(@swc/helpers@0.5.18))
- expect-playwright: 0.8.0
- jest: 29.7.0(@types/node@25.6.2)(ts-node@10.9.2(@swc/core@1.15.10(@swc/helpers@0.5.18))(@types/node@25.6.2)(typescript@5.8.3))
- jest-circus: 29.7.0
- jest-environment-node: 29.7.0
- jest-junit: 16.0.0
- jest-playwright-preset: 4.0.0(jest-circus@29.7.0)(jest-environment-node@29.7.0)(jest-runner@29.7.0)(jest@29.7.0(@types/node@25.6.2)(ts-node@10.9.2(@swc/core@1.15.10(@swc/helpers@0.5.18))(@types/node@25.6.2)(typescript@5.8.3)))
- jest-runner: 29.7.0
- jest-serializer-html: 7.1.0
- jest-watch-typeahead: 2.2.2(jest@29.7.0(@types/node@25.6.2)(ts-node@10.9.2(@swc/core@1.15.10(@swc/helpers@0.5.18))(@types/node@25.6.2)(typescript@5.8.3)))
- nyc: 15.1.0
- playwright: 1.58.0
- transitivePeerDependencies:
- - '@swc/helpers'
- - '@types/node'
- - babel-plugin-macros
- - debug
- - node-notifier
- - storybook
- - supports-color
- - ts-node
-
'@storybook/test@8.6.15(storybook@8.6.17(prettier@3.5.3))':
dependencies:
'@storybook/global': 5.0.0
@@ -14972,21 +14940,6 @@ snapshots:
- supports-color
- ts-node
- create-jest@29.7.0(@types/node@25.6.2)(ts-node@10.9.2(@swc/core@1.15.10(@swc/helpers@0.5.18))(@types/node@25.6.2)(typescript@5.8.3)):
- dependencies:
- '@jest/types': 29.6.3
- chalk: 4.1.2
- exit: 0.1.2
- graceful-fs: 4.2.11
- jest-config: 29.7.0(@types/node@25.6.2)(ts-node@10.9.2(@swc/core@1.15.10(@swc/helpers@0.5.18))(@types/node@25.6.2)(typescript@5.8.3))
- jest-util: 29.7.0
- prompts: 2.4.2
- transitivePeerDependencies:
- - '@types/node'
- - babel-plugin-macros
- - supports-color
- - ts-node
-
create-require@1.1.1: {}
critters@0.0.25:
@@ -16629,25 +16582,6 @@ snapshots:
- supports-color
- ts-node
- jest-cli@29.7.0(@types/node@25.6.2)(ts-node@10.9.2(@swc/core@1.15.10(@swc/helpers@0.5.18))(@types/node@25.6.2)(typescript@5.8.3)):
- dependencies:
- '@jest/core': 29.7.0(ts-node@10.9.2(@swc/core@1.15.10(@swc/helpers@0.5.18))(@types/node@25.6.2)(typescript@5.8.3))
- '@jest/test-result': 29.7.0
- '@jest/types': 29.6.3
- chalk: 4.1.2
- create-jest: 29.7.0(@types/node@25.6.2)(ts-node@10.9.2(@swc/core@1.15.10(@swc/helpers@0.5.18))(@types/node@25.6.2)(typescript@5.8.3))
- exit: 0.1.2
- import-local: 3.2.0
- jest-config: 29.7.0(@types/node@25.6.2)(ts-node@10.9.2(@swc/core@1.15.10(@swc/helpers@0.5.18))(@types/node@25.6.2)(typescript@5.8.3))
- jest-util: 29.7.0
- jest-validate: 29.7.0
- yargs: 17.7.2
- transitivePeerDependencies:
- - '@types/node'
- - babel-plugin-macros
- - supports-color
- - ts-node
-
jest-config@29.7.0(@types/node@22.20.0)(ts-node@10.9.2(@swc/core@1.15.10(@swc/helpers@0.5.18))(@types/node@22.20.0)(typescript@5.8.3)):
dependencies:
'@babel/core': 7.28.6
@@ -16710,37 +16644,6 @@ snapshots:
- babel-plugin-macros
- supports-color
- jest-config@29.7.0(@types/node@22.20.0)(ts-node@10.9.2(@swc/core@1.15.10(@swc/helpers@0.5.18))(@types/node@25.6.2)(typescript@5.8.3)):
- dependencies:
- '@babel/core': 7.28.6
- '@jest/test-sequencer': 29.7.0
- '@jest/types': 29.6.3
- babel-jest: 29.7.0(@babel/core@7.28.6)
- chalk: 4.1.2
- ci-info: 3.9.0
- deepmerge: 4.3.1
- glob: 7.2.3
- graceful-fs: 4.2.11
- jest-circus: 29.7.0
- jest-environment-node: 29.7.0
- jest-get-type: 29.6.3
- jest-regex-util: 29.6.3
- jest-resolve: 29.7.0
- jest-runner: 29.7.0
- jest-util: 29.7.0
- jest-validate: 29.7.0
- micromatch: 4.0.8
- parse-json: 5.2.0
- pretty-format: 29.7.0
- slash: 3.0.0
- strip-json-comments: 3.1.1
- optionalDependencies:
- '@types/node': 22.20.0
- ts-node: 10.9.2(@swc/core@1.15.10(@swc/helpers@0.5.18))(@types/node@25.6.2)(typescript@5.8.3)
- transitivePeerDependencies:
- - babel-plugin-macros
- - supports-color
-
jest-config@29.7.0(@types/node@25.0.10)(ts-node@10.9.2(@swc/core@1.15.10(@swc/helpers@0.5.18))(@types/node@25.0.10)(typescript@5.8.3)):
dependencies:
'@babel/core': 7.28.6
@@ -16772,37 +16675,6 @@ snapshots:
- babel-plugin-macros
- supports-color
- jest-config@29.7.0(@types/node@25.6.2)(ts-node@10.9.2(@swc/core@1.15.10(@swc/helpers@0.5.18))(@types/node@25.6.2)(typescript@5.8.3)):
- dependencies:
- '@babel/core': 7.28.6
- '@jest/test-sequencer': 29.7.0
- '@jest/types': 29.6.3
- babel-jest: 29.7.0(@babel/core@7.28.6)
- chalk: 4.1.2
- ci-info: 3.9.0
- deepmerge: 4.3.1
- glob: 7.2.3
- graceful-fs: 4.2.11
- jest-circus: 29.7.0
- jest-environment-node: 29.7.0
- jest-get-type: 29.6.3
- jest-regex-util: 29.6.3
- jest-resolve: 29.7.0
- jest-runner: 29.7.0
- jest-util: 29.7.0
- jest-validate: 29.7.0
- micromatch: 4.0.8
- parse-json: 5.2.0
- pretty-format: 29.7.0
- slash: 3.0.0
- strip-json-comments: 3.1.1
- optionalDependencies:
- '@types/node': 25.6.2
- ts-node: 10.9.2(@swc/core@1.15.10(@swc/helpers@0.5.18))(@types/node@25.6.2)(typescript@5.8.3)
- transitivePeerDependencies:
- - babel-plugin-macros
- - supports-color
-
jest-diff@29.7.0:
dependencies:
chalk: 4.1.2
@@ -16918,22 +16790,6 @@ snapshots:
- debug
- supports-color
- jest-playwright-preset@4.0.0(jest-circus@29.7.0)(jest-environment-node@29.7.0)(jest-runner@29.7.0)(jest@29.7.0(@types/node@25.6.2)(ts-node@10.9.2(@swc/core@1.15.10(@swc/helpers@0.5.18))(@types/node@25.6.2)(typescript@5.8.3))):
- dependencies:
- expect-playwright: 0.8.0
- jest: 29.7.0(@types/node@25.6.2)(ts-node@10.9.2(@swc/core@1.15.10(@swc/helpers@0.5.18))(@types/node@25.6.2)(typescript@5.8.3))
- jest-circus: 29.7.0
- jest-environment-node: 29.7.0
- jest-process-manager: 0.4.0
- jest-runner: 29.7.0
- nyc: 15.1.0
- playwright-core: 1.58.0
- rimraf: 3.0.2
- uuid: 8.3.2
- transitivePeerDependencies:
- - debug
- - supports-color
-
jest-pnp-resolver@1.2.3(jest-resolve@29.7.0):
optionalDependencies:
jest-resolve: 29.7.0
@@ -17099,17 +16955,6 @@ snapshots:
string-length: 5.0.1
strip-ansi: 7.1.2
- jest-watch-typeahead@2.2.2(jest@29.7.0(@types/node@25.6.2)(ts-node@10.9.2(@swc/core@1.15.10(@swc/helpers@0.5.18))(@types/node@25.6.2)(typescript@5.8.3))):
- dependencies:
- ansi-escapes: 6.2.1
- chalk: 5.6.2
- jest: 29.7.0(@types/node@25.6.2)(ts-node@10.9.2(@swc/core@1.15.10(@swc/helpers@0.5.18))(@types/node@25.6.2)(typescript@5.8.3))
- jest-regex-util: 29.6.3
- jest-watcher: 29.7.0
- slash: 5.1.0
- string-length: 5.0.1
- strip-ansi: 7.1.2
-
jest-watcher@29.7.0:
dependencies:
'@jest/test-result': 29.7.0
@@ -17158,18 +17003,6 @@ snapshots:
- supports-color
- ts-node
- jest@29.7.0(@types/node@25.6.2)(ts-node@10.9.2(@swc/core@1.15.10(@swc/helpers@0.5.18))(@types/node@25.6.2)(typescript@5.8.3)):
- dependencies:
- '@jest/core': 29.7.0(ts-node@10.9.2(@swc/core@1.15.10(@swc/helpers@0.5.18))(@types/node@25.6.2)(typescript@5.8.3))
- '@jest/types': 29.6.3
- import-local: 3.2.0
- jest-cli: 29.7.0(@types/node@25.6.2)(ts-node@10.9.2(@swc/core@1.15.10(@swc/helpers@0.5.18))(@types/node@25.6.2)(typescript@5.8.3))
- transitivePeerDependencies:
- - '@types/node'
- - babel-plugin-macros
- - supports-color
- - ts-node
-
jiti@2.6.1: {}
jju@1.4.0:
@@ -18765,14 +18598,14 @@ snapshots:
ajv: 6.12.6
ajv-keywords: 3.5.2(ajv@6.12.6)
- schema-utils@4.8.1(webpack@5.105.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.12)):
+ schema-utils@4.8.1(webpack@5.105.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.12)(postcss@8.5.31)):
dependencies:
'@types/json-schema': 7.0.15
ajv: 8.20.0
ajv-formats: 3.0.1(ajv@8.20.0)
ajv-keywords: 5.1.0(ajv@8.20.0)
optionalDependencies:
- webpack: 5.105.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.12)
+ webpack: 5.105.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.12)(postcss@8.5.31)
schema-utils@4.8.1(webpack@5.105.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.27.7)(postcss@8.5.31)):
dependencies:
@@ -18783,6 +18616,15 @@ snapshots:
optionalDependencies:
webpack: 5.105.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.27.7)(postcss@8.5.31)
+ schema-utils@4.8.1(webpack@5.105.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.27.7)):
+ dependencies:
+ '@types/json-schema': 7.0.15
+ ajv: 8.20.0
+ ajv-formats: 3.0.1(ajv@8.20.0)
+ ajv-keywords: 5.1.0(ajv@8.20.0)
+ optionalDependencies:
+ webpack: 5.105.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.27.7)
+
schema-utils@4.8.1(webpack@5.105.1(esbuild@0.27.7)):
dependencies:
'@types/json-schema': 7.0.15
@@ -18792,16 +18634,6 @@ snapshots:
optionalDependencies:
webpack: 5.105.1(esbuild@0.27.7)
- schema-utils@4.8.1(webpack@5.105.1):
- dependencies:
- '@types/json-schema': 7.0.15
- ajv: 8.20.0
- ajv-formats: 3.0.1(ajv@8.20.0)
- ajv-keywords: 5.1.0(ajv@8.20.0)
- optionalDependencies:
- webpack: 5.105.1
- optional: true
-
secure-compare@3.0.1: {}
semver@6.3.1: {}
@@ -19120,12 +18952,12 @@ snapshots:
- tslib
- webpack
- storybook-react-rsbuild@1.0.3(@rsbuild/core@1.7.3)(@storybook/test@8.6.15(storybook@8.6.17(prettier@3.9.10)))(@types/react@19.2.9)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(rollup@4.60.1)(storybook@8.6.17(prettier@3.9.10))(typescript@5.8.3)(webpack@5.105.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.12)):
+ storybook-react-rsbuild@1.0.3(@rsbuild/core@1.7.3)(@storybook/test@8.6.15(storybook@8.6.17(prettier@3.9.10)))(@types/react@19.2.9)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(rollup@4.60.1)(storybook@8.6.17(prettier@3.9.10))(typescript@5.8.3)(webpack@5.105.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.12)(postcss@8.5.31)):
dependencies:
'@rollup/pluginutils': 5.3.0(rollup@4.60.1)
'@rsbuild/core': 1.7.3
'@storybook/react': 8.6.15(@storybook/test@8.6.15(storybook@8.6.17(prettier@3.9.10)))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(storybook@8.6.17(prettier@3.9.10))(typescript@5.8.3)
- '@storybook/react-docgen-typescript-plugin': 1.0.1(typescript@5.8.3)(webpack@5.105.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.12))
+ '@storybook/react-docgen-typescript-plugin': 1.0.1(typescript@5.8.3)(webpack@5.105.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.12)(postcss@8.5.31))
'@types/node': 18.19.130
find-up: 5.0.0
magic-string: 0.30.21
@@ -19148,12 +18980,12 @@ snapshots:
- tslib
- webpack
- storybook-react-rsbuild@1.0.3(@rsbuild/core@1.7.3)(@storybook/test@8.6.15(storybook@8.6.17(prettier@3.9.10)))(@types/react@19.2.9)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(rollup@4.60.1)(storybook@8.6.17(prettier@3.9.10))(typescript@5.8.3)(webpack@5.105.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.27.7)(postcss@8.5.31)):
+ storybook-react-rsbuild@1.0.3(@rsbuild/core@1.7.3)(@storybook/test@8.6.15(storybook@8.6.17(prettier@3.9.10)))(@types/react@19.2.9)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(rollup@4.60.1)(storybook@8.6.17(prettier@3.9.10))(typescript@5.8.3)(webpack@5.105.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.27.7)):
dependencies:
'@rollup/pluginutils': 5.3.0(rollup@4.60.1)
'@rsbuild/core': 1.7.3
'@storybook/react': 8.6.15(@storybook/test@8.6.15(storybook@8.6.17(prettier@3.9.10)))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(storybook@8.6.17(prettier@3.9.10))(typescript@5.8.3)
- '@storybook/react-docgen-typescript-plugin': 1.0.1(typescript@5.8.3)(webpack@5.105.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.27.7)(postcss@8.5.31))
+ '@storybook/react-docgen-typescript-plugin': 1.0.1(typescript@5.8.3)(webpack@5.105.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.27.7))
'@types/node': 18.19.130
find-up: 5.0.0
magic-string: 0.30.21
@@ -19176,7 +19008,7 @@ snapshots:
- tslib
- webpack
- storybook-react-rsbuild@1.0.3(@rsbuild/core@1.7.3)(@storybook/test@8.6.15(storybook@8.6.17(prettier@3.9.10)))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(rollup@4.60.1)(storybook@8.6.17(prettier@3.9.10))(typescript@5.8.3)(webpack@5.105.1(esbuild@0.27.7)):
+ storybook-react-rsbuild@1.0.3(@rsbuild/core@1.7.3)(@storybook/test@8.6.15(storybook@8.6.17(prettier@3.9.10)))(@types/react@19.2.9)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(rollup@4.60.1)(storybook@8.6.17(prettier@3.9.10))(typescript@5.8.3)(webpack@5.105.1(esbuild@0.27.7)):
dependencies:
'@rollup/pluginutils': 5.3.0(rollup@4.60.1)
'@rsbuild/core': 1.7.3
@@ -19373,16 +19205,17 @@ snapshots:
tapable@2.3.3: {}
- terser-webpack-plugin@5.6.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.12)(webpack@5.105.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.12)):
+ terser-webpack-plugin@5.6.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.12)(postcss@8.5.31)(webpack@5.105.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.12)(postcss@8.5.31)):
dependencies:
'@jridgewell/trace-mapping': 0.3.31
jest-worker: 27.5.1
- schema-utils: 4.8.1(webpack@5.105.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.12))
+ schema-utils: 4.8.1(webpack@5.105.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.12)(postcss@8.5.31))
terser: 5.51.2
- webpack: 5.105.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.12)
+ webpack: 5.105.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.12)(postcss@8.5.31)
optionalDependencies:
'@swc/core': 1.15.10(@swc/helpers@0.5.18)
esbuild: 0.25.12
+ postcss: 8.5.31
terser-webpack-plugin@5.6.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.27.7)(postcss@8.5.31)(webpack@5.105.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.27.7)(postcss@8.5.31)):
dependencies:
@@ -19396,6 +19229,17 @@ snapshots:
esbuild: 0.27.7
postcss: 8.5.31
+ terser-webpack-plugin@5.6.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.27.7)(webpack@5.105.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.27.7)):
+ dependencies:
+ '@jridgewell/trace-mapping': 0.3.31
+ jest-worker: 27.5.1
+ schema-utils: 4.8.1(webpack@5.105.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.27.7))
+ terser: 5.51.2
+ webpack: 5.105.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.27.7)
+ optionalDependencies:
+ '@swc/core': 1.15.10(@swc/helpers@0.5.18)
+ esbuild: 0.27.7
+
terser-webpack-plugin@5.6.1(esbuild@0.27.7)(webpack@5.105.1(esbuild@0.27.7)):
dependencies:
'@jridgewell/trace-mapping': 0.3.31
@@ -19406,15 +19250,6 @@ snapshots:
optionalDependencies:
esbuild: 0.27.7
- terser-webpack-plugin@5.6.1(webpack@5.105.1):
- dependencies:
- '@jridgewell/trace-mapping': 0.3.31
- jest-worker: 27.5.1
- schema-utils: 4.8.1(webpack@5.105.1)
- terser: 5.51.2
- webpack: 5.105.1
- optional: true
-
terser@5.46.0:
dependencies:
'@jridgewell/source-map': 0.3.11
@@ -19574,27 +19409,6 @@ snapshots:
optionalDependencies:
'@swc/core': 1.15.10(@swc/helpers@0.5.18)
- ts-node@10.9.2(@swc/core@1.15.10(@swc/helpers@0.5.18))(@types/node@25.6.2)(typescript@5.8.3):
- dependencies:
- '@cspotcode/source-map-support': 0.8.1
- '@tsconfig/node10': 1.0.12
- '@tsconfig/node12': 1.0.11
- '@tsconfig/node14': 1.0.3
- '@tsconfig/node16': 1.0.4
- '@types/node': 25.6.2
- acorn: 8.15.0
- acorn-walk: 8.3.4
- arg: 4.1.3
- create-require: 1.1.1
- diff: 4.0.4
- make-error: 1.3.6
- typescript: 5.8.3
- v8-compile-cache-lib: 3.0.1
- yn: 3.1.1
- optionalDependencies:
- '@swc/core': 1.15.10(@swc/helpers@0.5.18)
- optional: true
-
tsconfck@3.1.6(typescript@5.8.3):
optionalDependencies:
typescript: 5.8.3
@@ -19849,6 +19663,18 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ vite-plugin-istanbul@6.0.2(vite@8.1.6(@types/node@22.20.0)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.51.2)(yaml@2.8.2)):
+ dependencies:
+ '@istanbuljs/load-nyc-config': 1.1.0
+ espree: 10.4.0
+ istanbul-lib-instrument: 6.0.3
+ picocolors: 1.1.1
+ source-map: 0.7.6
+ test-exclude: 6.0.0
+ vite: 8.1.6(@types/node@22.20.0)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.51.2)(yaml@2.8.2)
+ transitivePeerDependencies:
+ - supports-color
+
vite-plugin-istanbul@6.0.2(vite@8.1.6(@types/node@25.0.10)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.51.2)(yaml@2.8.2)):
dependencies:
'@istanbuljs/load-nyc-config': 1.1.0
@@ -19972,7 +19798,7 @@ snapshots:
transitivePeerDependencies:
- msw
- vitest@4.1.11(@types/node@22.20.0)(jsdom@28.0.0)(vite@8.1.6(@types/node@22.20.0)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.51.2)(yaml@2.8.2)):
+ vitest@4.1.11(@types/node@22.20.0)(@vitest/coverage-v8@4.0.18)(jsdom@28.0.0)(vite@8.1.6(@types/node@22.20.0)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.51.2)(yaml@2.8.2)):
dependencies:
'@vitest/expect': 4.1.11
'@vitest/mocker': 4.1.11(vite@8.1.6(@types/node@22.20.0)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.51.2)(yaml@2.8.2))
@@ -19996,6 +19822,7 @@ snapshots:
why-is-node-running: 2.3.0
optionalDependencies:
'@types/node': 22.20.0
+ '@vitest/coverage-v8': 4.0.18(vitest@4.1.11)
jsdom: 28.0.0
transitivePeerDependencies:
- msw
@@ -20104,7 +19931,7 @@ snapshots:
webpack-virtual-modules@0.6.2: {}
- webpack@5.105.1:
+ webpack@5.105.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.12)(postcss@8.5.31):
dependencies:
'@types/eslint-scope': 3.7.7
'@types/estree': 1.0.9
@@ -20126,9 +19953,9 @@ snapshots:
loader-runner: 4.3.2
mime-types: 2.1.35
neo-async: 2.6.2
- schema-utils: 4.8.1(webpack@5.105.1)
+ schema-utils: 4.8.1(webpack@5.105.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.12)(postcss@8.5.31))
tapable: 2.3.3
- terser-webpack-plugin: 5.6.1(webpack@5.105.1)
+ terser-webpack-plugin: 5.6.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.12)(postcss@8.5.31)(webpack@5.105.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.12)(postcss@8.5.31))
watchpack: 2.6.3
webpack-sources: 3.6.0
transitivePeerDependencies:
@@ -20144,9 +19971,8 @@ snapshots:
- lightningcss
- postcss
- uglify-js
- optional: true
- webpack@5.105.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.12):
+ webpack@5.105.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.27.7):
dependencies:
'@types/eslint-scope': 3.7.7
'@types/estree': 1.0.9
@@ -20168,9 +19994,9 @@ snapshots:
loader-runner: 4.3.2
mime-types: 2.1.35
neo-async: 2.6.2
- schema-utils: 4.8.1(webpack@5.105.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.12))
+ schema-utils: 4.8.1(webpack@5.105.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.27.7))
tapable: 2.3.3
- terser-webpack-plugin: 5.6.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.12)(webpack@5.105.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.12))
+ terser-webpack-plugin: 5.6.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.27.7)(webpack@5.105.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.27.7))
watchpack: 2.6.3
webpack-sources: 3.6.0
transitivePeerDependencies:
diff --git a/services/docs/.config/cli.ts b/services/docs/.config/cli.ts
index 7fe9889..542de72 100644
--- a/services/docs/.config/cli.ts
+++ b/services/docs/.config/cli.ts
@@ -10,6 +10,7 @@ export default defineCli({
sourceFilesPattern: 'content/**/*.{md,html}',
urlPrefixes: ['/static/', '/images/'],
bucket: env('COM_ERMNVLDMR_STATIC_S3_BUCKET'),
+ publicUrl: env('COM_ERMNVLDMR_STATIC_PUBLIC_URL', 'https://static.ermnvldmr.com'),
region: env('COM_ERMNVLDMR_STATIC_S3_REGION'),
endpoint: env('COM_ERMNVLDMR_STATIC_S3_ENDPOINT_URL'),
accessKeyId: env('COM_ERMNVLDMR_STATIC_AWS_ACCESS_KEY_ID'),
diff --git a/services/www/.config/cli.ts b/services/www/.config/cli.ts
index e839d4d..ba150de 100644
--- a/services/www/.config/cli.ts
+++ b/services/www/.config/cli.ts
@@ -7,6 +7,7 @@ export default defineCli({
description: 'Sync static assets to S3',
targetPrefix: 'www',
bucket: env('COM_ERMNVLDMR_STATIC_S3_BUCKET'),
+ publicUrl: env('COM_ERMNVLDMR_STATIC_PUBLIC_URL', 'https://static.ermnvldmr.com'),
region: env('COM_ERMNVLDMR_STATIC_S3_REGION'),
endpoint: env('COM_ERMNVLDMR_STATIC_S3_ENDPOINT_URL'),
accessKeyId: env('COM_ERMNVLDMR_STATIC_AWS_ACCESS_KEY_ID'),
diff --git a/services/www/package.json b/services/www/package.json
index 1d722bb..fb3d6ed 100644
--- a/services/www/package.json
+++ b/services/www/package.json
@@ -7,7 +7,9 @@
"cli": "ermnvldmr-cli",
"sync:static": "pnpm cli sync:static",
"dev": "rsbuild dev",
- "build": "rsbuild build",
+ "dev:en": "LOCALE=en rsbuild dev",
+ "dev:ru": "LOCALE=ru rsbuild dev",
+ "build": "pnpm run build:all",
"build:en": "LOCALE=en rsbuild build",
"build:ru": "LOCALE=ru rsbuild build",
"build:all": "pnpm run build:en && pnpm run build:ru",