diff --git a/.changeset/calm-routes-transform.md b/.changeset/calm-routes-transform.md new file mode 100644 index 00000000..03617123 --- /dev/null +++ b/.changeset/calm-routes-transform.md @@ -0,0 +1,5 @@ +--- +'rsbuild-plugin-react-router': minor +--- + +Keep route transforms inline by default and expose Rspack parallel loader workers as an explicit opt-in. diff --git a/README.md b/README.md index 51f43430..d83d620d 100644 --- a/README.md +++ b/README.md @@ -77,16 +77,16 @@ pluginReactRouter({ }); ``` -| Option | Default | Description | -| -------------------------------- | ----------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `customServer` | `false` | Disables the built-in development SSR middleware. Enable this when an app owns the server with `createDevServer()` or an adapter. | -| `serverOutput` | Derived | Emitted Rsbuild server format: `'module'` or `'commonjs'`. When omitted, React Router's `serverModuleFormat` selects the format (`'esm'` -> `'module'`, `'cjs'` -> `'commonjs'`); setting `serverOutput` overrides it. | -| `lazyCompilation` | `true` | Optional Rsbuild dev lazy-compilation config. When enabled here or through `dev.lazyCompilation`, React Router hydration-critical modules stay eager so the browser manifest and route modules are not replaced by lazy proxies. | -| `unstableLazyCompilationPrewarm` | `false` | Experimental prewarm for emitted Rspack lazy-compilation proxy modules after dev compiles. Enable with `true` when route JS proxy startup should happen shortly after compiler readiness. | -| `logPerformance` | `false` | Logs structured React Router plugin timing information through the Rsbuild logger. | -| `parallelRouteTransform` | `undefined` | Controls worker-thread route transforms. `undefined` auto-enables workers for 256+ routes, `true` forces the default worker count (in dev this is 0 on machines with 4 or fewer cores, where workers cost more than they save; production builds always use workers), a positive integer sets the worker count, and `false` keeps transforms inline. | -| `onRouteTopologyChange` | `undefined` | Notification for programmatic/custom dev servers. Recreate the Rsbuild server when route files are added, removed, or moved. The callback is not awaited. | -| `federation` | `false` | Enables the plugin's experimental Module Federation integration. | +| Option | Default | Description | +| -------------------------------- | ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `customServer` | `false` | Disables the built-in development SSR middleware. Enable this when an app owns the server with `createDevServer()` or an adapter. | +| `serverOutput` | Derived | Emitted Rsbuild server format: `'module'` or `'commonjs'`. When omitted, React Router's `serverModuleFormat` selects the format (`'esm'` -> `'module'`, `'cjs'` -> `'commonjs'`); setting `serverOutput` overrides it. | +| `lazyCompilation` | `true` | Optional Rsbuild dev lazy-compilation config. When enabled here or through `dev.lazyCompilation`, React Router hydration-critical modules stay eager so the browser manifest and route modules are not replaced by lazy proxies. | +| `unstableLazyCompilationPrewarm` | `false` | Experimental prewarm for emitted Rspack lazy-compilation proxy modules after dev compiles. Enable with `true` when route JS proxy startup should happen shortly after compiler readiness. | +| `logPerformance` | `false` | Logs structured React Router plugin timing information. | +| `parallelRouteTransform` | `undefined` | Controls worker-thread route transforms. `undefined` and `false` keep transforms inline, `true` uses Rspack's default worker count, and a positive integer sets the maximum worker count. | +| `onRouteTopologyChange` | `undefined` | Notification for programmatic/custom dev servers. Recreate the Rsbuild server when route files are added, removed, or moved. The callback is not awaited. | +| `federation` | `false` | Enables the plugin's experimental Module Federation integration. | When `federation` is enabled, configure the Module Federation plugin with `experiments.asyncStartup: true`. The dev server resolves async server build diff --git a/benchmarks/synthetic-web-bundler-benchmark/package.json b/benchmarks/synthetic-web-bundler-benchmark/package.json index 3e87bcae..55c29d06 100644 --- a/benchmarks/synthetic-web-bundler-benchmark/package.json +++ b/benchmarks/synthetic-web-bundler-benchmark/package.json @@ -37,7 +37,7 @@ "devDependencies": { "@babel/core": "7.29.0", "@react-router/dev": "7.14.0", - "@rsbuild/core": "2.1.0", + "@rsbuild/core": "2.1.5", "@rsbuild/plugin-babel": "2.0.0", "@rsbuild/plugin-react": "2.1.0", "@rsbuild/plugin-svgr": "2.0.4", diff --git a/config/package.json b/config/package.json index 8a53e8c1..2ca96bfa 100644 --- a/config/package.json +++ b/config/package.json @@ -3,8 +3,8 @@ "version": "1.0.1", "private": true, "devDependencies": { - "@rsbuild/core": "2.1.0", - "@rslib/core": "0.22.1", + "@rsbuild/core": "2.1.5", + "@rslib/core": "0.23.2", "@types/node": "^25.0.10", "typescript": "^5.9.3" } diff --git a/examples/client-only/package.json b/examples/client-only/package.json index 8a0712df..4988e505 100644 --- a/examples/client-only/package.json +++ b/examples/client-only/package.json @@ -20,7 +20,7 @@ "devDependencies": { "@playwright/test": "^1.61.1", "@react-router/dev": "^7.13.0", - "@rsbuild/core": "2.1.0", + "@rsbuild/core": "2.1.5", "@rsbuild/plugin-react": "^2.1.0", "@types/node": "^25.0.10", "@types/react": "^19.2.10", diff --git a/examples/cloudflare/package.json b/examples/cloudflare/package.json index 923af6d1..e37c3535 100644 --- a/examples/cloudflare/package.json +++ b/examples/cloudflare/package.json @@ -23,7 +23,7 @@ "@playwright/test": "^1.61.1", "@react-router/cloudflare": "^7.13.0", "@react-router/dev": "^7.13.0", - "@rsbuild/core": "2.1.0", + "@rsbuild/core": "2.1.5", "@rsbuild/plugin-react": "^2.1.0", "@tailwindcss/postcss": "^4.1.18", "@types/node": "^25.0.10", diff --git a/examples/custom-node-server/package.json b/examples/custom-node-server/package.json index d8da362a..4039805d 100644 --- a/examples/custom-node-server/package.json +++ b/examples/custom-node-server/package.json @@ -30,7 +30,7 @@ "devDependencies": { "@playwright/test": "^1.61.1", "@react-router/dev": "^7.13.0", - "@rsbuild/core": "2.1.0", + "@rsbuild/core": "2.1.5", "@rsbuild/plugin-react": "^2.1.0", "@rsdoctor/rspack-plugin": "^1.5.13", "@tailwindcss/postcss": "^4.1.18", diff --git a/examples/default-template/package.json b/examples/default-template/package.json index dafe66f5..cbb10e9b 100644 --- a/examples/default-template/package.json +++ b/examples/default-template/package.json @@ -23,7 +23,7 @@ "devDependencies": { "@playwright/test": "^1.61.1", "@react-router/dev": "^7.13.0", - "@rsbuild/core": "2.1.0", + "@rsbuild/core": "2.1.5", "@rsbuild/plugin-less": "^1.6.4", "@rsbuild/plugin-react": "^2.1.0", "@rsbuild/plugin-sass": "^1.5.3", diff --git a/examples/epic-stack/package.json b/examples/epic-stack/package.json index 270e31a1..adaaf6ba 100644 --- a/examples/epic-stack/package.json +++ b/examples/epic-stack/package.json @@ -66,7 +66,7 @@ "@react-router/node": "^7.13.0", "@react-router/remix-routes-option-adapter": "7.13.0", "@remix-run/server-runtime": "2.17.4", - "@rsbuild/core": "2.1.0", + "@rsbuild/core": "2.1.5", "@rsbuild/plugin-react": "2.1.0", "@sentry/node": "10.37.0", "@sentry/profiling-node": "10.37.0", @@ -121,6 +121,8 @@ "@faker-js/faker": "10.2.0", "@playwright/test": "^1.61.1", "@react-router/dev": "^7.13.0", + "@rstest/core": "0.11.0", + "@rstest/coverage-istanbul": "0.11.0", "@sentry/vite-plugin": "4.8.0", "@sly-cli/sly": "2.1.1", "@tailwindcss/nesting": "0.0.0-insiders.565cd3e", @@ -144,8 +146,6 @@ "@types/react-dom": "19.2.3", "@types/set-cookie-parser": "2.4.10", "@types/source-map-support": "0.5.10", - "@rstest/core": "0.8.1", - "@rstest/coverage-istanbul": "0.2.0", "@vitejs/plugin-react": "5.1.2", "autoprefixer": "10.4.23", "enforce-unique": "1.3.0", diff --git a/examples/federation/epic-stack-remote/package.json b/examples/federation/epic-stack-remote/package.json index 19a6fd22..edd4fcbb 100644 --- a/examples/federation/epic-stack-remote/package.json +++ b/examples/federation/epic-stack-remote/package.json @@ -48,8 +48,8 @@ "@epic-web/invariant": "1.0.0", "@epic-web/remember": "1.1.0", "@epic-web/totp": "4.0.1", - "@mjackson/headers": "0.6.1", "@mjackson/form-data-parser": "0.9.1", + "@mjackson/headers": "0.6.1", "@module-federation/enhanced": "2.5.1", "@module-federation/node": "2.7.44", "@module-federation/rsbuild-plugin": "2.5.1", @@ -70,7 +70,7 @@ "@react-router/node": "^7.13.0", "@react-router/remix-routes-option-adapter": "7.13.0", "@remix-run/server-runtime": "2.17.4", - "@rsbuild/core": "2.1.0", + "@rsbuild/core": "2.1.5", "@rsbuild/plugin-react": "2.1.0", "@sentry/node": "10.37.0", "@sentry/profiling-node": "10.37.0", @@ -124,6 +124,8 @@ "@faker-js/faker": "10.2.0", "@playwright/test": "^1.61.1", "@react-router/dev": "^7.13.0", + "@rstest/core": "0.11.0", + "@rstest/coverage-istanbul": "0.11.0", "@sly-cli/sly": "2.1.1", "@tailwindcss/nesting": "0.0.0-insiders.565cd3e", "@tailwindcss/postcss": "^4.1.18", @@ -146,8 +148,6 @@ "@types/react-dom": "19.2.3", "@types/set-cookie-parser": "2.4.10", "@types/source-map-support": "0.5.10", - "@rstest/core": "0.8.1", - "@rstest/coverage-istanbul": "0.2.0", "autoprefixer": "10.4.23", "enforce-unique": "1.3.0", "esbuild": "0.27.2", diff --git a/examples/federation/epic-stack/package.json b/examples/federation/epic-stack/package.json index 8e09e0a1..09a11311 100644 --- a/examples/federation/epic-stack/package.json +++ b/examples/federation/epic-stack/package.json @@ -48,8 +48,8 @@ "@epic-web/invariant": "1.0.0", "@epic-web/remember": "1.1.0", "@epic-web/totp": "4.0.1", - "@mjackson/headers": "0.6.1", "@mjackson/form-data-parser": "0.9.1", + "@mjackson/headers": "0.6.1", "@module-federation/enhanced": "2.5.1", "@module-federation/node": "2.7.44", "@module-federation/rsbuild-plugin": "2.5.1", @@ -70,7 +70,7 @@ "@react-router/node": "^7.13.0", "@react-router/remix-routes-option-adapter": "7.13.0", "@remix-run/server-runtime": "2.17.4", - "@rsbuild/core": "2.1.0", + "@rsbuild/core": "2.1.5", "@rsbuild/plugin-react": "2.1.0", "@sentry/node": "10.37.0", "@sentry/profiling-node": "10.37.0", @@ -124,6 +124,8 @@ "@faker-js/faker": "10.2.0", "@playwright/test": "^1.61.1", "@react-router/dev": "^7.13.0", + "@rstest/core": "0.11.0", + "@rstest/coverage-istanbul": "0.11.0", "@sly-cli/sly": "2.1.1", "@tailwindcss/nesting": "0.0.0-insiders.565cd3e", "@tailwindcss/postcss": "^4.1.18", @@ -146,8 +148,6 @@ "@types/react-dom": "19.2.3", "@types/set-cookie-parser": "2.4.10", "@types/source-map-support": "0.5.10", - "@rstest/core": "0.8.1", - "@rstest/coverage-istanbul": "0.2.0", "autoprefixer": "10.4.23", "enforce-unique": "1.3.0", "esbuild": "0.27.2", diff --git a/examples/prerender/package.json b/examples/prerender/package.json index e9347b8b..27932aa4 100644 --- a/examples/prerender/package.json +++ b/examples/prerender/package.json @@ -21,7 +21,7 @@ "devDependencies": { "@playwright/test": "^1.61.1", "@react-router/dev": "^7.13.0", - "@rsbuild/core": "2.1.0", + "@rsbuild/core": "2.1.5", "@rsbuild/plugin-react": "^2.1.0", "@tailwindcss/postcss": "^4.1.18", "@types/node": "^25.0.10", diff --git a/examples/spa-mode/package.json b/examples/spa-mode/package.json index 38eb796a..5a63f25b 100644 --- a/examples/spa-mode/package.json +++ b/examples/spa-mode/package.json @@ -21,7 +21,7 @@ "devDependencies": { "@playwright/test": "^1.61.1", "@react-router/dev": "^7.13.0", - "@rsbuild/core": "2.1.0", + "@rsbuild/core": "2.1.5", "@rsbuild/plugin-react": "^2.1.0", "@tailwindcss/postcss": "^4.1.18", "@types/node": "^25.0.10", diff --git a/package.json b/package.json index bffd2486..e72d5c14 100644 --- a/package.json +++ b/package.json @@ -74,6 +74,7 @@ "release:local": "pnpm build && changeset version && changeset publish && git add . && git commit -m \"chore: version packages\" && git push && git push --tags" }, "dependencies": { + "@jridgewell/remapping": "^2.3.5", "@react-router/node": "^7.13.0", "@remix-run/node-fetch-server": "^0.13.0", "@rspack/plugin-react-refresh": "^2.0.2", @@ -90,14 +91,15 @@ }, "devDependencies": { "@changesets/cli": "^2.29.8", + "@jridgewell/trace-mapping": "^0.3.31", "@react-router/dev": "^8.0.1", "@rsbuild/config": "workspace:*", - "@rsbuild/core": "2.1.0", + "@rsbuild/core": "2.1.5", "@rsbuild/plugin-react": "2.1.0", - "@rslib/core": "^0.22.1", - "@rspack/core": "2.1.0", - "@rstest/core": "^0.8.1", - "@rstest/coverage-istanbul": "^0.2.0", + "@rslib/core": "^0.23.2", + "@rspack/core": "2.1.3", + "@rstest/core": "^0.11.0", + "@rstest/coverage-istanbul": "^0.11.0", "@swc/helpers": "^0.5.23", "@types/fs-extra": "11.0.4", "@types/jsesc": "^3.0.3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8827fb90..4ee38549 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -11,6 +11,9 @@ importers: .: dependencies: + '@jridgewell/remapping': + specifier: ^2.3.5 + version: 2.3.5 '@react-router/node': specifier: ^7.13.0 version: 7.18.0(react-router@7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3) @@ -19,7 +22,7 @@ importers: version: 0.13.3 '@rspack/plugin-react-refresh': specifier: ^2.0.2 - version: 2.0.2(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(react-refresh@0.18.0) + version: 2.0.2(@rspack/core@2.1.3(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(react-refresh@0.18.0) execa: specifier: ^9.6.1 version: 9.6.1 @@ -54,6 +57,9 @@ importers: '@changesets/cli': specifier: ^2.29.8 version: 2.31.0(@types/node@25.0.10) + '@jridgewell/trace-mapping': + specifier: ^0.3.31 + version: 0.3.31 '@react-router/dev': specifier: ^8.0.1 version: 8.0.1(react-router@7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3)(vite@7.3.1(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0))(wrangler@4.105.0(@cloudflare/workers-types@4.20260628.1)) @@ -61,23 +67,23 @@ importers: specifier: workspace:* version: link:config '@rsbuild/core': - specifier: 2.1.0 - version: 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0) + specifier: 2.1.5 + version: 2.1.5(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0) '@rsbuild/plugin-react': specifier: 2.1.0 - version: 2.1.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23)) + version: 2.1.0(@rsbuild/core@2.1.5(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.3(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23)) '@rslib/core': - specifier: ^0.22.1 - version: 0.22.1(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0)(typescript@5.9.3) + specifier: ^0.23.2 + version: 0.23.2(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0)(typescript@5.9.3) '@rspack/core': - specifier: 2.1.0 - version: 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23) + specifier: 2.1.3 + version: 2.1.3(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23) '@rstest/core': - specifier: ^0.8.1 - version: 0.8.1(jsdom@27.4.0(@noble/hashes@2.2.0)) + specifier: ^0.11.0 + version: 0.11.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0)(jsdom@27.4.0(@noble/hashes@2.2.0)) '@rstest/coverage-istanbul': - specifier: ^0.2.0 - version: 0.2.0(@rstest/core@0.8.1(jsdom@27.4.0(@noble/hashes@2.2.0))) + specifier: ^0.11.0 + version: 0.11.0(@rstest/core@0.11.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0)(jsdom@27.4.0(@noble/hashes@2.2.0))) '@swc/helpers': specifier: ^0.5.23 version: 0.5.23 @@ -191,20 +197,20 @@ importers: specifier: 7.14.0 version: 7.14.0(@react-router/serve@7.14.0(react-router@7.14.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@6.0.1-rc))(@types/node@22.15.31)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(react-router@7.14.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0)(typescript@6.0.1-rc)(vite@7.3.1(@types/node@22.15.31)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0))(wrangler@4.105.0(@cloudflare/workers-types@4.20260628.1)) '@rsbuild/core': - specifier: 2.1.0 - version: 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0) + specifier: 2.1.5 + version: 2.1.5(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0) '@rsbuild/plugin-babel': specifier: 2.0.0 - version: 2.0.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0)) + version: 2.0.0(@rsbuild/core@2.1.5(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0)) '@rsbuild/plugin-react': specifier: 2.1.0 - version: 2.1.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23)) + version: 2.1.0(@rsbuild/core@2.1.5(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.3(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23)) '@rsbuild/plugin-svgr': specifier: 2.0.4 - version: 2.0.4(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(typescript@6.0.1-rc) + version: 2.0.4(@rsbuild/core@2.1.5(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(@rspack/core@2.1.3(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(typescript@6.0.1-rc) '@rsbuild/plugin-tailwindcss': specifier: 2.0.3 - version: 2.0.3(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.1(lightningcss@1.32.0)) + version: 2.0.3(@rsbuild/core@2.1.5(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(@rspack/core@2.1.3(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.1(lightningcss@1.32.0)) '@types/babel__core': specifier: 7.20.5 version: 7.20.5 @@ -233,11 +239,11 @@ importers: config: devDependencies: '@rsbuild/core': - specifier: 2.1.0 - version: 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0) + specifier: 2.1.5 + version: 2.1.5(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0) '@rslib/core': - specifier: 0.22.1 - version: 0.22.1(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0)(typescript@5.9.3) + specifier: 0.23.2 + version: 0.23.2(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0)(typescript@5.9.3) '@types/node': specifier: ^25.0.10 version: 25.0.10 @@ -276,11 +282,11 @@ importers: specifier: ^7.13.0 version: 7.18.0(@react-router/serve@7.18.0(react-router@7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3))(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(react-router@7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0)(typescript@5.9.3)(vite@7.3.1(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0))(wrangler@4.105.0(@cloudflare/workers-types@4.20260628.1)) '@rsbuild/core': - specifier: 2.1.0 - version: 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0) + specifier: 2.1.5 + version: 2.1.5(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0) '@rsbuild/plugin-react': specifier: ^2.1.0 - version: 2.1.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23)) + version: 2.1.0(@rsbuild/core@2.1.5(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.3(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23)) '@types/node': specifier: ^25.0.10 version: 25.0.10 @@ -334,11 +340,11 @@ importers: specifier: ^7.13.0 version: 7.18.0(@react-router/serve@7.18.0(react-router@7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3))(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(react-router@7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0)(typescript@5.9.3)(vite@7.3.1(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0))(wrangler@4.105.0(@cloudflare/workers-types@4.20260628.1)) '@rsbuild/core': - specifier: 2.1.0 - version: 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0) + specifier: 2.1.5 + version: 2.1.5(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0) '@rsbuild/plugin-react': specifier: ^2.1.0 - version: 2.1.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23)) + version: 2.1.0(@rsbuild/core@2.1.5(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.3(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23)) '@tailwindcss/postcss': specifier: ^4.1.18 version: 4.3.1 @@ -395,14 +401,14 @@ importers: specifier: ^7.13.0 version: 7.18.0(@react-router/serve@7.18.0(react-router@7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3))(@types/node@25.9.5)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(react-router@7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0)(typescript@5.9.3)(vite@7.3.1(@types/node@25.9.5)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0))(wrangler@4.105.0(@cloudflare/workers-types@4.20260628.1)) '@rsbuild/core': - specifier: 2.1.0 - version: 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0) + specifier: 2.1.5 + version: 2.1.5(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0) '@rsbuild/plugin-react': specifier: ^2.1.0 - version: 2.1.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23)) + version: 2.1.0(@rsbuild/core@2.1.5(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.3(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23)) '@rsdoctor/rspack-plugin': specifier: ^1.5.13 - version: 1.5.16(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.1(lightningcss@1.32.0)) + version: 1.5.16(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@rsbuild/core@2.1.5(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(@rspack/core@2.1.3(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.1(lightningcss@1.32.0)) '@tailwindcss/postcss': specifier: ^4.1.18 version: 4.3.1 @@ -459,17 +465,17 @@ importers: specifier: ^7.13.0 version: 7.18.0(@react-router/serve@7.18.0(react-router@7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3))(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(react-router@7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0)(typescript@5.9.3)(vite@7.3.1(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0))(wrangler@4.105.0(@cloudflare/workers-types@4.20260628.1)) '@rsbuild/core': - specifier: 2.1.0 - version: 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0) + specifier: 2.1.5 + version: 2.1.5(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0) '@rsbuild/plugin-less': specifier: ^1.6.4 - version: 1.6.4(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.1(lightningcss@1.32.0)) + version: 1.6.4(@rsbuild/core@2.1.5(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(@rspack/core@2.1.3(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.1(lightningcss@1.32.0)) '@rsbuild/plugin-react': specifier: ^2.1.0 - version: 2.1.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23)) + version: 2.1.0(@rsbuild/core@2.1.5(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.3(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23)) '@rsbuild/plugin-sass': specifier: ^1.5.3 - version: 1.5.3(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0)) + version: 1.5.3(@rsbuild/core@2.1.5(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0)) '@tailwindcss/postcss': specifier: ^4.1.18 version: 4.3.1 @@ -588,11 +594,11 @@ importers: specifier: 2.17.4 version: 2.17.4(typescript@5.9.3) '@rsbuild/core': - specifier: 2.1.0 - version: 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0) + specifier: 2.1.5 + version: 2.1.5(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0) '@rsbuild/plugin-react': specifier: 2.1.0 - version: 2.1.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23)) + version: 2.1.0(@rsbuild/core@2.1.5(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.3(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23)) '@sentry/node': specifier: 10.37.0 version: 10.37.0 @@ -748,11 +754,11 @@ importers: specifier: ^7.13.0 version: 7.18.0(@react-router/serve@7.18.0(react-router@7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3))(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(react-router@7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0)(typescript@5.9.3)(vite@7.3.1(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0))(wrangler@4.105.0(@cloudflare/workers-types@4.20260628.1)) '@rstest/core': - specifier: 0.8.1 - version: 0.8.1(jsdom@27.4.0(@noble/hashes@2.2.0)) + specifier: 0.11.0 + version: 0.11.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0)(jsdom@27.4.0(@noble/hashes@2.2.0)) '@rstest/coverage-istanbul': - specifier: 0.2.0 - version: 0.2.0(@rstest/core@0.8.1(jsdom@27.4.0(@noble/hashes@2.2.0))) + specifier: 0.11.0 + version: 0.11.0(@rstest/core@0.11.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0)(jsdom@27.4.0(@noble/hashes@2.2.0))) '@sentry/vite-plugin': specifier: 4.8.0 version: 4.8.0(encoding@0.1.13) @@ -911,13 +917,13 @@ importers: version: 0.6.1 '@module-federation/enhanced': specifier: 2.5.1 - version: 2.5.1(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.3)(webpack@5.108.1(esbuild@0.27.2)(lightningcss@1.32.0)(postcss@8.5.16)) + version: 2.5.1(@rspack/core@2.1.3(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.3)(webpack@5.108.1(esbuild@0.27.2)(lightningcss@1.32.0)(postcss@8.5.16)) '@module-federation/node': specifier: 2.7.44 - version: 2.7.44(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(typescript@5.9.3)(webpack@5.108.1(esbuild@0.27.2)(lightningcss@1.32.0)(postcss@8.5.16)) + version: 2.7.44(@rspack/core@2.1.3(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(typescript@5.9.3)(webpack@5.108.1(esbuild@0.27.2)(lightningcss@1.32.0)(postcss@8.5.16)) '@module-federation/rsbuild-plugin': specifier: 2.5.1 - version: 2.5.1(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.3)(webpack@5.108.1(esbuild@0.27.2)(lightningcss@1.32.0)(postcss@8.5.16)) + version: 2.5.1(@rsbuild/core@2.1.5(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.3(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.3)(webpack@5.108.1(esbuild@0.27.2)(lightningcss@1.32.0)(postcss@8.5.16)) '@nasa-gcn/remix-seo': specifier: 2.0.1 version: 2.0.1(@remix-run/react@2.17.5(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@5.9.3))(@remix-run/server-runtime@2.17.4(typescript@5.9.3)) @@ -970,11 +976,11 @@ importers: specifier: 2.17.4 version: 2.17.4(typescript@5.9.3) '@rsbuild/core': - specifier: 2.1.0 - version: 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0) + specifier: 2.1.5 + version: 2.1.5(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0) '@rsbuild/plugin-react': specifier: 2.1.0 - version: 2.1.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23)) + version: 2.1.0(@rsbuild/core@2.1.5(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.3(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23)) '@sentry/node': specifier: 10.37.0 version: 10.37.0 @@ -1127,11 +1133,11 @@ importers: specifier: ^7.13.0 version: 7.18.0(@react-router/serve@7.18.0(react-router@7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3))(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(react-router@7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0)(typescript@5.9.3)(vite@7.3.1(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0))(wrangler@4.105.0(@cloudflare/workers-types@4.20260628.1)) '@rstest/core': - specifier: 0.8.1 - version: 0.8.1(jsdom@27.4.0(@noble/hashes@2.2.0)) + specifier: 0.11.0 + version: 0.11.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0)(jsdom@27.4.0(@noble/hashes@2.2.0)) '@rstest/coverage-istanbul': - specifier: 0.2.0 - version: 0.2.0(@rstest/core@0.8.1(jsdom@27.4.0(@noble/hashes@2.2.0))) + specifier: 0.11.0 + version: 0.11.0(@rstest/core@0.11.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0)(jsdom@27.4.0(@noble/hashes@2.2.0))) '@sly-cli/sly': specifier: 2.1.1 version: 2.1.1(typescript@5.9.3) @@ -1275,13 +1281,13 @@ importers: version: 0.6.1 '@module-federation/enhanced': specifier: 2.5.1 - version: 2.5.1(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.3)(webpack@5.108.1(esbuild@0.27.2)(lightningcss@1.32.0)(postcss@8.5.16)) + version: 2.5.1(@rspack/core@2.1.3(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.3)(webpack@5.108.1(esbuild@0.27.2)(lightningcss@1.32.0)(postcss@8.5.16)) '@module-federation/node': specifier: 2.7.44 - version: 2.7.44(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(typescript@5.9.3)(webpack@5.108.1(esbuild@0.27.2)(lightningcss@1.32.0)(postcss@8.5.16)) + version: 2.7.44(@rspack/core@2.1.3(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(typescript@5.9.3)(webpack@5.108.1(esbuild@0.27.2)(lightningcss@1.32.0)(postcss@8.5.16)) '@module-federation/rsbuild-plugin': specifier: 2.5.1 - version: 2.5.1(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.3)(webpack@5.108.1(esbuild@0.27.2)(lightningcss@1.32.0)(postcss@8.5.16)) + version: 2.5.1(@rsbuild/core@2.1.5(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.3(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.3)(webpack@5.108.1(esbuild@0.27.2)(lightningcss@1.32.0)(postcss@8.5.16)) '@nasa-gcn/remix-seo': specifier: 2.0.1 version: 2.0.1(@remix-run/react@2.17.5(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@5.9.3))(@remix-run/server-runtime@2.17.4(typescript@5.9.3)) @@ -1334,11 +1340,11 @@ importers: specifier: 2.17.4 version: 2.17.4(typescript@5.9.3) '@rsbuild/core': - specifier: 2.1.0 - version: 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0) + specifier: 2.1.5 + version: 2.1.5(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0) '@rsbuild/plugin-react': specifier: 2.1.0 - version: 2.1.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23)) + version: 2.1.0(@rsbuild/core@2.1.5(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.3(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23)) '@sentry/node': specifier: 10.37.0 version: 10.37.0 @@ -1491,11 +1497,11 @@ importers: specifier: ^7.13.0 version: 7.18.0(@react-router/serve@7.18.0(react-router@7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3))(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(react-router@7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0)(typescript@5.9.3)(vite@7.3.1(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0))(wrangler@4.105.0(@cloudflare/workers-types@4.20260628.1)) '@rstest/core': - specifier: 0.8.1 - version: 0.8.1(jsdom@27.4.0(@noble/hashes@2.2.0)) + specifier: 0.11.0 + version: 0.11.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0)(jsdom@27.4.0(@noble/hashes@2.2.0)) '@rstest/coverage-istanbul': - specifier: 0.2.0 - version: 0.2.0(@rstest/core@0.8.1(jsdom@27.4.0(@noble/hashes@2.2.0))) + specifier: 0.11.0 + version: 0.11.0(@rstest/core@0.11.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0)(jsdom@27.4.0(@noble/hashes@2.2.0))) '@sly-cli/sly': specifier: 2.1.1 version: 2.1.1(typescript@5.9.3) @@ -1639,11 +1645,11 @@ importers: specifier: ^7.13.0 version: 7.18.0(@react-router/serve@7.18.0(react-router@7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3))(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(react-router@7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0)(typescript@5.9.3)(vite@7.3.1(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0))(wrangler@4.105.0(@cloudflare/workers-types@4.20260628.1)) '@rsbuild/core': - specifier: 2.1.0 - version: 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0) + specifier: 2.1.5 + version: 2.1.5(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0) '@rsbuild/plugin-react': specifier: ^2.1.0 - version: 2.1.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23)) + version: 2.1.0(@rsbuild/core@2.1.5(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.3(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23)) '@tailwindcss/postcss': specifier: ^4.1.18 version: 4.3.1 @@ -1718,11 +1724,11 @@ importers: specifier: ^7.13.0 version: 7.18.0(@react-router/serve@7.18.0(react-router@7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3))(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(react-router@7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0)(typescript@5.9.3)(vite@7.3.1(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0))(wrangler@4.105.0(@cloudflare/workers-types@4.20260628.1)) '@rsbuild/core': - specifier: 2.1.0 - version: 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0) + specifier: 2.1.5 + version: 2.1.5(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0) '@rsbuild/plugin-react': specifier: ^2.1.0 - version: 2.1.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23)) + version: 2.1.0(@rsbuild/core@2.1.5(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.3(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23)) '@tailwindcss/postcss': specifier: ^4.1.18 version: 4.3.1 @@ -2924,9 +2930,6 @@ packages: webpack: optional: true - '@module-federation/error-codes@0.22.0': - resolution: {integrity: sha512-xF9SjnEy7vTdx+xekjPCV5cIHOGCkdn3pIxo9vU7gEZMIw0SvAEdsy6Uh17xaCpm8V0FWvR0SZoK9Ik6jGOaug==} - '@module-federation/error-codes@2.5.1': resolution: {integrity: sha512-3KIR8XbEW0Y+Fn8IAnxzDWMvXQWiS40Z1TE/Fft9aTeXP9dDAM7AiVhjTh5yF2csAwHSt/1LJVZbiCmS13mE8A==} @@ -2970,27 +2973,15 @@ packages: vue-tsc: optional: true - '@module-federation/runtime-core@0.22.0': - resolution: {integrity: sha512-GR1TcD6/s7zqItfhC87zAp30PqzvceoeDGYTgF3Vx2TXvsfDrhP6Qw9T4vudDQL3uJRne6t7CzdT29YyVxlgIA==} - '@module-federation/runtime-core@2.5.1': resolution: {integrity: sha512-UMuMsWHXeMrm8Isl8YD6/s1jmTVau3SQhp9RO4Ln+eD2lrjM4hQSwOX2xPtfT1C1I4/E6hgyZQV1K1Q/3Zpr0Q==} - '@module-federation/runtime-tools@0.22.0': - resolution: {integrity: sha512-4ScUJ/aUfEernb+4PbLdhM/c60VHl698Gn1gY21m9vyC1Ucn69fPCA1y2EwcCB7IItseRMoNhdcWQnzt/OPCNA==} - '@module-federation/runtime-tools@2.5.1': resolution: {integrity: sha512-pYUNvaQQBEwP66TLrjmmfkDIrTmPnX0kK86HgClkWLQKkX/oCgnqDxEgNbjeCc75dwUvZP6fW2d0pZ5++XILTw==} - '@module-federation/runtime@0.22.0': - resolution: {integrity: sha512-38g5iPju2tPC3KHMPxRKmy4k4onNp6ypFPS1eKGsNLUkXgHsPMBFqAjDw96iEcjri91BrahG4XcdyKi97xZzlA==} - '@module-federation/runtime@2.5.1': resolution: {integrity: sha512-Tf33FIpnQMn8FjIUAQMtSTYQgGibfh5vEvJihFO3q/hG9LiWwLMErZvOz/+wcPsE81gzHjYPxQgMKGSP3BuG8g==} - '@module-federation/sdk@0.22.0': - resolution: {integrity: sha512-x4aFNBKn2KVQRuNVC5A7SnrSCSqyfIWmm1DvubjbO9iKFe7ith5niw8dqSFBekYBg2Fwy+eMg4sEFNVvCAdo6g==} - '@module-federation/sdk@2.5.1': resolution: {integrity: sha512-FDhCx81ZCxX1oT/fyt/bW+gpPt287GR156E/Thv1yhb9XyNHGNkqe8zqJOipOMfb07E22OMzSzOulCBvAOgn3g==} peerDependencies: @@ -3002,9 +2993,6 @@ packages: '@module-federation/third-party-dts-extractor@2.5.1': resolution: {integrity: sha512-/jD/o2IivDgg6jGUgdb9NZtlJWeoz1uKcblGL2BxYVzzlKT+1YS7Y2idTl1tqp4hNdFnDlPpQv6WLdKiLoOPNw==} - '@module-federation/webpack-bundler-runtime@0.22.0': - resolution: {integrity: sha512-aM8gCqXu+/4wBmJtVeMeeMN5guw3chf+2i6HajKtQv7SJfxV/f4IyNQJUeUQu9HfiAZHjqtMV5Lvq/Lvh8LdyA==} - '@module-federation/webpack-bundler-runtime@2.5.1': resolution: {integrity: sha512-0pUsP9aaWIUcfWUXqax/iSwozngORwf4RK0R1qTOYYC13qx+p4p1Ck28Rz6Tzj/6zpzJgcMQXR7nW4sL+ztaww==} @@ -3012,21 +3000,6 @@ packages: resolution: {integrity: sha512-EFd6cVbHsgLa6wa4RljGj6Wk75qoHxUSyc5asLyyPSyuhIcdS2Q3Phw6ImS1q+CkALthJRShiYfKANcQMuMqsQ==} engines: {node: '>=18'} - '@napi-rs/wasm-runtime@1.0.7': - resolution: {integrity: sha512-SeDnOO0Tk7Okiq6DbXmmBODgOAb9dp9gjlphokTUxmt8U3liIP1ZsozBahH69j/RJv+Rfs6IwUKHTgQYJ/HBAw==} - - '@napi-rs/wasm-runtime@1.1.4': - resolution: {integrity: sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==} - peerDependencies: - '@emnapi/core': ^1.7.1 - '@emnapi/runtime': ^1.7.1 - - '@napi-rs/wasm-runtime@1.1.5': - resolution: {integrity: sha512-AWPoBRJ9tsnVhor4sjO7rkni+7p+2IAEFj6cx06UgP10jkQHqay/36uRV/bFkgrh18D9vb4cr8Q0Pthskgzy+Q==} - peerDependencies: - '@emnapi/core': ^1.7.1 - '@emnapi/runtime': ^1.7.1 - '@napi-rs/wasm-runtime@1.1.6': resolution: {integrity: sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==} peerDependencies: @@ -4473,23 +4446,8 @@ packages: cpu: [x64] os: [win32] - '@rsbuild/core@1.7.2': - resolution: {integrity: sha512-VAFO6cM+cyg2ntxNW6g3tB2Jc5J5mpLjLluvm7VtW2uceNzyUlVv41o66Yp1t1ikxd3ljtqegViXem62JqzveA==} - engines: {node: '>=18.12.0'} - hasBin: true - - '@rsbuild/core@2.0.15': - resolution: {integrity: sha512-O8vmMhZu1YImO6jOqt/K/vlJSvkq7UtSq5YM1DIlcEd9LW8Gf6/dkQ1B2KPI6F+hSMFBnTTTumdcIowSLCw97g==} - engines: {node: ^20.19.0 || >=22.12.0} - hasBin: true - peerDependencies: - core-js: '>= 3.0.0' - peerDependenciesMeta: - core-js: - optional: true - - '@rsbuild/core@2.1.0': - resolution: {integrity: sha512-BNOp22xLGA+L8zvZ12Qg/3zhFhWFZCvZ7OcQRoGLSTw1pB9q/XDLK+WGey9SFPOtsRD3CRPXheXApxwvneH6uA==} + '@rsbuild/core@2.1.5': + resolution: {integrity: sha512-7TW4U1SH7VxQZzSTIOzvwj5lo9uNTmGpsmTXFr4axQAE4giLDKP3kVUA1ZW4P3/Mz4QQJJyvZP29mVcb8kZCfg==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: @@ -4588,196 +4546,83 @@ packages: '@rsdoctor/utils@1.5.16': resolution: {integrity: sha512-aFhv9gzDjJ5qyODO1Uno1NAfl/7WDhmJASJ5lQj1Qo8FV1VgtnYKFbbYdQYSpsowhxgklNISxVs905HdiBp6yg==} - '@rslib/core@0.22.1': - resolution: {integrity: sha512-RaqTITHFkpMDJG9fmD7Hu6FLE64hwctCo46asHOD2DipzQJWawg6K0pFGimTAyutYEZysIUfYgCwSYkbctDudg==} + '@rslib/core@0.23.2': + resolution: {integrity: sha512-KxSp+/y0BJ1O4oKwxX4ydBY7/ZVeJCUpWAmQniCIct8mTxIQFPGO/ibKbKq2IxZYqRuRpM8g+Dtyq0VbEQf9HQ==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: '@microsoft/api-extractor': ^7 - typescript: ^5 || ^6 + typescript: ^5 || ^6 || ^7.0.1-0 peerDependenciesMeta: '@microsoft/api-extractor': optional: true typescript: optional: true - '@rspack/binding-darwin-arm64@1.7.12': - resolution: {integrity: sha512-rbFprJaJiqrmfy8SHth8EsoRS0wg4bXcucwj9NiMzpGFq14Opw8c04iQ6H9BECYzgmN0PKZ9rh41LdVvhdZe4A==} - cpu: [arm64] - os: [darwin] - - '@rspack/binding-darwin-arm64@2.0.8': - resolution: {integrity: sha512-vCgbgH7B7qom+uID+RCZsTCOYFb9wC4/4+1U6rMfytrXGVJ72eNQs2tbdjOl0lb18CT3N/n+VkWynUiLk84GwA==} - cpu: [arm64] - os: [darwin] - - '@rspack/binding-darwin-arm64@2.1.0': - resolution: {integrity: sha512-1DdnXLCl4/7BydtxvFyJbqOyvo3dgeKIdukr5BrM7UUA5rJnpin0qZIq/C0Y+ZwTx7ML4zdYaJeR+WOujRQH1Q==} + '@rspack/binding-darwin-arm64@2.1.3': + resolution: {integrity: sha512-oOGI0RSL89Ehu9T22rugmfUY9OC2eBqLMeWRYsu7bhlUrjoXeVfGBBSEXCse666BQ1sAiM8hD/k7nqVria/okQ==} cpu: [arm64] os: [darwin] - '@rspack/binding-darwin-x64@1.7.12': - resolution: {integrity: sha512-jnOp+/UXOJa9xqUb8KXH03sysoO2e4Ij6tw6MqDdmdj8n/A8PQENRPUbW9AwXpPtVDJPus9r4fi7b3+6e4B8Hg==} - cpu: [x64] - os: [darwin] - - '@rspack/binding-darwin-x64@2.0.8': - resolution: {integrity: sha512-satPm2PD4B7jDTVlVAdvMVdUszwLvWUEnUDzLb77mvVkezKNDZmuhb+e8s+FfKs8hJpNbZ9VAejuA2rr8o985w==} - cpu: [x64] - os: [darwin] - - '@rspack/binding-darwin-x64@2.1.0': - resolution: {integrity: sha512-PJB6n/BaupvfLaErsfvC7q9W07WozkPe2Xw7sQqX6fblK+4tooBp0ZdAtKi76L+U2fR8t8/nQb0Jokco0co7Fg==} + '@rspack/binding-darwin-x64@2.1.3': + resolution: {integrity: sha512-sVqWXNiFTMXAyN362y6IA+eJc8LXZKfHdhEJ/zDuMmRp+u2IvhgaF8tk3vX/OmeB9jydVjySijuiqk8No/FoCA==} cpu: [x64] os: [darwin] - '@rspack/binding-linux-arm64-gnu@1.7.12': - resolution: {integrity: sha512-C8owWG+yvo7X0oVLIXetkoJhIFBP1LYNcAQqtgLmJnQLQDklGuP83dKC+zISGQWpjawHfZ1ER96vLgoTrxKZdw==} - cpu: [arm64] - os: [linux] - - '@rspack/binding-linux-arm64-gnu@2.0.8': - resolution: {integrity: sha512-pSI+npPQE/uDtiboqvcOIRJbEV2+B+H1xffmko/gw50la92oTUW60kVULFwsb6L0+GVCzIcwX3yq60GtYIn+Ug==} - cpu: [arm64] - os: [linux] - - '@rspack/binding-linux-arm64-gnu@2.1.0': - resolution: {integrity: sha512-TCmWIeI03ZZi8GjpIS2yl9JpaazsaA4F84zbX6a4kdZnFkrmFKRdvczZrquTNQvmggAEaJiPxkSrS8OC1LSAwA==} + '@rspack/binding-linux-arm64-gnu@2.1.3': + resolution: {integrity: sha512-aCy9Zli/2Qf+Ee5otXfFQ6mhv5fEyn0wIoBVmouqtJoqOO21et6UTtJ+LHLsMDolwGLyHERAljeSFSmYX3/O5A==} cpu: [arm64] os: [linux] - '@rspack/binding-linux-arm64-musl@1.7.12': - resolution: {integrity: sha512-i51WWI64aRpsfSki6rN0aepPqXkVfS+vZM7+4bWDcmnhUmdMvhIPcYg0QRk3DtyJnu33jqNLM0WHY78k00NyfA==} + '@rspack/binding-linux-arm64-musl@2.1.3': + resolution: {integrity: sha512-flIE7eluz0d21Fn28EVm3vPwoJooOSqtmjLFVSuOMcoCbwV9clfor195oIrAppp/W7dL/3XquFuVfrsa01Jy8Q==} cpu: [arm64] os: [linux] - '@rspack/binding-linux-arm64-musl@2.0.8': - resolution: {integrity: sha512-igjJ43yxWQ72GZqjDDZSSHax9/Vg+6rLMmOvFglTJUkQpB4Tyvu/YjW+WRjYj2xRw6blOjLxUSJWASvuSqqlvg==} - cpu: [arm64] - os: [linux] - - '@rspack/binding-linux-arm64-musl@2.1.0': - resolution: {integrity: sha512-HJzw5gG62qjj9fRQgj948naLucwE1Vg1bfcYHAxOr1/bVVIm4I4QvWGuqvd3XOu0MfLXPWvEyMAvJL+rtgamsw==} - cpu: [arm64] - os: [linux] - - '@rspack/binding-linux-riscv64-gnu@2.1.0': - resolution: {integrity: sha512-B3ENZHIBi5u1Apt6RJ62QSCabCijI5l86Sm2AEDYpQnqqBj3vIc+Br9HJHvNjK8PNWs1WfmD//UTUmQqZbYpKQ==} + '@rspack/binding-linux-riscv64-gnu@2.1.3': + resolution: {integrity: sha512-yojg8elye1nhsNeGncw0NrZ4pMGF7NVebR80CLg72TXyzfYwFJlFTdU5yUYb1Gy+JXIvrSCwzQt2QkyiEvmkfg==} cpu: [riscv64] os: [linux] - '@rspack/binding-linux-riscv64-musl@2.1.0': - resolution: {integrity: sha512-Qho1S8bW2BKRsJjl/f39GoyPRznF8ZarIgxZdVCIkn4k+3veggKWxqR1WWKoMj/LfykQd1uG3FF6n7zy5IfxWw==} + '@rspack/binding-linux-riscv64-musl@2.1.3': + resolution: {integrity: sha512-6PvbXb3FOK4X3S5QGvoSW/sqExmsvAoPnQ/YSFrXvTphkXFezA7wnobmGBHT8JQP31hcFRzJHIZSIOKktzSzzg==} cpu: [riscv64] os: [linux] - '@rspack/binding-linux-x64-gnu@1.7.12': - resolution: {integrity: sha512-MSos0FuPEefqo9V92ULd5hggKG29EkSNg1zDcypy0OkpsKh5pfjVxTLYFXgTcVyFoUQQbdG8zFBzYbwmJ8V4ew==} - cpu: [x64] - os: [linux] - - '@rspack/binding-linux-x64-gnu@2.0.8': - resolution: {integrity: sha512-zrkoEOnqj1hOEBO5T2I/2Ts2HSJsYFh1qXwMpK4dMJFGGNWDfNeUa6/LF5uq3VINF3JUl7RL47AgrucoSZJXPA==} - cpu: [x64] - os: [linux] - - '@rspack/binding-linux-x64-gnu@2.1.0': - resolution: {integrity: sha512-oE2CMALLdV3QNiA3YYDZ46tDGf+WRlqu/tQ+B79JYKVwt3sI0fpzvgwPNpx/gfRKUyA0phaeYS4kyOEnpltjpA==} - cpu: [x64] - os: [linux] - - '@rspack/binding-linux-x64-musl@1.7.12': - resolution: {integrity: sha512-JcAMVKXOnjfpC3coWjCFPWD3Yl8RBw6a+IXQQ8mfRlHaHMIiOv8IfZqx15XRxMUn49CtP7Z0Na8iiAg2aKrcfw==} - cpu: [x64] - os: [linux] - - '@rspack/binding-linux-x64-musl@2.0.8': - resolution: {integrity: sha512-6CtDaGZjNDvJd9TBp7a9zABbrPORO21W96+3ZcGBn0YNUPUk4ARxIxrTTpeJ/1F41QDM8AYIkGDdqEYMqTYBsA==} + '@rspack/binding-linux-x64-gnu@2.1.3': + resolution: {integrity: sha512-lMXjoGKf0SnviH596fmTszgtnXLHmWOoE90G8grG9MvKVa3pelRmfps5ewZL9s8ENf3NXRfOxIhIf/M9as6MqA==} cpu: [x64] os: [linux] - '@rspack/binding-linux-x64-musl@2.1.0': - resolution: {integrity: sha512-lxTFZgsfPPyyIt/DpOH5TK2u1ZROMB+gLp/LWvYBc8FSOtmR0Gl4L/AWmJdM2yqwPfy0hgSkVicf/7k80jHuVQ==} + '@rspack/binding-linux-x64-musl@2.1.3': + resolution: {integrity: sha512-0esT35v7pW2ZsJTMc/zDUHwpNXlPqyUCyuiLJvrAAUcdENrgOVe4DmFrgVJ2hwqI4GjeN1VBnGRJ8c+edAHH5w==} cpu: [x64] os: [linux] - '@rspack/binding-wasm32-wasi@1.7.12': - resolution: {integrity: sha512-n+ZqP6ZMc0nhOgvadg5VhEs9ojtbES80AcWeFnmGkbzIszvGSO63GKNiRkXtjJ9KFuRzytbbmsCqkUVH+Tywxg==} - cpu: [wasm32] - - '@rspack/binding-wasm32-wasi@2.0.8': - resolution: {integrity: sha512-Yf4SiqTUroT5Ju+te0YAY2xxKOb35tECsO21v7hYyGa705wrgoAK/MmF7enOvs9GR1iZIqgiLD/wxsIxl8GjJw==} - cpu: [wasm32] - - '@rspack/binding-wasm32-wasi@2.1.0': - resolution: {integrity: sha512-ZsDDduXaEF1SpyGz2OFuEU9Tzm0pKtbtCYviymiNtQS+3lx6rXyv+FaK0oIWn+gWL+gVNamplxKnNNR2jZsp5w==} + '@rspack/binding-wasm32-wasi@2.1.3': + resolution: {integrity: sha512-UsrDjD59UEP0mhfN/Z+uTc3vLgiUvIr+mn92WC1sbQi9gtZohTYvaQYFhWuMkBqsACGcmZp704JAbbSrVrVYCA==} cpu: [wasm32] - '@rspack/binding-win32-arm64-msvc@1.7.12': - resolution: {integrity: sha512-8+h5fYDXYdmugbdfZ+D1y8IQ3rv2EhSfyGP7vBe+bjNyaMa4jWrpucmZbtxojUL1AzaeuHbvMdj9UO/gelk/+g==} - cpu: [arm64] - os: [win32] - - '@rspack/binding-win32-arm64-msvc@2.0.8': - resolution: {integrity: sha512-8NCuiQsAhXrwRBy57QZoypqrws/zLBkaQVGiB8hksr6v++8hNigNjqpQARLbd0iyMuHsQQ++8+auGk6xlDXmzw==} - cpu: [arm64] - os: [win32] - - '@rspack/binding-win32-arm64-msvc@2.1.0': - resolution: {integrity: sha512-0uMWAZYgwdkk0ocE4X85w/0BNWT5GaKJTEZDVYxfSYcfVAxJvIsuM0VH/cjRjsQtEeE1rcY7JvJyd0rQ6j0DqA==} + '@rspack/binding-win32-arm64-msvc@2.1.3': + resolution: {integrity: sha512-42RS/SwKBTkNvXIPZXqTn0yEFN8zwGRfRe/ly0GDvPp/KxpLMFWxJmLxgtoLompU+0UCGvV4KpcBENt3oWs6BA==} cpu: [arm64] os: [win32] - '@rspack/binding-win32-ia32-msvc@1.7.12': - resolution: {integrity: sha512-cDMGwTRSa2p9fNBVe1wTRkF2AEXZ9ARWW36QeC5CkLaI0Ezz8lvhF2+CSOPnhaQ1O1qtn0L0SF+lFnrY+I7xGQ==} + '@rspack/binding-win32-ia32-msvc@2.1.3': + resolution: {integrity: sha512-thk43H1JHHbetNF3txcsGXeOwZi2m6Luf/uVUqNORXjxr5VV8woHAgaAx4QXVZRg70z1VDjd8mBWnHBnKI0FGA==} cpu: [ia32] os: [win32] - '@rspack/binding-win32-ia32-msvc@2.0.8': - resolution: {integrity: sha512-bxiekytbX7V9KFAra+HkwtNWC6pYfHEBBZFpiT0xUs3mCFOmAAFVBsBSQsoCP9AdCEXoMAvNdnrHNw3iov4OZw==} - cpu: [ia32] - os: [win32] - - '@rspack/binding-win32-ia32-msvc@2.1.0': - resolution: {integrity: sha512-MRuIZwF6w1tGyZgoJZ5dnpLaD/oMx8zAYSYQfNS7l0f7qjxbnp42625wkeNB8kPqrmDfqaWUWLwiOaRqFPmumA==} - cpu: [ia32] - os: [win32] - - '@rspack/binding-win32-x64-msvc@1.7.12': - resolution: {integrity: sha512-wIqFvlgFqrgUyj/6S/FJcvShnkZOmIeXTfqvheLY67MGq8qd8jb1YimQVKAIrmWB3yuJKUFACI3Ag1UBtEedEA==} - cpu: [x64] - os: [win32] - - '@rspack/binding-win32-x64-msvc@2.0.8': - resolution: {integrity: sha512-7zPs8YCe/ZVJTwd+5lpB0CP0tkn2pONf/T1ycmVY76u21Nrwt8mXQGc/2yH2eWP4B7fikYBr3hGr7mpR2fajqQ==} + '@rspack/binding-win32-x64-msvc@2.1.3': + resolution: {integrity: sha512-ObaUcj+BHo/aBL1weyM3orApaHyAR5Phr3YNEpQEifxjZbNKM1iO5X5prN4OqEv3H+7o5e/Wh9Fy3N7Vvd+psA==} cpu: [x64] os: [win32] - '@rspack/binding-win32-x64-msvc@2.1.0': - resolution: {integrity: sha512-Fme2Ifa647CtD7N6we9xvK+COzfzVJREtUayxdG+VArPdijURZyRQVUKKlYSBW+2qMg+G+kF1xo7gf7svG3sNA==} - cpu: [x64] - os: [win32] - - '@rspack/binding@1.7.12': - resolution: {integrity: sha512-f4HHuLbvuld8Ba4iB/4ibse5XrKxFrgmM3S4P2AOKnPlekAFlBjmltCuaTL/W2ggYvILaVY+YcFXrEH1rrKeQA==} - - '@rspack/binding@2.0.8': - resolution: {integrity: sha512-3uZ+y8aQxq33ty2srMxg2Nu0XuBI6vVrG50rkDaXqwWqOohfgGUSfFuQK7EnSUNy4aFUQlCG6NHialQHJov0wg==} - - '@rspack/binding@2.1.0': - resolution: {integrity: sha512-LsXFIOOYDutHk44SAOcVQa5iA7lhYwEbD+nZhgmCiGJvKKh0UIpBj6EAsBsB6omEK5GEXvjDeLFieKgbYW08QQ==} + '@rspack/binding@2.1.3': + resolution: {integrity: sha512-4UGXJqUHmm36tWG1GgFZz3p8sQ5JuSgWI+gq1xPPoihS41uYJL3cXuJnirTeWsmVrusmYZTQhgU3tl3VYGcJYg==} - '@rspack/core@1.7.12': - resolution: {integrity: sha512-6CwFIHlhRmXfZoMj3v9MZ1SMTPBn+cHVXeMIeaGp5sufqinKsISbsqHu6ZMJu2wDSmZLdmQJX6zLxkhcAUlhkQ==} - engines: {node: '>=18.12.0'} - peerDependencies: - '@swc/helpers': '>=0.5.1' - peerDependenciesMeta: - '@swc/helpers': - optional: true - - '@rspack/core@2.0.8': - resolution: {integrity: sha512-+NLGJf8gZxihDmMFzjlly3toc2SMjeDmuvz0/Cai9AMdV4F+Pqcnt2BA9V4e3SY2jmhJQtPwgyyLtR1RiJO77g==} + '@rspack/core@2.1.3': + resolution: {integrity: sha512-1iGnxLrP+iyY0ZSjLZeQTaxrISpQz4yLyqJPmaF/l4uw27/dBcrloszAeLOJQ9jiv7EJtU0T5zB+LOFPpivIxA==} engines: {node: ^20.19.0 || >=22.12.0} peerDependencies: '@module-federation/runtime-tools': ^0.24.1 || ^2.0.0 @@ -4788,24 +4633,6 @@ packages: '@swc/helpers': optional: true - '@rspack/core@2.1.0': - resolution: {integrity: sha512-dlZRzWQi90HzLYErGh0/xnEWAEMEAtDKXvNxERCEj5uIVIOVu9+uYwpNyAkKc9cK5sPhOz05kk9MIb1EaUJ5gg==} - engines: {node: ^20.19.0 || >=22.12.0} - peerDependencies: - '@module-federation/runtime-tools': ^0.24.1 || ^2.0.0 - '@swc/helpers': ^0.5.23 - peerDependenciesMeta: - '@module-federation/runtime-tools': - optional: true - '@swc/helpers': - optional: true - - '@rspack/lite-tapable@1.1.0': - resolution: {integrity: sha512-E2B0JhYFmVAwdDiG14+DW0Di4Ze4Jg10Pc4/lILUrd5DRCaklduz2OvJ5HYQ6G+hd+WTzqQb3QnDNfK4yvAFYw==} - - '@rspack/lite-tapable@1.1.2': - resolution: {integrity: sha512-1OnyWChLGE46YzWyjlmYJssOu/Y0STAnnr2ueKPqDCYTf63GJMs0mxNnCul4dNiVqHYPKv3/fxrTY3IpqoVwZQ==} - '@rspack/plugin-react-refresh@2.0.2': resolution: {integrity: sha512-dGNZiCxQxgAUI9sah7gd8u+O7OJZRCmqtEJNDOd8xW5RqcieC86F7p5qcShyw6onH5pKf57evpr2VjGbaFGkZg==} peerDependencies: @@ -4868,12 +4695,12 @@ packages: '@rspack/resolver@0.2.8': resolution: {integrity: sha512-FBWqdHhzS8mcf/WN4Ktzr7EaeaN+hsxbN98EweegX3924beZuY6H70CSFWCv1fIHAieCUv/9XCjKggHvhCsLwA==} - '@rstest/core@0.8.1': - resolution: {integrity: sha512-7d/2fm2V91pVx/rRtZ2gl6Zh4hVMivtDl4RgHFhBOrxi//UwhKISeF5gS/CSwpCgfOf10TzJRXqdI17ueUBNMQ==} - engines: {node: '>=18.12.0'} + '@rstest/core@0.11.0': + resolution: {integrity: sha512-buy6EV9R6d0O9WMOSG0C91jwMcNYDw4Q0uFQNuwPpQLH85rTlVyuUM7EnEnvVGv5lG6dpgGwNTdRWx6mvWybag==} + engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: - happy-dom: '*' + happy-dom: ^20.8.3 jsdom: '*' peerDependenciesMeta: happy-dom: @@ -4881,10 +4708,10 @@ packages: jsdom: optional: true - '@rstest/coverage-istanbul@0.2.0': - resolution: {integrity: sha512-ghFY2XkjL2PFarLaG+BxFTJYbVXtAtcqW5rjOqheiOYV6PqlOHFf/s+fLkKeOTc1oVoL3A/9vjMBc8fOgjAF/A==} + '@rstest/coverage-istanbul@0.11.0': + resolution: {integrity: sha512-WeQ8NPDNWFdv5THtncsii7vk+8cKECLfn15yuk6KT+OXNGn5Pz1raLX/tdQPz0qsB/rI2Zo65wiOvk5ktD7PYg==} peerDependencies: - '@rstest/core': ~0.8.0 + '@rstest/core': ^0.11.0 '@sec-ant/readable-stream@0.4.1': resolution: {integrity: sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==} @@ -9429,14 +9256,14 @@ packages: resolution: {integrity: sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==} engines: {node: '>= 18'} - rsbuild-plugin-dts@0.22.1: - resolution: {integrity: sha512-6vBfXgwK4j6GkjSRH13tuceBavc6qwSE5IVSoVCZ/wRuM0ZL7dIjdk+yGSWKJt5lNMfc4XwQoTErGIC9zhgztQ==} + rsbuild-plugin-dts@0.23.2: + resolution: {integrity: sha512-Ve8WOSPyTBjxH40O/6fChJtflL7yqcUtt5zbF0eHDM4hIt35jqLocRlSdCxFJdQm0blTjl6+hkmPa/vNI06ArA==} engines: {node: ^20.19.0 || >=22.12.0} peerDependencies: '@microsoft/api-extractor': ^7 '@rsbuild/core': ^1.0.0 || ^2.0.0-0 '@typescript/native-preview': ^7.0.0-0 - typescript: ^5 || ^6 + typescript: ^5 || ^6 || ^7.0.1-0 peerDependenciesMeta: '@microsoft/api-extractor': optional: true @@ -10027,10 +9854,6 @@ packages: resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} engines: {node: '>=12.0.0'} - tinypool@1.1.1: - resolution: {integrity: sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==} - engines: {node: ^18.0.0 || >=20.0.0} - tldts-core@7.4.5: resolution: {integrity: sha512-pGrwzZDvPwKe+7NNUqAunb6rqTfynr0VOUhCMdqbu5xlvNiszsAJygRzwvpVycdzejlbpY+SWJOn+s75Og7FEA==} @@ -11802,7 +11625,7 @@ snapshots: - node-fetch - utf-8-validate - '@module-federation/enhanced@2.5.1(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.3)(webpack@5.108.1(esbuild@0.27.2)(lightningcss@1.32.0)(postcss@8.5.16))': + '@module-federation/enhanced@2.5.1(@rspack/core@2.1.3(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.3)(webpack@5.108.1(esbuild@0.27.2)(lightningcss@1.32.0)(postcss@8.5.16))': dependencies: '@module-federation/bridge-react-webpack-plugin': 2.5.1(node-fetch@2.7.0(encoding@0.1.13)) '@module-federation/cli': 2.5.1(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.3) @@ -11811,7 +11634,7 @@ snapshots: '@module-federation/inject-external-runtime-core-plugin': 2.5.1(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13))) '@module-federation/managers': 2.5.1(node-fetch@2.7.0(encoding@0.1.13)) '@module-federation/manifest': 2.5.1(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.3) - '@module-federation/rspack': 2.5.1(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.3) + '@module-federation/rspack': 2.5.1(@rspack/core@2.1.3(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.3) '@module-federation/runtime-tools': 2.5.1(node-fetch@2.7.0(encoding@0.1.13)) '@module-federation/sdk': 2.5.1(node-fetch@2.7.0(encoding@0.1.13)) '@module-federation/webpack-bundler-runtime': 2.5.1(node-fetch@2.7.0(encoding@0.1.13)) @@ -11827,8 +11650,6 @@ snapshots: - node-fetch - utf-8-validate - '@module-federation/error-codes@0.22.0': {} - '@module-federation/error-codes@2.5.1': {} '@module-federation/inject-external-runtime-core-plugin@2.5.1(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))': @@ -11855,9 +11676,9 @@ snapshots: - utf-8-validate - vue-tsc - '@module-federation/node@2.7.44(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(typescript@5.9.3)(webpack@5.108.1(esbuild@0.27.2)(lightningcss@1.32.0)(postcss@8.5.16))': + '@module-federation/node@2.7.44(@rspack/core@2.1.3(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(typescript@5.9.3)(webpack@5.108.1(esbuild@0.27.2)(lightningcss@1.32.0)(postcss@8.5.16))': dependencies: - '@module-federation/enhanced': 2.5.1(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.3)(webpack@5.108.1(esbuild@0.27.2)(lightningcss@1.32.0)(postcss@8.5.16)) + '@module-federation/enhanced': 2.5.1(@rspack/core@2.1.3(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.3)(webpack@5.108.1(esbuild@0.27.2)(lightningcss@1.32.0)(postcss@8.5.16)) '@module-federation/runtime': 2.5.1(node-fetch@2.7.0(encoding@0.1.13)) '@module-federation/sdk': 2.5.1(node-fetch@2.7.0(encoding@0.1.13)) encoding: 0.1.13 @@ -11872,13 +11693,13 @@ snapshots: - utf-8-validate - vue-tsc - '@module-federation/rsbuild-plugin@2.5.1(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.3)(webpack@5.108.1(esbuild@0.27.2)(lightningcss@1.32.0)(postcss@8.5.16))': + '@module-federation/rsbuild-plugin@2.5.1(@rsbuild/core@2.1.5(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.3(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.3)(webpack@5.108.1(esbuild@0.27.2)(lightningcss@1.32.0)(postcss@8.5.16))': dependencies: - '@module-federation/enhanced': 2.5.1(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.3)(webpack@5.108.1(esbuild@0.27.2)(lightningcss@1.32.0)(postcss@8.5.16)) - '@module-federation/node': 2.7.44(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(typescript@5.9.3)(webpack@5.108.1(esbuild@0.27.2)(lightningcss@1.32.0)(postcss@8.5.16)) + '@module-federation/enhanced': 2.5.1(@rspack/core@2.1.3(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.3)(webpack@5.108.1(esbuild@0.27.2)(lightningcss@1.32.0)(postcss@8.5.16)) + '@module-federation/node': 2.7.44(@rspack/core@2.1.3(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(typescript@5.9.3)(webpack@5.108.1(esbuild@0.27.2)(lightningcss@1.32.0)(postcss@8.5.16)) '@module-federation/sdk': 2.5.1(node-fetch@2.7.0(encoding@0.1.13)) optionalDependencies: - '@rsbuild/core': 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0) + '@rsbuild/core': 2.1.5(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0) transitivePeerDependencies: - '@rspack/core' - bufferutil @@ -11888,7 +11709,7 @@ snapshots: - vue-tsc - webpack - '@module-federation/rspack@2.5.1(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.3)': + '@module-federation/rspack@2.5.1(@rspack/core@2.1.3(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.3)': dependencies: '@module-federation/bridge-react-webpack-plugin': 2.5.1(node-fetch@2.7.0(encoding@0.1.13)) '@module-federation/dts-plugin': 2.5.1(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.3) @@ -11897,7 +11718,7 @@ snapshots: '@module-federation/manifest': 2.5.1(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.3) '@module-federation/runtime-tools': 2.5.1(node-fetch@2.7.0(encoding@0.1.13)) '@module-federation/sdk': 2.5.1(node-fetch@2.7.0(encoding@0.1.13)) - '@rspack/core': 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23) + '@rspack/core': 2.1.3(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23) optionalDependencies: typescript: 5.9.3 transitivePeerDependencies: @@ -11905,11 +11726,6 @@ snapshots: - node-fetch - utf-8-validate - '@module-federation/runtime-core@0.22.0': - dependencies: - '@module-federation/error-codes': 0.22.0 - '@module-federation/sdk': 0.22.0 - '@module-federation/runtime-core@2.5.1(node-fetch@2.7.0(encoding@0.1.13))': dependencies: '@module-federation/error-codes': 2.5.1 @@ -11917,11 +11733,6 @@ snapshots: transitivePeerDependencies: - node-fetch - '@module-federation/runtime-tools@0.22.0': - dependencies: - '@module-federation/runtime': 0.22.0 - '@module-federation/webpack-bundler-runtime': 0.22.0 - '@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13))': dependencies: '@module-federation/runtime': 2.5.1(node-fetch@2.7.0(encoding@0.1.13)) @@ -11929,12 +11740,6 @@ snapshots: transitivePeerDependencies: - node-fetch - '@module-federation/runtime@0.22.0': - dependencies: - '@module-federation/error-codes': 0.22.0 - '@module-federation/runtime-core': 0.22.0 - '@module-federation/sdk': 0.22.0 - '@module-federation/runtime@2.5.1(node-fetch@2.7.0(encoding@0.1.13))': dependencies: '@module-federation/error-codes': 2.5.1 @@ -11943,8 +11748,6 @@ snapshots: transitivePeerDependencies: - node-fetch - '@module-federation/sdk@0.22.0': {} - '@module-federation/sdk@2.5.1(node-fetch@2.7.0(encoding@0.1.13))': optionalDependencies: node-fetch: 2.7.0(encoding@0.1.13) @@ -11954,11 +11757,6 @@ snapshots: find-pkg: 2.0.0 resolve: 1.22.8 - '@module-federation/webpack-bundler-runtime@0.22.0': - dependencies: - '@module-federation/runtime': 0.22.0 - '@module-federation/sdk': 0.22.0 - '@module-federation/webpack-bundler-runtime@2.5.1(node-fetch@2.7.0(encoding@0.1.13))': dependencies: '@module-federation/error-codes': 2.5.1 @@ -11976,27 +11774,6 @@ snapshots: outvariant: 1.4.3 strict-event-emitter: 0.5.1 - '@napi-rs/wasm-runtime@1.0.7': - dependencies: - '@emnapi/core': 1.11.1 - '@emnapi/runtime': 1.11.1 - '@tybys/wasm-util': 0.10.3 - optional: true - - '@napi-rs/wasm-runtime@1.1.4(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)': - dependencies: - '@emnapi/core': 1.10.0 - '@emnapi/runtime': 1.10.0 - '@tybys/wasm-util': 0.10.3 - optional: true - - '@napi-rs/wasm-runtime@1.1.5(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)': - dependencies: - '@emnapi/core': 1.11.1 - '@emnapi/runtime': 1.11.1 - '@tybys/wasm-util': 0.10.3 - optional: true - '@napi-rs/wasm-runtime@1.1.6(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)': dependencies: '@emnapi/core': 1.10.0 @@ -13460,33 +13237,16 @@ snapshots: '@rollup/rollup-win32-x64-msvc@4.62.2': optional: true - '@rsbuild/core@1.7.2': - dependencies: - '@rspack/core': 1.7.12(@swc/helpers@0.5.23) - '@rspack/lite-tapable': 1.1.2 - '@swc/helpers': 0.5.23 - core-js: 3.47.0 - jiti: 2.7.0 - - '@rsbuild/core@2.0.15(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0)': + '@rsbuild/core@2.1.5(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0)': dependencies: - '@rspack/core': 2.0.8(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23) + '@rspack/core': 2.1.3(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23) '@swc/helpers': 0.5.23 optionalDependencies: core-js: 3.47.0 transitivePeerDependencies: - '@module-federation/runtime-tools' - '@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0)': - dependencies: - '@rspack/core': 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23) - '@swc/helpers': 0.5.23 - optionalDependencies: - core-js: 3.47.0 - transitivePeerDependencies: - - '@module-federation/runtime-tools' - - '@rsbuild/plugin-babel@2.0.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))': + '@rsbuild/plugin-babel@2.0.0(@rsbuild/core@2.1.5(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))': dependencies: '@babel/core': 7.29.7 '@babel/plugin-proposal-decorators': 7.29.7(@babel/core@7.29.7) @@ -13495,11 +13255,11 @@ snapshots: '@types/babel__core': 7.20.5 reduce-configs: 1.1.2 optionalDependencies: - '@rsbuild/core': 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0) + '@rsbuild/core': 2.1.5(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0) transitivePeerDependencies: - supports-color - '@rsbuild/plugin-check-syntax@1.6.1(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))': + '@rsbuild/plugin-check-syntax@1.6.1(@rsbuild/core@2.1.5(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))': dependencies: acorn: 8.17.0 browserslist-to-es-version: 1.4.2 @@ -13507,30 +13267,30 @@ snapshots: picocolors: 1.1.1 source-map: 0.7.6 optionalDependencies: - '@rsbuild/core': 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0) + '@rsbuild/core': 2.1.5(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0) - '@rsbuild/plugin-less@1.6.4(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.1(lightningcss@1.32.0))': + '@rsbuild/plugin-less@1.6.4(@rsbuild/core@2.1.5(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(@rspack/core@2.1.3(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.1(lightningcss@1.32.0))': dependencies: deepmerge: 4.3.1 less: 4.6.7 - less-loader: 12.3.3(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(less@4.6.7)(webpack@5.108.1(lightningcss@1.32.0)) + less-loader: 12.3.3(@rspack/core@2.1.3(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(less@4.6.7)(webpack@5.108.1(lightningcss@1.32.0)) reduce-configs: 1.1.2 optionalDependencies: - '@rsbuild/core': 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0) + '@rsbuild/core': 2.1.5(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0) transitivePeerDependencies: - '@rspack/core' - webpack - '@rsbuild/plugin-react@2.1.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))': + '@rsbuild/plugin-react@2.1.0(@rsbuild/core@2.1.5(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.3(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))': dependencies: - '@rspack/plugin-react-refresh': 2.0.2(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(react-refresh@0.18.0) + '@rspack/plugin-react-refresh': 2.0.2(@rspack/core@2.1.3(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(react-refresh@0.18.0) react-refresh: 0.18.0 optionalDependencies: - '@rsbuild/core': 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0) + '@rsbuild/core': 2.1.5(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0) transitivePeerDependencies: - '@rspack/core' - '@rsbuild/plugin-sass@1.5.3(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))': + '@rsbuild/plugin-sass@1.5.3(@rsbuild/core@2.1.5(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))': dependencies: deepmerge: 4.3.1 loader-utils: 2.0.4 @@ -13538,41 +13298,41 @@ snapshots: reduce-configs: 1.1.2 sass-embedded: 1.100.0 optionalDependencies: - '@rsbuild/core': 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0) + '@rsbuild/core': 2.1.5(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0) - '@rsbuild/plugin-svgr@2.0.4(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(typescript@6.0.1-rc)': + '@rsbuild/plugin-svgr@2.0.4(@rsbuild/core@2.1.5(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(@rspack/core@2.1.3(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(typescript@6.0.1-rc)': dependencies: - '@rsbuild/plugin-react': 2.1.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23)) + '@rsbuild/plugin-react': 2.1.0(@rsbuild/core@2.1.5(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.3(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23)) '@svgr/core': 8.1.0(typescript@6.0.1-rc) '@svgr/plugin-jsx': 8.1.0(@svgr/core@8.1.0(typescript@6.0.1-rc)) '@svgr/plugin-svgo': 8.1.0(@svgr/core@8.1.0(typescript@6.0.1-rc))(typescript@6.0.1-rc) deepmerge: 4.3.1 loader-utils: 3.3.1 optionalDependencies: - '@rsbuild/core': 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0) + '@rsbuild/core': 2.1.5(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0) transitivePeerDependencies: - '@rspack/core' - supports-color - typescript - '@rsbuild/plugin-tailwindcss@2.0.3(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.1(lightningcss@1.32.0))': + '@rsbuild/plugin-tailwindcss@2.0.3(@rsbuild/core@2.1.5(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(@rspack/core@2.1.3(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.1(lightningcss@1.32.0))': dependencies: - '@tailwindcss/webpack': 4.3.2(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.1(lightningcss@1.32.0)) + '@tailwindcss/webpack': 4.3.2(@rspack/core@2.1.3(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.1(lightningcss@1.32.0)) optionalDependencies: - '@rsbuild/core': 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0) + '@rsbuild/core': 2.1.5(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0) transitivePeerDependencies: - '@rspack/core' - webpack '@rsdoctor/client@1.5.16': {} - '@rsdoctor/core@1.5.16(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.1(lightningcss@1.32.0))': + '@rsdoctor/core@1.5.16(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@rsbuild/core@2.1.5(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(@rspack/core@2.1.3(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.1(lightningcss@1.32.0))': dependencies: - '@rsbuild/plugin-check-syntax': 1.6.1(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0)) - '@rsdoctor/graph': 1.5.16(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.1(lightningcss@1.32.0)) - '@rsdoctor/sdk': 1.5.16(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.1(lightningcss@1.32.0)) - '@rsdoctor/types': 1.5.16(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.1(lightningcss@1.32.0)) - '@rsdoctor/utils': 1.5.16(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.1(lightningcss@1.32.0)) + '@rsbuild/plugin-check-syntax': 1.6.1(@rsbuild/core@2.1.5(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0)) + '@rsdoctor/graph': 1.5.16(@rspack/core@2.1.3(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.1(lightningcss@1.32.0)) + '@rsdoctor/sdk': 1.5.16(@rspack/core@2.1.3(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.1(lightningcss@1.32.0)) + '@rsdoctor/types': 1.5.16(@rspack/core@2.1.3(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.1(lightningcss@1.32.0)) + '@rsdoctor/utils': 1.5.16(@rspack/core@2.1.3(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.1(lightningcss@1.32.0)) '@rspack/resolver': 0.2.8(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1) browserslist-load-config: 1.0.3 es-toolkit: 1.49.0 @@ -13590,10 +13350,10 @@ snapshots: - utf-8-validate - webpack - '@rsdoctor/graph@1.5.16(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.1(lightningcss@1.32.0))': + '@rsdoctor/graph@1.5.16(@rspack/core@2.1.3(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.1(lightningcss@1.32.0))': dependencies: - '@rsdoctor/types': 1.5.16(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.1(lightningcss@1.32.0)) - '@rsdoctor/utils': 1.5.16(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.1(lightningcss@1.32.0)) + '@rsdoctor/types': 1.5.16(@rspack/core@2.1.3(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.1(lightningcss@1.32.0)) + '@rsdoctor/utils': 1.5.16(@rspack/core@2.1.3(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.1(lightningcss@1.32.0)) es-toolkit: 1.49.0 path-browserify: 1.0.1 source-map: 0.7.6 @@ -13601,15 +13361,15 @@ snapshots: - '@rspack/core' - webpack - '@rsdoctor/rspack-plugin@1.5.16(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.1(lightningcss@1.32.0))': + '@rsdoctor/rspack-plugin@1.5.16(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@rsbuild/core@2.1.5(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(@rspack/core@2.1.3(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.1(lightningcss@1.32.0))': dependencies: - '@rsdoctor/core': 1.5.16(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.1(lightningcss@1.32.0)) - '@rsdoctor/graph': 1.5.16(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.1(lightningcss@1.32.0)) - '@rsdoctor/sdk': 1.5.16(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.1(lightningcss@1.32.0)) - '@rsdoctor/types': 1.5.16(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.1(lightningcss@1.32.0)) - '@rsdoctor/utils': 1.5.16(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.1(lightningcss@1.32.0)) + '@rsdoctor/core': 1.5.16(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@rsbuild/core@2.1.5(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(@rspack/core@2.1.3(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.1(lightningcss@1.32.0)) + '@rsdoctor/graph': 1.5.16(@rspack/core@2.1.3(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.1(lightningcss@1.32.0)) + '@rsdoctor/sdk': 1.5.16(@rspack/core@2.1.3(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.1(lightningcss@1.32.0)) + '@rsdoctor/types': 1.5.16(@rspack/core@2.1.3(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.1(lightningcss@1.32.0)) + '@rsdoctor/utils': 1.5.16(@rspack/core@2.1.3(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.1(lightningcss@1.32.0)) optionalDependencies: - '@rspack/core': 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23) + '@rspack/core': 2.1.3(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23) transitivePeerDependencies: - '@emnapi/core' - '@emnapi/runtime' @@ -13619,12 +13379,12 @@ snapshots: - utf-8-validate - webpack - '@rsdoctor/sdk@1.5.16(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.1(lightningcss@1.32.0))': + '@rsdoctor/sdk@1.5.16(@rspack/core@2.1.3(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.1(lightningcss@1.32.0))': dependencies: '@rsdoctor/client': 1.5.16 - '@rsdoctor/graph': 1.5.16(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.1(lightningcss@1.32.0)) - '@rsdoctor/types': 1.5.16(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.1(lightningcss@1.32.0)) - '@rsdoctor/utils': 1.5.16(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.1(lightningcss@1.32.0)) + '@rsdoctor/graph': 1.5.16(@rspack/core@2.1.3(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.1(lightningcss@1.32.0)) + '@rsdoctor/types': 1.5.16(@rspack/core@2.1.3(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.1(lightningcss@1.32.0)) + '@rsdoctor/utils': 1.5.16(@rspack/core@2.1.3(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.1(lightningcss@1.32.0)) launch-editor: 2.14.1 safer-buffer: 2.1.2 socket.io: 4.8.1 @@ -13636,20 +13396,20 @@ snapshots: - utf-8-validate - webpack - '@rsdoctor/types@1.5.16(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.1(lightningcss@1.32.0))': + '@rsdoctor/types@1.5.16(@rspack/core@2.1.3(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.1(lightningcss@1.32.0))': dependencies: '@types/connect': 3.4.38 '@types/estree': 1.0.5 '@types/tapable': 2.3.0 source-map: 0.7.6 optionalDependencies: - '@rspack/core': 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23) + '@rspack/core': 2.1.3(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23) webpack: 5.108.1(lightningcss@1.32.0) - '@rsdoctor/utils@1.5.16(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.1(lightningcss@1.32.0))': + '@rsdoctor/utils@1.5.16(@rspack/core@2.1.3(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.1(lightningcss@1.32.0))': dependencies: '@babel/code-frame': 7.26.2 - '@rsdoctor/types': 1.5.16(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.1(lightningcss@1.32.0)) + '@rsdoctor/types': 1.5.16(@rspack/core@2.1.3(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.1(lightningcss@1.32.0)) '@types/estree': 1.0.5 acorn: 8.17.0 acorn-import-attributes: 1.9.5(acorn@8.17.0) @@ -13667,10 +13427,10 @@ snapshots: - '@rspack/core' - webpack - '@rslib/core@0.22.1(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0)(typescript@5.9.3)': + '@rslib/core@0.23.2(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0)(typescript@5.9.3)': dependencies: - '@rsbuild/core': 2.0.15(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0) - rsbuild-plugin-dts: 0.22.1(@rsbuild/core@2.0.15(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(typescript@5.9.3) + '@rsbuild/core': 2.1.5(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0) + rsbuild-plugin-dts: 0.23.2(@rsbuild/core@2.1.5(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(typescript@5.9.3) optionalDependencies: typescript: 5.9.3 transitivePeerDependencies: @@ -13678,184 +13438,73 @@ snapshots: - '@typescript/native-preview' - core-js - '@rspack/binding-darwin-arm64@1.7.12': - optional: true - - '@rspack/binding-darwin-arm64@2.0.8': - optional: true - - '@rspack/binding-darwin-arm64@2.1.0': - optional: true - - '@rspack/binding-darwin-x64@1.7.12': - optional: true - - '@rspack/binding-darwin-x64@2.0.8': - optional: true - - '@rspack/binding-darwin-x64@2.1.0': - optional: true - - '@rspack/binding-linux-arm64-gnu@1.7.12': - optional: true - - '@rspack/binding-linux-arm64-gnu@2.0.8': - optional: true - - '@rspack/binding-linux-arm64-gnu@2.1.0': - optional: true - - '@rspack/binding-linux-arm64-musl@1.7.12': - optional: true - - '@rspack/binding-linux-arm64-musl@2.0.8': - optional: true - - '@rspack/binding-linux-arm64-musl@2.1.0': - optional: true - - '@rspack/binding-linux-riscv64-gnu@2.1.0': - optional: true - - '@rspack/binding-linux-riscv64-musl@2.1.0': + '@rspack/binding-darwin-arm64@2.1.3': optional: true - '@rspack/binding-linux-x64-gnu@1.7.12': + '@rspack/binding-darwin-x64@2.1.3': optional: true - '@rspack/binding-linux-x64-gnu@2.0.8': + '@rspack/binding-linux-arm64-gnu@2.1.3': optional: true - '@rspack/binding-linux-x64-gnu@2.1.0': + '@rspack/binding-linux-arm64-musl@2.1.3': optional: true - '@rspack/binding-linux-x64-musl@1.7.12': + '@rspack/binding-linux-riscv64-gnu@2.1.3': optional: true - '@rspack/binding-linux-x64-musl@2.0.8': + '@rspack/binding-linux-riscv64-musl@2.1.3': optional: true - '@rspack/binding-linux-x64-musl@2.1.0': + '@rspack/binding-linux-x64-gnu@2.1.3': optional: true - '@rspack/binding-wasm32-wasi@1.7.12': - dependencies: - '@napi-rs/wasm-runtime': 1.0.7 - optional: true - - '@rspack/binding-wasm32-wasi@2.0.8': - dependencies: - '@emnapi/core': 1.10.0 - '@emnapi/runtime': 1.10.0 - '@napi-rs/wasm-runtime': 1.1.4(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) + '@rspack/binding-linux-x64-musl@2.1.3': optional: true - '@rspack/binding-wasm32-wasi@2.1.0': + '@rspack/binding-wasm32-wasi@2.1.3': dependencies: '@emnapi/core': 1.11.1 '@emnapi/runtime': 1.11.1 - '@napi-rs/wasm-runtime': 1.1.5(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1) - optional: true - - '@rspack/binding-win32-arm64-msvc@1.7.12': - optional: true - - '@rspack/binding-win32-arm64-msvc@2.0.8': - optional: true - - '@rspack/binding-win32-arm64-msvc@2.1.0': - optional: true - - '@rspack/binding-win32-ia32-msvc@1.7.12': - optional: true - - '@rspack/binding-win32-ia32-msvc@2.0.8': - optional: true - - '@rspack/binding-win32-ia32-msvc@2.1.0': + '@napi-rs/wasm-runtime': 1.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1) optional: true - '@rspack/binding-win32-x64-msvc@1.7.12': + '@rspack/binding-win32-arm64-msvc@2.1.3': optional: true - '@rspack/binding-win32-x64-msvc@2.0.8': + '@rspack/binding-win32-ia32-msvc@2.1.3': optional: true - '@rspack/binding-win32-x64-msvc@2.1.0': + '@rspack/binding-win32-x64-msvc@2.1.3': optional: true - '@rspack/binding@1.7.12': - optionalDependencies: - '@rspack/binding-darwin-arm64': 1.7.12 - '@rspack/binding-darwin-x64': 1.7.12 - '@rspack/binding-linux-arm64-gnu': 1.7.12 - '@rspack/binding-linux-arm64-musl': 1.7.12 - '@rspack/binding-linux-x64-gnu': 1.7.12 - '@rspack/binding-linux-x64-musl': 1.7.12 - '@rspack/binding-wasm32-wasi': 1.7.12 - '@rspack/binding-win32-arm64-msvc': 1.7.12 - '@rspack/binding-win32-ia32-msvc': 1.7.12 - '@rspack/binding-win32-x64-msvc': 1.7.12 - - '@rspack/binding@2.0.8': - optionalDependencies: - '@rspack/binding-darwin-arm64': 2.0.8 - '@rspack/binding-darwin-x64': 2.0.8 - '@rspack/binding-linux-arm64-gnu': 2.0.8 - '@rspack/binding-linux-arm64-musl': 2.0.8 - '@rspack/binding-linux-x64-gnu': 2.0.8 - '@rspack/binding-linux-x64-musl': 2.0.8 - '@rspack/binding-wasm32-wasi': 2.0.8 - '@rspack/binding-win32-arm64-msvc': 2.0.8 - '@rspack/binding-win32-ia32-msvc': 2.0.8 - '@rspack/binding-win32-x64-msvc': 2.0.8 - - '@rspack/binding@2.1.0': - optionalDependencies: - '@rspack/binding-darwin-arm64': 2.1.0 - '@rspack/binding-darwin-x64': 2.1.0 - '@rspack/binding-linux-arm64-gnu': 2.1.0 - '@rspack/binding-linux-arm64-musl': 2.1.0 - '@rspack/binding-linux-riscv64-gnu': 2.1.0 - '@rspack/binding-linux-riscv64-musl': 2.1.0 - '@rspack/binding-linux-x64-gnu': 2.1.0 - '@rspack/binding-linux-x64-musl': 2.1.0 - '@rspack/binding-wasm32-wasi': 2.1.0 - '@rspack/binding-win32-arm64-msvc': 2.1.0 - '@rspack/binding-win32-ia32-msvc': 2.1.0 - '@rspack/binding-win32-x64-msvc': 2.1.0 - - '@rspack/core@1.7.12(@swc/helpers@0.5.23)': - dependencies: - '@module-federation/runtime-tools': 0.22.0 - '@rspack/binding': 1.7.12 - '@rspack/lite-tapable': 1.1.0 + '@rspack/binding@2.1.3': optionalDependencies: - '@swc/helpers': 0.5.23 - - '@rspack/core@2.0.8(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23)': - dependencies: - '@rspack/binding': 2.0.8 + '@rspack/binding-darwin-arm64': 2.1.3 + '@rspack/binding-darwin-x64': 2.1.3 + '@rspack/binding-linux-arm64-gnu': 2.1.3 + '@rspack/binding-linux-arm64-musl': 2.1.3 + '@rspack/binding-linux-riscv64-gnu': 2.1.3 + '@rspack/binding-linux-riscv64-musl': 2.1.3 + '@rspack/binding-linux-x64-gnu': 2.1.3 + '@rspack/binding-linux-x64-musl': 2.1.3 + '@rspack/binding-wasm32-wasi': 2.1.3 + '@rspack/binding-win32-arm64-msvc': 2.1.3 + '@rspack/binding-win32-ia32-msvc': 2.1.3 + '@rspack/binding-win32-x64-msvc': 2.1.3 + + '@rspack/core@2.1.3(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23)': + dependencies: + '@rspack/binding': 2.1.3 optionalDependencies: '@module-federation/runtime-tools': 2.5.1(node-fetch@2.7.0(encoding@0.1.13)) '@swc/helpers': 0.5.23 - '@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23)': - dependencies: - '@rspack/binding': 2.1.0 - optionalDependencies: - '@module-federation/runtime-tools': 2.5.1(node-fetch@2.7.0(encoding@0.1.13)) - '@swc/helpers': 0.5.23 - - '@rspack/lite-tapable@1.1.0': {} - - '@rspack/lite-tapable@1.1.2': {} - - '@rspack/plugin-react-refresh@2.0.2(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(react-refresh@0.18.0)': + '@rspack/plugin-react-refresh@2.0.2(@rspack/core@2.1.3(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(react-refresh@0.18.0)': dependencies: react-refresh: 0.18.0 optionalDependencies: - '@rspack/core': 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23) + '@rspack/core': 2.1.3(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23) '@rspack/resolver-binding-darwin-arm64@0.2.8': optional: true @@ -13908,17 +13557,19 @@ snapshots: - '@emnapi/core' - '@emnapi/runtime' - '@rstest/core@0.8.1(jsdom@27.4.0(@noble/hashes@2.2.0))': + '@rstest/core@0.11.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0)(jsdom@27.4.0(@noble/hashes@2.2.0))': dependencies: - '@rsbuild/core': 1.7.2 + '@rsbuild/core': 2.1.5(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0) '@types/chai': 5.2.3 - tinypool: 1.1.1 optionalDependencies: jsdom: 27.4.0(@noble/hashes@2.2.0) + transitivePeerDependencies: + - '@module-federation/runtime-tools' + - core-js - '@rstest/coverage-istanbul@0.2.0(@rstest/core@0.8.1(jsdom@27.4.0(@noble/hashes@2.2.0)))': + '@rstest/coverage-istanbul@0.11.0(@rstest/core@0.11.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0)(jsdom@27.4.0(@noble/hashes@2.2.0)))': dependencies: - '@rstest/core': 0.8.1(jsdom@27.4.0(@noble/hashes@2.2.0)) + '@rstest/core': 0.11.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0)(jsdom@27.4.0(@noble/hashes@2.2.0)) istanbul-lib-coverage: 3.2.2 istanbul-lib-report: 3.0.1 istanbul-lib-source-maps: 5.0.6 @@ -14390,14 +14041,14 @@ snapshots: postcss: 8.5.15 tailwindcss: 4.3.1 - '@tailwindcss/webpack@4.3.2(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.1(lightningcss@1.32.0))': + '@tailwindcss/webpack@4.3.2(@rspack/core@2.1.3(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.1(lightningcss@1.32.0))': dependencies: '@alloc/quick-lru': 5.2.0 '@tailwindcss/node': 4.3.2 '@tailwindcss/oxide': 4.3.2 tailwindcss: 4.3.2 optionalDependencies: - '@rspack/core': 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23) + '@rspack/core': 2.1.3(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23) webpack: 5.108.1(lightningcss@1.32.0) '@tanstack/devtools-client@0.0.5': @@ -15688,7 +15339,8 @@ snapshots: dependencies: is-what: 4.1.16 - core-js@3.47.0: {} + core-js@3.47.0: + optional: true core-util-is@1.0.3: {} @@ -17339,11 +16991,11 @@ snapshots: leaflet@1.9.4: {} - less-loader@12.3.3(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(less@4.6.7)(webpack@5.108.1(lightningcss@1.32.0)): + less-loader@12.3.3(@rspack/core@2.1.3(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(less@4.6.7)(webpack@5.108.1(lightningcss@1.32.0)): dependencies: less: 4.6.7 optionalDependencies: - '@rspack/core': 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23) + '@rspack/core': 2.1.3(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23) webpack: 5.108.1(lightningcss@1.32.0) less@4.6.7: @@ -18615,10 +18267,10 @@ snapshots: transitivePeerDependencies: - supports-color - rsbuild-plugin-dts@0.22.1(@rsbuild/core@2.0.15(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(typescript@5.9.3): + rsbuild-plugin-dts@0.23.2(@rsbuild/core@2.1.5(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(typescript@5.9.3): dependencies: '@ast-grep/napi': 0.37.0 - '@rsbuild/core': 2.0.15(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0) + '@rsbuild/core': 2.1.5(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0) optionalDependencies: typescript: 5.9.3 @@ -19296,8 +18948,6 @@ snapshots: fdir: 6.5.0(picomatch@4.0.4) picomatch: 4.0.4 - tinypool@1.1.1: {} - tldts-core@7.4.5: {} tldts@7.4.5: diff --git a/rslib.config.ts b/rslib.config.ts index 566bf25c..80e3488e 100644 --- a/rslib.config.ts +++ b/rslib.config.ts @@ -11,8 +11,8 @@ const config = defineConfig({ source: { entry: { index: './src/index.ts', - 'parallel-route-transform-worker': - './src/parallel-route-transform-worker.ts', + 'route-module-transform-loader': + './src/route-module-transform-loader.ts', 'templates/entry.server': './src/templates/entry.server.tsx', 'templates/entry.client': './src/templates/entry.client.tsx', }, diff --git a/scripts/benchmark/results.mts b/scripts/benchmark/results.mts index 67c678be..52caa5e2 100644 --- a/scripts/benchmark/results.mts +++ b/scripts/benchmark/results.mts @@ -135,6 +135,8 @@ export const summarizePluginOperations = runs => { wallMs: null, maxMs: 0, reports: 0, + partialReports: 0, + workerIds: new Set(), }; current.count += count; current.totalMs += totalMs; @@ -143,19 +145,31 @@ export const summarizePluginOperations = runs => { } current.maxMs = Math.max(current.maxMs, maxMs); current.reports += 1; + if (report.partial === true) { + current.partialReports += 1; + } + if (typeof report.workerId === 'string') { + current.workerIds.add(report.workerId); + } operations.set(key, current); } } } - return [...operations.values()].sort((a, b) => { - if (b.totalMs !== a.totalMs) { - return b.totalMs - a.totalMs; - } - return `${a.environment}:${a.operation}`.localeCompare( - `${b.environment}:${b.operation}` - ); - }); + return [...operations.values()] + .map(({ workerIds, ...operation }) => ({ + ...operation, + wallMs: operation.partialReports > 0 ? null : operation.wallMs, + workers: workerIds.size, + })) + .sort((a, b) => { + if (b.totalMs !== a.totalMs) { + return b.totalMs - a.totalMs; + } + return `${a.environment}:${a.operation}`.localeCompare( + `${b.environment}:${b.operation}` + ); + }); }; const formatMs = value => @@ -174,7 +188,7 @@ const formatParallelRouteTransform = parallelRouteTransform => { if (parallelRouteTransform === true) { return 'true'; } - return `workers=${parallelRouteTransform}`; + return `maxWorkers=${parallelRouteTransform}`; }; const appendDevRequestSummary = (lines, benchmark, key, title) => { diff --git a/src/build-output-transforms.ts b/src/build-output-transforms.ts index 9b14d0ec..22038639 100644 --- a/src/build-output-transforms.ts +++ b/src/build-output-transforms.ts @@ -6,10 +6,10 @@ import { getReactRouterManifestForDev, type ReactRouterManifestStats, } from './manifest.js'; -import type { RouteTransformExecutor } from './parallel-route-transforms.js'; import type { ReactRouterPerformanceProfiler } from './performance.js'; import { createBundlerRouteExportResolver } from './route-export-resolution.js'; import type { RouteChunkConfig } from './route-chunks.js'; +import type { RouteTransformRunner } from './route-transform-tasks.js'; import type { PluginOptions, Route } from './types.js'; import { isSourceMapEnabled } from './warnings/warn-on-client-source-maps.js'; @@ -31,11 +31,12 @@ type RegisterBuildOutputTransformsOptions = { appDirectory: string; getAssetPrefix: () => string; routeChunkOptions: Parameters[5]; - routeTransformExecutor: RouteTransformExecutor; + routeTransformRunner: RouteTransformRunner; routeByFilePath: Map; routeChunkConfig: RouteChunkConfig; isBuild: boolean; splitRouteModules: boolean; + useRouteModuleTransformApi: boolean; ssr: boolean; isSpaMode: boolean; rootRoutePath: string; @@ -54,11 +55,12 @@ export const registerBuildOutputTransforms = ({ appDirectory, getAssetPrefix, routeChunkOptions, - routeTransformExecutor, + routeTransformRunner, routeByFilePath, routeChunkConfig, isBuild, splitRouteModules, + useRouteModuleTransformApi, ssr, isSpaMode, rootRoutePath, @@ -70,7 +72,7 @@ export const registerBuildOutputTransforms = ({ 'route:module', args.resource, async () => - routeTransformExecutor.run({ + routeTransformRunner({ kind: 'routeModule', code: args.code, resource: args.resource, @@ -154,7 +156,7 @@ export const registerBuildOutputTransforms = ({ 'route:client-entry', args.resource, async () => - routeTransformExecutor.run({ + routeTransformRunner({ kind: 'routeClientEntry', code: args.code, resourcePath: args.resourcePath, @@ -178,7 +180,7 @@ export const registerBuildOutputTransforms = ({ 'route:chunk', args.resource, async () => - routeTransformExecutor.run({ + routeTransformRunner({ kind: 'routeChunk', code: args.code, resource: args.resource, @@ -204,7 +206,7 @@ export const registerBuildOutputTransforms = ({ 'route:split-exports', args.resource, async () => - routeTransformExecutor.run({ + routeTransformRunner({ kind: 'splitRouteExports', code: args.code, resourcePath: args.resourcePath, @@ -244,7 +246,7 @@ export const registerBuildOutputTransforms = ({ 'module:client-only-stub', args.resource, async () => { - return routeTransformExecutor.run({ + return routeTransformRunner({ kind: 'clientOnlyStub', code: args.code, resourcePath: args.resourcePath, @@ -257,22 +259,24 @@ export const registerBuildOutputTransforms = ({ ) ); - api.transform( - { - resourceQuery: /\?react-router-route/, - order: 'post', - }, - transformRouteModule - ); + if (useRouteModuleTransformApi) { + api.transform( + { + resourceQuery: /\?react-router-route/, + order: 'post', + }, + transformRouteModule + ); - api.transform( - { - test: path => routeByFilePath.has(path), - resourceQuery: { - not: /__react-router-build-client-route|react-router-route|route-chunk=/, + api.transform( + { + test: path => routeByFilePath.has(path), + resourceQuery: { + not: /__react-router-build-client-route|react-router-route|route-chunk=/, + }, + order: 'post', }, - order: 'post', - }, - transformRouteModule - ); + transformRouteModule + ); + } }; diff --git a/src/dev-background-resources.ts b/src/dev-background-resources.ts index 64a979a3..c1e76f72 100644 --- a/src/dev-background-resources.ts +++ b/src/dev-background-resources.ts @@ -18,7 +18,6 @@ import { configRoutesToRouteManifestEntries, type ReactRouterManifestForDev, } from './manifest.js'; -import type { RouteTransformExecutor } from './parallel-route-transforms.js'; import { createRouteManifestSnapshot, createRouteTopologyWatcher, @@ -31,7 +30,6 @@ type RegisterReactRouterDevBackgroundResourcesOptions = { api: RsbuildPluginAPI; isBuild: boolean; lazyCompilationPrewarm: PluginOptions['unstableLazyCompilationPrewarm']; - routeTransformExecutor: RouteTransformExecutor; routeRestartMarkerPath: string; watchDirectory: string; getRouteTopology: () => Promise>; @@ -135,7 +133,6 @@ export const registerReactRouterDevBackgroundResources = ({ api, isBuild, lazyCompilationPrewarm, - routeTransformExecutor, routeRestartMarkerPath, watchDirectory, getRouteTopology, @@ -213,10 +210,6 @@ export const registerReactRouterDevBackgroundResources = ({ scheduleRouteTopologyWatcher(); lazyCompilationPrewarmController?.schedule(); }); - - // Spawn transform workers now so thread startup overlaps Rsbuild's own - // compiler creation instead of delaying the first route transform. - routeTransformExecutor.prewarm(); } const closeRouteTopologyWatcher = async (): Promise => { @@ -232,20 +225,12 @@ export const registerReactRouterDevBackgroundResources = ({ ); }; - const closeRouteTransformExecutor = (): Promise => - routeTransformExecutor.close(); - api.onCloseDevServer(() => closeAll( '[rsbuild-plugin-react-router] Failed to close dev server resources.', - [ - closeRouteTopologyWatcher, - closeLazyCompilationPrewarm, - closeRouteTransformExecutor, - ] + [closeRouteTopologyWatcher, closeLazyCompilationPrewarm] ) ); - api.onCloseBuild(closeRouteTransformExecutor); return { setManifest(manifest) { diff --git a/src/federation.ts b/src/federation.ts new file mode 100644 index 00000000..f97ef3ec --- /dev/null +++ b/src/federation.ts @@ -0,0 +1,35 @@ +import type { Rspack } from '@rsbuild/core'; + +type ModuleFederationPluginLike = { + name?: string; + _options?: { experiments?: { asyncStartup?: boolean } }; + options?: { experiments?: { asyncStartup?: boolean } }; +}; + +export const ensureFederationAsyncStartup = ( + rspackConfig: Rspack.Configuration | undefined +): void => { + if (!rspackConfig?.plugins?.length) { + return; + } + + for (const plugin of rspackConfig.plugins) { + if (!plugin || typeof plugin !== 'object') { + continue; + } + const federationPlugin = plugin as ModuleFederationPluginLike; + if (federationPlugin.name !== 'ModuleFederationPlugin') { + continue; + } + + const pluginOptions = federationPlugin._options ?? federationPlugin.options; + if (!pluginOptions) { + continue; + } + + pluginOptions.experiments = { + ...pluginOptions.experiments, + asyncStartup: true, + }; + } +}; diff --git a/src/index.ts b/src/index.ts index f7abb10b..8ad02f6d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -18,6 +18,7 @@ import { PLUGIN_NAME, } from './constants.js'; import { guardReactRouterLazyCompilation } from './lazy-compilation.js'; +import { ensureFederationAsyncStartup } from './federation.js'; import { createReactRouterDevServerSetup } from './dev-server.js'; import { generateWithProps, @@ -52,9 +53,13 @@ import { type RouteChunkConfig, } from './route-chunks.js'; import { - createRouteTransformExecutor, - shouldParallelizeRouteTransforms, -} from './parallel-route-transforms.js'; + registerRouteModuleTransformRules, + shouldUseRouteModuleTransformLoader, +} from './route-module-transform-rules.js'; +import { + executeRouteTransformTask, + type RouteTransformRunner, +} from './route-transform-tasks.js'; import { getRouteRestartMarkerPath, mergeWatchFiles } from './route-watch.js'; import { validateRouteConfig } from './route-config.js'; import { @@ -106,42 +111,6 @@ export const shouldParallelizeEnvironmentBuilds = ({ }): boolean => !isBuild && spareCoreCount >= MIN_PARALLEL_ENVIRONMENT_BUILD_SPARE_CORES; -type ModuleFederationPluginLike = { - name?: string; - _options?: { experiments?: { asyncStartup?: boolean } }; - options?: { experiments?: { asyncStartup?: boolean } }; -}; - -const ensureFederationAsyncStartup = ( - rspackConfig: Rspack.Configuration | undefined -): void => { - if (!rspackConfig?.plugins?.length) { - return; - } - - for (const plugin of rspackConfig.plugins) { - if (!plugin || typeof plugin !== 'object') { - continue; - } - const pluginName = (plugin as ModuleFederationPluginLike).name; - if (pluginName !== 'ModuleFederationPlugin') { - continue; - } - - const pluginOptions = - (plugin as ModuleFederationPluginLike)._options ?? - (plugin as ModuleFederationPluginLike).options; - if (!pluginOptions) { - continue; - } - - pluginOptions.experiments = { - ...pluginOptions.experiments, - asyncStartup: true, - }; - } -}; - const cssUrlAssetExtensions = /\.(?:css|less|sass|scss|styl|stylus|pcss|postcss|sss)$/; const urlAssetResourceQuery = @@ -429,14 +398,11 @@ export const pluginReactRouter = ( rootRouteFile, }; const routeChunkCache: RouteChunkCache = new Map(); - const routeTransformExecutor = createRouteTransformExecutor({ - parallelRouteTransform: - pluginOptions.parallelRouteTransform ?? - shouldParallelizeRouteTransforms(routeCount), - routeChunkCache, - splitRouteModules: Boolean(splitRouteModules), - isBuild, - }); + const useRouteModuleTransformLoader = shouldUseRouteModuleTransformLoader( + pluginOptions.parallelRouteTransform + ); + const routeTransformRunner: RouteTransformRunner = task => + executeRouteTransformTask(task, { routeChunkCache }); const routeChunkOptions = { splitRouteModules, rootRouteFile, @@ -457,7 +423,6 @@ export const pluginReactRouter = ( api, isBuild, lazyCompilationPrewarm: pluginOptions.unstableLazyCompilationPrewarm, - routeTransformExecutor, routeRestartMarkerPath, watchDirectory, getRouteTopology: routeTopology.getRouteTopology, @@ -783,7 +748,6 @@ export const pluginReactRouter = ( routeCount >= 256 && (config.performance?.printFileSize === undefined || config.performance.printFileSize === true); - return mergeRsbuildConfig(config, { ...(shouldCompactFileSizeReport ? { @@ -943,17 +907,34 @@ export const pluginReactRouter = ( return mergeEnvironmentConfig(config, { tools: { rspack: rspackConfig => { + const environmentDevHmrEnabled = + name === 'web' && + !isBuild && + devHmrRefreshRuntimePath !== undefined && + config.mode === 'development' && + config.dev?.hmr !== false && + isRspackSwcReactRefreshEnabled(rspackConfig); + + if (useRouteModuleTransformLoader) { + registerRouteModuleTransformRules(rspackConfig, { + environmentName: name, + ssr, + isBuild, + isSpaMode, + rootRoutePath, + devHmr: environmentDevHmrEnabled, + logPerformance, + routeByFilePath, + parallelRouteTransform: pluginOptions.parallelRouteTransform, + }); + } + if (pluginOptions.federation) { ensureFederationAsyncStartup(rspackConfig); } if (name === 'web') { - devHmrEnabled = - !isBuild && - devHmrRefreshRuntimePath !== undefined && - config.mode === 'development' && - config.dev?.hmr !== false && - isRspackSwcReactRefreshEnabled(rspackConfig); + devHmrEnabled = environmentDevHmrEnabled; if (devHmrEnabled) { devHdrSignal?.ensure(); } @@ -1023,11 +1004,12 @@ export const pluginReactRouter = ( appDirectory, getAssetPrefix: () => assetPrefix, routeChunkOptions, - routeTransformExecutor, + routeTransformRunner, routeByFilePath, routeChunkConfig, isBuild, splitRouteModules: Boolean(splitRouteModules), + useRouteModuleTransformApi: !useRouteModuleTransformLoader, ssr, isSpaMode, rootRoutePath, diff --git a/src/parallel-route-transform-protocol.ts b/src/parallel-route-transform-protocol.ts deleted file mode 100644 index 24929229..00000000 --- a/src/parallel-route-transform-protocol.ts +++ /dev/null @@ -1,35 +0,0 @@ -import type { - RouteTransformResult, - RouteTransformTask, -} from './route-transform-tasks.js'; - -type WithoutRequiredSource = Task extends RouteTransformTask - ? Omit & { code?: string } - : never; - -export type CachedRouteTransformTask = - WithoutRequiredSource; - -export type WorkerRequest = { - id: number; - task: RouteTransformTask | CachedRouteTransformTask; - sourceCacheKey?: string; -}; - -export type WorkerErrorPayload = { - name?: string; - message: string; - stack?: string; -}; - -export type WorkerResponse = - | { - id: number; - ok: true; - result: RouteTransformResult; - } - | { - id: number; - ok: false; - error: WorkerErrorPayload; - }; diff --git a/src/parallel-route-transform-worker.ts b/src/parallel-route-transform-worker.ts deleted file mode 100644 index 36e85e2f..00000000 --- a/src/parallel-route-transform-worker.ts +++ /dev/null @@ -1,82 +0,0 @@ -import { parentPort } from 'node:worker_threads'; -import { setBoundedCacheEntry } from './bounded-cache.js'; -import { - executeRouteTransformTask, - type RouteTransformTask, -} from './route-transform-tasks.js'; -import type { - WorkerErrorPayload, - WorkerRequest, - WorkerResponse, -} from './parallel-route-transform-protocol.js'; - -const serializeError = (error: unknown): WorkerErrorPayload => { - if (error instanceof Error) { - return { - name: error.name, - message: error.message, - stack: error.stack, - }; - } - return { - message: String(error), - }; -}; - -if (!parentPort) { - throw new Error('parallel route transform worker requires parentPort'); -} - -const MAX_SOURCE_CACHE_ENTRIES = 2048; -const sourceCache = new Map(); - -const hydrateTaskSource = ({ - task, - sourceCacheKey, -}: Pick): RouteTransformTask => { - if (!sourceCacheKey) { - return task as RouteTransformTask; - } - - if (typeof task.code === 'string') { - setBoundedCacheEntry( - sourceCache, - sourceCacheKey, - task.code, - MAX_SOURCE_CACHE_ENTRIES - ); - return task as RouteTransformTask; - } - - const code = sourceCache.get(sourceCacheKey); - if (code === undefined) { - throw new Error( - `Missing cached route transform source for ${sourceCacheKey}.` - ); - } - return { - ...task, - code, - } as RouteTransformTask; -}; - -parentPort.on( - 'message', - async ({ id, task, sourceCacheKey }: WorkerRequest) => { - try { - const hydratedTask = hydrateTaskSource({ task, sourceCacheKey }); - const result = await executeRouteTransformTask(hydratedTask); - parentPort?.postMessage({ - id, - ok: true, - result, - } satisfies WorkerResponse); - } catch (error) { - parentPort?.postMessage({ - id, - ok: false, - error: serializeError(error), - } satisfies WorkerResponse); - } - } -); diff --git a/src/parallel-route-transforms.ts b/src/parallel-route-transforms.ts deleted file mode 100644 index 06adae3e..00000000 --- a/src/parallel-route-transforms.ts +++ /dev/null @@ -1,458 +0,0 @@ -import { Worker } from 'node:worker_threads'; -import { setBoundedCacheEntry } from './bounded-cache.js'; -import { getAvailableCpuCount, getDefaultConcurrency } from './concurrency.js'; -import { normalizeEffectError } from './effect-runtime.js'; -import { - executeRouteTransformTask, - type RouteTransformResult, - type RouteTransformTask, - type RouteTransformTaskOptions, -} from './route-transform-tasks.js'; -import type { PluginOptions } from './types.js'; -import type { - WorkerErrorPayload, - WorkerRequest, - WorkerResponse, -} from './parallel-route-transform-protocol.js'; - -export type ParallelRouteTransformConfig = - NonNullable extends infer Config - ? Exclude - : never; - -export type RouteTransformExecutorOptions = RouteTransformTaskOptions & { - parallelRouteTransform?: PluginOptions['parallelRouteTransform']; - splitRouteModules?: boolean; - isBuild?: boolean; -}; - -export type RouteTransformExecutor = { - run: (task: RouteTransformTask) => Promise; - prewarm: () => void; - close: () => Promise; -}; - -type PendingTask = { - resolve: (result: RouteTransformResult) => void; - reject: (error: Error) => void; -}; - -type RouteTransformWorker = { - on(event: 'message', handler: (response: WorkerResponse) => void): unknown; - on(event: 'error', handler: (error: Error) => void): unknown; - on(event: 'exit', handler: (code: number) => void): unknown; - postMessage(message: WorkerRequest): void; - terminate(): Promise | number; -}; - -type RouteTransformWorkerFactory = () => RouteTransformWorker; - -type WorkerState = { - worker: RouteTransformWorker; - pending: Map; - sourceCache: Map; -}; - -class WorkerStartupError extends Error { - constructor(message: string) { - super(message); - this.name = 'WorkerStartupError'; - } -} - -const MAX_WORKER_SOURCE_CACHE_ENTRIES = 2048; -const MAX_ROUTE_SOURCE_WORKER_ENTRIES = 4096; -const AUTO_PARALLEL_ROUTE_THRESHOLD = 256; -const DEFAULT_WORKER_COUNT_LIMIT = 2; -// Benchmarked on a 4-core cpuset: in dev (large-355 fixture) a single worker -// recovered no wall time while worker startup and IPC cost ~0.5s of -// dev-server ready (9.09s -> 8.57s median when disabled), so small machines -// run dev transforms inline. Production builds are different: at 1024 routes -// the transform volume is large enough that two workers beat inline by ~8% -// (synthetic-1024-ssr-esm build, 4.46s inline vs 4.09s with workers), so -// builds keep the default worker count even on small machines. -const SMALL_MACHINE_CPU_COUNT = 4; - -export const getDefaultWorkerCount = ( - cpuCount?: number, - { isBuild = false }: { isBuild?: boolean } = {} -): number => { - const resolvedCpuCount = cpuCount ?? getAvailableCpuCount(); - if (!isBuild && resolvedCpuCount <= SMALL_MACHINE_CPU_COUNT) { - return 0; - } - return Math.min( - getDefaultConcurrency(resolvedCpuCount), - DEFAULT_WORKER_COUNT_LIMIT - ); -}; - -export const shouldParallelizeRouteTransforms = (routeCount: number): boolean => - routeCount >= AUTO_PARALLEL_ROUTE_THRESHOLD; - -const getConfiguredWorkerCount = ( - parallelRouteTransform: ParallelRouteTransformConfig, - isBuild: boolean -): number => { - if (parallelRouteTransform === true) { - return getDefaultWorkerCount(undefined, { isBuild }); - } - - if (!Number.isInteger(parallelRouteTransform) || parallelRouteTransform < 1) { - throw new Error( - '[react-router] parallelRouteTransform must be true, false, or a positive integer.' - ); - } - return parallelRouteTransform; -}; - -const hashString = (value: string): number => { - let hash = 0; - for (let index = 0; index < value.length; index += 1) { - hash = (hash * 31 + value.charCodeAt(index)) >>> 0; - } - return hash; -}; - -const deserializeWorkerError = (error: WorkerErrorPayload): Error => { - const result = new Error(error.message); - result.name = error.name ?? 'Error'; - if (error.stack) { - result.stack = error.stack; - } - return result; -}; - -const createWorkerUrl = (): URL => - new URL('./parallel-route-transform-worker.js', import.meta.url); - -const createDefaultWorker = (): RouteTransformWorker => - new Worker(createWorkerUrl()); - -const isWorkerStartupError = (error: unknown): error is WorkerStartupError => - error instanceof WorkerStartupError; - -const createWorkerStartupError = (error: unknown): WorkerStartupError => { - const normalized = normalizeEffectError(error); - const startupError = new WorkerStartupError(normalized.message); - startupError.stack = normalized.stack; - return startupError; -}; - -const rejectPendingTasks = (state: WorkerState, error: Error): void => { - for (const pending of state.pending.values()) { - pending.reject(error); - } - state.pending.clear(); -}; - -const createInlineRouteTransformExecutor = ( - options: RouteTransformTaskOptions -): RouteTransformExecutor => ({ - run: task => executeRouteTransformTask(task, options), - prewarm: () => {}, - close: async () => {}, -}); - -class ParallelRouteTransformExecutor implements RouteTransformExecutor { - #closed = false; - #closePromise: Promise | undefined; - #workersDisabled = false; - #nextId = 1; - #nextRouteSourceWorkerIndex = 0; - #routeSourceWorkers = new Map(); - #workers: Array | undefined; - #prewarmScheduled = false; - - constructor( - private readonly workerCount: number, - private readonly options: RouteTransformTaskOptions, - private readonly balanceRouteModuleTransforms: boolean, - private readonly createWorker: RouteTransformWorkerFactory - ) {} - - async run(task: RouteTransformTask): Promise { - if (this.#closed) { - return executeRouteTransformTask(task, this.options); - } - if (task.kind === 'clientOnlyStub' && task.resolveExportAllModule) { - return executeRouteTransformTask(task, this.options); - } - - try { - return await this.#runInWorker(task); - } catch (error) { - if (isWorkerStartupError(error)) { - return executeRouteTransformTask(task, this.options); - } - throw error; - } - } - - prewarm(): void { - if (this.#prewarmScheduled || this.#closed || this.#workersDisabled) { - return; - } - this.#prewarmScheduled = true; - void this.#prewarmWorkers(); - } - - async #prewarmWorkers(): Promise { - try { - for (let index = 0; index < this.workerCount; index += 1) { - if (this.#closed || this.#workersDisabled) { - return; - } - try { - this.#getWorker(index); - } catch (error) { - this.#disableWorkers(createWorkerStartupError(error)); - return; - } - } - } finally { - this.#prewarmScheduled = false; - } - } - - close(): Promise { - if (this.#closePromise) { - return this.#closePromise; - } - this.#closed = true; - const workers = (this.#workers ?? []).filter( - (state): state is WorkerState => Boolean(state) - ); - this.#workers = []; - this.#closePromise = Promise.all( - workers.map(async state => { - rejectPendingTasks(state, new Error('Route transform worker closed.')); - await state.worker.terminate(); - }) - ).then(() => undefined); - return this.#closePromise; - } - - #disableWorkers(error: WorkerStartupError): void { - if (this.#workersDisabled || this.#closed) { - return; - } - this.#workersDisabled = true; - const workers = (this.#workers ?? []).filter( - (state): state is WorkerState => Boolean(state) - ); - this.#workers = []; - for (const state of workers) { - rejectPendingTasks(state, error); - void state.worker.terminate(); - } - } - - #createWorkerState(): WorkerState { - const worker = this.createWorker(); - const state: WorkerState = { - worker, - pending: new Map(), - sourceCache: new Map(), - }; - - worker.on('message', (response: WorkerResponse) => { - const pending = state.pending.get(response.id); - if (!pending) { - return; - } - state.pending.delete(response.id); - if (response.ok) { - pending.resolve(response.result); - } else { - pending.reject(deserializeWorkerError(response.error)); - } - }); - - worker.on('error', (error: Error) => { - this.#disableWorkers(createWorkerStartupError(error)); - }); - - worker.on('exit', code => { - if (this.#closed || this.#workersDisabled) { - return; - } - const startupError = new WorkerStartupError( - `Route transform worker exited with code ${code}.` - ); - this.#disableWorkers(startupError); - }); - - return state; - } - - #getWorker(index: number): WorkerState | undefined { - if (this.#closed || this.#workersDisabled) { - return undefined; - } - this.#workers ??= new Array(this.workerCount); - const existingWorker = this.#workers[index]; - if (existingWorker) { - return existingWorker; - } - - const worker = this.#createWorkerState(); - this.#workers[index] = worker; - return worker; - } - - #runInWorker(task: RouteTransformTask): Promise { - const workerIndex = this.#getWorkerIndex(task, this.workerCount); - const state = this.#getWorker(workerIndex); - if (!state) { - return executeRouteTransformTask(task, this.options); - } - - const id = this.#nextId++; - const sourceCacheKey = task.resourcePath; - const requestTask = this.#createWorkerRequestTask( - state, - task, - sourceCacheKey - ); - return new Promise((resolve, reject) => { - state.pending.set(id, { resolve, reject }); - try { - state.worker.postMessage({ - id, - task: requestTask, - sourceCacheKey, - } satisfies WorkerRequest); - } catch (error) { - state.pending.delete(id); - // The worker may not have received the source update. Force the next - // request for this module to send its full source again. - state.sourceCache.delete(sourceCacheKey); - reject(normalizeEffectError(error)); - } - }); - } - - #createWorkerRequestTask( - state: WorkerState, - task: RouteTransformTask, - sourceCacheKey: string - ): WorkerRequest['task'] { - const cachedSource = state.sourceCache.get(sourceCacheKey); - if (cachedSource === task.code) { - const { code: _code, ...cachedTask } = task; - return cachedTask; - } - - setBoundedCacheEntry( - state.sourceCache, - sourceCacheKey, - task.code, - MAX_WORKER_SOURCE_CACHE_ENTRIES - ); - return task; - } - - #getWorkerIndex(task: RouteTransformTask, workerCount: number): number { - const safeWorkerCount = Math.max(1, workerCount); - if ( - this.balanceRouteModuleTransforms && - (task.kind === 'routeClientEntry' || - task.kind === 'routeChunk' || - task.kind === 'splitRouteExports') - ) { - return this.#getRouteSourceWorkerIndex( - task.resourcePath, - safeWorkerCount - ); - } - if ( - this.balanceRouteModuleTransforms && - task.kind === 'routeModule' && - !(task.environmentName === 'web' && !task.ssr && task.isSpaMode) - ) { - return this.#getRouteSourceWorkerIndex( - task.resourcePath, - safeWorkerCount - ); - } - return hashString(task.resourcePath) % safeWorkerCount; - } - - #getRouteSourceWorkerIndex( - resourcePath: string, - safeWorkerCount: number - ): number { - const existingWorkerIndex = this.#routeSourceWorkers.get(resourcePath); - if (existingWorkerIndex !== undefined) { - this.#routeSourceWorkers.delete(resourcePath); - this.#routeSourceWorkers.set(resourcePath, existingWorkerIndex); - return existingWorkerIndex % safeWorkerCount; - } - const workerIndex = this.#nextRouteSourceWorkerIndex % safeWorkerCount; - this.#nextRouteSourceWorkerIndex += 1; - setBoundedCacheEntry( - this.#routeSourceWorkers, - resourcePath, - workerIndex, - MAX_ROUTE_SOURCE_WORKER_ENTRIES - ); - return workerIndex; - } -} - -export const createRouteTransformExecutor = ({ - parallelRouteTransform, - routeChunkCache, - splitRouteModules, - isBuild, -}: RouteTransformExecutorOptions = {}): RouteTransformExecutor => { - return createRouteTransformExecutorWithWorkerFactory( - { - parallelRouteTransform, - routeChunkCache, - splitRouteModules, - isBuild, - }, - createDefaultWorker - ); -}; - -const createRouteTransformExecutorWithWorkerFactory = ( - { - parallelRouteTransform, - routeChunkCache, - splitRouteModules, - isBuild, - }: RouteTransformExecutorOptions = {}, - createWorker: RouteTransformWorkerFactory -): RouteTransformExecutor => { - const options = { routeChunkCache }; - if ( - parallelRouteTransform === undefined || - parallelRouteTransform === false - ) { - return createInlineRouteTransformExecutor(options); - } - - const workerCount = getConfiguredWorkerCount( - parallelRouteTransform, - Boolean(isBuild) - ); - if (workerCount < 1) { - return createInlineRouteTransformExecutor(options); - } - - return new ParallelRouteTransformExecutor( - workerCount, - options, - Boolean(splitRouteModules), - createWorker - ); -}; - -export const createRouteTransformExecutorForTesting = ( - options: RouteTransformExecutorOptions, - createWorker: RouteTransformWorkerFactory -): RouteTransformExecutor => - createRouteTransformExecutorWithWorkerFactory(options, createWorker); diff --git a/src/performance.ts b/src/performance.ts index f15c8e2e..bb0822c1 100644 --- a/src/performance.ts +++ b/src/performance.ts @@ -54,9 +54,15 @@ export const roundMs = (value: number): number => Math.round(value * 10) / 10; export type ReactRouterPerformanceReport = { environment: string; compilerLifecycleMs?: number; + partial?: boolean; + workerId?: string; operations: Record; }; +export const formatReactRouterPerformanceReport = ( + report: ReactRouterPerformanceReport +): string => `[react-router:performance] ${JSON.stringify(report)}`; + export type ReactRouterPerformanceProfiler = { record( environment: string | undefined, @@ -72,7 +78,10 @@ export type ReactRouterPerformanceProfiler = { ): T; flush( environment: string | undefined, - details?: Pick + details?: Pick< + ReactRouterPerformanceReport, + 'compilerLifecycleMs' | 'partial' | 'workerId' + > ): void; }; @@ -247,7 +256,7 @@ export const createReactRouterPerformanceProfiler = ({ ...details, operations, }; - log(`[react-router:performance] ${JSON.stringify(report)}`); + log(formatReactRouterPerformanceReport(report)); timingsByEnvironment.delete(resolvedEnvironment); }, }; diff --git a/src/route-module-transform-loader.ts b/src/route-module-transform-loader.ts new file mode 100644 index 00000000..c0e22504 --- /dev/null +++ b/src/route-module-transform-loader.ts @@ -0,0 +1,149 @@ +import { threadId } from 'node:worker_threads'; +import remapping, { type SourceMapInput } from '@jridgewell/remapping'; +import type { LoaderDefinition } from '@rspack/core'; +import { createReactRouterPerformanceProfiler } from './performance.js'; +import { executeRouteTransformTask } from './route-transform-tasks.js'; + +export type RouteModuleTransformLoaderOptions = { + environmentName: string; + performanceScopeId: string; + logPerformance: boolean; + ssr: boolean; + isBuild: boolean; + isSpaMode: boolean; + rootRoutePath: string | null; + devHmr: boolean; +}; + +type LoaderContextWithSourceMap = ThisParameterType< + LoaderDefinition +> & { + sourceMap?: boolean; +}; +type LoaderInputSourceMap = Parameters< + LoaderDefinition +>[1]; +type RawLoaderSourceMap = Exclude; + +const LOADER_PERFORMANCE_FLUSH_DELAY_MS = 50; +const performanceWorkerId = `process-${process.pid}:thread-${threadId}`; + +const composeSourceMaps = ( + generatedMap: SourceMapInput, + inputSourceMap: SourceMapInput +): RawLoaderSourceMap => + JSON.parse( + remapping([generatedMap, inputSourceMap], () => null).toString() + ) as RawLoaderSourceMap; + +const performanceProfilersByScope = new Map< + string, + Map> +>(); +let performanceFlushTimer: ReturnType | undefined; + +const getPerformanceProfiler = ( + scopeId: string, + environment: string | undefined +): ReturnType => { + let profilers = performanceProfilersByScope.get(scopeId); + if (!profilers) { + profilers = new Map(); + performanceProfilersByScope.set(scopeId, profilers); + } + const environmentKey = environment ?? 'unknown'; + let profiler = profilers.get(environmentKey); + if (!profiler) { + profiler = createReactRouterPerformanceProfiler({ + enabled: true, + log: message => console.info(message), + }); + profilers.set(environmentKey, profiler); + } + return profiler; +}; + +export const flushRouteModuleTransformLoaderPerformance = (): void => { + if (performanceFlushTimer) { + clearTimeout(performanceFlushTimer); + performanceFlushTimer = undefined; + } + + for (const profilers of performanceProfilersByScope.values()) { + for (const [environment, profiler] of profilers) { + profiler.flush(environment === 'unknown' ? undefined : environment, { + partial: true, + workerId: performanceWorkerId, + }); + } + } + performanceProfilersByScope.clear(); +}; + +const schedulePerformanceFlush = (): void => { + if (performanceFlushTimer) { + clearTimeout(performanceFlushTimer); + } + performanceFlushTimer = setTimeout( + flushRouteModuleTransformLoaderPerformance, + LOADER_PERFORMANCE_FLUSH_DELAY_MS + ); +}; + +process.once('beforeExit', flushRouteModuleTransformLoaderPerformance); + +const routeModuleTransformLoader: LoaderDefinition = + async function routeModuleTransformLoader( + this: LoaderContextWithSourceMap, + source, + inputSourceMap + ) { + const callback = this.async(); + const options = this.getOptions(); + + const transform = () => + executeRouteTransformTask({ + kind: 'routeModule', + code: String(source), + resource: this.resource, + resourcePath: this.resourcePath, + environmentName: options.environmentName, + sourceMaps: Boolean(this.sourceMap), + ssr: options.ssr, + isBuild: options.isBuild, + isSpaMode: options.isSpaMode, + rootRoutePath: options.rootRoutePath, + devHmr: options.devHmr, + }); + + try { + const result = options.logPerformance + ? await getPerformanceProfiler( + options.performanceScopeId, + options.environmentName + ).record( + options.environmentName, + 'route:module', + this.resource, + transform + ) + : await transform(); + + const outputSourceMap = + result.map && inputSourceMap + ? composeSourceMaps( + result.map as SourceMapInput, + inputSourceMap as SourceMapInput + ) + : result.map; + callback(null, result.code, outputSourceMap ?? undefined); + } catch (error) { + callback(error instanceof Error ? error : new Error(String(error))); + } finally { + if (options.logPerformance) { + schedulePerformanceFlush(); + } + } + }; + +export default routeModuleTransformLoader; diff --git a/src/route-module-transform-rules.ts b/src/route-module-transform-rules.ts new file mode 100644 index 00000000..f23bfc74 --- /dev/null +++ b/src/route-module-transform-rules.ts @@ -0,0 +1,115 @@ +import { fileURLToPath } from 'node:url'; +import type { Rspack } from '@rsbuild/core'; +import type { RouteModuleTransformLoaderOptions } from './route-module-transform-loader.js'; +import type { PluginOptions, Route } from './types.js'; + +const routeModuleTransformLoaderPath = fileURLToPath( + new URL('./route-module-transform-loader.js', import.meta.url) +); + +const getRouteModuleTransformParallel = ( + parallelRouteTransform: PluginOptions['parallelRouteTransform'] +): Rspack.RuleSetLoaderWithOptions['parallel'] => { + if ( + parallelRouteTransform === undefined || + parallelRouteTransform === false + ) { + return undefined; + } + if (parallelRouteTransform === true) { + return true; + } + if (typeof parallelRouteTransform === 'number') { + if ( + !Number.isInteger(parallelRouteTransform) || + parallelRouteTransform < 1 + ) { + throw new Error( + '[react-router] parallelRouteTransform must be true, false, or a positive integer.' + ); + } + return { maxWorkers: parallelRouteTransform }; + } + throw new Error( + '[react-router] parallelRouteTransform must be true, false, or a positive integer.' + ); +}; + +export const shouldUseRouteModuleTransformLoader = ( + parallelRouteTransform: PluginOptions['parallelRouteTransform'] +): boolean => + getRouteModuleTransformParallel(parallelRouteTransform) !== undefined; + +const createRouteModuleTransformUse = ( + options: RouteModuleTransformLoaderOptions, + parallelRouteTransform: PluginOptions['parallelRouteTransform'] +): Rspack.RuleSetLoaderWithOptions => { + const parallel = getRouteModuleTransformParallel(parallelRouteTransform); + return { + loader: routeModuleTransformLoaderPath, + options, + ...(parallel ? { parallel } : {}), + }; +}; + +export const registerRouteModuleTransformRules = ( + rspackConfig: Rspack.Configuration, + { + environmentName, + ssr, + isBuild, + isSpaMode, + rootRoutePath, + devHmr, + logPerformance, + routeByFilePath, + parallelRouteTransform, + }: { + environmentName: string; + ssr: boolean; + isBuild: boolean; + isSpaMode: boolean; + rootRoutePath: string | null; + devHmr: boolean; + logPerformance: boolean; + routeByFilePath: Map; + parallelRouteTransform: PluginOptions['parallelRouteTransform']; + } +): void => { + const routeModuleTransformUse = createRouteModuleTransformUse( + { + environmentName, + performanceScopeId: [ + environmentName, + isBuild ? 'build' : 'dev', + ssr ? 'ssr' : 'spa', + rootRoutePath ?? 'rootless', + ].join(':'), + logPerformance, + ssr, + isBuild, + isSpaMode, + rootRoutePath, + devHmr, + }, + parallelRouteTransform + ); + + rspackConfig.module ??= {}; + rspackConfig.module.rules ??= []; + rspackConfig.module.rules.push( + { + resourceQuery: /\?react-router-route/, + enforce: 'post', + use: [routeModuleTransformUse], + }, + { + test: path => routeByFilePath.has(path), + resourceQuery: { + not: /__react-router-build-client-route|react-router-route|route-chunk=/, + }, + enforce: 'post', + use: [routeModuleTransformUse], + } + ); +}; diff --git a/src/route-transform-tasks.ts b/src/route-transform-tasks.ts index 8deb9b7f..30e82b0c 100644 --- a/src/route-transform-tasks.ts +++ b/src/route-transform-tasks.ts @@ -24,10 +24,7 @@ import { type RouteChunkCache, type RouteChunkConfig, } from './route-chunks.js'; -import { - getProgram, - type AnyNode, -} from './route-ast.js'; +import { getProgram, type AnyNode } from './route-ast.js'; export type RouteTransformResult = { code: string; @@ -88,6 +85,10 @@ export type RouteTransformTaskOptions = { routeChunkCache?: RouteChunkCache; }; +export type RouteTransformRunner = ( + task: RouteTransformTask +) => Promise; + const defaultRouteChunkCache: RouteChunkCache = new Map(); const getRouteChunkCache = (options?: RouteTransformTaskOptions) => diff --git a/src/types.ts b/src/types.ts index 8029eec0..b0bea383 100644 --- a/src/types.ts +++ b/src/types.ts @@ -57,11 +57,10 @@ export type PluginOptions = { logPerformance?: boolean; /** - * Run route transforms in a worker-thread pool. - * Pass `true` to force the default worker count, a positive integer to set - * the worker count, or `false` to disable. - * @default Automatically enabled for 256+ resolved routes. The automatic - * pool uses available CPU cores minus 2. + * Run route module transforms with Rspack's parallel loader workers. + * Pass `true` to use Rspack's default worker count, a positive integer to set + * the maximum worker count, or `false` to disable. + * @default false */ parallelRouteTransform?: boolean | number; diff --git a/tests/benchmark-results.test.ts b/tests/benchmark-results.test.ts new file mode 100644 index 00000000..f31c544d --- /dev/null +++ b/tests/benchmark-results.test.ts @@ -0,0 +1,49 @@ +import { describe, expect, it } from '@rstest/core'; +import { summarizePluginOperations } from '../scripts/benchmark/results.mts'; + +describe('benchmark plugin result summaries', () => { + it('retains attribution for partial reports from distinct loader workers', () => { + const [operation] = summarizePluginOperations([ + { + pluginReports: [ + { + environment: 'web', + partial: true, + workerId: 'process-1:thread-1', + operations: { + 'route:module': { + count: 2, + totalMs: 10, + wallMs: 8, + maxMs: 6, + }, + }, + }, + { + environment: 'web', + partial: true, + workerId: 'process-1:thread-2', + operations: { + 'route:module': { + count: 3, + totalMs: 12, + wallMs: 9, + maxMs: 5, + }, + }, + }, + ], + }, + ]); + + expect(operation).toMatchObject({ + environment: 'web', + operation: 'route:module', + count: 5, + reports: 2, + partialReports: 2, + workers: 2, + wallMs: null, + }); + }); +}); diff --git a/tests/build-output-transforms.test.ts b/tests/build-output-transforms.test.ts index efefeaab..dfae76b6 100644 --- a/tests/build-output-transforms.test.ts +++ b/tests/build-output-transforms.test.ts @@ -23,7 +23,8 @@ const createTransformHarness = () => { }; const createBaseOptions = ( - transforms: ReturnType + transforms: ReturnType, + useRouteModuleTransformApi = true ) => { const appDirectory = resolve('/project/app'); const routePath = resolve(appDirectory, 'routes/page.tsx'); @@ -45,10 +46,9 @@ const createBaseOptions = ( appDirectory, getAssetPrefix: () => '/', routeChunkOptions: { isBuild: true }, - routeTransformExecutor: { - run: rstest.fn(async task => ({ code: `${task.kind}:${task.code}` })), - close: rstest.fn(async () => undefined), - }, + routeTransformRunner: rstest.fn(async task => ({ + code: `${task.kind}:${task.code}`, + })), routeByFilePath: new Map([[routePath, { id: 'page' }]]), routeChunkConfig: { splitRouteModules: true, @@ -57,6 +57,7 @@ const createBaseOptions = ( }, isBuild: true, splitRouteModules: true, + useRouteModuleTransformApi, ssr: true, isSpaMode: false, rootRoutePath: resolve(appDirectory, 'root.tsx'), @@ -81,7 +82,28 @@ const createTransformArgs = ( }) as never; describe('build output transforms', () => { - it('registers post-order route-module transforms for explicit and queryless route modules', async () => { + it('does not register route-module transforms through api.transform when the loader is enabled', () => { + const harness = createTransformHarness(); + const options = createBaseOptions(harness, false); + + registerBuildOutputTransforms(options); + + expect( + harness.transforms.find( + transform => + transform.descriptor.resourceQuery?.toString() === + String(/\?react-router-route/) + ) + ).toBeUndefined(); + expect( + harness.transforms.find(transform => transform.descriptor.order === 'post') + ).toBeUndefined(); + expect(options.routeTransformRunner).not.toHaveBeenCalledWith( + expect.objectContaining({ kind: 'routeModule' }) + ); + }); + + it('registers route-module transforms through api.transform by default', async () => { const harness = createTransformHarness(); const options = createBaseOptions(harness); @@ -99,11 +121,6 @@ describe('build output transforms', () => { expect(routeModuleTransforms[1].descriptor).toMatchObject({ order: 'post', }); - expect( - (routeModuleTransforms[1].descriptor.test as (path: string) => boolean)( - options.routePath - ) - ).toBe(true); await routeModuleTransforms[0].handler( createTransformArgs(options.routePath, '?react-router-route') @@ -112,16 +129,15 @@ describe('build output transforms', () => { createTransformArgs(options.routePath) ); - const run = options.routeTransformExecutor.run; - expect(run).toHaveBeenCalledWith( + expect(options.routeTransformRunner).toHaveBeenCalledWith( expect.objectContaining({ kind: 'routeModule' }) ); - expect(run).toHaveBeenCalledTimes(2); + expect(options.routeTransformRunner).toHaveBeenCalledTimes(2); }); it('reads dev HMR enablement when route transforms run', async () => { const harness = createTransformHarness(); - const options = createBaseOptions(harness); + const options = createBaseOptions(harness, true); let devHmrEnabled = false; registerBuildOutputTransforms({ @@ -138,30 +154,30 @@ describe('build output transforms', () => { devHmrEnabled = true; await routeModuleTransform!.handler(createTransformArgs(options.routePath)); - expect(options.routeTransformExecutor.run).toHaveBeenNthCalledWith( + expect(options.routeTransformRunner).toHaveBeenNthCalledWith( 1, expect.objectContaining({ devHmr: false }) ); - expect(options.routeTransformExecutor.run).toHaveBeenNthCalledWith( + expect(options.routeTransformRunner).toHaveBeenNthCalledWith( 2, expect.objectContaining({ devHmr: true }) ); }); - it('does not match queryless route-module transforms for internal route requests', () => { + it('does not match split-export transforms for internal route requests', () => { const harness = createTransformHarness(); const options = createBaseOptions(harness); registerBuildOutputTransforms(options); - const querylessRouteModuleTransform = harness.transforms.find( + const splitExportsTransform = harness.transforms.find( transform => - transform.descriptor.order === 'post' && + transform.descriptor.environments?.includes('web') && typeof transform.descriptor.test === 'function' ); - expect(querylessRouteModuleTransform).toBeDefined(); - const predicate = querylessRouteModuleTransform!.descriptor.test as ( + expect(splitExportsTransform).toBeDefined(); + const predicate = splitExportsTransform!.descriptor.test as ( path: string ) => boolean; expect(predicate(options.routePath)).toBe(true); @@ -169,8 +185,9 @@ describe('build output transforms', () => { false ); - const resourceQuery = querylessRouteModuleTransform!.descriptor - .resourceQuery as { not: RegExp }; + const resourceQuery = splitExportsTransform!.descriptor.resourceQuery as { + not: RegExp; + }; expect(resourceQuery.not.test('?__react-router-build-client-route')).toBe( true ); diff --git a/tests/dev-runtime.integration.test.ts b/tests/dev-runtime.integration.test.ts index 09501fdd..42ae0e4a 100644 --- a/tests/dev-runtime.integration.test.ts +++ b/tests/dev-runtime.integration.test.ts @@ -1,4 +1,3 @@ -import { EventEmitter } from 'node:events'; import { spawn } from 'node:child_process'; import * as fs from 'node:fs'; import { createServer, type Server as HttpServer } from 'node:http'; @@ -89,18 +88,6 @@ const getBuildMarker = (build: ServerBuild): string | undefined => { return undefined; }; -const createNoopWatcher = (): fs.FSWatcher => { - const watcher = new EventEmitter() as EventEmitter & { - close: () => void; - ref: () => fs.FSWatcher; - unref: () => fs.FSWatcher; - }; - watcher.close = () => undefined; - watcher.ref = () => watcher as fs.FSWatcher; - watcher.unref = () => watcher as fs.FSWatcher; - return watcher as fs.FSWatcher; -}; - const runEsmIntegrationSubprocess = (repositoryRoot: string): Promise => new Promise((resolve, reject) => { const rstestCli = join( @@ -193,9 +180,6 @@ const createDevRuntimeHarness = async (esm: boolean) => { mockRestore?: () => void; }; existsSyncMock.mockRestore?.(); - const watchMock = rstest - .spyOn(fs, 'watch') - .mockImplementation((() => createNoopWatcher()) as typeof fs.watch); const logger = createLogger({ level: 'silent' }); const loggerError = rstest.spyOn(logger, 'error'); const consoleError = rstest @@ -208,6 +192,7 @@ const createDevRuntimeHarness = async (esm: boolean) => { let compiler: Rspack.MultiCompiler | undefined; let compileAttempts = 0; let completedCompiles = 0; + let latestWebAssets = new Set(); let cleaned = false; const closeBuiltInServer = async (): Promise => { @@ -241,7 +226,6 @@ const createDevRuntimeHarness = async (esm: boolean) => { } loggerError.mockRestore(); consoleError.mockRestore(); - watchMock.mockRestore(); rmSync(fixtureRoot, { force: true, recursive: true }); } }; @@ -261,6 +245,13 @@ const createDevRuntimeHarness = async (esm: boolean) => { api.onAfterDevCompile(() => { completedCompiles += 1; }); + api.onAfterEnvironmentCompile(({ environment, stats }) => { + if (environment.name === 'web') { + latestWebAssets = new Set( + stats?.compilation.getAssets().map(asset => asset.name) + ); + } + }); api.onAfterCreateCompiler(({ compiler: createdCompiler }) => { if (!('compilers' in createdCompiler)) { throw new Error('Expected Rsbuild to create a MultiCompiler.'); @@ -397,8 +388,12 @@ const createDevRuntimeHarness = async (esm: boolean) => { loggerError.mock.calls.some(args => args.some(arg => String(arg).includes(marker)) ), + hasEmittedWebAsset: (relativePath: string) => + latestWebAssets.has(relativePath), loadBuild, loadRawEntry, + hasDiskOutput: (relativePath: string) => + fs.existsSync(join(fixtureRoot, relativePath)), rebuildRoute: (source: string) => invalidate(source, false), rebuildWebFirst: (source: string) => invalidate(source, true), requestBuiltInDocument, @@ -439,12 +434,13 @@ const expectFirstCommittedGeneration = async ( }) .toBeGreaterThan(completedBeforeRecovery); - const build = await withTimeout( - harness.loadBuild(), - 5_000, - `the first valid generation after ${harness.compileAttempts} attempts and ${harness.completedCompiles} completions` - ); - expect(getBuildMarker(build)).toBe('v1'); + await expect + .poll(async () => getBuildMarker(await harness.loadBuild()), { + intervals: [50, 100, 250], + timeout: 20_000, + }) + .toBe('v1'); + const build = await harness.loadBuild(); expect(build.assets).toBeTypeOf('object'); expect(build.entry).toBeTypeOf('object'); expect(build.routes).toBeTypeOf('object'); @@ -470,6 +466,15 @@ const expectFirstCommittedGeneration = async ( await expect(harness.loadRawEntry('static/js/app')).resolves.toMatchObject({ customServerMarker: 'custom-server-entry', }); + expect( + harness.hasDiskOutput('build/server/static/js/react-router-server-build.js') + ).toBe(false); + const browserManifestAsset = + 'static/js/virtual/react-router/browser-manifest.js'; + expect(harness.hasEmittedWebAsset(browserManifestAsset)).toBe(true); + expect( + harness.hasDiskOutput(`build/client/${browserManifestAsset}`) + ).toBe(false); await expect(harness.requestDocument()).resolves.toContain('v1'); await harness.startBuiltInServer(); await expect(harness.requestBuiltInDocument()).resolves.toContain('v1'); diff --git a/tests/federation.test.ts b/tests/federation.test.ts new file mode 100644 index 00000000..b53679ca --- /dev/null +++ b/tests/federation.test.ts @@ -0,0 +1,17 @@ +import { describe, expect, it } from '@rstest/core'; +import { ensureFederationAsyncStartup } from '../src/federation'; + +describe('federation helpers', () => { + it('enables async startup on module federation plugins', () => { + const moduleFederationPlugin = { + name: 'ModuleFederationPlugin', + options: { experiments: { asyncStartup: false } }, + }; + + ensureFederationAsyncStartup({ + plugins: [moduleFederationPlugin, { name: 'OtherPlugin' }], + }); + + expect(moduleFederationPlugin.options.experiments.asyncStartup).toBe(true); + }); +}); diff --git a/tests/index.test.ts b/tests/index.test.ts index 8452bebd..967d0b82 100644 --- a/tests/index.test.ts +++ b/tests/index.test.ts @@ -343,6 +343,34 @@ describe('pluginReactRouter', () => { ).toBe(true); }); + it('preserves user web splitChunks overrides', async () => { + const rsbuild = await createStubRsbuild({ + rsbuildConfig: { + environments: { + web: { + tools: { + rspack: { + optimization: { + splitChunks: { chunks: 'all' }, + }, + }, + }, + }, + }, + }, + }); + + rsbuild.addPlugins([pluginReactRouter()]); + const config = await rsbuild.unwrapConfig(); + + expect( + config.environments?.web?.tools?.rspack?.optimization?.splitChunks + ).toEqual({ chunks: 'all' }); + expect( + config.environments?.web?.tools?.rspack?.optimization?.runtimeChunk + ).toBe('single'); + }); + it('reduces file size reporting overhead for medium split route builds by default', async () => { const restoreEnv = captureEnv([ 'RR_TEST_SPLIT_ROUTE_MODULES', diff --git a/tests/parallel-route-transforms.test.ts b/tests/parallel-route-transforms.test.ts deleted file mode 100644 index 4ddbb724..00000000 --- a/tests/parallel-route-transforms.test.ts +++ /dev/null @@ -1,722 +0,0 @@ -import { describe, expect, it } from '@rstest/core'; -import { getExportNames } from '../src/export-utils'; -import { - executeRouteTransformTask, - type RouteTransformResult, - type RouteClientEntryTransformTask, - type RouteModuleTransformTask, -} from '../src/route-transform-tasks'; -import { - createRouteTransformExecutorForTesting, - createRouteTransformExecutor, - getDefaultWorkerCount, -} from '../src/parallel-route-transforms'; -import type { - WorkerRequest, - WorkerResponse, -} from '../src/parallel-route-transform-protocol'; -import type { RouteChunkConfig } from '../src/route-chunks'; - -const routeChunkConfig: RouteChunkConfig = { - splitRouteModules: true, - appDirectory: '/app', - rootRouteFile: 'root.tsx', -}; - -const disabledRouteChunkConfig: RouteChunkConfig = { - ...routeChunkConfig, - splitRouteModules: false, -}; - -const resourcePath = '/app/routes/demo.tsx'; -const createRouteModuleTask = ( - overrides: Partial> = {} -): RouteModuleTransformTask => ({ - kind: 'routeModule' as const, - code: ` - import { serverValue } from '../server-data.server'; - export async function loader() { return serverValue; } - export default function Route() { return null; } - `, - resource: `${resourcePath}?react-router-route`, - resourcePath, - environmentName: 'web', - sourceMaps: true, - ssr: true, - isBuild: false, - isSpaMode: false, - rootRoutePath: '/app/root.tsx', - ...overrides, -}); - -const createRouteClientEntryTask = ( - overrides: Partial> = {} -): RouteClientEntryTransformTask => ({ - kind: 'routeClientEntry' as const, - code: ` - export async function clientLoader() { return null; } - export default function Route() { return null; } - `, - resourcePath, - environmentName: 'web', - isBuild: true, - routeChunkConfig, - ...overrides, -}); - -type FakeWorkerHandler = (value: any) => void; - -class FakeRouteTransformWorker { - readonly messages: WorkerRequest[] = []; - readonly handlers = new Map(); - failNextPostMessage = false; - terminateCalls = 0; - - on(event: string, handler: FakeWorkerHandler): this { - this.handlers.set(event, [...(this.handlers.get(event) ?? []), handler]); - return this; - } - - postMessage(message: WorkerRequest): void { - if (this.failNextPostMessage) { - this.failNextPostMessage = false; - throw new Error('postMessage failed'); - } - this.messages.push(message); - } - - async terminate(): Promise { - this.terminateCalls += 1; - return 0; - } - - emit(event: string, value: unknown): void { - for (const handler of this.handlers.get(event) ?? []) { - handler(value); - } - } -} - -const resolveWorkerMessage = ( - worker: FakeRouteTransformWorker, - result: RouteTransformResult, - messageIndex = worker.messages.length - 1 -): void => { - worker.emit('message', { - id: worker.messages[messageIndex]!.id, - ok: true, - result, - } satisfies WorkerResponse); -}; - -describe('parallel route transforms', () => { - it.each([ - [1, 0], - [2, 0], - [3, 0], - [4, 0], - [5, 2], - [6, 2], - [8, 2], - [10, 2], - [12, 2], - [24, 2], - ])('caps default worker count by available CPUs', (cpus, workers) => { - expect(getDefaultWorkerCount(cpus)).toBe(workers); - }); - - it.each([ - [1, 0], - [2, 0], - [3, 1], - [4, 2], - [5, 2], - [8, 2], - ])( - 'keeps build workers on small machines (%s CPUs -> %s workers)', - (cpus, workers) => { - expect(getDefaultWorkerCount(cpus, { isBuild: true })).toBe(workers); - } - ); - - it.each([0, Number.NaN, 1.5])( - 'rejects invalid explicit worker count %s', - workerCount => { - expect(() => - createRouteTransformExecutor({ - parallelRouteTransform: workerCount, - }) - ).toThrow('must be true, false, or a positive integer'); - } - ); - - it('honors an explicit worker count', async () => { - const executor = createRouteTransformExecutor({ - parallelRouteTransform: 2, - }); - - try { - const result = await executor.run(createRouteModuleTask()); - - expect(result.code).toContain('export default _withComponentProps'); - expect(result.code).not.toContain('loader'); - } finally { - await executor.close(); - } - }); - - it('forces the default worker count with true', async () => { - const executor = createRouteTransformExecutor({ - parallelRouteTransform: true, - }); - - try { - const result = await executor.run(createRouteModuleTask()); - - expect(result.code).toContain('export default _withComponentProps'); - expect(result.code).not.toContain('loader'); - } finally { - await executor.close(); - } - }); - - it('runs route builds inline when parallel transforms are disabled', async () => { - const executor = createRouteTransformExecutor({ - parallelRouteTransform: false, - }); - - try { - const result = await executor.run(createRouteModuleTask()); - - expect(result.code).toContain('export default _withComponentProps'); - expect(result.code).not.toContain('loader'); - } finally { - await executor.close(); - } - }); - - it('does not create route transform workers until work is scheduled', async () => { - let createdWorkers = 0; - const worker = new FakeRouteTransformWorker(); - const executor = createRouteTransformExecutorForTesting( - { - parallelRouteTransform: 1, - }, - () => { - createdWorkers += 1; - return worker; - } - ); - - expect(createdWorkers).toBe(0); - - const pending = executor.run(createRouteModuleTask()); - expect(createdWorkers).toBe(1); - resolveWorkerMessage(worker, { code: 'created lazily' }); - await expect(pending).resolves.toEqual({ code: 'created lazily' }); - - await executor.close(); - expect(worker.terminateCalls).toBe(1); - }); - - it('rejects in-flight worker tasks on idempotent close and runs inline afterward', async () => { - const worker = new FakeRouteTransformWorker(); - const executor = createRouteTransformExecutorForTesting( - { - parallelRouteTransform: 1, - }, - () => worker - ); - - const pending = executor.run(createRouteModuleTask()); - expect(worker.messages).toHaveLength(1); - - const firstClose = executor.close(); - const secondClose = executor.close(); - - await expect(pending).rejects.toThrow('Route transform worker closed.'); - await expect(Promise.all([firstClose, secondClose])).resolves.toEqual([ - undefined, - undefined, - ]); - expect(worker.terminateCalls).toBe(1); - - const inlineResult = await executor.run(createRouteModuleTask()); - expect(inlineResult.code).toContain('export default _withComponentProps'); - expect(worker.messages).toHaveLength(1); - }); - - it('sends full source again after a cached worker request fails to post', async () => { - const worker = new FakeRouteTransformWorker(); - const executor = createRouteTransformExecutorForTesting( - { - parallelRouteTransform: 1, - }, - () => worker - ); - const task = createRouteModuleTask(); - - const firstRun = executor.run(task); - expect(worker.messages[0]?.task.code).toBe(task.code); - resolveWorkerMessage(worker, { code: 'first' }, 0); - await expect(firstRun).resolves.toEqual({ code: 'first' }); - - worker.failNextPostMessage = true; - await expect(executor.run(task)).rejects.toThrow('postMessage failed'); - - const thirdRun = executor.run(task); - expect(worker.messages[1]?.task.code).toBe(task.code); - resolveWorkerMessage(worker, { code: 'third' }, 1); - await expect(thirdRun).resolves.toEqual({ code: 'third' }); - - await executor.close(); - }); - - it('creates only worker slots that receive scheduled work', async () => { - const workers: FakeRouteTransformWorker[] = []; - const executor = createRouteTransformExecutorForTesting( - { - parallelRouteTransform: 4, - splitRouteModules: true, - }, - () => { - const worker = new FakeRouteTransformWorker(); - workers.push(worker); - return worker; - } - ); - - const first = executor.run(createRouteModuleTask()); - const second = executor.run( - createRouteModuleTask({ resourcePath: '/app/routes/other.tsx' }) - ); - - expect(workers).toHaveLength(2); - - for (const worker of workers) { - resolveWorkerMessage(worker, { code: 'done' }); - } - - await expect(Promise.all([first, second])).resolves.toEqual([ - { code: 'done' }, - { code: 'done' }, - ]); - - await executor.close(); - expect(workers.map(worker => worker.terminateCalls)).toEqual([1, 1]); - }); - - it('keeps related route source transforms on the same worker', async () => { - const workers: FakeRouteTransformWorker[] = []; - const executor = createRouteTransformExecutorForTesting( - { - parallelRouteTransform: 2, - splitRouteModules: true, - }, - () => { - const worker = new FakeRouteTransformWorker(); - workers.push(worker); - return worker; - } - ); - const code = ` - export async function clientLoader() { return null; } - export default function Route() { return null; } - `; - - const clientEntry = executor.run(createRouteClientEntryTask({ code })); - expect(workers).toHaveLength(1); - expect(workers[0]?.messages[0]?.task.code).toBe(code); - resolveWorkerMessage(workers[0]!, { code: 'client' }); - await expect(clientEntry).resolves.toEqual({ code: 'client' }); - - const routeModule = executor.run( - createRouteModuleTask({ - code, - environmentName: 'node', - isBuild: true, - }) - ); - expect(workers).toHaveLength(1); - expect(workers[0]?.messages[1]?.task.code).toBeUndefined(); - resolveWorkerMessage(workers[0]!, { code: 'module' }); - await expect(routeModule).resolves.toEqual({ code: 'module' }); - - await executor.close(); - }); - - it('can prewarm worker slots before the first route transform', async () => { - const workers: FakeRouteTransformWorker[] = []; - const executor = createRouteTransformExecutorForTesting( - { - parallelRouteTransform: 4, - }, - () => { - const worker = new FakeRouteTransformWorker(); - workers.push(worker); - return worker; - } - ); - - executor.prewarm(); - executor.prewarm(); - expect(workers).toHaveLength(4); - - executor.prewarm(); - expect(workers).toHaveLength(4); - - await executor.close(); - expect(workers.map(worker => worker.terminateCalls)).toEqual([ - 1, 1, 1, 1, - ]); - }); - - it('executes route client entry tasks through the shared task executor', async () => { - await expect( - executeRouteTransformTask({ - kind: 'routeClientEntry', - code: ` - export async function loader() { return null; } - export async function clientLoader() { return null; } - export default function Route() { return null; } - `, - resourcePath, - environmentName: 'web', - isBuild: false, - routeChunkConfig: disabledRouteChunkConfig, - }) - ).resolves.toEqual({ - code: `export { clientLoader, default } from "${resourcePath}?react-router-route";`, - }); - }); - - it('can execute route module tasks through worker-backed parallelism', async () => { - const executor = createRouteTransformExecutor({ - parallelRouteTransform: 2, - }); - - try { - const result = await executor.run(createRouteModuleTask()); - - expect(result.code).toContain('export default _withComponentProps'); - expect(result.code).not.toContain('loader'); - } finally { - await executor.close(); - } - }); - - it('produces identical build route modules when environments need the same output', async () => { - const executor = createRouteTransformExecutor({ - parallelRouteTransform: 2, - splitRouteModules: true, - }); - const task = createRouteModuleTask({ - code: ` - export async function clientLoader() { return null; } - export default function Route() { return null; } - `, - environmentName: 'node', - isBuild: true, - }); - - try { - const nodeResult = await executor.run(task); - const webResult = await executor.run({ - ...task, - environmentName: 'web', - }); - - expect(webResult).toEqual(nodeResult); - } finally { - await executor.close(); - } - }); - - it('keeps environment-specific build route module output isolated', async () => { - const executor = createRouteTransformExecutor({ - parallelRouteTransform: 2, - splitRouteModules: true, - }); - const task = createRouteModuleTask({ - environmentName: 'node', - isBuild: true, - }); - - try { - const nodeResult = await executor.run(task); - const webResult = await executor.run({ - ...task, - environmentName: 'web', - }); - - await expect(getExportNames(nodeResult.code)).resolves.toContain( - 'loader' - ); - await expect(getExportNames(webResult.code)).resolves.not.toContain( - 'loader' - ); - } finally { - await executor.close(); - } - }); - - it('isolates escaped server exports across build environments', async () => { - const executor = createRouteTransformExecutor({ - parallelRouteTransform: 2, - splitRouteModules: true, - }); - const task = createRouteModuleTask({ - code: String.raw` - const implementation = async () => null; - export { implementation as lo\u0061der }; - export default function Route() { return null; } - `, - environmentName: 'node', - isBuild: true, - }); - - try { - const nodeResult = await executor.run(task); - const webResult = await executor.run({ - ...task, - environmentName: 'web', - }); - - expect(nodeResult.code).toContain('loader'); - expect(webResult.code).not.toContain('loader'); - } finally { - await executor.close(); - } - }); - - it('preserves runtime TypeScript for the downstream Rsbuild SWC stage', async () => { - const result = await executeRouteTransformTask( - createRouteModuleTask({ - code: ` - export enum Status { Active } - export default function Route() { return Status.Active; } - `, - environmentName: 'node', - isBuild: true, - }) - ); - - expect(result.code).toContain('enum Status'); - expect(result.code).toContain('Status.Active'); - }); - - it('preserves value imports when web route modules have no server-only exports', async () => { - const result = await executeRouteTransformTask( - createRouteModuleTask({ - code: ` - import { setup } from './side-effect'; - export default function Route() { return null; } - `, - environmentName: 'web', - ssr: false, - isBuild: true, - }) - ); - - expect(result.code).toContain(`import { setup } from './side-effect';`); - }); - - it('rejects invalid SPA route module exports from the route transform AST', async () => { - await expect( - executeRouteTransformTask( - createRouteModuleTask({ - code: ` - export async function action() { return null; } - export default function Route() { return null; } - `, - ssr: false, - isSpaMode: true, - }) - ) - ).rejects.toThrow('SPA Mode: 1 invalid route export'); - }); - - it('generates route module source maps when the environment requests them', async () => { - const task = createRouteModuleTask({ - code: ` - export async function loader() { return null; } - export default function Route() { return null; } - `, - }); - - const buildResult = await executeRouteTransformTask({ - ...task, - isBuild: true, - }); - expect(buildResult.map).not.toBeNull(); - - const devResult = await executeRouteTransformTask({ - ...task, - isBuild: false, - }); - - expect(devResult.map).not.toBeNull(); - - const withoutSourceMaps = await executeRouteTransformTask({ - ...task, - sourceMaps: false, - }); - expect(withoutSourceMaps.map).toBeNull(); - }); - - // These pin `collectUnregisteredComponentNames` (route-transform-tasks.ts, - // the dev-HMR $RefreshReg$ backfill for pre-lowered JSX, e.g. MDX routes) - // against react-refresh/babel's own component-detection ruleset - // (react-refresh/cjs/react-refresh-babel.development.js). The heuristic is - // now aligned with that ruleset: single-declarator gate, curried-arrow bail, - // require*/import*/Import callee rejection, first-argument recursion for - // wrapping calls, and a runtime guard that matches `register()`'s own - // function-or-exotic-object acceptance. - describe('dev HMR $RefreshReg$ backfill for pre-lowered routes', () => { - const devHmrTask = ( - code: string, - overrides: Partial> = {} - ) => - createRouteModuleTask({ - code, - devHmr: true, - environmentName: 'web', - isBuild: false, - ...overrides, - }); - - it('registers a top-level function declaration component (matches babel)', async () => { - const result = await executeRouteTransformTask( - devHmrTask(`export function App(){return null}`) - ); - - expect(result.code).toContain('$RefreshReg$(App, "App")'); - }); - - it('registers a single-declarator arrow-fn const referenced as default export (matches babel)', async () => { - const result = await executeRouteTransformTask( - devHmrTask(`const Foo = () => null; export default Foo;`) - ); - - expect(result.code).toContain('$RefreshReg$(Foo, "Foo")'); - }); - - it('skips a multi-declarator export entirely (aligned with react-refresh as of this change)', async () => { - // Babel's react-refresh visitor only registers a VariableDeclaration - // when it has exactly one declarator. We now bail on the whole - // declaration too, so neither `A` (componentish) nor `B` is registered. - const result = await executeRouteTransformTask( - devHmrTask(`export const A = ()=>null, B = 2;`) - ); - - expect(result.code).not.toContain('$RefreshReg$(A, "A")'); - expect(result.code).not.toContain('$RefreshReg$(B, "B")'); - }); - - it('rejects a CallExpression init whose argument is not a component (aligned with react-refresh as of this change)', async () => { - // Babel recurses into the call's first argument and only registers - // when it resolves to a component (fn expr / componentish identifier / - // nested call). We now recurse the first argument too, so an HOC-like - // call with a plain string argument such as `createBox('div')` is no - // longer a false positive. - const result = await executeRouteTransformTask( - devHmrTask(`const Layout = createBox('div'); export default Layout;`) - ); - - expect(result.code).not.toContain('$RefreshReg$(Layout, "Layout")'); - }); - - it('emits a memo()-wrapped component registration that fires at runtime (aligned with react-refresh as of this change)', async () => { - // We collect `Card` and emit a guarded `$RefreshReg$` call. The guard - // now also accepts non-null objects, so the `memo`/`forwardRef` exotic - // object registers -- matching react-refresh's runtime `register()`, - // which tags functions and `$$typeof` exotic objects alike. - const result = await executeRouteTransformTask( - devHmrTask( - `import { memo } from 'react'; const Card = memo(()=>null); export default Card;` - ) - ); - - expect(result.code).toContain( - "if (typeof Card === 'function' || (typeof Card === 'object' && Card !== null)) $RefreshReg$(Card, \"Card\");" - ); - }); - - it('rejects a require/interop-wrapped init (aligned with react-refresh as of this change)', async () => { - // Babel excludes CallExpression inits whose callee is - // `require*`/`import*`/`Import`, and otherwise only registers when the - // first argument resolves to a component. We now do both, so - // `_interopRequireDefault(x)` -- whose lowercase `x` argument is not - // componentish -- is rejected. - const result = await executeRouteTransformTask( - devHmrTask( - `const Fragment = _interopRequireDefault(x); export default Fragment;` - ) - ); - - expect(result.code).not.toContain('$RefreshReg$(Fragment, "Fragment")'); - }); - - it('registers an inline `export default function Route(){}` (matches babel, not via the collector\'s own export-default handling)', async () => { - // NOTE: collectUnregisteredComponentNames never inspects - // ExportDefaultDeclaration itself, so in isolation it would miss this - // case. In the real pipeline it still matches babel here because - // transformRoute() runs *before* the refresh backfill and rewrites - // `export default function Route(){}` into a bare `function Route(){}` - // declaration plus a separate `export default - // _withComponentProps(Route)` statement -- so the bare function - // declaration is picked up by the FunctionDeclaration branch instead. - const result = await executeRouteTransformTask( - devHmrTask(`export default function Route(){return null}`) - ); - - expect(result.code).toContain('$RefreshReg$(Route, "Route")'); - }); - - it('skips class components and non-function capitalized constants (matches babel)', async () => { - const classDecl = await executeRouteTransformTask( - devHmrTask(`class App{ render(){ return null } } export default App;`) - ); - expect(classDecl.code).not.toContain('$RefreshReg$'); - - const classExpr = await executeRouteTransformTask( - devHmrTask(`const App = class{}; export default App;`) - ); - expect(classExpr.code).not.toContain('$RefreshReg$'); - - const nonFnConst = await executeRouteTransformTask( - devHmrTask( - `export const N = 5; export default function Other(){return null}` - ) - ); - expect(nonFnConst.code).not.toContain('$RefreshReg$(N, "N")'); - expect(nonFnConst.code).toContain('$RefreshReg$(Other, "Other")'); - }); - - it('subtracts components already registered via an existing $RefreshReg$ call (matches babel/SWC dedupe)', async () => { - const result = await executeRouteTransformTask( - devHmrTask( - `function App(){return null} $RefreshReg$(_c, "App"); export default App;` - ) - ); - - // Only the pre-existing call should be present; no backfill block - // should be appended for a name that is already registered. - expect(result.code).not.toContain( - "if (typeof $RefreshReg$ === 'function')" - ); - }); - - it('bails on a curried arrow (aligned with react-refresh as of this change)', async () => { - // Babel breaks out of registration when the arrow's body is itself an - // arrow function (a curried component factory, not a component). We now - // apply the same bail, so the outer arrow's identifier is not - // registered. - const result = await executeRouteTransformTask( - devHmrTask(`const A = () => () => null; export default A;`) - ); - - expect(result.code).not.toContain('$RefreshReg$(A, "A")'); - }); - }); -}); diff --git a/tests/route-module-transform-loader.test.ts b/tests/route-module-transform-loader.test.ts new file mode 100644 index 00000000..736bd324 --- /dev/null +++ b/tests/route-module-transform-loader.test.ts @@ -0,0 +1,219 @@ +import { + originalPositionFor, + TraceMap, +} from '@jridgewell/trace-mapping'; +import { describe, expect, it, rstest } from '@rstest/core'; +import routeModuleTransformLoader, { + flushRouteModuleTransformLoaderPerformance, + type RouteModuleTransformLoaderOptions, +} from '../src/route-module-transform-loader'; + +type LoaderInputSourceMap = Parameters[1]; + +const defaultOptions: RouteModuleTransformLoaderOptions = { + environmentName: 'web', + performanceScopeId: 'web:dev:ssr:/project/app/root.tsx', + logPerformance: false, + ssr: true, + isBuild: false, + isSpaMode: false, + rootRoutePath: '/project/app/root.tsx', + devHmr: false, +}; + +const runLoader = ( + code: string, + { + options = defaultOptions, + sourceMap = false, + inputSourceMap, + }: { + options?: RouteModuleTransformLoaderOptions; + sourceMap?: boolean; + inputSourceMap?: LoaderInputSourceMap; + } = {} +) => + new Promise<{ code: string; map: LoaderInputSourceMap }>((resolve, reject) => { + const context = { + resource: '/project/app/routes/page.tsx?react-router-route', + resourcePath: '/project/app/routes/page.tsx', + sourceMap, + async: + () => + ( + error: Error | null, + result?: string, + map?: LoaderInputSourceMap + ) => { + if (error) { + reject(error); + return; + } + resolve({ code: result ?? '', map }); + }, + getOptions: () => options, + }; + + void routeModuleTransformLoader.call( + context as never, + code, + inputSourceMap + ); + }); + +describe('route module transform loader', () => { + it('transforms route modules through the loader API', async () => { + const result = await runLoader(` + export async function loader() { return null; } + export default function Route() { return null; } + `); + + expect(result.code).toContain('export default _withComponentProps'); + expect(result.code).not.toContain('export async function loader'); + expect(result.map).toBeUndefined(); + }); + + it('emits a source map when the loader context requests one', async () => { + const result = await runLoader( + ` + export default function Route() { return null; } + `, + { sourceMap: true } + ); + + expect(result.map).toBeDefined(); + }); + + it('composes its source map with the map from the preceding loader', async () => { + const source = ` + export default function Route() { return null; } + `; + const result = await runLoader(source, { + sourceMap: true, + inputSourceMap: { + version: 3, + file: 'compiled-route.js', + names: [], + sources: ['original-route.tsx'], + sourcesContent: [source], + mappings: 'AAAA;AACA;AACA', + }, + }); + + expect(result.map).toMatchObject({ + sources: ['original-route.tsx'], + sourcesContent: [source], + }); + const transformedExportLine = + result.code + .split('\n') + .findIndex(line => line.startsWith('export default')) + 1; + expect( + originalPositionFor(new TraceMap(result.map!), { + line: transformedExportLine, + column: 0, + }) + ).toMatchObject({ + source: 'original-route.tsx', + line: 2, + }); + }); + + it('registers lowered route components for dev HMR', async () => { + const result = await runLoader( + ` + const Route = () => React.createElement('h1', null, 'Hello'); + export default Route; + `, + { options: { ...defaultOptions, devHmr: true } } + ); + + expect(result.code).toContain('$RefreshReg$(Route, "Route")'); + }); + + it('aggregates route-module performance reports when enabled', async () => { + const info = rstest.spyOn(console, 'info').mockImplementation(() => {}); + + try { + await runLoader( + ` + export default function Route() { return null; } + `, + { options: { ...defaultOptions, logPerformance: true } } + ); + await runLoader( + ` + export default function Route() { return null; } + `, + { options: { ...defaultOptions, logPerformance: true } } + ); + + expect(info).not.toHaveBeenCalled(); + flushRouteModuleTransformLoaderPerformance(); + expect(info).toHaveBeenCalledTimes(1); + const message = String(info.mock.calls[0][0]); + const prefix = '[react-router:performance] '; + expect(message.startsWith(prefix)).toBe(true); + + const report = JSON.parse(message.slice(prefix.length)); + expect(report.environment).toBe('web'); + expect(report.partial).toBe(true); + expect(report.workerId).toMatch(/^process-\d+:thread-\d+$/); + expect(report.operations['route:module']).toMatchObject({ + count: 2, + }); + expect(report.operations['route:module'].slowest[0].resource).toBe( + '/project/app/routes/page.tsx?react-router-route' + ); + } finally { + flushRouteModuleTransformLoaderPerformance(); + info.mockRestore(); + } + }); + + it('keeps route-module performance scopes isolated', async () => { + const info = rstest.spyOn(console, 'info').mockImplementation(() => {}); + + try { + await runLoader( + ` + export default function Route() { return null; } + `, + { + options: { + ...defaultOptions, + logPerformance: true, + performanceScopeId: 'scope-a', + }, + } + ); + await runLoader( + ` + export default function Route() { return null; } + `, + { + options: { + ...defaultOptions, + logPerformance: true, + performanceScopeId: 'scope-b', + }, + } + ); + + flushRouteModuleTransformLoaderPerformance(); + + expect(info).toHaveBeenCalledTimes(2); + for (const call of info.mock.calls) { + const message = String(call[0]); + const report = JSON.parse( + message.slice('[react-router:performance] '.length) + ); + expect(report.environment).toBe('web'); + expect(report.operations['route:module'].count).toBe(1); + } + } finally { + flushRouteModuleTransformLoaderPerformance(); + info.mockRestore(); + } + }); +}); diff --git a/tests/route-module-transform-rules.test.ts b/tests/route-module-transform-rules.test.ts new file mode 100644 index 00000000..8d76665b --- /dev/null +++ b/tests/route-module-transform-rules.test.ts @@ -0,0 +1,94 @@ +import { describe, expect, it } from '@rstest/core'; +import { + registerRouteModuleTransformRules, + shouldUseRouteModuleTransformLoader, +} from '../src/route-module-transform-rules'; + +const createRuleConfig = ( + parallelRouteTransform: boolean | number | undefined, + { isBuild = false, devHmr = false } = {} +) => { + const routePath = '/project/app/routes/page.tsx'; + const rspackConfig = {}; + + registerRouteModuleTransformRules(rspackConfig, { + environmentName: 'web', + ssr: true, + isBuild, + isSpaMode: false, + rootRoutePath: '/project/app/root.tsx', + devHmr, + logPerformance: true, + routeByFilePath: new Map([[routePath, { id: 'page' }]]), + parallelRouteTransform, + }); + + return rspackConfig as { + module: { + rules: Array<{ + test?: (path: string) => boolean; + use: Array<{ + loader: string; + options: Record; + parallel?: true | { maxWorkers: number }; + }>; + }>; + }; + }; +}; + +describe('route module transform rules', () => { + it('uses the loader only when parallel route transforms are opted into', () => { + expect(shouldUseRouteModuleTransformLoader(undefined)).toBe(false); + expect(shouldUseRouteModuleTransformLoader(false)).toBe(false); + expect(shouldUseRouteModuleTransformLoader(true)).toBe(true); + expect(shouldUseRouteModuleTransformLoader(3)).toBe(true); + expect(() => shouldUseRouteModuleTransformLoader(0)).toThrow( + '[react-router] parallelRouteTransform must be true, false, or a positive integer.' + ); + }); + + it('registers route module loader rules with loader options', () => { + const config = createRuleConfig(true, { devHmr: true }); + const [queryRule, routeRule] = config.module.rules; + + expect(config.module.rules).toHaveLength(2); + expect(queryRule.use[0]).toMatchObject({ + options: { + environmentName: 'web', + performanceScopeId: 'web:dev:ssr:/project/app/root.tsx', + logPerformance: true, + ssr: true, + isBuild: false, + isSpaMode: false, + rootRoutePath: '/project/app/root.tsx', + devHmr: true, + }, + parallel: true, + }); + expect(routeRule.test?.('/project/app/routes/page.tsx')).toBe(true); + expect(routeRule.test?.('/project/app/routes/other.tsx')).toBe(false); + }); + + it('passes explicit worker counts to Rspack loader parallelism', () => { + const config = createRuleConfig(3); + + expect(config.module.rules[0].use[0].parallel).toEqual({ + maxWorkers: 3, + }); + }); + + it('rejects invalid worker counts', () => { + expect(() => createRuleConfig(0)).toThrow( + '[react-router] parallelRouteTransform must be true, false, or a positive integer.' + ); + expect(() => + shouldUseRouteModuleTransformLoader('workers' as never) + ).toThrow( + '[react-router] parallelRouteTransform must be true, false, or a positive integer.' + ); + expect(() => shouldUseRouteModuleTransformLoader(null as never)).toThrow( + '[react-router] parallelRouteTransform must be true, false, or a positive integer.' + ); + }); +});