rsbuild-plugin-react-router 0.6.6 → 0.7.1

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/dist/index.js CHANGED
@@ -38,6 +38,8 @@ let getAvailableCpuCount = ()=>'function' == typeof availableParallelism ? avail
38
38
  }, resolveEntryWithTemplate = ({ appDirectory, entryName, templateName, templatesDirectory })=>{
39
39
  let userEntryPath = findEntryFile(external_pathe_resolve(appDirectory, entryName));
40
40
  return existsSync(userEntryPath) ? userEntryPath : external_pathe_resolve(templatesDirectory, templateName);
41
+ }, getModuleFederationOptions = (plugin)=>{
42
+ if (plugin && 'object' == typeof plugin && ('ModuleFederationPlugin' === plugin.name || 'RspackModuleFederationPlugin' === plugin.name)) return plugin._options ?? plugin.options;
41
43
  }, Function_dual = function(arity, body) {
42
44
  if ("function" == typeof arity) return function() {
43
45
  return arity(arguments) ? body.apply(this, arguments) : (self)=>body(self, ...arguments);
@@ -7356,6 +7358,9 @@ let createReactRouterManifestOptions = ({ routeChunks, routeModuleAnalysis })=>{
7356
7358
  routeModuleAnalysis
7357
7359
  } : {}
7358
7360
  };
7361
+ }, isManifestJsAsset = (asset)=>/(?<!\.hot-update)\.[cm]?js(?:\?.*)?$/.test(asset), isManifestCssAsset = (asset)=>/\.css(?:\?.*)?$/.test(asset), hasSome = (iterable)=>{
7362
+ for (let _ of iterable)return !0;
7363
+ return !1;
7359
7364
  }, collectManifestFilesByName = (items, names, getFiles)=>{
7360
7365
  let filesByName = {};
7361
7366
  if (!names) {
@@ -7372,15 +7377,7 @@ let createReactRouterManifestOptions = ({ routeChunks, routeModuleAnalysis })=>{
7372
7377
  return filesByName;
7373
7378
  }, createReactRouterManifestStats = (compilation, chunkNames)=>{
7374
7379
  if (!compilation) return;
7375
- let assetsByChunkName = collectManifestFilesByName(compilation.namedChunks, chunkNames, (chunkName, chunk)=>{
7376
- var files;
7377
- let ownChunkAsset, ownFileIndex;
7378
- return files = Array.from(chunk.files ?? []), ownChunkAsset = `${chunkName}.js`, (ownFileIndex = files.findIndex((file)=>file.endsWith(ownChunkAsset))) <= 0 ? files : [
7379
- files[ownFileIndex],
7380
- ...files.slice(0, ownFileIndex),
7381
- ...files.slice(ownFileIndex + 1)
7382
- ];
7383
- }), entrypointFilesByName = compilation.entrypoints ? collectManifestFilesByName(compilation.entrypoints, chunkNames, (_name, entrypoint)=>Array.from(entrypoint.getFiles?.() ?? [])) : {};
7380
+ 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?.() ?? [])) : {};
7384
7381
  return Object.keys(entrypointFilesByName).length > 0 ? {
7385
7382
  assetsByChunkName,
7386
7383
  entrypointFilesByName
@@ -7416,9 +7413,9 @@ async function generateReactRouterManifestForDev(routes, _options, clientStats,
7416
7413
  return chunkAssetsByName.set(chunkName, result), result;
7417
7414
  }
7418
7415
  let cssAssets = new Set(), jsAssets = new Set();
7419
- for (let asset of assets)asset.endsWith('.css') ? cssAssets.add(asset) : asset.endsWith('.js') && jsAssets.add(asset);
7420
- for (let asset of clientStats?.entrypointFilesByName?.[chunkName] ?? [])asset.endsWith('.css') ? cssAssets.add(asset) : isBuild && asset.endsWith('.js') && jsAssets.add(asset);
7421
- 0 === jsAssets.size && jsAssets.add(`${DEFAULT_JS_DIST_PATH}/${chunkName}.js`);
7416
+ for (let asset of assets)isManifestCssAsset(asset) ? cssAssets.add(asset) : isManifestJsAsset(asset) && jsAssets.add(asset);
7417
+ for (let asset of clientStats?.entrypointFilesByName?.[chunkName] ?? [])isManifestCssAsset(asset) ? cssAssets.add(asset) : isBuild && isManifestJsAsset(asset) && jsAssets.add(asset);
7418
+ 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.`);
7422
7419
  let result = {
7423
7420
  js: [
7424
7421
  ...jsAssets
@@ -7906,7 +7903,7 @@ let redirectStatusCodes = new Set([
7906
7903
  }
7907
7904
  };
7908
7905
  }, BROWSER_MANIFEST_ASSET = 'static/js/virtual/react-router/browser-manifest.js', ABSOLUTE_URL_RE = /^[a-zA-Z][a-zA-Z\d+\-.]*:/, addIntegrity = (sri, assetPrefix, assetName, integrity)=>{
7909
- 'string' == typeof assetName && assetName.endsWith('.js') && 'string' == typeof integrity && (sri[ABSOLUTE_URL_RE.test(assetName) || assetName.startsWith('//') || assetName.startsWith('/') ? assetName : combineURLs(assetPrefix, assetName)] = integrity);
7906
+ 'string' == typeof assetName && isManifestJsAsset(assetName) && 'string' == typeof integrity && (sri[ABSOLUTE_URL_RE.test(assetName) || assetName.startsWith('//') || assetName.startsWith('/') ? assetName : combineURLs(assetPrefix, assetName)] = integrity);
7910
7907
  }, computeSubresourceIntegrity = (source)=>{
7911
7908
  if (source) return `sha384-${createHash('sha384').update(source.source()).digest('base64')}`;
7912
7909
  };
@@ -8596,12 +8593,14 @@ export function EnsureClientRouteModuleForHMR___() { return ___EnsureClientRoute
8596
8593
  }, createServerRouteEntry = async (options)=>{
8597
8594
  let plan = await createRscRouteExportPlan(options);
8598
8595
  validateRscRouteExportPlan(plan, options);
8599
- let lines = [], needsReactImport = !1, needsEnsureHmrImport = !1, needsStyleEntryImport = !1, streamsClientRouteCss = !plan.exportNames.some(isServerComponentExport) && plan.exportNames.includes('default') && programHasSideEffectStyleImports(getProgram(yuku_parse(options.code, {
8596
+ let lines = [], needsReactImport = !1, needsEnsureHmrImport = !1, needsStyleEntryImport = !1, pushStylesheetLinks = (entryCssFilesExpression)=>{
8597
+ lines.push(` ...(${entryCssFilesExpression} ?? []).map(href =>`), lines.push(' React.createElement("link", { key: href, rel: "stylesheet", href: href, precedence: "default" })),');
8598
+ }, streamsClientRouteCss = !plan.exportNames.some(isServerComponentExport) && plan.exportNames.includes('default') && programHasSideEffectStyleImports(getProgram(yuku_parse(options.code, {
8600
8599
  sourceType: 'module'
8601
8600
  })));
8602
8601
  for (let exportName of plan.exportNames){
8603
8602
  if (streamsClientRouteCss && 'default' === exportName) {
8604
- 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('}');
8603
+ 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('}');
8605
8604
  continue;
8606
8605
  }
8607
8606
  if (isClientRouteExport(exportName)) {
@@ -8609,7 +8608,7 @@ export function EnsureClientRouteModuleForHMR___() { return ___EnsureClientRoute
8609
8608
  continue;
8610
8609
  }
8611
8610
  if (isServerComponentExport(exportName)) {
8612
- 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('}');
8611
+ 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('}');
8613
8612
  continue;
8614
8613
  }
8615
8614
  lines.push(createReexport(exportName, plan.serverTarget));
@@ -8803,6 +8802,7 @@ export function EnsureClientRouteModuleForHMR___() { return ___EnsureClientRoute
8803
8802
  'allowed-action-origins',
8804
8803
  'client-version',
8805
8804
  'react-router-serve-config',
8805
+ 'manifest-prefix',
8806
8806
  "bootstrap-scripts",
8807
8807
  'server-manifest'
8808
8808
  ], setupReactRouterRscPlugin = async ({ api, entryRscPath, entrySsrPath, pluginName, rsc })=>{
@@ -9528,8 +9528,8 @@ export function EnsureClientRouteModuleForHMR___() { return ___EnsureClientRoute
9528
9528
  layer: RSC_LAYERS.rsc
9529
9529
  }
9530
9530
  },
9531
- createVirtualModules: (publicPath, jsDistPath)=>(({ allowedActionOrigins, appDirectory, basename, buildDirectory, isBuild, jsDistPath, outputClientPath, publicPath, routeDiscovery, routes, ssr })=>{
9532
- let rscAssetsBuildDirectory = relative(external_pathe_resolve(buildDirectory, 'server'), outputClientPath), bootstrapPublicPath = normalizeAssetPrefix(publicPath);
9531
+ createVirtualModules: (publicPath)=>(({ allowedActionOrigins, appDirectory, basename, buildDirectory, isBuild, outputClientPath, publicPath, routeDiscovery, routes, ssr })=>{
9532
+ let rscAssetsBuildDirectory = relative(external_pathe_resolve(buildDirectory, 'server'), outputClientPath), serverPublicPath = normalizeAssetPrefix(publicPath);
9533
9533
  return {
9534
9534
  'virtual/react-router/unstable_rsc/routes': (({ appDirectory, routes })=>{
9535
9535
  let componentResolutionCode = RSC_ROUTE_COMPONENT_EXPORTS.map(({ routeProperty, clientExport, serverExport })=>{
@@ -9625,9 +9625,41 @@ export default [`;
9625
9625
  assetsBuildDirectory: rscAssetsBuildDirectory,
9626
9626
  publicPath
9627
9627
  }),
9628
- "virtual/react-router/unstable_rsc/bootstrap-scripts": defaultExport([
9629
- combineURLs(bootstrapPublicPath, `${jsDistPath}/index.js`)
9630
- ]),
9628
+ 'virtual/react-router/unstable_rsc/manifest-prefix': `const manifest = __webpack_require__.rscM;
9629
+ const serverPrefix = ${JSON.stringify(serverPublicPath)};
9630
+ const appliedPrefix = manifest?.moduleLoading?.prefix;
9631
+ // An empty applied prefix (web \`assetPrefix: ''\`) yields relative references
9632
+ // such as "static/js/index.js"; those are rebased too, while absolute and
9633
+ // protocol-relative URLs are left alone.
9634
+ const isAbsoluteUrl = url => /^(?:[a-z][a-z\\d+.-]*:|\\/\\/|\\/)/i.test(url);
9635
+ if (typeof appliedPrefix === "string" && appliedPrefix !== serverPrefix) {
9636
+ const rewrite = url =>
9637
+ typeof url !== "string"
9638
+ ? url
9639
+ : appliedPrefix !== "" && url.startsWith(appliedPrefix)
9640
+ ? serverPrefix + url.slice(appliedPrefix.length)
9641
+ : appliedPrefix === "" && !isAbsoluteUrl(url)
9642
+ ? serverPrefix + url
9643
+ : url;
9644
+ const rewriteAll = list => {
9645
+ if (Array.isArray(list)) for (let i = 0; i < list.length; i++) list[i] = rewrite(list[i]);
9646
+ };
9647
+ manifest.moduleLoading.prefix = serverPrefix;
9648
+ rewriteAll(manifest.entryJsFiles);
9649
+ for (const files of Object.values(manifest.entryCssFiles ?? {})) rewriteAll(files);
9650
+ for (const reference of Object.values(manifest.clientManifest ?? {})) rewriteAll(reference.cssFiles);
9651
+ }
9652
+ export const rscManifest = manifest;
9653
+ `,
9654
+ "virtual/react-router/unstable_rsc/bootstrap-scripts": `import { rscManifest } from "virtual/react-router/unstable_rsc/manifest-prefix";
9655
+ const entryJsFiles = rscManifest?.entryJsFiles;
9656
+ if (!entryJsFiles?.length) {
9657
+ throw new Error(
9658
+ "[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."
9659
+ );
9660
+ }
9661
+ export default entryJsFiles;
9662
+ `,
9631
9663
  'virtual/react-router/unstable_rsc/server-manifest': `export default function getServerManifest() {
9632
9664
  return __webpack_require__.rscM?.serverManifest;
9633
9665
  }
@@ -9667,7 +9699,6 @@ export {
9667
9699
  basename,
9668
9700
  buildDirectory,
9669
9701
  isBuild,
9670
- jsDistPath,
9671
9702
  outputClientPath,
9672
9703
  publicPath,
9673
9704
  routeDiscovery,
@@ -9849,7 +9880,7 @@ export {
9849
9880
  defaultEntryName,
9850
9881
  serverBundleEntries: artifacts.serverBundleEntries
9851
9882
  }),
9852
- createVirtualModules: (publicPath, _jsDistPath)=>(({ allowedActionOrigins, appDirectory, assetsBuildDirectory, basename, devHmrRuntimeModule, entryServerPath, future, isSpaMode, prerenderPaths, publicPath, routeDiscovery, routes, routesByServerBundleId, ssr })=>{
9883
+ createVirtualModules: (publicPath)=>(({ allowedActionOrigins, appDirectory, assetsBuildDirectory, basename, devHmrRuntimeModule, entryServerPath, future, isSpaMode, prerenderPaths, publicPath, routeDiscovery, routes, routesByServerBundleId, ssr })=>{
9853
9884
  let serverBuildOptions = {
9854
9885
  entryServerPath,
9855
9886
  assetsBuildDirectory,
@@ -9938,10 +9969,7 @@ export {
9938
9969
  library: {
9939
9970
  type: 'module'
9940
9971
  },
9941
- module: !0,
9942
- ...isBuild ? {
9943
- chunkFilename: 'static/js/async/[id]-[contenthash:16].js'
9944
- } : {}
9972
+ module: !0
9945
9973
  },
9946
9974
  webOptimization: {
9947
9975
  avoidEntryIife: !0,
@@ -10105,11 +10133,14 @@ export {
10105
10133
  (isSourceMapEnabled(sourceMapSetting) || !0 === devtoolSetting || ('string' == typeof devtoolSetting ? devtoolSetting.includes('source-map') : null !== devtoolSetting && 'object' == typeof devtoolSetting)) && warn("\n WARNING: Source maps are enabled in production\n This makes your server code publicly visible in the browser.\n This is highly discouraged! If you insist, ensure that you are using\n environment variables for secrets and not hard-coding them in your source code.\n");
10106
10134
  }(api.getNormalizedConfig(), (msg)=>api.logger.warn(msg), 'web');
10107
10135
  }), api.onBeforeCreateCompiler(()=>{
10108
- let normalized = api.getNormalizedConfig();
10136
+ let root = api.getNormalizedConfig(), web = root.environments.web;
10109
10137
  assetPrefix = resolveEffectiveAssetPrefix({
10110
- dev: normalized.dev,
10111
- output: normalized.output,
10138
+ dev: web?.dev,
10139
+ output: web?.output,
10112
10140
  isBuild: 'build' === api.context.action
10141
+ }, {
10142
+ dev: root.dev,
10143
+ output: root.output
10113
10144
  });
10114
10145
  });
10115
10146
  let configPath = findEntryFile(external_pathe_resolve('react-router.config')), configExists = existsSync(configPath), configWatchPaths = configExists ? configPath : JS_EXTENSIONS.map((extension)=>external_pathe_resolve(`react-router.config${extension}`)), reactRouterUserConfig1 = {};
@@ -10889,8 +10920,27 @@ if (typeof window !== 'undefined' && import.meta.webpackHot) {
10889
10920
  initialRouteTopology: routeTopology.initialRouteTopology,
10890
10921
  onRouteTopologyChange: pluginOptions.onRouteTopologyChange
10891
10922
  });
10892
- api.onAfterEnvironmentCompile(({ stats, environment })=>{
10893
- if ('web' === environment.name && (clientStats = createReactRouterManifestStats(stats?.compilation, manifestChunkNames)), pluginOptions.federation && ssr) {
10923
+ if (api.onAfterEnvironmentCompile(({ stats, environment })=>{
10924
+ if ('web' === environment.name && (clientStats = createReactRouterManifestStats(stats?.compilation, manifestChunkNames), isRscMode && stats)) {
10925
+ let unsupported = ((compilation)=>{
10926
+ let unsupported = new Set();
10927
+ for (let chunk of compilation.chunks){
10928
+ if (!(chunk.contentHash?.javascript !== void 0 || hasSome(compilation.chunkGraph.getChunkModulesIterableBySourceType(chunk, "javascript")))) continue;
10929
+ let pathData = {
10930
+ chunk,
10931
+ contentHashType: "javascript"
10932
+ }, template = chunk.canBeInitial() ? compilation.outputOptions.filename : compilation.outputOptions.chunkFilename, resolvedTemplate = 'function' == typeof template ? template(pathData) : template;
10933
+ if ('string' != typeof resolvedTemplate) continue;
10934
+ let file = compilation.getPath(resolvedTemplate, pathData);
10935
+ file.endsWith('.js') || unsupported.add(file);
10936
+ }
10937
+ return [
10938
+ ...unsupported
10939
+ ];
10940
+ })(stats.compilation);
10941
+ if (unsupported.length > 0) throw Error(`[${PLUGIN_NAME}] RSC mode requires every browser JavaScript asset to be named "*.js" (no query, no other extension): rspack's RSC manifest omits ${unsupported.slice(0, 5).map((asset)=>JSON.stringify(asset)).join(', ')}${unsupported.length > 5 ? ` and ${unsupported.length - 5} more` : ''}. Adjust web \`output.filename.js\` / \`chunkFilename\`.`);
10942
+ }
10943
+ if (pluginOptions.federation && ssr) {
10894
10944
  let serverBuildDir = external_pathe_resolve(buildDirectory, 'server'), clientBuildDir = external_pathe_resolve(buildDirectory, 'client');
10895
10945
  if (existsSync(serverBuildDir)) {
10896
10946
  let ssrDir = external_pathe_resolve(clientBuildDir, 'static');
@@ -10934,16 +10984,17 @@ if (typeof window !== 'undefined' && import.meta.webpackHot) {
10934
10984
  basename
10935
10985
  })))), api.modifyRsbuildConfig(async (config, { mergeRsbuildConfig })=>{
10936
10986
  var existing;
10937
- let publicPath, jsDistPath, webDistPath = config.environments?.web?.output?.distPath, rootDistPath = config.output?.distPath, jsDistPath1 = ('object' == typeof webDistPath ? webDistPath.js : void 0) ?? ('object' == typeof rootDistPath ? rootDistPath.js : void 0) ?? DEFAULT_JS_DIST_PATH, assetPrefix = resolveEffectiveAssetPrefix({
10938
- dev: config.dev,
10939
- output: config.output,
10987
+ let publicPath, webConfig = config.environments?.web, vmodPlugin = (publicPath = resolveEffectiveAssetPrefix({
10988
+ dev: webConfig?.dev,
10989
+ output: webConfig?.output,
10940
10990
  isBuild
10941
- }), vmodPlugin = (publicPath = assetPrefix, jsDistPath = jsDistPath1, new rspack.experiments.VirtualModulesPlugin(Object.fromEntries(Object.entries(modePlan.createVirtualModules(publicPath, jsDistPath)).map(([moduleId, contents])=>[
10991
+ }, {
10992
+ dev: config.dev,
10993
+ output: config.output
10994
+ }), new rspack.experiments.VirtualModulesPlugin(Object.fromEntries(Object.entries(modePlan.createVirtualModules(publicPath)).map(([moduleId, contents])=>[
10942
10995
  getVirtualModuleFilePath(moduleId),
10943
10996
  contents
10944
- ])))), useAsyncNodeChunkLoading = options.federation && 'commonjs' === resolvedServerOutput, nodeChunkLoading = 'require';
10945
- 'module' === resolvedServerOutput ? nodeChunkLoading = 'import' : useAsyncNodeChunkLoading && (nodeChunkLoading = 'async-node');
10946
- let guardedLazyCompilation = (({ lazyCompilation, entryClientPath, prewarmReactRouterModules = !1 })=>{
10997
+ ])))), guardedLazyCompilation = (({ lazyCompilation, entryClientPath, prewarmReactRouterModules = !1 })=>{
10947
10998
  if (void 0 === lazyCompilation || !1 === lazyCompilation) return lazyCompilation;
10948
10999
  let options = !0 === lazyCompilation ? {
10949
11000
  entries: !0,
@@ -11035,9 +11086,6 @@ if (typeof window !== 'undefined' && import.meta.webpackHot) {
11035
11086
  }
11036
11087
  },
11037
11088
  output: {
11038
- filename: {
11039
- js: '[name].js'
11040
- },
11041
11089
  distPath: {
11042
11090
  root: outputClientPath
11043
11091
  }
@@ -11056,13 +11104,6 @@ if (typeof window !== 'undefined' && import.meta.webpackHot) {
11056
11104
  ]
11057
11105
  },
11058
11106
  externalsType: modePlan.webExternalsType,
11059
- output: {
11060
- ...modePlan.webOutput,
11061
- publicPath: assetPrefix,
11062
- ...options.federation ? {
11063
- chunkLoading: 'import'
11064
- } : {}
11065
- },
11066
11107
  optimization: modePlan.webOptimization
11067
11108
  }
11068
11109
  }
@@ -11100,36 +11141,60 @@ if (typeof window !== 'undefined' && import.meta.webpackHot) {
11100
11141
  },
11101
11142
  externals: modePlan.nodeExternals,
11102
11143
  ...modePlan.nodeDependencies,
11103
- externalsType: resolvedServerOutput,
11104
- output: {
11105
- chunkFormat: resolvedServerOutput,
11106
- chunkLoading: nodeChunkLoading,
11107
- devtoolModuleFilenameTemplate: '[absolute-resource-path]',
11108
- devtoolFallbackModuleFilenameTemplate: '[absolute-resource-path]?[hash]',
11109
- workerChunkLoading: nodeChunkLoading,
11110
- wasmLoading: 'fetch',
11111
- module: 'module' === resolvedServerOutput,
11112
- chunkFilename: 'static/js/async/[name].js'
11113
- }
11144
+ externalsType: resolvedServerOutput
11114
11145
  }
11115
11146
  }
11116
11147
  }
11117
11148
  }
11118
11149
  });
11119
- }), (({ api, federation, resolvedServerOutput })=>{
11120
- api.modifyEnvironmentConfig(async (config, { name, mergeEnvironmentConfig })=>'web' !== name && 'node' !== name ? config : mergeEnvironmentConfig(config, {
11150
+ }), (({ api, federation, resolvedServerOutput, webOutput })=>{
11151
+ let nodeChunkLoading = 'module' === resolvedServerOutput ? 'import' : federation ? 'async-node' : 'require';
11152
+ api.modifyRspackConfig((rspackConfig, { environment, mergeConfig })=>'web' === environment.name ? mergeConfig(rspackConfig, {
11153
+ output: {
11154
+ ...webOutput,
11155
+ ...federation ? {
11156
+ chunkLoading: 'import'
11157
+ } : {}
11158
+ }
11159
+ }) : 'node' === environment.name ? mergeConfig(rspackConfig, {
11160
+ output: {
11161
+ chunkFormat: resolvedServerOutput,
11162
+ chunkLoading: nodeChunkLoading,
11163
+ devtoolModuleFilenameTemplate: '[absolute-resource-path]',
11164
+ devtoolFallbackModuleFilenameTemplate: '[absolute-resource-path]?[hash]',
11165
+ workerChunkLoading: nodeChunkLoading,
11166
+ wasmLoading: 'fetch',
11167
+ module: 'module' === resolvedServerOutput,
11168
+ chunkFilename: 'static/js/async/[name].js'
11169
+ }
11170
+ }) : rspackConfig), api.modifyEnvironmentConfig(async (config, { name, mergeEnvironmentConfig })=>'web' !== name && 'node' !== name ? config : mergeEnvironmentConfig(config, {
11121
11171
  tools: {
11122
11172
  rspack: (rspackConfig)=>{
11123
- if (federation && ((rspackConfig)=>{
11173
+ if (federation) {
11124
11174
  if (rspackConfig?.plugins?.length) for (let plugin of rspackConfig.plugins){
11125
- if (!plugin || 'object' != typeof plugin || 'ModuleFederationPlugin' !== plugin.name && 'RspackModuleFederationPlugin' !== plugin.name) continue;
11126
- let pluginOptions = plugin._options ?? plugin.options;
11175
+ let pluginOptions = getModuleFederationOptions(plugin);
11127
11176
  pluginOptions && (pluginOptions.experiments = {
11128
11177
  ...pluginOptions.experiments,
11129
11178
  asyncStartup: !0
11130
11179
  });
11131
11180
  }
11132
- })(rspackConfig), 'node' === name) {
11181
+ if ('web' === name) ((rspackConfig)=>{
11182
+ let containers = new Set((rspackConfig?.plugins ?? []).map(getModuleFederationOptions).map((options)=>options?.name).filter((name)=>'string' == typeof name));
11183
+ if (!rspackConfig || 0 === containers.size) return;
11184
+ let current = rspackConfig.optimization?.runtimeChunk, appRuntimeName = 'object' == typeof current && 'string' == typeof current?.name ? current.name : 'runtime';
11185
+ rspackConfig.optimization = {
11186
+ ...rspackConfig.optimization,
11187
+ runtimeChunk: {
11188
+ name: (entrypoint)=>containers.has(entrypoint.name) ? `runtime-${entrypoint.name}` : appRuntimeName
11189
+ }
11190
+ };
11191
+ })(rspackConfig);
11192
+ else {
11193
+ let splitChunks = rspackConfig?.optimization?.splitChunks;
11194
+ if (splitChunks) for (let group of (splitChunks.chunks = 'async', Object.values(splitChunks.cacheGroups ?? {})))group && 'object' == typeof group && 'chunks' in group && (group.chunks = 'async');
11195
+ }
11196
+ }
11197
+ if ('node' === name) {
11133
11198
  let output = rspackConfig.output;
11134
11199
  if (output) {
11135
11200
  let library = output.library, libraryOptions = library && 'object' == typeof library && !Array.isArray(library) ? library : {};
@@ -11149,8 +11214,22 @@ if (typeof window !== 'undefined' && import.meta.webpackHot) {
11149
11214
  })({
11150
11215
  api,
11151
11216
  federation: pluginOptions.federation,
11152
- resolvedServerOutput
11153
- }), 'classic' === modePlan.kind && useRouteModuleTransformLoader && api.modifyEnvironmentConfig(async (config, { name, mergeEnvironmentConfig })=>'web' !== name && 'node' !== name ? config : mergeEnvironmentConfig(config, {
11217
+ resolvedServerOutput,
11218
+ webOutput: modePlan.webOutput
11219
+ }), pluginOptions.federation && 'classic' === modePlan.kind) {
11220
+ let browserEntryModules = new Set([
11221
+ finalEntryClientPath,
11222
+ ...routeByFilePath.keys()
11223
+ ]);
11224
+ api.transform({
11225
+ environments: [
11226
+ 'web'
11227
+ ],
11228
+ order: 'post',
11229
+ test: (resourcePath)=>browserEntryModules.has(resourcePath)
11230
+ }, ({ code })=>`${code}\nexport {};\nawait Promise.resolve();\n`);
11231
+ }
11232
+ 'classic' === modePlan.kind && useRouteModuleTransformLoader && api.modifyEnvironmentConfig(async (config, { name, mergeEnvironmentConfig })=>'web' !== name && 'node' !== name ? config : mergeEnvironmentConfig(config, {
11154
11233
  tools: {
11155
11234
  rspack: (rspackConfig)=>{
11156
11235
  let environmentDevHmrEnabled = 'web' === name && !isBuild && void 0 !== devHmrRefreshRuntimePath && 'development' === config.mode && config.dev?.hmr !== !1 && isRspackSwcReactRefreshEnabled(rspackConfig);
@@ -11286,7 +11365,7 @@ if (typeof window !== 'undefined' && import.meta.webpackHot) {
11286
11365
  compilation.updateAsset(BROWSER_MANIFEST_ASSET, new sources.RawSource(newSource));
11287
11366
  }
11288
11367
  if (isBuild) {
11289
- let entryAssets = stats?.assetsByChunkName?.['entry.client'], entryJsAssets = entryAssets?.filter((asset)=>asset.endsWith('.js')) || [], manifestPath = getReactRouterManifestPath({
11368
+ let entryAssets = stats?.assetsByChunkName?.['entry.client'], entryJsAssets = entryAssets?.filter(isManifestJsAsset) || [], manifestPath = getReactRouterManifestPath({
11290
11369
  version: manifest.version,
11291
11370
  isBuild: !0,
11292
11371
  entryModulePath: entryJsAssets[0]
@@ -11298,7 +11377,7 @@ if (typeof window !== 'undefined' && import.meta.webpackHot) {
11298
11377
  let sri = withSri ? ((stats, compilation, assetPrefix = '/')=>{
11299
11378
  let sri = {};
11300
11379
  for (let asset of stats?.assets ?? [])addIntegrity(sri, assetPrefix, asset.name, asset.integrity);
11301
- 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);
11380
+ 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);
11302
11381
  return Object.keys(sri).length > 0 ? sri : void 0;
11303
11382
  })(compilation.getStats().toJson({
11304
11383
  all: !1,
@@ -64,6 +64,37 @@ type ReactRouterManifestStatsCompilation = {
64
64
  namedChunks: ReactRouterManifestStatsLookup<ReactRouterManifestStatsChunk>;
65
65
  entrypoints?: ReactRouterManifestStatsLookup<ReactRouterManifestStatsEntrypoint>;
66
66
  };
67
+ export declare const isManifestJsAsset: (asset: string) => boolean;
68
+ export declare const isManifestCssAsset: (asset: string) => boolean;
69
+ /**
70
+ * The minimal compilation surface for `collectUnsupportedRscScriptAssets`.
71
+ * Chunks are classified as JavaScript-emitting from compilation metadata (their
72
+ * `javascript` content hash / modules), never from a filename.
73
+ */
74
+ export type RscScriptAssetCompilation = {
75
+ chunks: Iterable<RscScriptAssetChunk>;
76
+ chunkGraph: {
77
+ getChunkModulesIterableBySourceType(chunk: RscScriptAssetChunk, sourceType: string): Iterable<unknown>;
78
+ };
79
+ outputOptions: {
80
+ filename?: unknown;
81
+ chunkFilename?: unknown;
82
+ };
83
+ getPath(filename: string, data: Record<string, unknown>): string;
84
+ };
85
+ export type RscScriptAssetChunk = {
86
+ contentHash?: Record<string, string>;
87
+ canBeInitial(): boolean;
88
+ };
89
+ /**
90
+ * Browser JavaScript assets rspack's RSC manifest would drop: it only records
91
+ * chunk files whose emitted name ends in ".js", so `.mjs` names, query-hash
92
+ * names (`[name].js?v=...`), or any other extension vanish from
93
+ * `entryJsFiles` and the client manifest. The emitted script name is derived
94
+ * from the chunk's own filename template (entry or async) the same way rspack
95
+ * emits it, so function templates and `tools.rspack` overrides are covered.
96
+ */
97
+ export declare const collectUnsupportedRscScriptAssets: (compilation: RscScriptAssetCompilation) => string[];
67
98
  export declare const createReactRouterManifestStats: (compilation: ReactRouterManifestStatsCompilation | undefined, chunkNames?: ReadonlySet<string>) => ReactRouterManifestStats | undefined;
68
99
  export type RouteManifestModuleExports = Record<string, readonly string[]>;
69
100
  export type ReactRouterManifestGenerationResult = {
@@ -10,7 +10,7 @@ type CommonModePlan = {
10
10
  manifestChunkNames: Set<string>;
11
11
  webEntries: Record<string, string | RsbuildEntryDescription>;
12
12
  nodeEntries: Record<string, string | RsbuildEntryDescription>;
13
- createVirtualModules(publicPath: string, jsDistPath: string): Record<string, string>;
13
+ createVirtualModules(publicPath: string): Record<string, string>;
14
14
  createResolveConfig(rootPath: string): Rspack.Configuration['resolve'];
15
15
  server: RsbuildConfig['server'] | undefined;
16
16
  webExternalsType: 'module' | undefined;
@@ -7,26 +7,32 @@ export declare const getPackageVersion: (packageName: string, resolvePackagePath
7
7
  export declare const escapeHtml: (value: string) => string;
8
8
  export declare function combineURLs(baseURL: string, relativeURL: string): string;
9
9
  export declare function normalizeAssetPrefix(assetPrefix?: string): string;
10
- /**
11
- * Resolve the asset prefix Rsbuild applies to emitted asset URLs for the given
12
- * mode. In development the effective prefix is `dev.assetPrefix` (which Rsbuild
13
- * defaults from `server.base`, falling back to `output.assetPrefix`); in a
14
- * production build it is `output.assetPrefix`. Both fields are already resolved
15
- * on the normalized config, so this mirrors Rsbuild's own precedence rather than
16
- * re-deriving it from `server.base`.
17
- *
18
- * `dev.assetPrefix` may be a boolean on the raw config (`false` disables it);
19
- * boolean/`'auto'`/empty values normalize to the root prefix `'/'`.
20
- */
21
- export declare function resolveEffectiveAssetPrefix(config: {
10
+ type AssetPrefixConfig = {
22
11
  dev?: {
23
12
  assetPrefix?: unknown;
24
13
  };
25
14
  output?: {
26
15
  assetPrefix?: unknown;
27
16
  };
17
+ };
18
+ /**
19
+ * Resolve the absolute asset prefix the server build and browser manifest use
20
+ * for asset URLs. In development the effective prefix is `dev.assetPrefix`
21
+ * (which Rsbuild defaults from `server.base`, falling back to
22
+ * `output.assetPrefix`); in a production build it is `output.assetPrefix`.
23
+ *
24
+ * `fallbacks` are consulted in order (e.g. the root config after the web
25
+ * environment) when the preceding config only offers a prefix the server
26
+ * cannot use: `'auto'` and empty values are browser-runtime-only, so a root
27
+ * CDN prefix must survive a web `'auto'`. The choice happens before
28
+ * normalization so that `'auto'` is not first folded into `'/'`.
29
+ *
30
+ * `dev.assetPrefix` may be a boolean on the raw config (`false` disables it);
31
+ * boolean/`'auto'`/empty values ultimately normalize to the root prefix `'/'`.
32
+ */
33
+ export declare function resolveEffectiveAssetPrefix(config: AssetPrefixConfig & {
28
34
  isBuild: boolean;
29
- }): string;
35
+ }, ...fallbacks: AssetPrefixConfig[]): string;
30
36
  export declare function createRouteId(file: string): string;
31
37
  export declare function findEntryFile(basePath: string): string;
32
38
  export declare function generateWithProps(): string;
@@ -6,8 +6,6 @@ type RscVirtualModulesOptions = {
6
6
  basename: string;
7
7
  buildDirectory: string;
8
8
  isBuild: boolean;
9
- /** Resolved web `output.distPath.js` segment, e.g. `static/js`. */
10
- jsDistPath: string;
11
9
  outputClientPath: string;
12
10
  publicPath: string;
13
11
  routeDiscovery: Config['routeDiscovery'];
@@ -17,5 +15,5 @@ type RscVirtualModulesOptions = {
17
15
  export declare const createReactRouterRscResolveAliases: (rootPath: string, options?: {
18
16
  entrySsrPath?: string;
19
17
  }) => Record<string, string>;
20
- export declare const createReactRouterRscVirtualModules: ({ allowedActionOrigins, appDirectory, basename, buildDirectory, isBuild, jsDistPath, outputClientPath, publicPath, routeDiscovery, routes, ssr, }: RscVirtualModulesOptions) => Record<string, string>;
18
+ export declare const createReactRouterRscVirtualModules: ({ allowedActionOrigins, appDirectory, basename, buildDirectory, isBuild, outputClientPath, publicPath, routeDiscovery, routes, ssr, }: RscVirtualModulesOptions) => Record<string, string>;
21
19
  export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rsbuild-plugin-react-router",
3
- "version": "0.6.6",
3
+ "version": "0.7.1",
4
4
  "description": "React Router plugin for Rsbuild",
5
5
  "repository": {
6
6
  "type": "git",
@@ -1,15 +1,68 @@
1
- import type { RsbuildPluginAPI } from '@rsbuild/core';
2
- import { ensureFederationAsyncStartup } from './federation.js';
1
+ import type { RsbuildPluginAPI, Rspack } from '@rsbuild/core';
2
+ import {
3
+ enforceAsyncOnlyServerSplitChunks,
4
+ ensureFederationAsyncStartup,
5
+ isolateFederationContainerRuntime,
6
+ } from './federation.js';
3
7
 
8
+ /**
9
+ * Rspack `output` policy for the web and node environments, in two tiers:
10
+ *
11
+ * 1. Overridable defaults, registered through `modifyRspackConfig`. Rsbuild
12
+ * runs the user's `tools.rspack` (object or function form) after this hook,
13
+ * so user output settings such as `chunkFilename` take precedence
14
+ * (#129, #130). Neither `filename` nor `publicPath` is set here: Rsbuild
15
+ * derives them from `output.filename`/`output.filenameHash`/`output.distPath`
16
+ * and the environment's `output.assetPrefix`, which keeps `'auto'` intact.
17
+ * 2. Enforced invariants, registered through a `tools.rspack` function that
18
+ * runs after the user's own `tools.rspack`: the server library type must
19
+ * match the server module format, and federation builds need async startup.
20
+ */
4
21
  export const registerReactRouterEnvironmentOutput = ({
5
22
  api,
6
23
  federation,
7
24
  resolvedServerOutput,
25
+ webOutput,
8
26
  }: {
9
27
  api: RsbuildPluginAPI;
10
28
  federation: boolean | undefined;
11
29
  resolvedServerOutput: 'commonjs' | 'module';
30
+ webOutput: NonNullable<Rspack.Configuration['output']>;
12
31
  }): void => {
32
+ const nodeChunkLoading =
33
+ resolvedServerOutput === 'module'
34
+ ? 'import'
35
+ : federation
36
+ ? 'async-node'
37
+ : 'require';
38
+
39
+ api.modifyRspackConfig((rspackConfig, { environment, mergeConfig }) => {
40
+ if (environment.name === 'web') {
41
+ return mergeConfig(rspackConfig, {
42
+ output: {
43
+ ...webOutput,
44
+ ...(federation ? { chunkLoading: 'import' } : {}),
45
+ },
46
+ });
47
+ }
48
+ if (environment.name === 'node') {
49
+ return mergeConfig(rspackConfig, {
50
+ output: {
51
+ chunkFormat: resolvedServerOutput,
52
+ chunkLoading: nodeChunkLoading,
53
+ devtoolModuleFilenameTemplate: '[absolute-resource-path]',
54
+ devtoolFallbackModuleFilenameTemplate:
55
+ '[absolute-resource-path]?[hash]',
56
+ workerChunkLoading: nodeChunkLoading,
57
+ wasmLoading: 'fetch',
58
+ module: resolvedServerOutput === 'module',
59
+ chunkFilename: 'static/js/async/[name].js',
60
+ },
61
+ });
62
+ }
63
+ return rspackConfig;
64
+ });
65
+
13
66
  api.modifyEnvironmentConfig(
14
67
  async (config, { name, mergeEnvironmentConfig }) => {
15
68
  if (name !== 'web' && name !== 'node') {
@@ -21,6 +74,11 @@ export const registerReactRouterEnvironmentOutput = ({
21
74
  rspack: rspackConfig => {
22
75
  if (federation) {
23
76
  ensureFederationAsyncStartup(rspackConfig);
77
+ if (name === 'web') {
78
+ isolateFederationContainerRuntime(rspackConfig);
79
+ } else {
80
+ enforceAsyncOnlyServerSplitChunks(rspackConfig);
81
+ }
24
82
  }
25
83
 
26
84
  if (name === 'node') {