rsbuild-plugin-react-router 0.3.1 → 0.4.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.cjs CHANGED
@@ -11056,7 +11056,7 @@ const external_react_router_namespaceObject = require("react-router"), normalize
11056
11056
  }))([
11057
11057
  'Warning: Paths with dynamic/splat params cannot be prerendered when using `prerender: true`.',
11058
11058
  'You may want to use the `prerender()` API to prerender the following paths:',
11059
- ...paramRoutes.map((path)=>` - ${path}`)
11059
+ ...paramRoutes.map((path)=>` - ${path.replace(/^\/(?=[:*])/, '')}`)
11060
11060
  ].join('\n')), paths;
11061
11061
  if ('function' == typeof pathsConfig) {
11062
11062
  let resolved = await pathsConfig({
@@ -11636,11 +11636,11 @@ const redirectStatusCodes = new Set([
11636
11636
  }))), (controller)=>sync(()=>{
11637
11637
  controller.abort();
11638
11638
  })), withBuildRequest = (input, init, handle)=>runPluginEffect(createBuildRequestEffect(input, init, handle)), prerenderData = async ({ handler, prerenderPath, onlyRoutes, clientBuildDir, basename, trailingSlashAwareDataRequests, api, requestInit })=>{
11639
- let dataRequestPath = createDataRequestPath(prerenderPath, trailingSlashAwareDataRequests), normalizedPath = `${basename}${dataRequestPath}`.replace(/\/\/+/g, '/'), url = new URL(`http://localhost${normalizedPath}`);
11639
+ let dataOutputPath = createDataRequestPath(prerenderPath, trailingSlashAwareDataRequests), dataRequestPath = trailingSlashAwareDataRequests && '/' === prerenderPath ? '/_root.data' : dataOutputPath, normalizedPath = `${basename}${dataRequestPath}`.replace(/\/\/+/g, '/'), outputNormalizedPath = dataOutputPath === dataRequestPath ? normalizedPath : `${basename}${dataOutputPath}`.replace(/\/\/+/g, '/'), url = new URL(`http://localhost${normalizedPath}`);
11640
11640
  return onlyRoutes?.length && url.searchParams.set('_routes', onlyRoutes.join(',')), withBuildRequest(url, requestInit, async (request)=>{
11641
11641
  let response = await handler(request), data = await response.text();
11642
11642
  if (200 !== response.status && 202 !== response.status) throw Error(`Prerender (data): Received a ${response.status} status code from \`entry.server.tsx\` while prerendering the \`${prerenderPath}\` path.\n${normalizedPath}`);
11643
- let outputPath = (0, external_pathe_namespaceObject.resolve)(clientBuildDir, ...normalizedPath.split('/'));
11643
+ let outputPath = (0, external_pathe_namespaceObject.resolve)(clientBuildDir, ...outputNormalizedPath.split('/'));
11644
11644
  return await (0, promises_namespaceObject.mkdir)((0, external_pathe_namespaceObject.dirname)(outputPath), {
11645
11645
  recursive: !0
11646
11646
  }), await (0, promises_namespaceObject.writeFile)(outputPath, data), api.logger.info(`Prerender (data): ${prerenderPath} -> ${(0, external_pathe_namespaceObject.relative)(process.cwd(), outputPath)}`), data;
@@ -11751,13 +11751,16 @@ const redirectStatusCodes = new Set([
11751
11751
  clientBuildDir,
11752
11752
  basename,
11753
11753
  api,
11754
- requestInit: data ? {
11755
- headers: {
11756
- 'X-React-Router-Prerender-Data': encodeURI(data)
11757
- }
11758
- } : void 0
11754
+ requestInit: data ? createPrerenderDataRequestInit(data) : void 0
11759
11755
  }));
11760
- }), runPrerenderPaths = async ({ build, requestHandler, clientBuildDir, options })=>{
11756
+ }), createPrerenderDataRequestInit = (data)=>{
11757
+ let encodedData = encodeURI(data);
11758
+ return encodedData.length < 8192 ? {
11759
+ headers: {
11760
+ 'X-React-Router-Prerender-Data': encodedData
11761
+ }
11762
+ } : void 0;
11763
+ }, runPrerenderPaths = async ({ build, requestHandler, clientBuildDir, options })=>{
11761
11764
  let { prerenderConfig, prerenderPaths } = options, buildRoutes = createPrerenderRoutes(build.routes), concurrency = getPrerenderConcurrency(prerenderConfig);
11762
11765
  await runPluginEffect(createBoundedPrerenderTasksEffect(prerenderPaths, concurrency, (path)=>createPrerenderPathEffect({
11763
11766
  path,
@@ -12113,7 +12116,7 @@ function warnOnClientSourceMaps(normalized, warn, clientEnvName = 'web') {
12113
12116
  let sourceMapSetting = getClientSourceMapSetting(normalized, clientEnvName), devtoolSetting = getClientDevtoolSetting(normalized, clientEnvName);
12114
12117
  (isSourceMapEnabled(sourceMapSetting) || isDevtoolSourceMap(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");
12115
12118
  }
12116
- const registerBuildOutputTransforms = ({ api, resolvedServerOutput, performanceProfiler, getLatestServerManifest, getLatestServerManifestByBundleId, routes, pluginOptions, getClientStats, appDirectory, getAssetPrefix, routeChunkOptions, routeTransformExecutor, routeByFilePath, routeChunkConfig, isBuild, splitRouteModules, ssr, isSpaMode, rootRoutePath })=>{
12119
+ const registerBuildOutputTransforms = ({ api, resolvedServerOutput, performanceProfiler, getLatestServerManifest, getLatestServerManifestByBundleId, routes, pluginOptions, getClientStats, appDirectory, getAssetPrefix, routeChunkOptions, routeTransformExecutor, routeByFilePath, routeChunkConfig, isBuild, splitRouteModules, ssr, isSpaMode, rootRoutePath, isDevHmrEnabled = ()=>!1 })=>{
12117
12120
  let transformRouteModule = async (args)=>performanceProfiler.record(args.environment?.name, 'route:module', args.resource, async ()=>routeTransformExecutor.run({
12118
12121
  kind: 'routeModule',
12119
12122
  code: args.code,
@@ -12124,7 +12127,8 @@ const registerBuildOutputTransforms = ({ api, resolvedServerOutput, performanceP
12124
12127
  ssr,
12125
12128
  isBuild,
12126
12129
  isSpaMode,
12127
- rootRoutePath
12130
+ rootRoutePath,
12131
+ devHmr: isDevHmrEnabled()
12128
12132
  }));
12129
12133
  api.processAssets({
12130
12134
  stage: 'additional',
@@ -12154,7 +12158,9 @@ const registerBuildOutputTransforms = ({ api, resolvedServerOutput, performanceP
12154
12158
  resourcePath: args.resourcePath,
12155
12159
  environmentName: args.environment?.name,
12156
12160
  isBuild,
12157
- routeChunkConfig
12161
+ routeChunkConfig,
12162
+ routeId: routeByFilePath.get(args.resourcePath)?.id,
12163
+ devHmr: isDevHmrEnabled()
12158
12164
  }))), api.transform({
12159
12165
  resourceQuery: /route-chunk=/,
12160
12166
  environments: [
@@ -12240,17 +12246,70 @@ const registerBuildOutputTransforms = ({ api, resolvedServerOutput, performanceP
12240
12246
  code: generated.code,
12241
12247
  map
12242
12248
  };
12243
- }, buildRouteClientEntryCode = ({ exportNames, chunkedExports, isServer, resourcePath })=>{
12244
- let chunkedExportSet = chunkedExports.length > 0 ? new Set(chunkedExports) : void 0, reexports = exportNames.filter((exp)=>!chunkedExportSet?.has(exp) && (CLIENT_ROUTE_EXPORTS_SET.has(exp) || isServer && SERVER_ONLY_ROUTE_EXPORTS_SET.has(exp))).sort(), target = `${resourcePath}?react-router-route`;
12245
- return `export { ${reexports.join(', ')} } from ${JSON.stringify(target)};`;
12246
- }, createRouteClientEntryArtifact = async ({ code, resourcePath, environmentName, isBuild, routeChunkCache, routeChunkConfig })=>{
12249
+ }, HMR_PATCHABLE_ROUTE_FLAGS = [
12250
+ 'hasAction',
12251
+ 'hasClientAction',
12252
+ 'hasClientLoader',
12253
+ 'hasClientMiddleware',
12254
+ 'hasErrorBoundary',
12255
+ 'hasLoader'
12256
+ ], HMR_FLAG_EXPORT_NAME = {
12257
+ hasAction: SERVER_EXPORTS.action,
12258
+ hasClientAction: CLIENT_EXPORTS.clientAction,
12259
+ hasClientLoader: CLIENT_EXPORTS.clientLoader,
12260
+ hasClientMiddleware: CLIENT_EXPORTS.clientMiddleware,
12261
+ hasErrorBoundary: CLIENT_EXPORTS.ErrorBoundary,
12262
+ hasLoader: SERVER_EXPORTS.loader
12263
+ }, buildRouteHmrFlags = (exportNames)=>{
12264
+ let exports1 = new Set(exportNames), flags = 0;
12265
+ return HMR_PATCHABLE_ROUTE_FLAGS.forEach((flag, index)=>{
12266
+ exports1.has(HMR_FLAG_EXPORT_NAME[flag]) && (flags |= 1 << index);
12267
+ }), flags;
12268
+ }, buildRouteClientEntryHmrCode = ({ routeId, target, acceptTarget, flags })=>{
12269
+ let targetJson = JSON.stringify(target), acceptTargetJson = JSON.stringify(acceptTarget);
12270
+ return `
12271
+ import * as __rrm from ${targetJson};
12272
+ import {
12273
+ registerReactRouterRouteExports as __rrr,
12274
+ scheduleReactRouterRouteUpdate as __rru,
12275
+ } from "virtual/react-router/hmr-runtime";
12276
+
12277
+ const __rrid = ${JSON.stringify(routeId)};
12278
+ const __rrf = ${flags};
12279
+ const __rrg = () => __rrm;
12280
+ const __rru0 = () => {
12281
+ __rrr(__rrid, __rrm);
12282
+ __rru(__rrid, __rrf, __rrg);
12283
+ };
12284
+
12285
+ __rrr(__rrid, __rrm);
12286
+
12287
+ if (import.meta.webpackHot) {
12288
+ const __rrh = import.meta.webpackHot;
12289
+ __rrh.accept(${acceptTargetJson}, __rru0);
12290
+ __rrh.accept();
12291
+ __rrh.dispose(data => { data.__rr = true; });
12292
+ if (__rrh.data && __rrh.data.__rr) __rru0();
12293
+ }
12294
+ `;
12295
+ }, createRouteHmrAcceptTarget = (resourcePath)=>`./${(0, external_pathe_namespaceObject.basename)(resourcePath)}?react-router-route`, buildRouteClientEntryCode = ({ exportNames, chunkedExports, isServer, resourcePath, routeId, devHmr })=>{
12296
+ let chunkedExportSet = chunkedExports.length > 0 ? new Set(chunkedExports) : void 0, reexports = exportNames.filter((exp)=>!chunkedExportSet?.has(exp) && (CLIENT_ROUTE_EXPORTS_SET.has(exp) || isServer && SERVER_ONLY_ROUTE_EXPORTS_SET.has(exp))).sort(), target = `${resourcePath}?react-router-route`, reexportCode = `export { ${reexports.join(', ')} } from ${JSON.stringify(target)};`;
12297
+ return !devHmr || isServer || void 0 === routeId ? reexportCode : reexportCode + buildRouteClientEntryHmrCode({
12298
+ routeId,
12299
+ target,
12300
+ acceptTarget: createRouteHmrAcceptTarget(resourcePath),
12301
+ flags: buildRouteHmrFlags(exportNames)
12302
+ });
12303
+ }, createRouteClientEntryArtifact = async ({ code, resourcePath, environmentName, isBuild, routeChunkCache, routeChunkConfig, routeId, devHmr })=>{
12247
12304
  let isServer = 'node' === environmentName, routeChunkInfo = !isServer && isBuild && shouldAnalyzeRouteChunks(routeChunkConfig, resourcePath, code) ? await detectRouteChunksIfEnabled(routeChunkCache, routeChunkConfig, resourcePath, code) : null;
12248
12305
  return {
12249
12306
  code: buildRouteClientEntryCode({
12250
12307
  exportNames: routeChunkInfo?.exportNames ?? await getExportNames(code, resourcePath),
12251
12308
  chunkedExports: routeChunkInfo?.chunkedExports ?? [],
12252
12309
  isServer,
12253
- resourcePath
12310
+ resourcePath,
12311
+ routeId,
12312
+ devHmr: devHmr && !isBuild
12254
12313
  })
12255
12314
  };
12256
12315
  }, createRouteChunkArtifact = async ({ code, resource, resourcePath, isBuild, routeChunkCache, routeChunkConfig })=>{
@@ -12295,7 +12354,71 @@ const registerBuildOutputTransforms = ({ api, resolvedServerOutput, performanceP
12295
12354
  }, createClientOnlyStub = async (task)=>({
12296
12355
  code: Array.from(await collectClientOnlyStubExportNames(task.code, task.resourcePath, task.resolveExportAllModule)).map((name)=>'default' === name ? 'export default undefined;' : `export const ${name} = undefined;`).join('\n'),
12297
12356
  map: null
12298
- }), route_transform_tasks_transformRouteModule = async (task)=>{
12357
+ }), isComponentishName = (name)=>/^[A-Z]/.test(name), argumentResolvesToComponent = (node)=>{
12358
+ switch(node?.type){
12359
+ case 'FunctionExpression':
12360
+ return !0;
12361
+ case 'ArrowFunctionExpression':
12362
+ return node.body?.type !== 'ArrowFunctionExpression';
12363
+ case 'Identifier':
12364
+ let name;
12365
+ return !!node.name && (name = node.name, /^[A-Z]/.test(name));
12366
+ case 'CallExpression':
12367
+ return callResolvesToComponent(node);
12368
+ default:
12369
+ return !1;
12370
+ }
12371
+ }, callResolvesToComponent = (node)=>{
12372
+ let args = node.arguments ?? [];
12373
+ if (0 === args.length) return !1;
12374
+ let callee = node.callee;
12375
+ if (!callee || 'Import' === callee.type) return !1;
12376
+ if ('Identifier' === callee.type) {
12377
+ let calleeName = callee.name ?? '';
12378
+ if (calleeName.startsWith('require') || calleeName.startsWith('import')) return !1;
12379
+ } else if ('MemberExpression' !== callee.type) return !1;
12380
+ return argumentResolvesToComponent(args[0]);
12381
+ }, initResolvesToComponent = (init)=>{
12382
+ switch(init.type){
12383
+ case 'FunctionExpression':
12384
+ case 'TaggedTemplateExpression':
12385
+ return !0;
12386
+ case 'ArrowFunctionExpression':
12387
+ return init.body?.type !== 'ArrowFunctionExpression';
12388
+ case 'CallExpression':
12389
+ return callResolvesToComponent(init);
12390
+ default:
12391
+ return !1;
12392
+ }
12393
+ }, collectDeclaredComponentNames = (declaration, names)=>{
12394
+ let name, name1;
12395
+ if ('FunctionDeclaration' === declaration.type && declaration.id?.name && (name = declaration.id.name, /^[A-Z]/.test(name))) return void names.add(declaration.id.name);
12396
+ if ('VariableDeclaration' !== declaration.type) return;
12397
+ let declarators = declaration.declarations ?? [];
12398
+ if (1 !== declarators.length) return;
12399
+ let [declarator] = declarators;
12400
+ declarator?.id?.type === 'Identifier' && declarator.id.name && (name1 = declarator.id.name, /^[A-Z]/.test(name1)) && declarator.init && initResolvesToComponent(declarator.init) && names.add(declarator.id.name);
12401
+ }, collectUnregisteredComponentNames = (program)=>{
12402
+ let declared = new Set(), registered = new Set();
12403
+ for (let statement of program.body ?? []){
12404
+ if ('ExportNamedDeclaration' === statement.type && statement.declaration) {
12405
+ collectDeclaredComponentNames(statement.declaration, declared);
12406
+ continue;
12407
+ }
12408
+ if ('ExpressionStatement' === statement.type && statement.expression?.type === 'CallExpression' && statement.expression.callee?.type === 'Identifier' && '$RefreshReg$' === statement.expression.callee.name) {
12409
+ let nameArgument = statement.expression.arguments?.[1];
12410
+ 'string' == typeof nameArgument?.value && registered.add(nameArgument.value);
12411
+ continue;
12412
+ }
12413
+ collectDeclaredComponentNames(statement, declared);
12414
+ }
12415
+ return [
12416
+ ...declared
12417
+ ].filter((name)=>!registered.has(name));
12418
+ }, buildComponentRefreshRegistrations = (names)=>{
12419
+ let registrations = names.map((name)=>` if (typeof ${name} === 'function' || (typeof ${name} === 'object' && ${name} !== null)) $RefreshReg$(${name}, ${JSON.stringify(name)});`).join('\n');
12420
+ return `\nif (typeof $RefreshReg$ === 'function') {\n${registrations}\n}\n`;
12421
+ }, route_transform_tasks_transformRouteModule = async (task)=>{
12299
12422
  let code = task.code, defaultExportMatch = code.match(/\n\s{0,}([\w\d_]+)\sas default,?/);
12300
12423
  defaultExportMatch && 'number' == typeof defaultExportMatch.index && (code = code.slice(0, defaultExportMatch.index) + code.slice(defaultExportMatch.index + defaultExportMatch[0].length) + `\nexport default ${defaultExportMatch[1]};`);
12301
12424
  let ast = yuku_parse(code, {
@@ -12310,11 +12433,17 @@ const registerBuildOutputTransforms = ({ api, resolvedServerOutput, performanceP
12310
12433
  if (!isRootRoute && resolvedExportNames.includes('HydrateFallback')) throw Error(`SPA Mode: Invalid \`HydrateFallback\` export found in \`${relativePath}\`. \`HydrateFallback\` is only permitted on the root route in SPA Mode. See https://reactrouter.com/how-to/spa for more information.`);
12311
12434
  }
12312
12435
  let removedServerOnlyExports = 'web' === task.environmentName && removeExports(ast, SERVER_ONLY_ROUTE_EXPORTS, SERVER_ONLY_ROUTE_EXPORTS_SET);
12313
- return transformRoute(ast), removedServerOnlyExports && removeUnusedImports(ast), yuku_generate(ast, {
12436
+ transformRoute(ast), removedServerOnlyExports && removeUnusedImports(ast);
12437
+ let result = yuku_generate(ast, {
12314
12438
  sourceMaps: task.sourceMaps,
12315
12439
  filename: task.resource,
12316
12440
  sourceFileName: task.resourcePath
12317
12441
  });
12442
+ if (task.devHmr && 'web' === task.environmentName && !task.isBuild) {
12443
+ let unregisteredComponents = collectUnregisteredComponentNames(ast.program ?? ast);
12444
+ unregisteredComponents.length > 0 && (result.code += buildComponentRefreshRegistrations(unregisteredComponents));
12445
+ }
12446
+ return result;
12318
12447
  }, executeRouteTransformTask = async (task, options)=>{
12319
12448
  switch(task.kind){
12320
12449
  case 'routeClientEntry':
@@ -12324,7 +12453,9 @@ const registerBuildOutputTransforms = ({ api, resolvedServerOutput, performanceP
12324
12453
  environmentName: task.environmentName,
12325
12454
  isBuild: task.isBuild,
12326
12455
  routeChunkCache: getRouteChunkCache(options),
12327
- routeChunkConfig: task.routeChunkConfig
12456
+ routeChunkConfig: task.routeChunkConfig,
12457
+ routeId: task.routeId,
12458
+ devHmr: task.devHmr
12328
12459
  });
12329
12460
  case 'routeChunk':
12330
12461
  return createRouteChunkArtifact({
@@ -13280,7 +13411,306 @@ const MAX_SLOWEST_ENTRIES = 5, insertSlowestEntry = (slowest, entry)=>{
13280
13411
  }, loadReactRouterServerBuild = (server, entryName)=>{
13281
13412
  let runtime1 = Reflect.get(server, DEV_RUNTIME_KEY);
13282
13413
  return runtime1 ? runtime1.load(entryName) : Promise.reject(Error('[rsbuild-plugin-react-router] This Rsbuild development server is not registered with the React Router plugin. Add pluginReactRouter() before calling loadReactRouterServerBuild().'));
13283
- }, createDevRuntimeSessionManager = (closeBinding)=>{
13414
+ }, DEV_HMR_RUNTIME_MODULE_ID = 'virtual/react-router/hmr-runtime', dev_hmr_isObject = (value)=>null !== value && 'object' == typeof value, isSwcLoader = (loader)=>'string' == typeof loader && loader.includes('builtin:swc-loader'), hasReactRefresh = (options)=>options?.jsc?.transform?.react?.refresh === !0, readSwcLoaderRefresh = (value)=>Array.isArray(value) ? value.some(readSwcLoaderRefresh) : !!dev_hmr_isObject(value) && (isSwcLoader(value.loader) ? hasReactRefresh(value.options) : Object.values(value).some(readSwcLoaderRefresh)), readRuleSwcRefresh = (rule)=>!!dev_hmr_isObject(rule) && (isSwcLoader(rule.loader) ? hasReactRefresh(rule.options) : readSwcLoaderRefresh(rule.use) || readRuleSetSwcRefresh(rule.oneOf) || readRuleSetSwcRefresh(rule.rules)), readRuleSetSwcRefresh = (rules)=>Array.isArray(rules) && rules.some(readRuleSwcRefresh), isRspackSwcReactRefreshEnabled = (rspackConfig)=>readRuleSetSwcRefresh(rspackConfig.module?.rules), resolveReactRefreshRuntimePath = (rootPath)=>{
13415
+ let resolveFrom = (base, request)=>(0, external_node_module_namespaceObject.createRequire)(base).resolve(request), rootPackageJson = (0, external_pathe_namespaceObject.join)(rootPath, 'package.json');
13416
+ try {
13417
+ let pluginReactEntry = resolveFrom(rootPackageJson, '@rsbuild/plugin-react'), refreshPluginEntry = resolveFrom(pluginReactEntry, '@rspack/plugin-react-refresh');
13418
+ return resolveFrom(refreshPluginEntry, 'react-refresh/runtime');
13419
+ } catch {
13420
+ return;
13421
+ }
13422
+ }, hdrRevisionModuleContent = (revision)=>`export default ${revision};\n`, DEV_HDR_REVISION_RELATIVE_PATH = '.react-router/hdr-revision.mjs', getDevHdrRevisionFilePath = (rootPath)=>(0, external_pathe_namespaceObject.join)(rootPath, DEV_HDR_REVISION_RELATIVE_PATH), createDevHdrRevisionSignal = ({ filePath, onError })=>{
13423
+ let revision = 0, dirEnsured = !1, write = ()=>{
13424
+ try {
13425
+ dirEnsured || ((0, external_node_fs_namespaceObject.mkdirSync)((0, external_pathe_namespaceObject.dirname)(filePath), {
13426
+ recursive: !0
13427
+ }), dirEnsured = !0), (0, external_node_fs_namespaceObject.writeFileSync)(filePath, hdrRevisionModuleContent(revision));
13428
+ } catch (error) {
13429
+ onError?.(error instanceof Error ? error : Error(String(error)));
13430
+ }
13431
+ };
13432
+ return {
13433
+ ensure: write,
13434
+ bump () {
13435
+ revision += 1, write();
13436
+ }
13437
+ };
13438
+ }, generateDevHmrRuntimeModule = ({ reactRefreshRuntimePath, hdrRevisionFilePath })=>`
13439
+ import * as __refreshRuntimeModule from ${JSON.stringify(reactRefreshRuntimePath)};
13440
+ // Read revision so the import survives sideEffects: false tree-shaking.
13441
+ import __hdrRevision from ${JSON.stringify(hdrRevisionFilePath)};
13442
+
13443
+ void __hdrRevision;
13444
+
13445
+ const RefreshRuntime =
13446
+ __refreshRuntimeModule && __refreshRuntimeModule.performReactRefresh
13447
+ ? __refreshRuntimeModule
13448
+ : __refreshRuntimeModule.default;
13449
+
13450
+ const pendingRouteUpdates = new Map();
13451
+ let flushTimeout;
13452
+ let pendingRevalidation = false;
13453
+
13454
+ function getCurrentRouterPath(router) {
13455
+ const basename = router.basename || '/';
13456
+ let pathname = window.location.pathname;
13457
+ if (basename !== '/' && pathname.startsWith(basename)) {
13458
+ pathname = pathname.slice(basename.length) || '/';
13459
+ // A trailing-slash basename (e.g. "/mybase/") consumes the leading slash,
13460
+ // leaving a relative path that react-router resolves against the current
13461
+ // location and doubles. Force it back to absolute.
13462
+ if (pathname[0] !== '/') pathname = '/' + pathname;
13463
+ }
13464
+ return pathname + window.location.search + window.location.hash;
13465
+ }
13466
+
13467
+ export function registerReactRouterRouteExports(routeId, moduleExports) {
13468
+ if (
13469
+ typeof window === 'undefined' ||
13470
+ !RefreshRuntime ||
13471
+ typeof RefreshRuntime.register !== 'function'
13472
+ ) {
13473
+ return;
13474
+ }
13475
+ for (const key in moduleExports) {
13476
+ if (key === '__esModule') continue;
13477
+ const exportValue = moduleExports[key];
13478
+ if (RefreshRuntime.isLikelyComponentType(exportValue)) {
13479
+ RefreshRuntime.register(exportValue, routeId + ' export ' + key);
13480
+ }
13481
+ }
13482
+ }
13483
+
13484
+ export function scheduleReactRouterRouteUpdate(
13485
+ routeId,
13486
+ routeFlags,
13487
+ getRouteModuleExports
13488
+ ) {
13489
+ pendingRouteUpdates.set(routeId, { routeFlags, getRouteModuleExports });
13490
+ scheduleFlush();
13491
+ }
13492
+
13493
+ export function scheduleReactRouterRevalidation() {
13494
+ pendingRevalidation = true;
13495
+ scheduleFlush();
13496
+ }
13497
+
13498
+ function scheduleFlush() {
13499
+ if (typeof window === 'undefined') {
13500
+ return;
13501
+ }
13502
+ clearTimeout(flushTimeout);
13503
+ flushTimeout = setTimeout(flush, 16);
13504
+ }
13505
+
13506
+ function takePendingRouteUpdates() {
13507
+ const updates = Array.from(pendingRouteUpdates, ([routeId, update]) => ({
13508
+ routeId,
13509
+ update,
13510
+ }));
13511
+ pendingRouteUpdates.clear();
13512
+ return updates;
13513
+ }
13514
+
13515
+ function getRouteMetadata(routeFlags) {
13516
+ return {
13517
+ ${HMR_PATCHABLE_ROUTE_FLAGS.map((flag, index)=>` ${flag}: Boolean(routeFlags & ${1 << index}),`).join('\n')}
13518
+ };
13519
+ }
13520
+
13521
+ function applyRouteModuleUpdate(routeId, update, routeEntry, routeModules) {
13522
+ Object.assign(routeEntry, getRouteMetadata(update.routeFlags));
13523
+ const imported = update.getRouteModuleExports();
13524
+ registerReactRouterRouteExports(routeId, imported);
13525
+ const current = routeModules[routeId];
13526
+ const preserveIdentity = key =>
13527
+ imported[key] ? (current && current[key]) || imported[key] : imported[key];
13528
+ routeModules[routeId] = {
13529
+ ...imported,
13530
+ default: preserveIdentity('default'),
13531
+ ErrorBoundary: preserveIdentity('ErrorBoundary'),
13532
+ HydrateFallback: preserveIdentity('HydrateFallback'),
13533
+ };
13534
+ }
13535
+
13536
+ function getRouteById(routes, routeId) {
13537
+ for (const route of routes) {
13538
+ if (route.id === routeId) {
13539
+ return route;
13540
+ }
13541
+ if (route.children) {
13542
+ const child = getRouteById(route.children, routeId);
13543
+ if (child) {
13544
+ return child;
13545
+ }
13546
+ }
13547
+ }
13548
+ }
13549
+
13550
+ // Deliberate coupling to React Router's private dev API: patching the live
13551
+ // match objects is the only way to swap route implementations without a
13552
+ // navigation. The typeof guard below degrades to a no-op if RR removes it.
13553
+ function patchCurrentRouteMatches(router, routes) {
13554
+ if (
13555
+ !router.state ||
13556
+ !Array.isArray(router.state.matches) ||
13557
+ typeof router._internalSetStateDoNotUseOrYouWillBreakYourApp !== 'function'
13558
+ ) {
13559
+ return;
13560
+ }
13561
+
13562
+ let changed = false;
13563
+ const matches = router.state.matches.map(match => {
13564
+ const route = getRouteById(routes, match.route.id);
13565
+ if (!route || route === match.route) {
13566
+ return match;
13567
+ }
13568
+ changed = true;
13569
+ return { ...match, route };
13570
+ });
13571
+
13572
+ if (changed) {
13573
+ router._internalSetStateDoNotUseOrYouWillBreakYourApp({ matches });
13574
+ }
13575
+ }
13576
+
13577
+ function applyPendingRouteUpdates(router, routeModules, manifest, context) {
13578
+ if (pendingRouteUpdates.size === 0) {
13579
+ return {
13580
+ nextManifest: undefined,
13581
+ shouldRefreshRouteState: false,
13582
+ routesToRevalidate: new Set(),
13583
+ };
13584
+ }
13585
+
13586
+ // Clone only entries mutated before the manifest is committed in flush().
13587
+ const nextManifest = { ...manifest, routes: { ...manifest.routes } };
13588
+ const routesToRevalidate = new Set();
13589
+ let shouldRefreshRouteState = false;
13590
+ for (const { routeId, update } of takePendingRouteUpdates()) {
13591
+ const existingEntry = nextManifest.routes[routeId];
13592
+ if (!existingEntry) continue;
13593
+
13594
+ // Shallow clone is enough: only top-level flags are mutated below.
13595
+ const routeEntry = { ...existingEntry };
13596
+ nextManifest.routes[routeId] = routeEntry;
13597
+ applyRouteModuleUpdate(routeId, update, routeEntry, routeModules);
13598
+ if (
13599
+ routeEntry.hasLoader ||
13600
+ routeEntry.hasClientLoader ||
13601
+ routeEntry.hasClientMiddleware
13602
+ ) {
13603
+ routesToRevalidate.add(routeId);
13604
+ }
13605
+ if (
13606
+ existingEntry.hasLoader ||
13607
+ existingEntry.hasClientLoader ||
13608
+ existingEntry.hasClientMiddleware ||
13609
+ routeEntry.hasLoader ||
13610
+ routeEntry.hasClientLoader ||
13611
+ routeEntry.hasClientMiddleware
13612
+ ) {
13613
+ shouldRefreshRouteState = true;
13614
+ }
13615
+ }
13616
+
13617
+ if (
13618
+ typeof router.createRoutesForHMR === 'function' &&
13619
+ typeof router._internalSetRoutes === 'function'
13620
+ ) {
13621
+ const routes = router.createRoutesForHMR(
13622
+ routesToRevalidate,
13623
+ nextManifest.routes,
13624
+ routeModules,
13625
+ context.ssr,
13626
+ context.isSpaMode
13627
+ );
13628
+ router._internalSetRoutes(routes);
13629
+ patchCurrentRouteMatches(router, routes);
13630
+ }
13631
+
13632
+ return { nextManifest, shouldRefreshRouteState, routesToRevalidate };
13633
+ }
13634
+
13635
+ async function withHdrActive(fn) {
13636
+ try {
13637
+ window.__reactRouterHdrActive = true;
13638
+ await fn();
13639
+ } finally {
13640
+ window.__reactRouterHdrActive = false;
13641
+ }
13642
+ }
13643
+
13644
+ async function revalidateRouter(router) {
13645
+ if (typeof router.revalidate === 'function') {
13646
+ await withHdrActive(() => router.revalidate());
13647
+ return;
13648
+ }
13649
+ if (typeof router.navigate === 'function') {
13650
+ await withHdrActive(() =>
13651
+ router.navigate(getCurrentRouterPath(router), {
13652
+ replace: true,
13653
+ preventScrollReset: true,
13654
+ })
13655
+ );
13656
+ }
13657
+ }
13658
+
13659
+ async function refreshRouteState(router) {
13660
+ if (typeof router.revalidate === 'function') {
13661
+ await withHdrActive(() => router.revalidate());
13662
+ return true;
13663
+ }
13664
+ return false;
13665
+ }
13666
+
13667
+ function performReactRefresh() {
13668
+ if (
13669
+ RefreshRuntime &&
13670
+ typeof RefreshRuntime.performReactRefresh === 'function'
13671
+ ) {
13672
+ RefreshRuntime.performReactRefresh();
13673
+ }
13674
+ }
13675
+
13676
+ async function flush() {
13677
+ const router = window.__reactRouterDataRouter;
13678
+ const routeModules = window.__reactRouterRouteModules;
13679
+ const manifest = window.__reactRouterManifest;
13680
+ const context = window.__reactRouterContext;
13681
+ if (!router || !routeModules || !manifest || !context) {
13682
+ return;
13683
+ }
13684
+
13685
+ let shouldRevalidate = pendingRevalidation;
13686
+ pendingRevalidation = false;
13687
+ const { nextManifest, shouldRefreshRouteState, routesToRevalidate } =
13688
+ applyPendingRouteUpdates(router, routeModules, manifest, context);
13689
+ if (nextManifest) {
13690
+ Object.assign(manifest, nextManifest);
13691
+ }
13692
+ // Component-only updates do not need a full loader revalidation.
13693
+ if (
13694
+ shouldRefreshRouteState &&
13695
+ (routesToRevalidate.size > 0 || shouldRevalidate)
13696
+ ) {
13697
+ if (await refreshRouteState(router)) {
13698
+ shouldRevalidate = false;
13699
+ }
13700
+ }
13701
+ if (shouldRevalidate) {
13702
+ await revalidateRouter(router);
13703
+ }
13704
+ performReactRefresh();
13705
+ }
13706
+
13707
+ if (typeof window !== 'undefined' && import.meta.webpackHot) {
13708
+ import.meta.webpackHot.accept(
13709
+ ${JSON.stringify(hdrRevisionFilePath)},
13710
+ scheduleReactRouterRevalidation
13711
+ );
13712
+ }
13713
+ `, createDevRuntimeSessionManager = (closeBinding)=>{
13284
13714
  let state = {
13285
13715
  status: 'idle'
13286
13716
  }, nextSessionId = 1, closeObservationByServer = new WeakMap(), isCurrentBinding = (binding)=>('active' === state.status || 'closing' === state.status) && state.binding === binding, applyCloseOutcome = (observation, outcome)=>{
@@ -13352,7 +13782,10 @@ const MAX_SLOWEST_ENTRIES = 5, insertSlowestEntry = (slowest, entry)=>{
13352
13782
  });
13353
13783
  }
13354
13784
  };
13355
- }, escapeHtml = (value)=>value.replaceAll('&', '&amp;').replaceAll('<', '&lt;').replaceAll('>', '&gt;'), CSS_SOURCE_RELOAD_DELAY_MS = 1000, createReactRouterDevRuntimeController = ({ api, isBuild, buildPlan })=>{
13785
+ }, escapeHtml = (value)=>value.replaceAll('&', '&amp;').replaceAll('<', '&lt;').replaceAll('>', '&gt;'), CSS_SOURCE_RELOAD_DELAY_MS = 1000, isHdrRevisionFile = (file)=>file.includes(DEV_HDR_REVISION_RELATIVE_PATH), isCssSourceFile = (file)=>/\.css(?:\.[cm]?[jt]s)?$/.test(file), hasHdrTriggeringChange = (files)=>{
13786
+ for (let file of files)if (!isHdrRevisionFile(file) && !isCssSourceFile(file)) return !0;
13787
+ return !1;
13788
+ }, createReactRouterDevRuntimeController = ({ api, isBuild, buildPlan, onNodeRebuildCommitted })=>{
13356
13789
  let scheduledCssAssetOwnershipReload;
13357
13790
  if (isBuild) return {
13358
13791
  captureWeb () {},
@@ -13373,8 +13806,11 @@ const MAX_SLOWEST_ENTRIES = 5, insertSlowestEntry = (slowest, entry)=>{
13373
13806
  scheduledCssAssetOwnershipReload && (clearTimeout(scheduledCssAssetOwnershipReload), scheduledCssAssetOwnershipReload = void 0), reloadAfterCssAssetOwnershipRemoval = !1;
13374
13807
  let pair = binding.compilers;
13375
13808
  pair && resetDevCompilerPair(pair), binding.compilers = void 0, binding.runtime.close(error), unregisterReactRouterDevRuntime(binding.server, binding.runtime);
13376
- }, sessions = createDevRuntimeSessionManager(closeBinding), compilationIdentities = createCompilationIdentityTracker(), { getCompilationIdentity } = compilationIdentities, finishRuntimeAttempt = (binding, pair, stats, changes, identity1)=>runPluginEffect(tryPluginPromise(()=>binding.runtime.finishAttempt(stats, changes, identity1)).pipe(core_flatMap((result)=>tryPluginSync(()=>{
13377
- 'retry-node' === result && sessions.getActiveBinding()?.id === binding.id && pair.node.watching?.invalidate();
13809
+ }, sessions = createDevRuntimeSessionManager(closeBinding), compilationIdentities = createCompilationIdentityTracker(), { getCompilationIdentity } = compilationIdentities, hdrSignaledNodeIdentity = new WeakMap(), finishRuntimeAttempt = (binding, pair, stats, changes, identity1)=>runPluginEffect(tryPluginPromise(()=>binding.runtime.finishAttempt(stats, changes, identity1)).pipe(core_flatMap((result)=>tryPluginSync(()=>{
13810
+ if (sessions.getActiveBinding()?.id === binding.id) {
13811
+ if ('retry-node' === result) return void pair.node.watching?.invalidate();
13812
+ 'committed' === result && changes.node.known && void 0 !== identity1.node && hdrSignaledNodeIdentity.get(pair) !== identity1.node && hasHdrTriggeringChange(changes.node.files) && (hdrSignaledNodeIdentity.set(pair, identity1.node), onNodeRebuildCommitted?.());
13813
+ }
13378
13814
  })), catchAll((cause)=>tryPluginSync(()=>{
13379
13815
  sessions.getActiveBinding()?.id === binding.id && binding.runtime.failAttempt(normalizeEffectError(cause));
13380
13816
  })))), flushSettledAttempt = (binding, pair)=>{
@@ -13974,10 +14410,14 @@ const MAX_SLOWEST_ENTRIES = 5, insertSlowestEntry = (slowest, entry)=>{
13974
14410
  routesByServerBundleId,
13975
14411
  serverBuildFile,
13976
14412
  defaultEntryName: devServerBuildEntryName
13977
- }), { serverBundleEntries } = serverBuildPlan, devRuntime = createReactRouterDevRuntimeController({
14413
+ }), { serverBundleEntries } = serverBuildPlan, devHmrRefreshRuntimePath = isBuild ? void 0 : resolveReactRefreshRuntimePath(api.context.rootPath), devHdrSignal = devHmrRefreshRuntimePath ? createDevHdrRevisionSignal({
14414
+ filePath: getDevHdrRevisionFilePath(api.context.rootPath),
14415
+ onError: (error)=>api.logger.debug(`[${PLUGIN_NAME}] Failed to signal hot data revalidation: ${error.message}`)
14416
+ }) : void 0, devHmrEnabled = !1, devRuntime = createReactRouterDevRuntimeController({
13978
14417
  api,
13979
14418
  isBuild,
13980
- buildPlan: serverBuildPlan
14419
+ buildPlan: serverBuildPlan,
14420
+ onNodeRebuildCommitted: ()=>devHdrSignal?.bump()
13981
14421
  });
13982
14422
  api.onAfterEnvironmentCompile(({ stats, environment })=>{
13983
14423
  if ('web' === environment.name && (clientStats = createReactRouterManifestStats(stats?.compilation, manifestChunkNames)), pluginOptions.federation && ssr) {
@@ -14058,7 +14498,13 @@ const MAX_SLOWEST_ENTRIES = 5, insertSlowestEntry = (slowest, entry)=>{
14058
14498
  }),
14059
14499
  ...bundleVirtualModules,
14060
14500
  ...bundleManifestModules,
14061
- 'virtual/react-router/with-props': generateWithProps()
14501
+ 'virtual/react-router/with-props': generateWithProps(),
14502
+ ...devHmrRefreshRuntimePath ? {
14503
+ 'virtual/react-router/hmr-runtime': generateDevHmrRuntimeModule({
14504
+ reactRefreshRuntimePath: devHmrRefreshRuntimePath,
14505
+ hdrRevisionFilePath: getDevHdrRevisionFilePath(api.context.rootPath)
14506
+ })
14507
+ } : {}
14062
14508
  }))), useAsyncNodeChunkLoading = options.federation && 'commonjs' === resolvedServerOutput, nodeChunkLoading = 'require';
14063
14509
  'module' === resolvedServerOutput ? nodeChunkLoading = 'import' : useAsyncNodeChunkLoading && (nodeChunkLoading = 'async-node');
14064
14510
  let nodeEntries = createReactRouterNodeEntries({
@@ -14087,7 +14533,6 @@ const MAX_SLOWEST_ENTRIES = 5, insertSlowestEntry = (slowest, entry)=>{
14087
14533
  assetPrefix: config.output?.assetPrefix || '/'
14088
14534
  },
14089
14535
  dev: {
14090
- writeToDisk: !0,
14091
14536
  ...void 0 === guardedLazyCompilation ? {} : {
14092
14537
  lazyCompilation: guardedLazyCompilation
14093
14538
  },
@@ -14218,7 +14663,7 @@ const MAX_SLOWEST_ENTRIES = 5, insertSlowestEntry = (slowest, entry)=>{
14218
14663
  }), api.modifyEnvironmentConfig(async (config, { name, mergeEnvironmentConfig })=>'web' !== name && 'node' !== name ? config : mergeEnvironmentConfig(config, {
14219
14664
  tools: {
14220
14665
  rspack: (rspackConfig)=>{
14221
- if (pluginOptions.federation && ensureFederationAsyncStartup(rspackConfig), 'node' === name) {
14666
+ if (pluginOptions.federation && ensureFederationAsyncStartup(rspackConfig), 'web' === name && (devHmrEnabled = !isBuild && void 0 !== devHmrRefreshRuntimePath && 'development' === config.mode && config.dev?.hmr !== !1 && isRspackSwcReactRefreshEnabled(rspackConfig)) && devHdrSignal?.ensure(), 'node' === name) {
14222
14667
  let output = rspackConfig.output;
14223
14668
  if (output) {
14224
14669
  let library = output.library, libraryOptions = library && 'object' == typeof library && !Array.isArray(library) ? library : {};
@@ -14285,7 +14730,8 @@ const MAX_SLOWEST_ENTRIES = 5, insertSlowestEntry = (slowest, entry)=>{
14285
14730
  splitRouteModules: !!splitRouteModules,
14286
14731
  ssr,
14287
14732
  isSpaMode,
14288
- rootRoutePath
14733
+ rootRoutePath,
14734
+ isDevHmrEnabled: ()=>devHmrEnabled
14289
14735
  });
14290
14736
  }
14291
14737
  });
package/dist/index.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import { type RsbuildPlugin } from '@rsbuild/core';
2
2
  import type { PluginOptions } from './types.js';
3
3
  import { resolveReactRouterServerBuild } from './server-utils.js';
4
+ export type { Config as ReactRouterRsbuildConfig } from './react-router-config.js';
4
5
  export { loadReactRouterServerBuild } from './dev-generation.js';
5
6
  export { resolveReactRouterServerBuild };
6
7
  export declare const shouldParallelizeEnvironmentBuilds: ({ isBuild, spareCoreCount, }: {