rsbuild-plugin-react-router 0.6.5 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -343,6 +343,28 @@ If you configure `output.assetPrefix` in Rsbuild, the plugin uses that value
343
343
  for the React Router browser manifest and server build `publicPath` so asset
344
344
  URLs resolve correctly when serving from a CDN or sub-path.
345
345
 
346
+ The web environment's own `output.assetPrefix` is passed through to the browser
347
+ compiler untouched, so `environments.web.output.assetPrefix: 'auto'` lets the
348
+ browser runtime resolve async chunks and stylesheets relative to the loaded
349
+ script. The server build and browser manifest need an absolute prefix: they use
350
+ the web environment's prefix when it is usable and otherwise fall back to the
351
+ root `output.assetPrefix`. A common CDN setup is therefore:
352
+
353
+ ```ts
354
+ export default defineConfig({
355
+ output: { assetPrefix: 'https://cdn.example.com/app/' }, // server-rendered URLs
356
+ environments: {
357
+ web: { output: { assetPrefix: 'auto' } }, // browser runtime resolves itself
358
+ },
359
+ });
360
+ ```
361
+
362
+ The plugin does not set `output.filename`, `chunkFilename`, or `publicPath`
363
+ for the web environment. Production browser entries use Rsbuild's default
364
+ content-hashed filenames, and `output.filename`, `output.filenameHash`,
365
+ `output.distPath`, and `tools.rspack` output settings you configure govern
366
+ the emitted files and the manifest URLs that reference them.
367
+
346
368
  ## Custom Server Setup
347
369
 
348
370
  The plugin supports two ways to handle server-side rendering:
@@ -465,6 +487,29 @@ and every configured bundle are
465
487
  evaluated and published as one generation; one failing bundle keeps the whole
466
488
  previous generation active.
467
489
 
490
+ ### Sharing `createContext()` instances with a custom server
491
+
492
+ React Router middleware contexts are matched by identity, so a custom server's
493
+ `getLoadContext` must use the same `createContext()` instance the routes import.
494
+ With a bundled server build that instance lives inside the build. Re-export it
495
+ from `app/entry.server.tsx` and read it from `build.entry.module`:
496
+
497
+ ```ts
498
+ // app/entry.server.tsx
499
+ export { valueContext } from './context';
500
+ ```
501
+
502
+ ```js
503
+ // server.js
504
+ getLoadContext: async () => {
505
+ const { valueContext } = (await build()).entry.module;
506
+ return new RouterContextProvider([[valueContext, 'value']]);
507
+ },
508
+ ```
509
+
510
+ This works in development through `loadReactRouterServerBuild` and in
511
+ production through `resolveReactRouterServerBuild`.
512
+
468
513
  `resolveReactRouterServerBuild` accepts an imported production server module,
469
514
  normalizes ESM and CommonJS namespace shapes, resolves supported asynchronous
470
515
  build exports, and validates the result before it reaches React Router.
package/dist/511.js CHANGED
@@ -375,9 +375,16 @@ function combineURLs(baseURL, relativeURL) {
375
375
  function normalizeAssetPrefix(assetPrefix) {
376
376
  return assetPrefix && 'auto' !== assetPrefix ? assetPrefix.endsWith('/') ? assetPrefix : `${assetPrefix}/` : '/';
377
377
  }
378
- function resolveEffectiveAssetPrefix(config) {
379
- let outputPrefix = 'string' == typeof config.output?.assetPrefix ? config.output.assetPrefix : void 0;
380
- return config.isBuild ? normalizeAssetPrefix(outputPrefix) : normalizeAssetPrefix(('string' == typeof config.dev?.assetPrefix ? config.dev.assetPrefix : void 0) ?? outputPrefix);
378
+ let asString = (value)=>'string' == typeof value ? value : void 0, pickConfiguredAssetPrefix = ({ dev, output }, isBuild)=>isBuild ? asString(output?.assetPrefix) : asString(dev?.assetPrefix) ?? asString(output?.assetPrefix);
379
+ function resolveEffectiveAssetPrefix(config, ...fallbacks) {
380
+ for (let candidate of [
381
+ config,
382
+ ...fallbacks
383
+ ]){
384
+ let prefix = pickConfiguredAssetPrefix(candidate, config.isBuild);
385
+ if (prefix && 'auto' !== prefix) return normalizeAssetPrefix(prefix);
386
+ }
387
+ return '/';
381
388
  }
382
389
  function createRouteId(file) {
383
390
  return normalize(file.replace(/\.[^/.]+$/, ''));
@@ -1,6 +1,20 @@
1
- import type { RsbuildPluginAPI } from '@rsbuild/core';
2
- export declare const registerReactRouterEnvironmentOutput: ({ api, federation, resolvedServerOutput, }: {
1
+ import type { RsbuildPluginAPI, Rspack } from '@rsbuild/core';
2
+ /**
3
+ * Rspack `output` policy for the web and node environments, in two tiers:
4
+ *
5
+ * 1. Overridable defaults, registered through `modifyRspackConfig`. Rsbuild
6
+ * runs the user's `tools.rspack` (object or function form) after this hook,
7
+ * so user output settings such as `chunkFilename` take precedence
8
+ * (#129, #130). Neither `filename` nor `publicPath` is set here: Rsbuild
9
+ * derives them from `output.filename`/`output.filenameHash`/`output.distPath`
10
+ * and the environment's `output.assetPrefix`, which keeps `'auto'` intact.
11
+ * 2. Enforced invariants, registered through a `tools.rspack` function that
12
+ * runs after the user's own `tools.rspack`: the server library type must
13
+ * match the server module format, and federation builds need async startup.
14
+ */
15
+ export declare const registerReactRouterEnvironmentOutput: ({ api, federation, resolvedServerOutput, webOutput, }: {
3
16
  api: RsbuildPluginAPI;
4
17
  federation: boolean | undefined;
5
18
  resolvedServerOutput: "commonjs" | "module";
19
+ webOutput: NonNullable<Rspack.Configuration["output"]>;
6
20
  }) => void;
package/dist/index.cjs CHANGED
@@ -543,9 +543,16 @@ function combineURLs(baseURL, relativeURL) {
543
543
  function normalizeAssetPrefix(assetPrefix) {
544
544
  return assetPrefix && 'auto' !== assetPrefix ? assetPrefix.endsWith('/') ? assetPrefix : `${assetPrefix}/` : '/';
545
545
  }
546
- function resolveEffectiveAssetPrefix(config) {
547
- let outputPrefix = 'string' == typeof config.output?.assetPrefix ? config.output.assetPrefix : void 0;
548
- return config.isBuild ? normalizeAssetPrefix(outputPrefix) : normalizeAssetPrefix(('string' == typeof config.dev?.assetPrefix ? config.dev.assetPrefix : void 0) ?? outputPrefix);
546
+ const asString = (value)=>'string' == typeof value ? value : void 0, pickConfiguredAssetPrefix = ({ dev, output }, isBuild)=>isBuild ? asString(output?.assetPrefix) : asString(dev?.assetPrefix) ?? asString(output?.assetPrefix);
547
+ function resolveEffectiveAssetPrefix(config, ...fallbacks) {
548
+ for (let candidate of [
549
+ config,
550
+ ...fallbacks
551
+ ]){
552
+ let prefix = pickConfiguredAssetPrefix(candidate, config.isBuild);
553
+ if (prefix && 'auto' !== prefix) return normalizeAssetPrefix(prefix);
554
+ }
555
+ return '/';
549
556
  }
550
557
  function createRouteId(file) {
551
558
  return (0, external_pathe_namespaceObject.normalize)(file.replace(/\.[^/.]+$/, ''));
@@ -619,8 +626,27 @@ const resolveEntryWithTemplate = ({ appDirectory, entryName, templateName, templ
619
626
  asyncStartup: !0
620
627
  });
621
628
  }
622
- }, registerReactRouterEnvironmentOutput = ({ api, federation, resolvedServerOutput })=>{
623
- api.modifyEnvironmentConfig(async (config, { name, mergeEnvironmentConfig })=>'web' !== name && 'node' !== name ? config : mergeEnvironmentConfig(config, {
629
+ }, registerReactRouterEnvironmentOutput = ({ api, federation, resolvedServerOutput, webOutput })=>{
630
+ let nodeChunkLoading = 'module' === resolvedServerOutput ? 'import' : federation ? 'async-node' : 'require';
631
+ api.modifyRspackConfig((rspackConfig, { environment, mergeConfig })=>'web' === environment.name ? mergeConfig(rspackConfig, {
632
+ output: {
633
+ ...webOutput,
634
+ ...federation ? {
635
+ chunkLoading: 'import'
636
+ } : {}
637
+ }
638
+ }) : 'node' === environment.name ? mergeConfig(rspackConfig, {
639
+ output: {
640
+ chunkFormat: resolvedServerOutput,
641
+ chunkLoading: nodeChunkLoading,
642
+ devtoolModuleFilenameTemplate: '[absolute-resource-path]',
643
+ devtoolFallbackModuleFilenameTemplate: '[absolute-resource-path]?[hash]',
644
+ workerChunkLoading: nodeChunkLoading,
645
+ wasmLoading: 'fetch',
646
+ module: 'module' === resolvedServerOutput,
647
+ chunkFilename: 'static/js/async/[name].js'
648
+ }
649
+ }) : rspackConfig), api.modifyEnvironmentConfig(async (config, { name, mergeEnvironmentConfig })=>'web' !== name && 'node' !== name ? config : mergeEnvironmentConfig(config, {
624
650
  tools: {
625
651
  rspack: (rspackConfig)=>{
626
652
  if (federation && ensureFederationAsyncStartup(rspackConfig), 'node' === name) {
@@ -11790,14 +11816,7 @@ const createReactRouterManifestOptions = ({ routeChunks, routeModuleAnalysis })=
11790
11816
  routeModuleAnalysis
11791
11817
  } : {}
11792
11818
  };
11793
- }, orderChunkFiles = (chunkName, files)=>{
11794
- let ownChunkAsset = `${chunkName}.js`, ownFileIndex = files.findIndex((file)=>file.endsWith(ownChunkAsset));
11795
- return ownFileIndex <= 0 ? files : [
11796
- files[ownFileIndex],
11797
- ...files.slice(0, ownFileIndex),
11798
- ...files.slice(ownFileIndex + 1)
11799
- ];
11800
- }, collectManifestFilesByName = (items, names, getFiles)=>{
11819
+ }, isManifestJsAsset = (asset)=>/(?<!\.hot-update)\.[cm]?js(?:\?.*)?$/.test(asset), isManifestCssAsset = (asset)=>/\.css(?:\?.*)?$/.test(asset), collectManifestFilesByName = (items, names, getFiles)=>{
11801
11820
  let filesByName = {};
11802
11821
  if (!names) {
11803
11822
  for (let [name, item] of items)null != item && (filesByName[name] = getFiles(name, item));
@@ -11813,14 +11832,14 @@ const createReactRouterManifestOptions = ({ routeChunks, routeModuleAnalysis })=
11813
11832
  return filesByName;
11814
11833
  }, createReactRouterManifestStats = (compilation, chunkNames)=>{
11815
11834
  if (!compilation) return;
11816
- let assetsByChunkName = collectManifestFilesByName(compilation.namedChunks, chunkNames, (chunkName, chunk)=>orderChunkFiles(chunkName, Array.from(chunk.files ?? []))), entrypointFilesByName = compilation.entrypoints ? collectManifestFilesByName(compilation.entrypoints, chunkNames, (_name, entrypoint)=>Array.from(entrypoint.getFiles?.() ?? [])) : {};
11835
+ let assetsByChunkName = collectManifestFilesByName(compilation.namedChunks, chunkNames, (_chunkName, chunk)=>Array.from(chunk.files ?? [])), entrypointFilesByName = compilation.entrypoints ? collectManifestFilesByName(compilation.entrypoints, chunkNames, (_name, entrypoint)=>Array.from(entrypoint.getFiles?.() ?? [])) : {};
11817
11836
  return Object.keys(entrypointFilesByName).length > 0 ? {
11818
11837
  assetsByChunkName,
11819
11838
  entrypointFilesByName
11820
11839
  } : {
11821
11840
  assetsByChunkName
11822
11841
  };
11823
- }, DEFAULT_MANIFEST_DIR = DEFAULT_JS_DIST_PATH, CSS_IMPORT_RE = /\.(?:css|less|sass|scss)(?:\?[^'"`]+)?['"`]/, createChunkAssetResolver = (clientStats, includeEntrypointJs)=>{
11842
+ }, DEFAULT_MANIFEST_DIR = 'static/js', CSS_IMPORT_RE = /\.(?:css|less|sass|scss)(?:\?[^'"`]+)?['"`]/, createChunkAssetResolver = (clientStats, includeEntrypointJs)=>{
11824
11843
  let chunkAssetsByName = new Map();
11825
11844
  return (chunkName)=>{
11826
11845
  let cached = chunkAssetsByName.get(chunkName);
@@ -11829,16 +11848,16 @@ const createReactRouterManifestOptions = ({ routeChunks, routeModuleAnalysis })=
11829
11848
  if (!assets) {
11830
11849
  let result = {
11831
11850
  js: [
11832
- `${DEFAULT_JS_DIST_PATH}/${chunkName}.js`
11851
+ `${DEFAULT_MANIFEST_DIR}/${chunkName}.js`
11833
11852
  ],
11834
11853
  css: []
11835
11854
  };
11836
11855
  return chunkAssetsByName.set(chunkName, result), result;
11837
11856
  }
11838
11857
  let cssAssets = new Set(), jsAssets = new Set();
11839
- for (let asset of assets)asset.endsWith('.css') ? cssAssets.add(asset) : asset.endsWith('.js') && jsAssets.add(asset);
11840
- for (let asset of clientStats?.entrypointFilesByName?.[chunkName] ?? [])asset.endsWith('.css') ? cssAssets.add(asset) : includeEntrypointJs && asset.endsWith('.js') && jsAssets.add(asset);
11841
- 0 === jsAssets.size && jsAssets.add(`${DEFAULT_JS_DIST_PATH}/${chunkName}.js`);
11858
+ for (let asset of assets)isManifestCssAsset(asset) ? cssAssets.add(asset) : isManifestJsAsset(asset) && jsAssets.add(asset);
11859
+ for (let asset of clientStats?.entrypointFilesByName?.[chunkName] ?? [])isManifestCssAsset(asset) ? cssAssets.add(asset) : includeEntrypointJs && isManifestJsAsset(asset) && jsAssets.add(asset);
11860
+ if (0 === jsAssets.size) throw Error(`[react-router] Chunk "${chunkName}" emitted no JavaScript asset the browser manifest can reference (files: ${assets.join(', ') || 'none'}). Check the web \`output.filename.js\` scheme.`);
11842
11861
  let result = {
11843
11862
  js: [
11844
11863
  ...jsAssets
@@ -11851,7 +11870,7 @@ const createReactRouterManifestOptions = ({ routeChunks, routeModuleAnalysis })=
11851
11870
  };
11852
11871
  }, analyzeRouteForManifestEffect = ({ discoveredCssAssets, isBuild, routeChunkCache, routeChunkConfig, routeEntryName, routeFilePath, route, routeModuleAnalysis })=>tryPluginPromise(async ()=>{
11853
11872
  let { code, exports: exportNames } = await routeModuleAnalysis?.(routeFilePath, route) ?? await getRouteModuleAnalysis(routeFilePath), cssAssets = !isBuild && 0 === discoveredCssAssets.length && CSS_IMPORT_RE.test(code) ? [
11854
- `${DEFAULT_JS_DIST_PATH.replace('/js', '/css')}/${routeEntryName}.css`
11873
+ `${DEFAULT_MANIFEST_DIR.replace('/js', '/css')}/${routeEntryName}.css`
11855
11874
  ] : discoveredCssAssets, chunkInfo = isBuild && routeChunkConfig ? await detectRouteChunksIfEnabled(routeChunkCache, routeChunkConfig, routeFilePath, code) : null;
11856
11875
  return {
11857
11876
  cssAssets,
@@ -11865,9 +11884,9 @@ const createReactRouterManifestOptions = ({ routeChunks, routeModuleAnalysis })=
11865
11884
  routeModuleExports: [],
11866
11885
  hasRouteChunkByExportName: null
11867
11886
  })))), getManifestDirFromEntryAsset = (entryModulePath)=>{
11868
- if (!entryModulePath) return DEFAULT_JS_DIST_PATH;
11887
+ if (!entryModulePath) return DEFAULT_MANIFEST_DIR;
11869
11888
  let dir = (0, external_pathe_namespaceObject.dirname)(entryModulePath);
11870
- return '.' === dir ? DEFAULT_JS_DIST_PATH : dir;
11889
+ return '.' === dir ? DEFAULT_MANIFEST_DIR : dir;
11871
11890
  }, getReactRouterManifestPath = ({ version, isBuild, entryModulePath })=>{
11872
11891
  if (!isBuild) return 'static/js/virtual/react-router/browser-manifest.js';
11873
11892
  let dir = getManifestDirFromEntryAsset(entryModulePath);
@@ -12418,13 +12437,13 @@ const redirectStatusCodes = new Set([
12418
12437
  }), external_jsesc_namespaceObject = require("jsesc");
12419
12438
  var external_jsesc_default = __webpack_require__.n(external_jsesc_namespaceObject);
12420
12439
  const BROWSER_MANIFEST_ASSET = 'static/js/virtual/react-router/browser-manifest.js', ABSOLUTE_URL_RE = /^[a-zA-Z][a-zA-Z\d+\-.]*:/, toManifestAssetUrl = (assetPrefix, assetName)=>ABSOLUTE_URL_RE.test(assetName) || assetName.startsWith('//') || assetName.startsWith('/') ? assetName : combineURLs(assetPrefix, assetName), addIntegrity = (sri, assetPrefix, assetName, integrity)=>{
12421
- 'string' == typeof assetName && assetName.endsWith('.js') && 'string' == typeof integrity && (sri[toManifestAssetUrl(assetPrefix, assetName)] = integrity);
12440
+ 'string' == typeof assetName && isManifestJsAsset(assetName) && 'string' == typeof integrity && (sri[toManifestAssetUrl(assetPrefix, assetName)] = integrity);
12422
12441
  }, computeSubresourceIntegrity = (source)=>{
12423
12442
  if (source) return `sha384-${(0, external_node_crypto_namespaceObject.createHash)('sha384').update(source.source()).digest('base64')}`;
12424
12443
  }, collectSubresourceIntegrity = (stats, compilation, assetPrefix = '/')=>{
12425
12444
  let sri = {};
12426
12445
  for (let asset of stats?.assets ?? [])addIntegrity(sri, assetPrefix, asset.name, asset.integrity);
12427
- if ('function' == typeof compilation?.getAssets) for (let asset of compilation.getAssets())asset.name.endsWith('.js') && addIntegrity(sri, assetPrefix, asset.name, computeSubresourceIntegrity(asset.source) ?? asset.info?.integrity);
12446
+ if ('function' == typeof compilation?.getAssets) for (let asset of compilation.getAssets())isManifestJsAsset(asset.name) && addIntegrity(sri, assetPrefix, asset.name, computeSubresourceIntegrity(asset.source) ?? asset.info?.integrity);
12428
12447
  return Object.keys(sri).length > 0 ? sri : void 0;
12429
12448
  };
12430
12449
  function registerModifyBrowserManifestAssets(api, routes, pluginOptions, appDirectory, assetPrefix = '/', routeChunkOptions, options) {
@@ -12444,7 +12463,7 @@ function registerModifyBrowserManifestAssets(api, routes, pluginOptions, appDire
12444
12463
  compilation.updateAsset(BROWSER_MANIFEST_ASSET, new sources.RawSource(newSource));
12445
12464
  }
12446
12465
  if (isBuild) {
12447
- let entryAssets = stats?.assetsByChunkName?.['entry.client'], entryJsAssets = entryAssets?.filter((asset)=>asset.endsWith('.js')) || [], manifestPath = getReactRouterManifestPath({
12466
+ let entryAssets = stats?.assetsByChunkName?.['entry.client'], entryJsAssets = entryAssets?.filter(isManifestJsAsset) || [], manifestPath = getReactRouterManifestPath({
12448
12467
  version: manifest.version,
12449
12468
  isBuild: !0,
12450
12469
  entryModulePath: entryJsAssets[0]
@@ -14418,12 +14437,14 @@ export function EnsureClientRouteModuleForHMR___() { return ___EnsureClientRoute
14418
14437
  }, createServerRouteEntry = async (options)=>{
14419
14438
  let ast, plan = await createRscRouteExportPlan(options);
14420
14439
  validateRscRouteExportPlan(plan, options);
14421
- let lines = [], needsReactImport = !1, needsEnsureHmrImport = !1, needsStyleEntryImport = !1, streamsClientRouteCss = !plan.exportNames.some(isServerComponentExport) && plan.exportNames.includes('default') && programHasSideEffectStyleImports((ast = yuku_parse(options.code, {
14440
+ let lines = [], needsReactImport = !1, needsEnsureHmrImport = !1, needsStyleEntryImport = !1, pushStylesheetLinks = (entryCssFilesExpression)=>{
14441
+ lines.push(` ...(${entryCssFilesExpression} ?? []).map(href =>`), lines.push(' React.createElement("link", { key: href, rel: "stylesheet", href: href, precedence: "default" })),');
14442
+ }, streamsClientRouteCss = !plan.exportNames.some(isServerComponentExport) && plan.exportNames.includes('default') && programHasSideEffectStyleImports((ast = yuku_parse(options.code, {
14422
14443
  sourceType: 'module'
14423
14444
  })).program ?? ast);
14424
14445
  for (let exportName of plan.exportNames){
14425
14446
  if (streamsClientRouteCss && 'default' === exportName) {
14426
- needsReactImport = !0, needsStyleEntryImport = !0, lines.push(`import RscClientRouteDefault___ from ${JSON.stringify(plan.clientTargetFor('default'))};`), lines.push('export default function RscClientRouteWithStyles___(props) {'), lines.push(' return React.createElement(React.Fragment, null,'), lines.push(` ...(${RSC_ROUTE_STYLE_ENTRY_EXPORT}.entryCssFiles ?? []).map(href =>`), lines.push(' React.createElement("link", { key: href, rel: "stylesheet", href: href, precedence: "default" })),'), lines.push(' React.createElement(RscClientRouteDefault___, props),'), lines.push(' );'), lines.push('}');
14447
+ needsReactImport = !0, needsStyleEntryImport = !0, lines.push(`import RscClientRouteDefault___ from ${JSON.stringify(plan.clientTargetFor('default'))};`), lines.push('export default function RscClientRouteWithStyles___(props) {'), lines.push(' return React.createElement(React.Fragment, null,'), pushStylesheetLinks(`${RSC_ROUTE_STYLE_ENTRY_EXPORT}.entryCssFiles`), lines.push(' React.createElement(RscClientRouteDefault___, props),'), lines.push(' );'), lines.push('}');
14427
14448
  continue;
14428
14449
  }
14429
14450
  if (isClientRouteExport(exportName)) {
@@ -14431,7 +14452,7 @@ export function EnsureClientRouteModuleForHMR___() { return ___EnsureClientRoute
14431
14452
  continue;
14432
14453
  }
14433
14454
  if (isServerComponentExport(exportName)) {
14434
- needsReactImport = !0, needsEnsureHmrImport = !0, lines.push(`import { ${exportName} as ${exportName}WithoutClientChunk } from ${JSON.stringify(plan.serverTarget)};`), lines.push(`export function ${exportName}(props) {`), lines.push(' return React.createElement(React.Fragment, null,'), lines.push(` ...(${exportName}WithoutClientChunk.entryCssFiles ?? []).map(href =>`), lines.push(' React.createElement("link", { key: href, rel: "stylesheet", href: href, precedence: "default" })),'), lines.push(' React.createElement(EnsureClientRouteModuleForHMR___, null),'), lines.push(` React.createElement(${exportName}WithoutClientChunk, props),`), lines.push(' );'), lines.push('}');
14455
+ needsReactImport = !0, needsEnsureHmrImport = !0, lines.push(`import { ${exportName} as ${exportName}WithoutClientChunk } from ${JSON.stringify(plan.serverTarget)};`), lines.push(`export function ${exportName}(props) {`), lines.push(' return React.createElement(React.Fragment, null,'), pushStylesheetLinks(`${exportName}WithoutClientChunk.entryCssFiles`), lines.push(' React.createElement(EnsureClientRouteModuleForHMR___, null),'), lines.push(` React.createElement(${exportName}WithoutClientChunk, props),`), lines.push(' );'), lines.push('}');
14435
14456
  continue;
14436
14457
  }
14437
14458
  lines.push(createReexport(exportName, plan.serverTarget));
@@ -14689,9 +14710,14 @@ export {
14689
14710
  'allowed-action-origins',
14690
14711
  'client-version',
14691
14712
  'react-router-serve-config',
14713
+ 'manifest-prefix',
14692
14714
  "bootstrap-scripts",
14693
14715
  'server-manifest'
14694
- ], createReactRouterRscResolveAliases = (rootPath)=>({
14716
+ ], createReactRouterRscResolveAliases = (rootPath, options = {})=>({
14717
+ ...options.entrySsrPath ? {
14718
+ 'virtual:react-router/unstable_rsc/entry-ssr': options.entrySsrPath,
14719
+ 'virtual/react-router/unstable_rsc/entry-ssr': options.entrySsrPath
14720
+ } : {},
14695
14721
  ...Object.fromEntries(RSC_VIRTUAL_ALIAS_IDS.flatMap((id)=>{
14696
14722
  let moduleId = `virtual/react-router/unstable_rsc/${id}`, modulePath = (0, external_pathe_namespaceObject.resolve)(rootPath, getVirtualModuleFilePath(moduleId));
14697
14723
  return [
@@ -14706,8 +14732,8 @@ export {
14706
14732
  ];
14707
14733
  })),
14708
14734
  'react-router/internal/react-server-client': (0, external_pathe_namespaceObject.resolve)(rootPath, getVirtualModuleFilePath('virtual/react-router/rsc-internal-client'))
14709
- }), createReactRouterRscVirtualModules = ({ allowedActionOrigins, appDirectory, basename, buildDirectory, isBuild, jsDistPath, outputClientPath, publicPath, routeDiscovery, routes, ssr })=>{
14710
- let rscAssetsBuildDirectory = (0, external_pathe_namespaceObject.relative)((0, external_pathe_namespaceObject.resolve)(buildDirectory, 'server'), outputClientPath), bootstrapPublicPath = normalizeAssetPrefix(publicPath);
14735
+ }), createReactRouterRscVirtualModules = ({ allowedActionOrigins, appDirectory, basename, buildDirectory, isBuild, outputClientPath, publicPath, routeDiscovery, routes, ssr })=>{
14736
+ let rscAssetsBuildDirectory = (0, external_pathe_namespaceObject.relative)((0, external_pathe_namespaceObject.resolve)(buildDirectory, 'server'), outputClientPath), serverPublicPath = normalizeAssetPrefix(publicPath);
14711
14737
  return {
14712
14738
  'virtual/react-router/unstable_rsc/routes': createRscRouteConfig({
14713
14739
  appDirectory,
@@ -14729,9 +14755,33 @@ export {
14729
14755
  assetsBuildDirectory: rscAssetsBuildDirectory,
14730
14756
  publicPath
14731
14757
  }),
14732
- "virtual/react-router/unstable_rsc/bootstrap-scripts": defaultExport([
14733
- combineURLs(bootstrapPublicPath, `${jsDistPath}/index.js`)
14734
- ]),
14758
+ 'virtual/react-router/unstable_rsc/manifest-prefix': `const manifest = __webpack_require__.rscM;
14759
+ const serverPrefix = ${JSON.stringify(serverPublicPath)};
14760
+ const appliedPrefix = manifest?.moduleLoading?.prefix;
14761
+ if (appliedPrefix && appliedPrefix !== serverPrefix) {
14762
+ const rewrite = url =>
14763
+ typeof url === "string" && url.startsWith(appliedPrefix)
14764
+ ? serverPrefix + url.slice(appliedPrefix.length)
14765
+ : url;
14766
+ const rewriteAll = list => {
14767
+ if (Array.isArray(list)) for (let i = 0; i < list.length; i++) list[i] = rewrite(list[i]);
14768
+ };
14769
+ manifest.moduleLoading.prefix = serverPrefix;
14770
+ rewriteAll(manifest.entryJsFiles);
14771
+ for (const files of Object.values(manifest.entryCssFiles ?? {})) rewriteAll(files);
14772
+ for (const reference of Object.values(manifest.clientManifest ?? {})) rewriteAll(reference.cssFiles);
14773
+ }
14774
+ export const rscManifest = manifest;
14775
+ `,
14776
+ "virtual/react-router/unstable_rsc/bootstrap-scripts": `import { rscManifest } from "virtual/react-router/unstable_rsc/manifest-prefix";
14777
+ const entryJsFiles = rscManifest?.entryJsFiles;
14778
+ if (!entryJsFiles?.length) {
14779
+ throw new Error(
14780
+ "[rsbuild-plugin-react-router] The rspack RSC manifest lists no browser entry script (entryJsFiles is empty), so the server cannot render bootstrap scripts. Rspack only records entry files whose name ends in \\".js\\"; web output.filename.js values with a query (for example \\"[name].js?v=[contenthash:8]\\") or another extension are not supported in RSC mode."
14781
+ );
14782
+ }
14783
+ export default entryJsFiles;
14784
+ `,
14735
14785
  'virtual/react-router/unstable_rsc/server-manifest': `export default function getServerManifest() {
14736
14786
  return __webpack_require__.rscM?.serverManifest;
14737
14787
  }
@@ -15559,7 +15609,7 @@ export {
15559
15609
  'react-router/dom$': reactRouterDomPath
15560
15610
  } : {}
15561
15611
  };
15562
- }, createRscModePlan = async ({ allowedActionOriginsForBuild, api, appDirectory, basename, buildDirectory, customServer, finalEntryRscClientPath, finalEntryRscPath, isBuild, outputClientPath, pluginName, prerenderConfig, routeConfig, routeDiscovery, routes, rootRouteFile, serverBuildFile, splitRouteModules, ssr })=>{
15612
+ }, createRscModePlan = async ({ allowedActionOriginsForBuild, api, appDirectory, basename, buildDirectory, customServer, finalEntryRscClientPath, finalEntryRscPath, finalEntryRscSsrPath, isBuild, outputClientPath, pluginName, prerenderConfig, routeConfig, routeDiscovery, routes, rootRouteFile, serverBuildFile, splitRouteModules, ssr })=>{
15563
15613
  let rscServerEntryName = (serverBuildFile || 'index.js').replace(/\.js$/, '');
15564
15614
  return {
15565
15615
  kind: 'rsc',
@@ -15587,13 +15637,12 @@ export {
15587
15637
  layer: RSC_LAYERS.rsc
15588
15638
  }
15589
15639
  },
15590
- createVirtualModules: (publicPath, jsDistPath)=>createReactRouterRscVirtualModules({
15640
+ createVirtualModules: (publicPath)=>createReactRouterRscVirtualModules({
15591
15641
  allowedActionOrigins: allowedActionOriginsForBuild,
15592
15642
  appDirectory,
15593
15643
  basename,
15594
15644
  buildDirectory,
15595
15645
  isBuild,
15596
- jsDistPath,
15597
15646
  outputClientPath,
15598
15647
  publicPath,
15599
15648
  routeDiscovery,
@@ -15605,7 +15654,9 @@ export {
15605
15654
  (0, external_pathe_namespaceObject.resolve)(rootPath, 'node_modules'),
15606
15655
  'node_modules'
15607
15656
  ],
15608
- alias: createReactRouterRscResolveAliases(rootPath)
15657
+ alias: createReactRouterRscResolveAliases(rootPath, {
15658
+ entrySsrPath: finalEntryRscSsrPath
15659
+ })
15609
15660
  }),
15610
15661
  server: customServer ? void 0 : {
15611
15662
  setup: createReactRouterRscDevServerSetup({
@@ -15685,7 +15736,7 @@ export {
15685
15736
  defaultEntryName,
15686
15737
  serverBundleEntries: artifacts.serverBundleEntries
15687
15738
  }),
15688
- createVirtualModules: (publicPath, _jsDistPath)=>createClassicVirtualModules({
15739
+ createVirtualModules: (publicPath)=>createClassicVirtualModules({
15689
15740
  allowedActionOrigins: allowedActionOriginsForBuild,
15690
15741
  appDirectory,
15691
15742
  assetsBuildDirectory,
@@ -15720,10 +15771,7 @@ export {
15720
15771
  library: {
15721
15772
  type: 'module'
15722
15773
  },
15723
- module: !0,
15724
- ...isBuild ? {
15725
- chunkFilename: 'static/js/async/[id]-[contenthash:16].js'
15726
- } : {}
15774
+ module: !0
15727
15775
  },
15728
15776
  webOptimization: {
15729
15777
  avoidEntryIife: !0,
@@ -15805,11 +15853,14 @@ export {
15805
15853
  }), api.onBeforeBuild(()=>{
15806
15854
  warnOnClientSourceMaps(api.getNormalizedConfig(), (msg)=>api.logger.warn(msg), 'web');
15807
15855
  }), api.onBeforeCreateCompiler(()=>{
15808
- let normalized = api.getNormalizedConfig();
15856
+ let root = api.getNormalizedConfig(), web = root.environments.web;
15809
15857
  assetPrefix = resolveEffectiveAssetPrefix({
15810
- dev: normalized.dev,
15811
- output: normalized.output,
15858
+ dev: web?.dev,
15859
+ output: web?.output,
15812
15860
  isBuild: 'build' === api.context.action
15861
+ }, {
15862
+ dev: root.dev,
15863
+ output: root.output
15813
15864
  });
15814
15865
  });
15815
15866
  let configPath = findEntryFile((0, external_pathe_namespaceObject.resolve)('react-router.config')), configExists = (0, external_node_fs_namespaceObject.existsSync)(configPath), configWatchPaths = configExists ? configPath : JS_EXTENSIONS.map((extension)=>(0, external_pathe_namespaceObject.resolve)(`react-router.config${extension}`)), reactRouterUserConfig = {};
@@ -15964,6 +16015,7 @@ export {
15964
16015
  buildDirectory,
15965
16016
  finalEntryRscClientPath,
15966
16017
  finalEntryRscPath,
16018
+ finalEntryRscSsrPath,
15967
16019
  outputClientPath,
15968
16020
  pluginName: PLUGIN_NAME,
15969
16021
  serverBuildFile
@@ -16076,13 +16128,16 @@ export {
16076
16128
  prerenderPaths: modePlan.prerenderPaths,
16077
16129
  basename
16078
16130
  })))), api.modifyRsbuildConfig(async (config, { mergeRsbuildConfig })=>{
16079
- let webDistPath = config.environments?.web?.output?.distPath, rootDistPath = config.output?.distPath, jsDistPath = ('object' == typeof webDistPath ? webDistPath.js : void 0) ?? ('object' == typeof rootDistPath ? rootDistPath.js : void 0) ?? DEFAULT_JS_DIST_PATH, assetPrefix = resolveEffectiveAssetPrefix({
16080
- dev: config.dev,
16081
- output: config.output,
16131
+ let publicPath, webConfig = config.environments?.web, webJsFilename = webConfig?.output?.filename?.js ?? config.output?.filename?.js;
16132
+ if (isRscMode && 'string' == typeof webJsFilename && !/\.js$/.test(webJsFilename)) throw Error(`[${PLUGIN_NAME}] RSC mode requires web \`output.filename.js\` to end in ".js" (got ${JSON.stringify(webJsFilename)}): rspack's RSC manifest omits entry files with a query or another extension, so the server could not render bootstrap scripts.`);
16133
+ let vmodPlugin = (publicPath = resolveEffectiveAssetPrefix({
16134
+ dev: webConfig?.dev,
16135
+ output: webConfig?.output,
16082
16136
  isBuild
16083
- }), vmodPlugin = new core_namespaceObject.rspack.experiments.VirtualModulesPlugin(mapVirtualModules(modePlan.createVirtualModules(assetPrefix, jsDistPath))), useAsyncNodeChunkLoading = options.federation && 'commonjs' === resolvedServerOutput, nodeChunkLoading = 'require';
16084
- 'module' === resolvedServerOutput ? nodeChunkLoading = 'import' : useAsyncNodeChunkLoading && (nodeChunkLoading = 'async-node');
16085
- let guardedLazyCompilation = guardReactRouterLazyCompilation({
16137
+ }, {
16138
+ dev: config.dev,
16139
+ output: config.output
16140
+ }), new core_namespaceObject.rspack.experiments.VirtualModulesPlugin(mapVirtualModules(modePlan.createVirtualModules(publicPath)))), guardedLazyCompilation = guardReactRouterLazyCompilation({
16086
16141
  lazyCompilation: Object.prototype.hasOwnProperty.call(options, 'lazyCompilation') ? pluginOptions.lazyCompilation : config.dev?.lazyCompilation ?? pluginOptions.lazyCompilation,
16087
16142
  entryClientPath: isRscMode ? finalEntryRscClientPath : finalEntryClientPath,
16088
16143
  prewarmReactRouterModules: !!pluginOptions.unstableLazyCompilationPrewarm
@@ -16136,9 +16191,6 @@ export {
16136
16191
  }
16137
16192
  },
16138
16193
  output: {
16139
- filename: {
16140
- js: '[name].js'
16141
- },
16142
16194
  distPath: {
16143
16195
  root: outputClientPath
16144
16196
  }
@@ -16157,13 +16209,6 @@ export {
16157
16209
  ]
16158
16210
  },
16159
16211
  externalsType: modePlan.webExternalsType,
16160
- output: {
16161
- ...modePlan.webOutput,
16162
- publicPath: assetPrefix,
16163
- ...options.federation ? {
16164
- chunkLoading: 'import'
16165
- } : {}
16166
- },
16167
16212
  optimization: modePlan.webOptimization
16168
16213
  }
16169
16214
  }
@@ -16201,17 +16246,7 @@ export {
16201
16246
  },
16202
16247
  externals: modePlan.nodeExternals,
16203
16248
  ...modePlan.nodeDependencies,
16204
- externalsType: resolvedServerOutput,
16205
- output: {
16206
- chunkFormat: resolvedServerOutput,
16207
- chunkLoading: nodeChunkLoading,
16208
- devtoolModuleFilenameTemplate: '[absolute-resource-path]',
16209
- devtoolFallbackModuleFilenameTemplate: '[absolute-resource-path]?[hash]',
16210
- workerChunkLoading: nodeChunkLoading,
16211
- wasmLoading: 'fetch',
16212
- module: 'module' === resolvedServerOutput,
16213
- chunkFilename: 'static/js/async/[name].js'
16214
- }
16249
+ externalsType: resolvedServerOutput
16215
16250
  }
16216
16251
  }
16217
16252
  }
@@ -16220,7 +16255,8 @@ export {
16220
16255
  }), registerReactRouterEnvironmentOutput({
16221
16256
  api,
16222
16257
  federation: pluginOptions.federation,
16223
- resolvedServerOutput
16258
+ resolvedServerOutput,
16259
+ webOutput: modePlan.webOutput
16224
16260
  }), 'classic' === modePlan.kind && useRouteModuleTransformLoader && api.modifyEnvironmentConfig(async (config, { name, mergeEnvironmentConfig })=>'web' !== name && 'node' !== name ? config : mergeEnvironmentConfig(config, {
16225
16261
  tools: {
16226
16262
  rspack: (rspackConfig)=>{