rsbuild-plugin-react-router 0.6.2 → 0.6.4

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/511.js CHANGED
@@ -679,7 +679,7 @@ let mightContainRouteChunkExportName = (source)=>routeChunkExportNames.some((exp
679
679
  invalidChunks.map((name)=>`- ${name}`).join('\n'),
680
680
  `${plural ? 'These exports' : 'This export'} could not be split into ${plural ? 'their own chunks' : 'its own chunk'} because ${plural ? 'they share' : 'it shares'} code with other exports. You should extract any shared code into its own module and then import it within the route module.`
681
681
  ].join('\n\n'));
682
- }, getRouteEntryBaseName = (route, appDirectory)=>normalizeRelativeFilePath(route.file, appDirectory).replace(/^[A-Za-z]:/, '').split('/').filter((segment)=>'' !== segment && '.' !== segment).map((segment)=>'..' === segment ? '__' : segment).join('/').replace(/\.[^/.]+$/, ''), getRouteChunkEntryName = (route, chunkName, appDirectory)=>`${getRouteEntryBaseName(route, appDirectory)}-${routeChunkEntrySuffix[chunkName]}`, setBoundedCacheEntry = (cache, key, value, maxEntries)=>{
682
+ }, getRouteEntryBaseName = (route, appDirectory)=>createRouteId(normalizeRelativeFilePath(route.file, appDirectory).replace(/^[A-Za-z]:/, '').split('/').filter((segment)=>'' !== segment && '.' !== segment).map((segment)=>'..' === segment ? '__' : segment).join('/')), getRouteChunkEntryName = (routeEntryBaseName, chunkName)=>`${routeEntryBaseName}-${routeChunkEntrySuffix[chunkName]}`, setBoundedCacheEntry = (cache, key, value, maxEntries)=>{
683
683
  if (maxEntries <= 0) return void cache.clear();
684
684
  if (!cache.has(key) && cache.size >= maxEntries) {
685
685
  let oldestEntry = cache.keys().next();
package/dist/index.cjs CHANGED
@@ -11665,7 +11665,7 @@ const external_react_router_namespaceObject = require("react-router"), normalize
11665
11665
  invalidChunks.map((name)=>`- ${name}`).join('\n'),
11666
11666
  `${plural ? 'These exports' : 'This export'} could not be split into ${plural ? 'their own chunks' : 'its own chunk'} because ${plural ? 'they share' : 'it shares'} code with other exports. You should extract any shared code into its own module and then import it within the route module.`
11667
11667
  ].join('\n\n'));
11668
- }, getRouteEntryBaseName = (route, appDirectory)=>toSafeEntryPath(normalizeRelativeFilePath(route.file, appDirectory)).replace(/\.[^/.]+$/, ''), getRouteChunkEntryName = (route, chunkName, appDirectory)=>`${getRouteEntryBaseName(route, appDirectory)}-${routeChunkEntrySuffix[chunkName]}`, setBoundedCacheEntry = (cache1, key, value, maxEntries)=>{
11668
+ }, getRouteEntryBaseName = (route, appDirectory)=>createRouteId(toSafeEntryPath(normalizeRelativeFilePath(route.file, appDirectory))), getRouteChunkEntryName = (routeEntryBaseName, chunkName)=>`${routeEntryBaseName}-${routeChunkEntrySuffix[chunkName]}`, setBoundedCacheEntry = (cache1, key, value, maxEntries)=>{
11669
11669
  if (maxEntries <= 0) return void cache1.clear();
11670
11670
  if (!cache1.has(key) && cache1.size >= maxEntries) {
11671
11671
  let oldestEntry = cache1.keys().next();
@@ -11872,16 +11872,25 @@ const createReactRouterManifestOptions = ({ routeChunks, routeModuleAnalysis })=
11872
11872
  if (!isBuild) return 'static/js/virtual/react-router/browser-manifest.js';
11873
11873
  let dir = getManifestDirFromEntryAsset(entryModulePath);
11874
11874
  return `${dir}/manifest-${version}.js`;
11875
- }, getManifestVersion = (fingerprintedValues)=>(0, external_node_crypto_namespaceObject.createHash)('md5').update(JSON.stringify(fingerprintedValues)).digest('hex').slice(0, 8), getReactRouterManifestChunkNames = (routes, appDirectory, splitRouteModules = !1)=>{
11875
+ }, getReactRouterManifestChunkNames = (routes, appDirectory, splitRouteModules = !1)=>{
11876
11876
  let chunkNames = new Set([
11877
11877
  'entry.client'
11878
11878
  ]);
11879
- for (let route of Object.values(routes))if (chunkNames.add(getRouteEntryBaseName(route, appDirectory)), splitRouteModules && 'root' !== route.id) for (let exportName of routeChunkExportNames)chunkNames.add(getRouteChunkEntryName(route, exportName, appDirectory));
11879
+ for (let route of Object.values(routes)){
11880
+ let routeEntryName = getRouteEntryBaseName(route, appDirectory);
11881
+ if (chunkNames.add(routeEntryName), splitRouteModules && 'root' !== route.id) for (let exportName of routeChunkExportNames)chunkNames.add(getRouteChunkEntryName(routeEntryName, exportName));
11882
+ }
11880
11883
  return chunkNames;
11881
- }, createRouteManifestItem = ({ route, appDirectory, assetPrefix, jsAssets, routeAnalysis, getModulePathForChunk, getCssAssetsForChunk })=>{
11882
- let routeChunkMap = routeAnalysis.hasRouteChunkByExportName, chunkModulePath = (exportName)=>routeChunkMap?.[exportName] ? getModulePathForChunk(getRouteChunkEntryName(route, exportName, appDirectory)) : void 0, cssAssets = [
11884
+ }, createRouteManifestItem = ({ route, routeEntryName, assetPrefix, jsAssets, routeAnalysis, getModulePathForChunk, getCssAssetsForChunk })=>{
11885
+ let routeChunkMap = routeAnalysis.hasRouteChunkByExportName, chunkEntryName = (exportName)=>routeChunkMap?.[exportName] ? getRouteChunkEntryName(routeEntryName, exportName) : void 0, chunkModulePath = (exportName)=>{
11886
+ let name = chunkEntryName(exportName);
11887
+ return name ? getModulePathForChunk(name) : void 0;
11888
+ }, cssAssets = [
11883
11889
  ...routeAnalysis.cssAssets,
11884
- ...routeChunkExportNames.flatMap((exportName)=>routeChunkMap?.[exportName] ? getCssAssetsForChunk(getRouteChunkEntryName(route, exportName, appDirectory)) : [])
11890
+ ...routeChunkExportNames.flatMap((exportName)=>{
11891
+ let name = chunkEntryName(exportName);
11892
+ return name ? getCssAssetsForChunk(name) : [];
11893
+ })
11885
11894
  ];
11886
11895
  return {
11887
11896
  id: route.id,
@@ -11933,7 +11942,7 @@ function generateReactRouterManifestForDevEffect(routes, clientStats, context, a
11933
11942
  key,
11934
11943
  createRouteManifestItem({
11935
11944
  route,
11936
- appDirectory: context,
11945
+ routeEntryName,
11937
11946
  assetPrefix,
11938
11947
  jsAssets,
11939
11948
  routeAnalysis,
@@ -11953,7 +11962,7 @@ function generateReactRouterManifestForDevEffect(routes, clientStats, context, a
11953
11962
  css: entryCssAssets.map((asset)=>combineURLs(assetPrefix, asset))
11954
11963
  },
11955
11964
  routes: result
11956
- }, version = getManifestVersion(fingerprintedValues), manifestPath = getReactRouterManifestPath({
11965
+ }, version = (0, external_node_crypto_namespaceObject.createHash)('md5').update(JSON.stringify(fingerprintedValues)).digest('hex').slice(0, 8), manifestPath = getReactRouterManifestPath({
11957
11966
  version,
11958
11967
  isBuild,
11959
11968
  entryModulePath: entryJsAssets[0]
@@ -12305,6 +12314,7 @@ const redirectStatusCodes = new Set([
12305
12314
  if (manifestPath && !manifestPath.startsWith('/')) throw Error('The `routeDiscovery.manifestPath` config must be a root-relative pathname beginning with a slash (i.e., "/__manifest")');
12306
12315
  return userRouteDiscovery;
12307
12316
  }, createDefaultFutureConfig = ()=>({
12317
+ unstable_enableNodeReadableStream: !1,
12308
12318
  unstable_optimizeDeps: !1,
12309
12319
  unstable_subResourceIntegrity: !1,
12310
12320
  unstable_trailingSlashAwareDataRequests: getDefaultTrailingSlashAwareDataRequests(),
@@ -15476,9 +15486,10 @@ export {
15476
15486
  }, createClassicWebRouteEntries = ({ appDirectory, isBuild, routes, splitRouteModules })=>{
15477
15487
  let manifestChunkNames = new Set([
15478
15488
  'entry.client'
15479
- ]), webRouteEntries = Object.values(routes).reduce((acc, route)=>{
15480
- let entryName = getRouteEntryBaseName(route, appDirectory), routeFilePath = (0, external_pathe_namespaceObject.resolve)(appDirectory, route.file);
15481
- if (manifestChunkNames.add(entryName), acc[entryName] = {
15489
+ ]), routeFileByEntryName = new Map(), webRouteEntries = Object.values(routes).reduce((acc, route)=>{
15490
+ let entryName = getRouteEntryBaseName(route, appDirectory), routeFilePath = (0, external_pathe_namespaceObject.resolve)(appDirectory, route.file), existingRouteFile = routeFileByEntryName.get(entryName);
15491
+ if (void 0 !== existingRouteFile && existingRouteFile !== routeFilePath) throw Error(`[rsbuild-plugin-react-router] Route files ${JSON.stringify(existingRouteFile)} and ${JSON.stringify(routeFilePath)} both resolve to the entry name ${JSON.stringify(entryName)}. Rename one of them so their paths relative to the app directory differ.`);
15492
+ if (routeFileByEntryName.set(entryName, routeFilePath), manifestChunkNames.add(entryName), acc[entryName] = {
15482
15493
  import: `${routeFilePath}${BUILD_CLIENT_ROUTE_QUERY_STRING}`,
15483
15494
  html: !1
15484
15495
  }, isBuild && splitRouteModules && 'root' !== route.id) {
@@ -15491,7 +15502,7 @@ export {
15491
15502
  }
15492
15503
  for (let exportName of routeChunkExportNames){
15493
15504
  if (!source.includes(exportName)) continue;
15494
- let chunkEntryName = getRouteChunkEntryName(route, exportName, appDirectory);
15505
+ let chunkEntryName = getRouteChunkEntryName(entryName, exportName);
15495
15506
  manifestChunkNames.add(chunkEntryName), acc[chunkEntryName] = {
15496
15507
  import: getRouteChunkModuleId(routeFilePath, exportName),
15497
15508
  html: !1
@@ -15813,7 +15824,7 @@ export {
15813
15824
  throw Error(`Error loading ${displayPath}: ${error}`);
15814
15825
  }
15815
15826
  } else console.warn('No react-router.config found, using default configuration.');
15816
- let { resolved: resolvedConfig, userAndPresetConfig, presets: configPresets, hasConfiguredServerModuleFormat } = await effectRuntime.runPromise(resolveReactRouterConfigEffect(reactRouterUserConfig)), { appDirectory, basename, buildDirectory, future, allowedActionOrigins, routeDiscovery: userRouteDiscovery, ssr, prerender: prerenderConfig, serverBuildFile, serverBundles, serverModuleFormat, splitRouteModules, subResourceIntegrity, buildEnd } = resolvedConfig;
15827
+ let { resolved: resolvedConfig, presets: configPresets, hasConfiguredServerModuleFormat } = await effectRuntime.runPromise(resolveReactRouterConfigEffect(reactRouterUserConfig)), { appDirectory, basename, buildDirectory, future, allowedActionOrigins, routeDiscovery: userRouteDiscovery, ssr, prerender: prerenderConfig, serverBuildFile, serverBundles, serverModuleFormat, splitRouteModules, subResourceIntegrity, buildEnd } = resolvedConfig;
15817
15828
  await registerReactRouterTypegen(api, {
15818
15829
  runtime: effectRuntime,
15819
15830
  appDirectory
@@ -15909,10 +15920,7 @@ export {
15909
15920
  for (let preset of configPresets)await preset.reactRouterConfigResolved?.({
15910
15921
  reactRouterConfig: resolvedConfigForPreset
15911
15922
  });
15912
- let buildEndReactRouterConfig = {
15913
- ...resolvedConfigWithRoutes,
15914
- future: userAndPresetConfig.future ?? {}
15915
- }, isBuild = 'build' === api.context.action;
15923
+ let isBuild = 'build' === api.context.action;
15916
15924
  isBuild || api.onAfterEnvironmentCompile(({ environment, stats })=>{
15917
15925
  'node' === environment.name && stats && !stats.hasErrors() && registerDevServerSourceMaps(stats.compilation);
15918
15926
  });
@@ -16056,7 +16064,7 @@ export {
16056
16064
  routeChunkOptions: modePlan.routeChunkOptions,
16057
16065
  routeModuleAnalysis,
16058
16066
  buildManifest: modePlan.artifacts.buildManifest,
16059
- buildEndReactRouterConfig,
16067
+ buildEndReactRouterConfig: resolvedConfigWithRoutes,
16060
16068
  buildEnd
16061
16069
  })))) : api.onAfterBuild(({ environments })=>effectRuntime.runPromise(tryPluginPromise(()=>runReactRouterRscPrerenderBuild({
16062
16070
  api,
package/dist/index.js CHANGED
@@ -7463,10 +7463,16 @@ async function generateReactRouterManifestForDev(routes, _options, clientStats,
7463
7463
  valid: buildManifestChunkValidity(routeAnalysis.exports, routeAnalysis.hasRouteChunkByExportName ?? createEmptyRouteChunkByExportName())
7464
7464
  }), [
7465
7465
  key,
7466
- (({ route, appDirectory, assetPrefix, jsAssets, routeAnalysis, getModulePathForChunk, getCssAssetsForChunk })=>{
7467
- let routeChunkMap = routeAnalysis.hasRouteChunkByExportName, chunkModulePath = (exportName)=>routeChunkMap?.[exportName] ? getModulePathForChunk(getRouteChunkEntryName(route, exportName, appDirectory)) : void 0, cssAssets = [
7466
+ (({ route, routeEntryName, assetPrefix, jsAssets, routeAnalysis, getModulePathForChunk, getCssAssetsForChunk })=>{
7467
+ let routeChunkMap = routeAnalysis.hasRouteChunkByExportName, chunkEntryName = (exportName)=>routeChunkMap?.[exportName] ? getRouteChunkEntryName(routeEntryName, exportName) : void 0, chunkModulePath = (exportName)=>{
7468
+ let name = chunkEntryName(exportName);
7469
+ return name ? getModulePathForChunk(name) : void 0;
7470
+ }, cssAssets = [
7468
7471
  ...routeAnalysis.cssAssets,
7469
- ...routeChunkExportNames.flatMap((exportName)=>routeChunkMap?.[exportName] ? getCssAssetsForChunk(getRouteChunkEntryName(route, exportName, appDirectory)) : [])
7472
+ ...routeChunkExportNames.flatMap((exportName)=>{
7473
+ let name = chunkEntryName(exportName);
7474
+ return name ? getCssAssetsForChunk(name) : [];
7475
+ })
7470
7476
  ];
7471
7477
  return {
7472
7478
  id: route.id,
@@ -7491,7 +7497,7 @@ async function generateReactRouterManifestForDev(routes, _options, clientStats,
7491
7497
  };
7492
7498
  })({
7493
7499
  route,
7494
- appDirectory: context,
7500
+ routeEntryName,
7495
7501
  assetPrefix: assetPrefix,
7496
7502
  jsAssets,
7497
7503
  routeAnalysis,
@@ -7874,6 +7880,7 @@ let redirectStatusCodes = new Set([
7874
7880
  subResourceIntegrity: !1,
7875
7881
  ssr: !0,
7876
7882
  future: {
7883
+ unstable_enableNodeReadableStream: !1,
7877
7884
  unstable_optimizeDeps: !1,
7878
7885
  unstable_subResourceIntegrity: !1,
7879
7886
  unstable_trailingSlashAwareDataRequests: ((reactRouterVersion = getPackageVersion('react-router'))=>(parseVersionMajorMinor(reactRouterVersion)?.major ?? 0) >= 8)(),
@@ -9741,9 +9748,10 @@ export {
9741
9748
  let reactRouterPath, reactRouterDomPath, { manifestChunkNames, webRouteEntries } = (({ appDirectory, isBuild, routes, splitRouteModules })=>{
9742
9749
  let manifestChunkNames = new Set([
9743
9750
  'entry.client'
9744
- ]), webRouteEntries = Object.values(routes).reduce((acc, route)=>{
9745
- let entryName = getRouteEntryBaseName(route, appDirectory), routeFilePath = external_pathe_resolve(appDirectory, route.file);
9746
- if (manifestChunkNames.add(entryName), acc[entryName] = {
9751
+ ]), routeFileByEntryName = new Map(), webRouteEntries = Object.values(routes).reduce((acc, route)=>{
9752
+ let entryName = getRouteEntryBaseName(route, appDirectory), routeFilePath = external_pathe_resolve(appDirectory, route.file), existingRouteFile = routeFileByEntryName.get(entryName);
9753
+ if (void 0 !== existingRouteFile && existingRouteFile !== routeFilePath) throw Error(`[rsbuild-plugin-react-router] Route files ${JSON.stringify(existingRouteFile)} and ${JSON.stringify(routeFilePath)} both resolve to the entry name ${JSON.stringify(entryName)}. Rename one of them so their paths relative to the app directory differ.`);
9754
+ if (routeFileByEntryName.set(entryName, routeFilePath), manifestChunkNames.add(entryName), acc[entryName] = {
9747
9755
  import: `${routeFilePath}${BUILD_CLIENT_ROUTE_QUERY_STRING}`,
9748
9756
  html: !1
9749
9757
  }, isBuild && splitRouteModules && 'root' !== route.id) {
@@ -9756,7 +9764,7 @@ export {
9756
9764
  }
9757
9765
  for (let exportName of routeChunkExportNames){
9758
9766
  if (!source.includes(exportName)) continue;
9759
- let chunkEntryName = getRouteChunkEntryName(route, exportName, appDirectory);
9767
+ let chunkEntryName = getRouteChunkEntryName(entryName, exportName);
9760
9768
  manifestChunkNames.add(chunkEntryName), acc[chunkEntryName] = {
9761
9769
  import: getRouteChunkModuleId(routeFilePath, exportName),
9762
9770
  html: !1
@@ -10110,7 +10118,7 @@ export {
10110
10118
  throw Error(`Error loading ${displayPath}: ${error}`);
10111
10119
  }
10112
10120
  } else console.warn('No react-router.config found, using default configuration.');
10113
- let { resolved: resolvedConfig, userAndPresetConfig, presets: configPresets, hasConfiguredServerModuleFormat } = await effectRuntime.runPromise((reactRouterUserConfig = reactRouterUserConfig1, core_gen(function*() {
10121
+ let { resolved: resolvedConfig, presets: configPresets, hasConfiguredServerModuleFormat } = await effectRuntime.runPromise((reactRouterUserConfig = reactRouterUserConfig1, core_gen(function*() {
10114
10122
  let userAndPresetConfigs = ((...configs)=>configs.reduce((configA, configB)=>{
10115
10123
  let mergeRequired = (key)=>void 0 !== configA[key] && void 0 !== configB[key];
10116
10124
  return {
@@ -10392,10 +10400,7 @@ export {
10392
10400
  for (let preset of configPresets)await preset.reactRouterConfigResolved?.({
10393
10401
  reactRouterConfig: resolvedConfigForPreset
10394
10402
  });
10395
- let buildEndReactRouterConfig = {
10396
- ...resolvedConfigWithRoutes,
10397
- future: userAndPresetConfig.future ?? {}
10398
- }, isBuild = 'build' === api.context.action;
10403
+ let isBuild = 'build' === api.context.action;
10399
10404
  isBuild || api.onAfterEnvironmentCompile(({ environment, stats })=>{
10400
10405
  'node' === environment.name && stats && !stats.hasErrors() && ((compilation)=>{
10401
10406
  let outputPath = compilation.outputOptions.path;
@@ -10909,7 +10914,7 @@ if (typeof window !== 'undefined' && import.meta.webpackHot) {
10909
10914
  routeChunkOptions: modePlan.routeChunkOptions,
10910
10915
  routeModuleAnalysis,
10911
10916
  buildManifest: modePlan.artifacts.buildManifest,
10912
- buildEndReactRouterConfig,
10917
+ buildEndReactRouterConfig: resolvedConfigWithRoutes,
10913
10918
  buildEnd
10914
10919
  })))) : api.onAfterBuild(({ environments })=>effectRuntime.runPromise(tryPluginPromise(()=>runReactRouterRscPrerenderBuild({
10915
10920
  api,
@@ -11253,7 +11258,10 @@ if (typeof window !== 'undefined' && import.meta.webpackHot) {
11253
11258
  let chunkNames = new Set([
11254
11259
  'entry.client'
11255
11260
  ]);
11256
- for (let route of Object.values(routes))if (chunkNames.add(getRouteEntryBaseName(route, appDirectory)), splitRouteModules && 'root' !== route.id) for (let exportName of routeChunkExportNames)chunkNames.add(getRouteChunkEntryName(route, exportName, appDirectory));
11261
+ for (let route of Object.values(routes)){
11262
+ let routeEntryName = getRouteEntryBaseName(route, appDirectory);
11263
+ if (chunkNames.add(routeEntryName), splitRouteModules && 'root' !== route.id) for (let exportName of routeChunkExportNames)chunkNames.add(getRouteChunkEntryName(routeEntryName, exportName));
11264
+ }
11257
11265
  return chunkNames;
11258
11266
  })(routes, appDirectory, routeChunkOptions?.splitRouteModules), isBuild = !!routeChunkOptions?.isBuild, finalizeSri = !!(isBuild && (options?.subResourceIntegrity ?? options?.future?.unstable_subResourceIntegrity)), buildAndEmitManifest = async ({ assets, sources, compilation }, { withSri })=>{
11259
11267
  let currentAssetPrefix = getAssetPrefix(), stats = createReactRouterManifestStats(compilation, manifestChunkNames), { manifest, moduleExportsByRouteId } = await generateReactRouterManifestForDev(routes, pluginOptions, stats, appDirectory, currentAssetPrefix, createReactRouterManifestOptions({
@@ -19,6 +19,7 @@ export type Config = Omit<ReactRouterConfig, 'buildEnd' | 'future' | 'prerender'
19
19
  subResourceIntegrity?: boolean;
20
20
  };
21
21
  type FutureConfig = {
22
+ unstable_enableNodeReadableStream: boolean;
22
23
  unstable_optimizeDeps: boolean;
23
24
  unstable_subResourceIntegrity: boolean;
24
25
  unstable_trailingSlashAwareDataRequests: boolean;
@@ -40,7 +40,5 @@ export declare const validateRouteChunks: (args: {
40
40
  export declare const getRouteEntryBaseName: (route: {
41
41
  file: string;
42
42
  }, appDirectory: string) => string;
43
- export declare const getRouteChunkEntryName: (route: {
44
- file: string;
45
- }, chunkName: RouteChunkExportName, appDirectory: string) => string;
43
+ export declare const getRouteChunkEntryName: (routeEntryBaseName: string, chunkName: RouteChunkExportName) => string;
46
44
  export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rsbuild-plugin-react-router",
3
- "version": "0.6.2",
3
+ "version": "0.6.4",
4
4
  "description": "React Router plugin for Rsbuild",
5
5
  "repository": {
6
6
  "type": "git",
@@ -81,11 +81,11 @@
81
81
  "@codspeed/vitest-plugin": "^5.7.1",
82
82
  "@jridgewell/trace-mapping": "^0.3.31",
83
83
  "@playwright/test": "^1.61.1",
84
- "@react-router/dev": "^8.0.1",
85
- "@react-router/express": "^8.0.1",
86
- "@react-router/fs-routes": "^8.0.1",
87
- "@react-router/remix-routes-option-adapter": "^8.0.1",
88
- "@react-router/serve": "^8.0.1",
84
+ "@react-router/dev": "^8.3.1",
85
+ "@react-router/express": "^8.3.1",
86
+ "@react-router/fs-routes": "^8.3.1",
87
+ "@react-router/remix-routes-option-adapter": "^8.3.1",
88
+ "@react-router/serve": "^8.3.1",
89
89
  "@rsbuild/core": "2.2.0",
90
90
  "@rsbuild/plugin-less": "1.6.4",
91
91
  "@rsbuild/plugin-mdx": "^1.1.3",
@@ -119,8 +119,8 @@
119
119
  "prettier": "3.8.1",
120
120
  "react": "^19.2.4",
121
121
  "react-dom": "^19.2.4",
122
- "react-router": "^7.18.0",
123
- "react-router-dom": "^7.18.0",
122
+ "react-router": "^7.18.3",
123
+ "react-router-dom": "^7.18.3",
124
124
  "react-server-dom-rspack": "0.1.0",
125
125
  "rsbuild-plugin-rsc": "^0.1.1",
126
126
  "semver": "^7.8.5",
@@ -143,10 +143,26 @@ export const createClassicWebRouteEntries = ({
143
143
  webRouteEntries: Record<string, RsbuildEntryDescription>;
144
144
  } => {
145
145
  const manifestChunkNames = new Set<string>(['entry.client']);
146
+ // Entry names are sanitized paths, so two distinct route files could in
147
+ // principle map to one name (for example `../shared/x.tsx` and
148
+ // `__/shared/x.tsx`). Refuse that instead of letting one route silently
149
+ // serve the other's module. Routes that share a file intentionally share
150
+ // an entry.
151
+ const routeFileByEntryName = new Map<string, string>();
146
152
  const webRouteEntries = Object.values(routes).reduce(
147
153
  (acc, route) => {
148
154
  const entryName = getRouteEntryBaseName(route, appDirectory);
149
155
  const routeFilePath = resolve(appDirectory, route.file);
156
+ const existingRouteFile = routeFileByEntryName.get(entryName);
157
+ if (
158
+ existingRouteFile !== undefined &&
159
+ existingRouteFile !== routeFilePath
160
+ ) {
161
+ throw new Error(
162
+ `[rsbuild-plugin-react-router] Route files ${JSON.stringify(existingRouteFile)} and ${JSON.stringify(routeFilePath)} both resolve to the entry name ${JSON.stringify(entryName)}. Rename one of them so their paths relative to the app directory differ.`
163
+ );
164
+ }
165
+ routeFileByEntryName.set(entryName, routeFilePath);
150
166
  manifestChunkNames.add(entryName);
151
167
  acc[entryName] = {
152
168
  import: `${routeFilePath}${BUILD_CLIENT_ROUTE_QUERY_STRING}`,
@@ -167,11 +183,7 @@ export const createClassicWebRouteEntries = ({
167
183
  if (!source.includes(exportName)) {
168
184
  continue;
169
185
  }
170
- const chunkEntryName = getRouteChunkEntryName(
171
- route,
172
- exportName,
173
- appDirectory
174
- );
186
+ const chunkEntryName = getRouteChunkEntryName(entryName, exportName);
175
187
  manifestChunkNames.add(chunkEntryName);
176
188
  acc[chunkEntryName] = {
177
189
  import: getRouteChunkModuleId(routeFilePath, exportName),
package/src/index.ts CHANGED
@@ -263,7 +263,6 @@ export const pluginReactRouter = (
263
263
 
264
264
  const {
265
265
  resolved: resolvedConfig,
266
- userAndPresetConfig,
267
266
  presets: configPresets,
268
267
  hasConfiguredServerModuleFormat,
269
268
  } = await effectRuntime.runPromise(
@@ -472,10 +471,7 @@ export const pluginReactRouter = (
472
471
  resolvedConfigForPreset as ReactRouterPresetResolvedConfig,
473
472
  });
474
473
  }
475
- const buildEndReactRouterConfig: ResolvedReactRouterConfig = {
476
- ...resolvedConfigWithRoutes,
477
- future: userAndPresetConfig.future ?? {},
478
- } as ResolvedReactRouterConfig;
474
+ const buildEndReactRouterConfig = resolvedConfigWithRoutes;
479
475
 
480
476
  const isBuild = api.context.action === 'build';
481
477
  if (!isBuild) {
package/src/manifest.ts CHANGED
@@ -413,26 +413,6 @@ export const getReactRouterManifestPath = ({
413
413
  return `${dir}/manifest-${version}.js`;
414
414
  };
415
415
 
416
- // The version is derived from the manifest content in every mode. React
417
- // Router's stale-client detection compares the version the browser loaded with
418
- // the one the server build was pinned to, and answers a mismatch with a
419
- // document reload. In development the browser manifest asset is served from
420
- // the latest web compilation while the server build stays pinned to the
421
- // compilation it was committed with, so a random per-compilation version made
422
- // the two disagree whenever a web compilation completed without changing the
423
- // manifest (for example the hot data revalidation recompile that follows a
424
- // server change), reloading the document for no reason. Equal content now
425
- // yields an equal version, and a real manifest change still busts the browser
426
- // cache through the `?v=` query on the development manifest URL.
427
- const getManifestVersion = (fingerprintedValues: {
428
- entry: unknown;
429
- routes: unknown;
430
- }): string =>
431
- createHash('md5')
432
- .update(JSON.stringify(fingerprintedValues))
433
- .digest('hex')
434
- .slice(0, 8);
435
-
436
416
  export const getReactRouterManifestChunkNames = (
437
417
  routes: Record<string, Route>,
438
418
  appDirectory: string,
@@ -440,12 +420,13 @@ export const getReactRouterManifestChunkNames = (
440
420
  ): Set<string> => {
441
421
  const chunkNames = new Set<string>(['entry.client']);
442
422
  for (const route of Object.values(routes)) {
443
- chunkNames.add(getRouteEntryBaseName(route, appDirectory));
423
+ const routeEntryName = getRouteEntryBaseName(route, appDirectory);
424
+ chunkNames.add(routeEntryName);
444
425
  if (!splitRouteModules || route.id === 'root') {
445
426
  continue;
446
427
  }
447
428
  for (const exportName of routeChunkExportNames) {
448
- chunkNames.add(getRouteChunkEntryName(route, exportName, appDirectory));
429
+ chunkNames.add(getRouteChunkEntryName(routeEntryName, exportName));
449
430
  }
450
431
  }
451
432
  return chunkNames;
@@ -453,7 +434,7 @@ export const getReactRouterManifestChunkNames = (
453
434
 
454
435
  const createRouteManifestItem = ({
455
436
  route,
456
- appDirectory,
437
+ routeEntryName,
457
438
  assetPrefix,
458
439
  jsAssets,
459
440
  routeAnalysis,
@@ -461,7 +442,7 @@ const createRouteManifestItem = ({
461
442
  getCssAssetsForChunk,
462
443
  }: {
463
444
  route: Route;
464
- appDirectory: string;
445
+ routeEntryName: string;
465
446
  assetPrefix: string;
466
447
  jsAssets: string[];
467
448
  routeAnalysis: RouteManifestAnalysis;
@@ -469,21 +450,20 @@ const createRouteManifestItem = ({
469
450
  getCssAssetsForChunk: (chunkName: string) => string[];
470
451
  }): RouteManifestItem => {
471
452
  const routeChunkMap = routeAnalysis.hasRouteChunkByExportName;
472
- const chunkModulePath = (exportName: RouteChunkExportName) =>
453
+ const chunkEntryName = (exportName: RouteChunkExportName) =>
473
454
  routeChunkMap?.[exportName]
474
- ? getModulePathForChunk(
475
- getRouteChunkEntryName(route, exportName, appDirectory)
476
- )
455
+ ? getRouteChunkEntryName(routeEntryName, exportName)
477
456
  : undefined;
457
+ const chunkModulePath = (exportName: RouteChunkExportName) => {
458
+ const name = chunkEntryName(exportName);
459
+ return name ? getModulePathForChunk(name) : undefined;
460
+ };
478
461
  const cssAssets = [
479
462
  ...routeAnalysis.cssAssets,
480
- ...routeChunkExportNames.flatMap(exportName =>
481
- routeChunkMap?.[exportName]
482
- ? getCssAssetsForChunk(
483
- getRouteChunkEntryName(route, exportName, appDirectory)
484
- )
485
- : []
486
- ),
463
+ ...routeChunkExportNames.flatMap(exportName => {
464
+ const name = chunkEntryName(exportName);
465
+ return name ? getCssAssetsForChunk(name) : [];
466
+ }),
487
467
  ];
488
468
 
489
469
  return {
@@ -581,7 +561,7 @@ function generateReactRouterManifestForDevEffect(
581
561
  key,
582
562
  createRouteManifestItem({
583
563
  route,
584
- appDirectory: context,
564
+ routeEntryName,
585
565
  assetPrefix,
586
566
  jsAssets,
587
567
  routeAnalysis,
@@ -625,7 +605,14 @@ function generateReactRouterManifestForDevEffect(
625
605
  },
626
606
  routes: result,
627
607
  };
628
- const version = getManifestVersion(fingerprintedValues);
608
+ // Content-derived in development too. React Router's stale-client check
609
+ // compares the browser's manifest version with the pinned server build's,
610
+ // and a random per-compilation version made unchanged manifests differ
611
+ // whenever a web compilation completed without a new server pin.
612
+ const version = createHash('md5')
613
+ .update(JSON.stringify(fingerprintedValues))
614
+ .digest('hex')
615
+ .slice(0, 8);
629
616
  const manifestPath = getReactRouterManifestPath({
630
617
  version,
631
618
  isBuild,
@@ -36,6 +36,10 @@ export type Config = Omit<
36
36
  };
37
37
 
38
38
  type FutureConfig = {
39
+ // React Router 8.3 selects the web-streams server entry when this is set.
40
+ // The plugin ships its own server entry, so the flag is resolved and passed
41
+ // through to presets and `buildEnd` but does not change plugin behavior.
42
+ unstable_enableNodeReadableStream: boolean;
39
43
  unstable_optimizeDeps: boolean;
40
44
  unstable_subResourceIntegrity: boolean;
41
45
  unstable_trailingSlashAwareDataRequests: boolean;
@@ -118,6 +122,7 @@ export type ResolvedReactRouterConfig = Readonly<{
118
122
  }>;
119
123
 
120
124
  const createDefaultFutureConfig = (): FutureConfig => ({
125
+ unstable_enableNodeReadableStream: false,
121
126
  unstable_optimizeDeps: false,
122
127
  unstable_subResourceIntegrity: false,
123
128
  unstable_trailingSlashAwareDataRequests:
@@ -7,6 +7,7 @@ import { print } from 'yuku-codegen';
7
7
  import { walk } from 'yuku-parser';
8
8
  import { dirname, normalize, relative, resolve } from 'pathe';
9
9
  import { SERVER_ONLY_ROUTE_EXPORTS_SET } from './constants.js';
10
+ import { createRouteId } from './plugin-utils.js';
10
11
 
11
12
  type AnyNode = Record<string, any>;
12
13
 
@@ -984,16 +985,11 @@ export const getRouteEntryBaseName = (
984
985
  route: { file: string },
985
986
  appDirectory: string
986
987
  ): string =>
987
- toSafeEntryPath(normalizeRelativeFilePath(route.file, appDirectory)).replace(
988
- /\.[^/.]+$/,
989
- ''
988
+ createRouteId(
989
+ toSafeEntryPath(normalizeRelativeFilePath(route.file, appDirectory))
990
990
  );
991
991
 
992
992
  export const getRouteChunkEntryName = (
993
- route: { file: string },
994
- chunkName: RouteChunkExportName,
995
- appDirectory: string
996
- ): string =>
997
- `${getRouteEntryBaseName(route, appDirectory)}-${
998
- routeChunkEntrySuffix[chunkName]
999
- }`;
993
+ routeEntryBaseName: string,
994
+ chunkName: RouteChunkExportName
995
+ ): string => `${routeEntryBaseName}-${routeChunkEntrySuffix[chunkName]}`;