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/README.md +34 -12
- package/dist/451.js +128 -10
- package/dist/build-output-transforms.d.ts +2 -1
- package/dist/dev-hmr.d.ts +45 -0
- package/dist/dev-runtime-controller.d.ts +6 -1
- package/dist/index.cjs +476 -30
- package/dist/index.d.ts +1 -0
- package/dist/index.js +346 -23
- package/dist/route-artifacts.d.ts +10 -2
- package/dist/route-transform-tasks.d.ts +3 -0
- package/package.json +8 -2
- package/src/build-output-transforms.ts +5 -0
- package/src/dev-hmr.ts +431 -0
- package/src/dev-runtime-controller.ts +47 -3
- package/src/index.ts +48 -1
- package/src/prerender-build.ts +29 -9
- package/src/prerender.ts +1 -1
- package/src/route-artifacts.ts +120 -1
- package/src/route-transform-tasks.ts +173 -2
package/dist/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
let globalStore, o, origin, bigint1e6, characters, charactersLength, O, quoteValue, whitespace, options, boundaries, description, options1, f;
|
|
2
|
-
import { existsSync, readFileSync, realpathSync, watch } from "node:fs";
|
|
2
|
+
import { existsSync, mkdirSync, readFileSync, realpathSync, watch, writeFileSync } from "node:fs";
|
|
3
3
|
import fs_extra from "fs-extra";
|
|
4
4
|
import { rspack } from "@rsbuild/core";
|
|
5
5
|
import { createJiti } from "jiti";
|
|
6
|
-
import { dirname, isAbsolute, relative, resolve as external_pathe_resolve } from "pathe";
|
|
6
|
+
import { dirname, isAbsolute, join as external_pathe_join, relative, resolve as external_pathe_resolve } from "pathe";
|
|
7
7
|
import { availableParallelism, cpus } from "node:os";
|
|
8
8
|
import { createRequestHandler as external_react_router_createRequestHandler, matchRoutes } from "react-router";
|
|
9
9
|
import { access, mkdir, readdir, writeFile } from "node:fs/promises";
|
|
@@ -13,7 +13,7 @@ import jsesc from "jsesc";
|
|
|
13
13
|
import { Worker } from "node:worker_threads";
|
|
14
14
|
import { createRequire } from "node:module";
|
|
15
15
|
import { dirname as external_node_path_dirname, isAbsolute as external_node_path_isAbsolute, relative as external_node_path_relative, resolve as external_node_path_resolve, sep as external_node_path_sep, dirname as __rspack_dirname } from "node:path";
|
|
16
|
-
import { createRouteId, SERVER_EXPORTS, combineURLs, JS_EXTENSIONS, executeRouteTransformTask, setBoundedCacheEntry, PLUGIN_NAME, generateWithProps, getRouteModuleAnalysis, createEmptyRouteChunkByExportName, findEntryFile, routeChunkExportNames, validateRouteChunks, CLIENT_EXPORTS, buildManifestChunkValidity, BUILD_CLIENT_ROUTE_QUERY_STRING, getRouteChunkEntryName, getRouteChunkModuleId, normalizeAssetPrefix, detectRouteChunksIfEnabled, createBundlerRouteExportResolver } from "./451.js";
|
|
16
|
+
import { createRouteId, HMR_PATCHABLE_ROUTE_FLAGS, SERVER_EXPORTS, combineURLs, JS_EXTENSIONS, executeRouteTransformTask, setBoundedCacheEntry, PLUGIN_NAME, generateWithProps, getRouteModuleAnalysis, createEmptyRouteChunkByExportName, findEntryFile, routeChunkExportNames, validateRouteChunks, CLIENT_EXPORTS, buildManifestChunkValidity, BUILD_CLIENT_ROUTE_QUERY_STRING, getRouteChunkEntryName, getRouteChunkModuleId, normalizeAssetPrefix, detectRouteChunksIfEnabled, createBundlerRouteExportResolver } from "./451.js";
|
|
17
17
|
let getAvailableCpuCount = ()=>'function' == typeof availableParallelism ? availableParallelism() : cpus().length, getDefaultConcurrency = (cpuCount = getAvailableCpuCount())=>Math.max(0, Math.floor(cpuCount) - 2), getCappedPluginConcurrency = (cap = 16)=>Math.max(1, Math.min(cap, getDefaultConcurrency() || 1)), createReactRouterHydrationModuleTest = (entryClientPath)=>{
|
|
18
18
|
let eagerPatterns = [
|
|
19
19
|
'virtual/react-router/browser-manifest',
|
|
@@ -7118,7 +7118,7 @@ let normalizePath = (value)=>value.replace(/\/\/+/g, '/').replace(/(.+)\/$/, '$1
|
|
|
7118
7118
|
}))([
|
|
7119
7119
|
'Warning: Paths with dynamic/splat params cannot be prerendered when using `prerender: true`.',
|
|
7120
7120
|
'You may want to use the `prerender()` API to prerender the following paths:',
|
|
7121
|
-
...paramRoutes1.map((path)=>` - ${path}`)
|
|
7121
|
+
...paramRoutes1.map((path)=>` - ${path.replace(/^\/(?=[:*])/, '')}`)
|
|
7122
7122
|
].join('\n')), paths1;
|
|
7123
7123
|
if ('function' == typeof pathsConfig) {
|
|
7124
7124
|
let resolved = await pathsConfig({
|
|
@@ -7334,11 +7334,11 @@ let redirectStatusCodes = new Set([
|
|
|
7334
7334
|
}))), (controller)=>sync(()=>{
|
|
7335
7335
|
controller.abort();
|
|
7336
7336
|
}))), prerenderData = async ({ handler, prerenderPath, onlyRoutes, clientBuildDir, basename, trailingSlashAwareDataRequests, api, requestInit })=>{
|
|
7337
|
-
let
|
|
7337
|
+
let dataOutputPath = trailingSlashAwareDataRequests ? prerenderPath.endsWith('/') ? `${prerenderPath}_.data` : `${prerenderPath}.data` : '/' === prerenderPath ? '/_root.data' : `${prerenderPath.replace(/\/$/, '')}.data`, 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}`);
|
|
7338
7338
|
return onlyRoutes?.length && url.searchParams.set('_routes', onlyRoutes.join(',')), withBuildRequest(url, requestInit, async (request)=>{
|
|
7339
7339
|
let response = await handler(request), data = await response.text();
|
|
7340
7340
|
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}`);
|
|
7341
|
-
let outputPath = external_pathe_resolve(clientBuildDir, ...
|
|
7341
|
+
let outputPath = external_pathe_resolve(clientBuildDir, ...outputNormalizedPath.split('/'));
|
|
7342
7342
|
return await mkdir(dirname(outputPath), {
|
|
7343
7343
|
recursive: !0
|
|
7344
7344
|
}), await writeFile(outputPath, data), api.logger.info(`Prerender (data): ${prerenderPath} -> ${relative(process.cwd(), outputPath)}`), data;
|
|
@@ -7431,18 +7431,21 @@ let redirectStatusCodes = new Set([
|
|
|
7431
7431
|
trailingSlashAwareDataRequests: future.unstable_trailingSlashAwareDataRequests,
|
|
7432
7432
|
api
|
|
7433
7433
|
})) : void 0;
|
|
7434
|
-
yield* tryPluginPromise(()=>
|
|
7434
|
+
yield* tryPluginPromise(()=>{
|
|
7435
|
+
let encodedData;
|
|
7436
|
+
return prerenderRoute({
|
|
7435
7437
|
handler: requestHandler,
|
|
7436
7438
|
prerenderPath: path,
|
|
7437
7439
|
clientBuildDir,
|
|
7438
7440
|
basename,
|
|
7439
7441
|
api,
|
|
7440
|
-
requestInit: data ? {
|
|
7442
|
+
requestInit: data && (encodedData = encodeURI(data)).length < 8192 ? {
|
|
7441
7443
|
headers: {
|
|
7442
|
-
'X-React-Router-Prerender-Data':
|
|
7444
|
+
'X-React-Router-Prerender-Data': encodedData
|
|
7443
7445
|
}
|
|
7444
7446
|
} : void 0
|
|
7445
|
-
})
|
|
7447
|
+
});
|
|
7448
|
+
});
|
|
7446
7449
|
}))({
|
|
7447
7450
|
path,
|
|
7448
7451
|
build,
|
|
@@ -8002,7 +8005,7 @@ let roundMs = (value)=>Math.round(10 * value) / 10, VIRTUAL_MODULE_PREFIX = 'vir
|
|
|
8002
8005
|
}, hasSameRouteMetadata = (previous, next)=>previous.caseSensitive === next.caseSensitive && previous.clientActionModule === next.clientActionModule && previous.clientLoaderModule === next.clientLoaderModule && previous.clientMiddlewareModule === next.clientMiddlewareModule && previous.hasErrorBoundary === next.hasErrorBoundary && previous.hasAction === next.hasAction && previous.hasClientAction === next.hasClientAction && previous.hasClientLoader === next.hasClientLoader && previous.hasClientMiddleware === next.hasClientMiddleware && previous.hasDefaultExport === next.hasDefaultExport && previous.hasLoader === next.hasLoader && previous.hydrateFallbackModule === next.hydrateFallbackModule && previous.id === next.id && previous.index === next.index && previous.parentId === next.parentId && previous.path === next.path, DEV_RUNTIME_KEY = Symbol.for('rsbuild-plugin-react-router.dev-runtime.v1'), loadReactRouterServerBuild = (server, entryName)=>{
|
|
8003
8006
|
let runtime = Reflect.get(server, DEV_RUNTIME_KEY);
|
|
8004
8007
|
return runtime ? runtime.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().'));
|
|
8005
|
-
}, TYPEGEN_IDLE_DELAY_MS = 10000, loadDefaultExeca = async ()=>{
|
|
8008
|
+
}, 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), DEV_HDR_REVISION_RELATIVE_PATH = '.react-router/hdr-revision.mjs', isHdrRevisionFile = (file)=>file.includes(DEV_HDR_REVISION_RELATIVE_PATH), isCssSourceFile = (file)=>/\.css(?:\.[cm]?[jt]s)?$/.test(file), TYPEGEN_IDLE_DELAY_MS = 10000, loadDefaultExeca = async ()=>{
|
|
8006
8009
|
let { execa } = await import("execa");
|
|
8007
8010
|
return execa;
|
|
8008
8011
|
}, config_imports_normalizePath = (filePath)=>external_pathe_resolve(filePath), isNodeModulePath = (filePath)=>filePath.split(/[\\/]/).includes('node_modules'), collectConfigImportWatchPaths = (configPath, moduleCache, previousCacheKeys)=>{
|
|
@@ -8780,7 +8783,35 @@ let shouldParallelizeEnvironmentBuilds = ({ isBuild, spareCoreCount = getDefault
|
|
|
8780
8783
|
routesByServerBundleId,
|
|
8781
8784
|
serverBuildFile,
|
|
8782
8785
|
defaultEntryName: devServerBuildEntryName
|
|
8783
|
-
}), { serverBundleEntries } = serverBuildPlan,
|
|
8786
|
+
}), { serverBundleEntries } = serverBuildPlan, devHmrRefreshRuntimePath = isBuild ? void 0 : ((rootPath)=>{
|
|
8787
|
+
let resolveFrom = (base, request)=>createRequire(base).resolve(request), rootPackageJson = external_pathe_join(rootPath, 'package.json');
|
|
8788
|
+
try {
|
|
8789
|
+
let pluginReactEntry = resolveFrom(rootPackageJson, '@rsbuild/plugin-react'), refreshPluginEntry = resolveFrom(pluginReactEntry, '@rspack/plugin-react-refresh');
|
|
8790
|
+
return resolveFrom(refreshPluginEntry, 'react-refresh/runtime');
|
|
8791
|
+
} catch {
|
|
8792
|
+
return;
|
|
8793
|
+
}
|
|
8794
|
+
})(api.context.rootPath), devHdrSignal = devHmrRefreshRuntimePath ? (({ filePath, onError })=>{
|
|
8795
|
+
let revision = 0, dirEnsured = !1, write = ()=>{
|
|
8796
|
+
try {
|
|
8797
|
+
let revision1;
|
|
8798
|
+
dirEnsured || (mkdirSync(dirname(filePath), {
|
|
8799
|
+
recursive: !0
|
|
8800
|
+
}), dirEnsured = !0), writeFileSync(filePath, (revision1 = revision, `export default ${revision1};\n`));
|
|
8801
|
+
} catch (error) {
|
|
8802
|
+
onError?.(error instanceof Error ? error : Error(String(error)));
|
|
8803
|
+
}
|
|
8804
|
+
};
|
|
8805
|
+
return {
|
|
8806
|
+
ensure: write,
|
|
8807
|
+
bump () {
|
|
8808
|
+
revision += 1, write();
|
|
8809
|
+
}
|
|
8810
|
+
};
|
|
8811
|
+
})({
|
|
8812
|
+
filePath: external_pathe_join(api.context.rootPath, DEV_HDR_REVISION_RELATIVE_PATH),
|
|
8813
|
+
onError: (error)=>api.logger.debug(`[${PLUGIN_NAME}] Failed to signal hot data revalidation: ${error.message}`)
|
|
8814
|
+
}) : void 0, devHmrEnabled = !1, devRuntime = (({ api, isBuild, buildPlan, onNodeRebuildCommitted })=>{
|
|
8784
8815
|
let scheduledCssAssetOwnershipReload, state, nextSessionId, closeObservationByServer, isCurrentBinding, applyCloseOutcome, observeClose, identityByCompilation, webIdentityByNodeCompilation, attemptIdentityByCompilation;
|
|
8785
8816
|
if (isBuild) return {
|
|
8786
8817
|
captureWeb () {},
|
|
@@ -8887,8 +8918,14 @@ let shouldParallelizeEnvironmentBuilds = ({ isBuild, spareCoreCount = getDefault
|
|
|
8887
8918
|
setWebIdentityForNodeCompilation (compilation, identity) {
|
|
8888
8919
|
webIdentityByNodeCompilation.set(compilation, identity);
|
|
8889
8920
|
}
|
|
8890
|
-
}), { getCompilationIdentity } = compilationIdentities, finishRuntimeAttempt = (binding, pair, stats, changes, identity)=>runPluginEffect(tryPluginPromise(()=>binding.runtime.finishAttempt(stats, changes, identity)).pipe(core_flatMap((result)=>tryPluginSync(()=>{
|
|
8891
|
-
|
|
8921
|
+
}), { getCompilationIdentity } = compilationIdentities, hdrSignaledNodeIdentity = new WeakMap(), finishRuntimeAttempt = (binding, pair, stats, changes, identity)=>runPluginEffect(tryPluginPromise(()=>binding.runtime.finishAttempt(stats, changes, identity)).pipe(core_flatMap((result)=>tryPluginSync(()=>{
|
|
8922
|
+
if (sessions.getActiveBinding()?.id === binding.id) {
|
|
8923
|
+
if ('retry-node' === result) return void pair.node.watching?.invalidate();
|
|
8924
|
+
'committed' === result && changes.node.known && void 0 !== identity.node && hdrSignaledNodeIdentity.get(pair) !== identity.node && ((files)=>{
|
|
8925
|
+
for (let file of files)if (!isHdrRevisionFile(file) && !isCssSourceFile(file)) return !0;
|
|
8926
|
+
return !1;
|
|
8927
|
+
})(changes.node.files) && (hdrSignaledNodeIdentity.set(pair, identity.node), onNodeRebuildCommitted?.());
|
|
8928
|
+
}
|
|
8892
8929
|
})), catchAll((cause)=>tryPluginSync(()=>{
|
|
8893
8930
|
sessions.getActiveBinding()?.id === binding.id && binding.runtime.failAttempt(normalizeEffectError(cause));
|
|
8894
8931
|
})))), flushSettledAttempt = (binding, pair)=>{
|
|
@@ -9233,7 +9270,8 @@ let shouldParallelizeEnvironmentBuilds = ({ isBuild, spareCoreCount = getDefault
|
|
|
9233
9270
|
})({
|
|
9234
9271
|
api,
|
|
9235
9272
|
isBuild,
|
|
9236
|
-
buildPlan: serverBuildPlan
|
|
9273
|
+
buildPlan: serverBuildPlan,
|
|
9274
|
+
onNodeRebuildCommitted: ()=>devHdrSignal?.bump()
|
|
9237
9275
|
});
|
|
9238
9276
|
api.onAfterEnvironmentCompile(({ stats, environment })=>{
|
|
9239
9277
|
if ('web' === environment.name && (clientStats = createReactRouterManifestStats(stats?.compilation, manifestChunkNames)), pluginOptions.federation && ssr) {
|
|
@@ -9314,7 +9352,288 @@ let shouldParallelizeEnvironmentBuilds = ({ isBuild, spareCoreCount = getDefault
|
|
|
9314
9352
|
}),
|
|
9315
9353
|
...bundleVirtualModules,
|
|
9316
9354
|
...bundleManifestModules,
|
|
9317
|
-
'virtual/react-router/with-props': generateWithProps()
|
|
9355
|
+
'virtual/react-router/with-props': generateWithProps(),
|
|
9356
|
+
...devHmrRefreshRuntimePath ? {
|
|
9357
|
+
'virtual/react-router/hmr-runtime': (({ reactRefreshRuntimePath, hdrRevisionFilePath })=>`
|
|
9358
|
+
import * as __refreshRuntimeModule from ${JSON.stringify(reactRefreshRuntimePath)};
|
|
9359
|
+
// Read revision so the import survives sideEffects: false tree-shaking.
|
|
9360
|
+
import __hdrRevision from ${JSON.stringify(hdrRevisionFilePath)};
|
|
9361
|
+
|
|
9362
|
+
void __hdrRevision;
|
|
9363
|
+
|
|
9364
|
+
const RefreshRuntime =
|
|
9365
|
+
__refreshRuntimeModule && __refreshRuntimeModule.performReactRefresh
|
|
9366
|
+
? __refreshRuntimeModule
|
|
9367
|
+
: __refreshRuntimeModule.default;
|
|
9368
|
+
|
|
9369
|
+
const pendingRouteUpdates = new Map();
|
|
9370
|
+
let flushTimeout;
|
|
9371
|
+
let pendingRevalidation = false;
|
|
9372
|
+
|
|
9373
|
+
function getCurrentRouterPath(router) {
|
|
9374
|
+
const basename = router.basename || '/';
|
|
9375
|
+
let pathname = window.location.pathname;
|
|
9376
|
+
if (basename !== '/' && pathname.startsWith(basename)) {
|
|
9377
|
+
pathname = pathname.slice(basename.length) || '/';
|
|
9378
|
+
// A trailing-slash basename (e.g. "/mybase/") consumes the leading slash,
|
|
9379
|
+
// leaving a relative path that react-router resolves against the current
|
|
9380
|
+
// location and doubles. Force it back to absolute.
|
|
9381
|
+
if (pathname[0] !== '/') pathname = '/' + pathname;
|
|
9382
|
+
}
|
|
9383
|
+
return pathname + window.location.search + window.location.hash;
|
|
9384
|
+
}
|
|
9385
|
+
|
|
9386
|
+
export function registerReactRouterRouteExports(routeId, moduleExports) {
|
|
9387
|
+
if (
|
|
9388
|
+
typeof window === 'undefined' ||
|
|
9389
|
+
!RefreshRuntime ||
|
|
9390
|
+
typeof RefreshRuntime.register !== 'function'
|
|
9391
|
+
) {
|
|
9392
|
+
return;
|
|
9393
|
+
}
|
|
9394
|
+
for (const key in moduleExports) {
|
|
9395
|
+
if (key === '__esModule') continue;
|
|
9396
|
+
const exportValue = moduleExports[key];
|
|
9397
|
+
if (RefreshRuntime.isLikelyComponentType(exportValue)) {
|
|
9398
|
+
RefreshRuntime.register(exportValue, routeId + ' export ' + key);
|
|
9399
|
+
}
|
|
9400
|
+
}
|
|
9401
|
+
}
|
|
9402
|
+
|
|
9403
|
+
export function scheduleReactRouterRouteUpdate(
|
|
9404
|
+
routeId,
|
|
9405
|
+
routeFlags,
|
|
9406
|
+
getRouteModuleExports
|
|
9407
|
+
) {
|
|
9408
|
+
pendingRouteUpdates.set(routeId, { routeFlags, getRouteModuleExports });
|
|
9409
|
+
scheduleFlush();
|
|
9410
|
+
}
|
|
9411
|
+
|
|
9412
|
+
export function scheduleReactRouterRevalidation() {
|
|
9413
|
+
pendingRevalidation = true;
|
|
9414
|
+
scheduleFlush();
|
|
9415
|
+
}
|
|
9416
|
+
|
|
9417
|
+
function scheduleFlush() {
|
|
9418
|
+
if (typeof window === 'undefined') {
|
|
9419
|
+
return;
|
|
9420
|
+
}
|
|
9421
|
+
clearTimeout(flushTimeout);
|
|
9422
|
+
flushTimeout = setTimeout(flush, 16);
|
|
9423
|
+
}
|
|
9424
|
+
|
|
9425
|
+
function takePendingRouteUpdates() {
|
|
9426
|
+
const updates = Array.from(pendingRouteUpdates, ([routeId, update]) => ({
|
|
9427
|
+
routeId,
|
|
9428
|
+
update,
|
|
9429
|
+
}));
|
|
9430
|
+
pendingRouteUpdates.clear();
|
|
9431
|
+
return updates;
|
|
9432
|
+
}
|
|
9433
|
+
|
|
9434
|
+
function getRouteMetadata(routeFlags) {
|
|
9435
|
+
return {
|
|
9436
|
+
${HMR_PATCHABLE_ROUTE_FLAGS.map((flag, index)=>` ${flag}: Boolean(routeFlags & ${1 << index}),`).join('\n')}
|
|
9437
|
+
};
|
|
9438
|
+
}
|
|
9439
|
+
|
|
9440
|
+
function applyRouteModuleUpdate(routeId, update, routeEntry, routeModules) {
|
|
9441
|
+
Object.assign(routeEntry, getRouteMetadata(update.routeFlags));
|
|
9442
|
+
const imported = update.getRouteModuleExports();
|
|
9443
|
+
registerReactRouterRouteExports(routeId, imported);
|
|
9444
|
+
const current = routeModules[routeId];
|
|
9445
|
+
const preserveIdentity = key =>
|
|
9446
|
+
imported[key] ? (current && current[key]) || imported[key] : imported[key];
|
|
9447
|
+
routeModules[routeId] = {
|
|
9448
|
+
...imported,
|
|
9449
|
+
default: preserveIdentity('default'),
|
|
9450
|
+
ErrorBoundary: preserveIdentity('ErrorBoundary'),
|
|
9451
|
+
HydrateFallback: preserveIdentity('HydrateFallback'),
|
|
9452
|
+
};
|
|
9453
|
+
}
|
|
9454
|
+
|
|
9455
|
+
function getRouteById(routes, routeId) {
|
|
9456
|
+
for (const route of routes) {
|
|
9457
|
+
if (route.id === routeId) {
|
|
9458
|
+
return route;
|
|
9459
|
+
}
|
|
9460
|
+
if (route.children) {
|
|
9461
|
+
const child = getRouteById(route.children, routeId);
|
|
9462
|
+
if (child) {
|
|
9463
|
+
return child;
|
|
9464
|
+
}
|
|
9465
|
+
}
|
|
9466
|
+
}
|
|
9467
|
+
}
|
|
9468
|
+
|
|
9469
|
+
// Deliberate coupling to React Router's private dev API: patching the live
|
|
9470
|
+
// match objects is the only way to swap route implementations without a
|
|
9471
|
+
// navigation. The typeof guard below degrades to a no-op if RR removes it.
|
|
9472
|
+
function patchCurrentRouteMatches(router, routes) {
|
|
9473
|
+
if (
|
|
9474
|
+
!router.state ||
|
|
9475
|
+
!Array.isArray(router.state.matches) ||
|
|
9476
|
+
typeof router._internalSetStateDoNotUseOrYouWillBreakYourApp !== 'function'
|
|
9477
|
+
) {
|
|
9478
|
+
return;
|
|
9479
|
+
}
|
|
9480
|
+
|
|
9481
|
+
let changed = false;
|
|
9482
|
+
const matches = router.state.matches.map(match => {
|
|
9483
|
+
const route = getRouteById(routes, match.route.id);
|
|
9484
|
+
if (!route || route === match.route) {
|
|
9485
|
+
return match;
|
|
9486
|
+
}
|
|
9487
|
+
changed = true;
|
|
9488
|
+
return { ...match, route };
|
|
9489
|
+
});
|
|
9490
|
+
|
|
9491
|
+
if (changed) {
|
|
9492
|
+
router._internalSetStateDoNotUseOrYouWillBreakYourApp({ matches });
|
|
9493
|
+
}
|
|
9494
|
+
}
|
|
9495
|
+
|
|
9496
|
+
function applyPendingRouteUpdates(router, routeModules, manifest, context) {
|
|
9497
|
+
if (pendingRouteUpdates.size === 0) {
|
|
9498
|
+
return {
|
|
9499
|
+
nextManifest: undefined,
|
|
9500
|
+
shouldRefreshRouteState: false,
|
|
9501
|
+
routesToRevalidate: new Set(),
|
|
9502
|
+
};
|
|
9503
|
+
}
|
|
9504
|
+
|
|
9505
|
+
// Clone only entries mutated before the manifest is committed in flush().
|
|
9506
|
+
const nextManifest = { ...manifest, routes: { ...manifest.routes } };
|
|
9507
|
+
const routesToRevalidate = new Set();
|
|
9508
|
+
let shouldRefreshRouteState = false;
|
|
9509
|
+
for (const { routeId, update } of takePendingRouteUpdates()) {
|
|
9510
|
+
const existingEntry = nextManifest.routes[routeId];
|
|
9511
|
+
if (!existingEntry) continue;
|
|
9512
|
+
|
|
9513
|
+
// Shallow clone is enough: only top-level flags are mutated below.
|
|
9514
|
+
const routeEntry = { ...existingEntry };
|
|
9515
|
+
nextManifest.routes[routeId] = routeEntry;
|
|
9516
|
+
applyRouteModuleUpdate(routeId, update, routeEntry, routeModules);
|
|
9517
|
+
if (
|
|
9518
|
+
routeEntry.hasLoader ||
|
|
9519
|
+
routeEntry.hasClientLoader ||
|
|
9520
|
+
routeEntry.hasClientMiddleware
|
|
9521
|
+
) {
|
|
9522
|
+
routesToRevalidate.add(routeId);
|
|
9523
|
+
}
|
|
9524
|
+
if (
|
|
9525
|
+
existingEntry.hasLoader ||
|
|
9526
|
+
existingEntry.hasClientLoader ||
|
|
9527
|
+
existingEntry.hasClientMiddleware ||
|
|
9528
|
+
routeEntry.hasLoader ||
|
|
9529
|
+
routeEntry.hasClientLoader ||
|
|
9530
|
+
routeEntry.hasClientMiddleware
|
|
9531
|
+
) {
|
|
9532
|
+
shouldRefreshRouteState = true;
|
|
9533
|
+
}
|
|
9534
|
+
}
|
|
9535
|
+
|
|
9536
|
+
if (
|
|
9537
|
+
typeof router.createRoutesForHMR === 'function' &&
|
|
9538
|
+
typeof router._internalSetRoutes === 'function'
|
|
9539
|
+
) {
|
|
9540
|
+
const routes = router.createRoutesForHMR(
|
|
9541
|
+
routesToRevalidate,
|
|
9542
|
+
nextManifest.routes,
|
|
9543
|
+
routeModules,
|
|
9544
|
+
context.ssr,
|
|
9545
|
+
context.isSpaMode
|
|
9546
|
+
);
|
|
9547
|
+
router._internalSetRoutes(routes);
|
|
9548
|
+
patchCurrentRouteMatches(router, routes);
|
|
9549
|
+
}
|
|
9550
|
+
|
|
9551
|
+
return { nextManifest, shouldRefreshRouteState, routesToRevalidate };
|
|
9552
|
+
}
|
|
9553
|
+
|
|
9554
|
+
async function withHdrActive(fn) {
|
|
9555
|
+
try {
|
|
9556
|
+
window.__reactRouterHdrActive = true;
|
|
9557
|
+
await fn();
|
|
9558
|
+
} finally {
|
|
9559
|
+
window.__reactRouterHdrActive = false;
|
|
9560
|
+
}
|
|
9561
|
+
}
|
|
9562
|
+
|
|
9563
|
+
async function revalidateRouter(router) {
|
|
9564
|
+
if (typeof router.revalidate === 'function') {
|
|
9565
|
+
await withHdrActive(() => router.revalidate());
|
|
9566
|
+
return;
|
|
9567
|
+
}
|
|
9568
|
+
if (typeof router.navigate === 'function') {
|
|
9569
|
+
await withHdrActive(() =>
|
|
9570
|
+
router.navigate(getCurrentRouterPath(router), {
|
|
9571
|
+
replace: true,
|
|
9572
|
+
preventScrollReset: true,
|
|
9573
|
+
})
|
|
9574
|
+
);
|
|
9575
|
+
}
|
|
9576
|
+
}
|
|
9577
|
+
|
|
9578
|
+
async function refreshRouteState(router) {
|
|
9579
|
+
if (typeof router.revalidate === 'function') {
|
|
9580
|
+
await withHdrActive(() => router.revalidate());
|
|
9581
|
+
return true;
|
|
9582
|
+
}
|
|
9583
|
+
return false;
|
|
9584
|
+
}
|
|
9585
|
+
|
|
9586
|
+
function performReactRefresh() {
|
|
9587
|
+
if (
|
|
9588
|
+
RefreshRuntime &&
|
|
9589
|
+
typeof RefreshRuntime.performReactRefresh === 'function'
|
|
9590
|
+
) {
|
|
9591
|
+
RefreshRuntime.performReactRefresh();
|
|
9592
|
+
}
|
|
9593
|
+
}
|
|
9594
|
+
|
|
9595
|
+
async function flush() {
|
|
9596
|
+
const router = window.__reactRouterDataRouter;
|
|
9597
|
+
const routeModules = window.__reactRouterRouteModules;
|
|
9598
|
+
const manifest = window.__reactRouterManifest;
|
|
9599
|
+
const context = window.__reactRouterContext;
|
|
9600
|
+
if (!router || !routeModules || !manifest || !context) {
|
|
9601
|
+
return;
|
|
9602
|
+
}
|
|
9603
|
+
|
|
9604
|
+
let shouldRevalidate = pendingRevalidation;
|
|
9605
|
+
pendingRevalidation = false;
|
|
9606
|
+
const { nextManifest, shouldRefreshRouteState, routesToRevalidate } =
|
|
9607
|
+
applyPendingRouteUpdates(router, routeModules, manifest, context);
|
|
9608
|
+
if (nextManifest) {
|
|
9609
|
+
Object.assign(manifest, nextManifest);
|
|
9610
|
+
}
|
|
9611
|
+
// Component-only updates do not need a full loader revalidation.
|
|
9612
|
+
if (
|
|
9613
|
+
shouldRefreshRouteState &&
|
|
9614
|
+
(routesToRevalidate.size > 0 || shouldRevalidate)
|
|
9615
|
+
) {
|
|
9616
|
+
if (await refreshRouteState(router)) {
|
|
9617
|
+
shouldRevalidate = false;
|
|
9618
|
+
}
|
|
9619
|
+
}
|
|
9620
|
+
if (shouldRevalidate) {
|
|
9621
|
+
await revalidateRouter(router);
|
|
9622
|
+
}
|
|
9623
|
+
performReactRefresh();
|
|
9624
|
+
}
|
|
9625
|
+
|
|
9626
|
+
if (typeof window !== 'undefined' && import.meta.webpackHot) {
|
|
9627
|
+
import.meta.webpackHot.accept(
|
|
9628
|
+
${JSON.stringify(hdrRevisionFilePath)},
|
|
9629
|
+
scheduleReactRouterRevalidation
|
|
9630
|
+
);
|
|
9631
|
+
}
|
|
9632
|
+
`)({
|
|
9633
|
+
reactRefreshRuntimePath: devHmrRefreshRuntimePath,
|
|
9634
|
+
hdrRevisionFilePath: external_pathe_join(api.context.rootPath, DEV_HDR_REVISION_RELATIVE_PATH)
|
|
9635
|
+
})
|
|
9636
|
+
} : {}
|
|
9318
9637
|
}).map(([moduleId, contents])=>[
|
|
9319
9638
|
((moduleId)=>{
|
|
9320
9639
|
if (!moduleId.startsWith(VIRTUAL_MODULE_PREFIX)) throw Error(`Virtual module id must start with ${JSON.stringify(VIRTUAL_MODULE_PREFIX)}: ${moduleId}`);
|
|
@@ -9374,7 +9693,6 @@ let shouldParallelizeEnvironmentBuilds = ({ isBuild, spareCoreCount = getDefault
|
|
|
9374
9693
|
assetPrefix: config.output?.assetPrefix || '/'
|
|
9375
9694
|
},
|
|
9376
9695
|
dev: {
|
|
9377
|
-
writeToDisk: !0,
|
|
9378
9696
|
...void 0 === guardedLazyCompilation ? {} : {
|
|
9379
9697
|
lazyCompilation: guardedLazyCompilation
|
|
9380
9698
|
},
|
|
@@ -9530,7 +9848,7 @@ let shouldParallelizeEnvironmentBuilds = ({ isBuild, spareCoreCount = getDefault
|
|
|
9530
9848
|
}), api.modifyEnvironmentConfig(async (config, { name, mergeEnvironmentConfig })=>'web' !== name && 'node' !== name ? config : mergeEnvironmentConfig(config, {
|
|
9531
9849
|
tools: {
|
|
9532
9850
|
rspack: (rspackConfig)=>{
|
|
9533
|
-
|
|
9851
|
+
(pluginOptions.federation && ((rspackConfig)=>{
|
|
9534
9852
|
if (rspackConfig?.plugins?.length) for (let plugin of rspackConfig.plugins){
|
|
9535
9853
|
if (!plugin || 'object' != typeof plugin || 'ModuleFederationPlugin' !== plugin.name) continue;
|
|
9536
9854
|
let pluginOptions = plugin._options ?? plugin.options;
|
|
@@ -9539,7 +9857,8 @@ let shouldParallelizeEnvironmentBuilds = ({ isBuild, spareCoreCount = getDefault
|
|
|
9539
9857
|
asyncStartup: !0
|
|
9540
9858
|
});
|
|
9541
9859
|
}
|
|
9542
|
-
})(rspackConfig), '
|
|
9860
|
+
})(rspackConfig), 'web' === name) && (devHmrEnabled = !isBuild && void 0 !== devHmrRefreshRuntimePath && 'development' === config.mode && config.dev?.hmr !== !1 && readRuleSetSwcRefresh(rspackConfig.module?.rules)) && devHdrSignal?.ensure();
|
|
9861
|
+
if ('node' === name) {
|
|
9543
9862
|
let output = rspackConfig.output;
|
|
9544
9863
|
if (output) {
|
|
9545
9864
|
let library = output.library, libraryOptions = library && 'object' == typeof library && !Array.isArray(library) ? library : {};
|
|
@@ -9649,7 +9968,7 @@ let shouldParallelizeEnvironmentBuilds = ({ isBuild, spareCoreCount = getDefault
|
|
|
9649
9968
|
isBuild || devRuntime.captureWeb(compilation, manifestsByEntryName);
|
|
9650
9969
|
});
|
|
9651
9970
|
}
|
|
9652
|
-
}), (({ api, resolvedServerOutput, performanceProfiler, getLatestServerManifest, getLatestServerManifestByBundleId, routes, pluginOptions, getClientStats, appDirectory, getAssetPrefix, routeChunkOptions, routeTransformExecutor, routeByFilePath, routeChunkConfig, isBuild, splitRouteModules, ssr, isSpaMode, rootRoutePath })=>{
|
|
9971
|
+
}), (({ api, resolvedServerOutput, performanceProfiler, getLatestServerManifest, getLatestServerManifestByBundleId, routes, pluginOptions, getClientStats, appDirectory, getAssetPrefix, routeChunkOptions, routeTransformExecutor, routeByFilePath, routeChunkConfig, isBuild, splitRouteModules, ssr, isSpaMode, rootRoutePath, isDevHmrEnabled = ()=>!1 })=>{
|
|
9653
9972
|
let transformRouteModule = async (args)=>performanceProfiler.record(args.environment?.name, 'route:module', args.resource, async ()=>routeTransformExecutor.run({
|
|
9654
9973
|
kind: 'routeModule',
|
|
9655
9974
|
code: args.code,
|
|
@@ -9660,7 +9979,8 @@ let shouldParallelizeEnvironmentBuilds = ({ isBuild, spareCoreCount = getDefault
|
|
|
9660
9979
|
ssr,
|
|
9661
9980
|
isBuild,
|
|
9662
9981
|
isSpaMode,
|
|
9663
|
-
rootRoutePath
|
|
9982
|
+
rootRoutePath,
|
|
9983
|
+
devHmr: isDevHmrEnabled()
|
|
9664
9984
|
}));
|
|
9665
9985
|
api.processAssets({
|
|
9666
9986
|
stage: 'additional',
|
|
@@ -9690,7 +10010,9 @@ let shouldParallelizeEnvironmentBuilds = ({ isBuild, spareCoreCount = getDefault
|
|
|
9690
10010
|
resourcePath: args.resourcePath,
|
|
9691
10011
|
environmentName: args.environment?.name,
|
|
9692
10012
|
isBuild,
|
|
9693
|
-
routeChunkConfig
|
|
10013
|
+
routeChunkConfig,
|
|
10014
|
+
routeId: routeByFilePath.get(args.resourcePath)?.id,
|
|
10015
|
+
devHmr: isDevHmrEnabled()
|
|
9694
10016
|
}))), api.transform({
|
|
9695
10017
|
resourceQuery: /route-chunk=/,
|
|
9696
10018
|
environments: [
|
|
@@ -9767,7 +10089,8 @@ let shouldParallelizeEnvironmentBuilds = ({ isBuild, spareCoreCount = getDefault
|
|
|
9767
10089
|
splitRouteModules: !!splitRouteModules,
|
|
9768
10090
|
ssr,
|
|
9769
10091
|
isSpaMode,
|
|
9770
|
-
rootRoutePath
|
|
10092
|
+
rootRoutePath,
|
|
10093
|
+
isDevHmrEnabled: ()=>devHmrEnabled
|
|
9771
10094
|
});
|
|
9772
10095
|
}
|
|
9773
10096
|
});
|
|
@@ -6,6 +6,10 @@ export type RouteClientEntryArtifactOptions = {
|
|
|
6
6
|
isBuild: boolean;
|
|
7
7
|
routeChunkCache?: RouteChunkCache;
|
|
8
8
|
routeChunkConfig: RouteChunkConfig;
|
|
9
|
+
/** React Router route id for this route module, when known. */
|
|
10
|
+
routeId?: string;
|
|
11
|
+
/** Emit development HMR/HDR glue into web route client entries. */
|
|
12
|
+
devHmr?: boolean;
|
|
9
13
|
};
|
|
10
14
|
type RouteClientEntryArtifact = {
|
|
11
15
|
code: string;
|
|
@@ -22,12 +26,16 @@ type RouteChunkArtifact = {
|
|
|
22
26
|
code: string;
|
|
23
27
|
map: null;
|
|
24
28
|
};
|
|
25
|
-
export declare const
|
|
29
|
+
export declare const HMR_PATCHABLE_ROUTE_FLAGS: readonly ["hasAction", "hasClientAction", "hasClientLoader", "hasClientMiddleware", "hasErrorBoundary", "hasLoader"];
|
|
30
|
+
export declare const buildRouteHmrFlags: (exportNames: readonly string[]) => number;
|
|
31
|
+
export declare const buildRouteClientEntryCode: ({ exportNames, chunkedExports, isServer, resourcePath, routeId, devHmr, }: {
|
|
26
32
|
exportNames: readonly string[];
|
|
27
33
|
chunkedExports: readonly string[];
|
|
28
34
|
isServer: boolean;
|
|
29
35
|
resourcePath: string;
|
|
36
|
+
routeId?: string;
|
|
37
|
+
devHmr?: boolean;
|
|
30
38
|
}) => string;
|
|
31
|
-
export declare const createRouteClientEntryArtifact: ({ code, resourcePath, environmentName, isBuild, routeChunkCache, routeChunkConfig, }: RouteClientEntryArtifactOptions) => Promise<RouteClientEntryArtifact>;
|
|
39
|
+
export declare const createRouteClientEntryArtifact: ({ code, resourcePath, environmentName, isBuild, routeChunkCache, routeChunkConfig, routeId, devHmr, }: RouteClientEntryArtifactOptions) => Promise<RouteClientEntryArtifact>;
|
|
32
40
|
export declare const createRouteChunkArtifact: ({ code, resource, resourcePath, isBuild, routeChunkCache, routeChunkConfig, }: RouteChunkArtifactOptions) => Promise<RouteChunkArtifact>;
|
|
33
41
|
export {};
|
|
@@ -14,6 +14,8 @@ export type RouteClientEntryTransformTask = BaseRouteTransformTask & {
|
|
|
14
14
|
environmentName?: string;
|
|
15
15
|
isBuild: boolean;
|
|
16
16
|
routeChunkConfig: RouteChunkConfig;
|
|
17
|
+
routeId?: string;
|
|
18
|
+
devHmr?: boolean;
|
|
17
19
|
};
|
|
18
20
|
export type RouteChunkTransformTask = BaseRouteTransformTask & {
|
|
19
21
|
kind: 'routeChunk';
|
|
@@ -38,6 +40,7 @@ export type RouteModuleTransformTask = BaseRouteTransformTask & {
|
|
|
38
40
|
isBuild: boolean;
|
|
39
41
|
isSpaMode: boolean;
|
|
40
42
|
rootRoutePath: string | null;
|
|
43
|
+
devHmr?: boolean;
|
|
41
44
|
};
|
|
42
45
|
export type RouteTransformTask = RouteClientEntryTransformTask | RouteChunkTransformTask | SplitRouteExportsTransformTask | ClientOnlyStubTransformTask | RouteModuleTransformTask;
|
|
43
46
|
export type RouteTransformTaskOptions = {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rsbuild-plugin-react-router",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.1",
|
|
4
4
|
"description": "React Router plugin for Rsbuild",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -82,7 +82,7 @@
|
|
|
82
82
|
"es-module-lexer": "1.7.0",
|
|
83
83
|
"kill-port": "^2.0.1",
|
|
84
84
|
"pkg-pr-new": "^0.0.75",
|
|
85
|
-
"playwright": "1.
|
|
85
|
+
"playwright": "^1.61.1",
|
|
86
86
|
"prettier": "3.8.1",
|
|
87
87
|
"react": "^19.2.4",
|
|
88
88
|
"react-dom": "^19.2.4",
|
|
@@ -92,8 +92,14 @@
|
|
|
92
92
|
"@rsbuild/config": "1.0.1"
|
|
93
93
|
},
|
|
94
94
|
"peerDependencies": {
|
|
95
|
+
"@react-router/dev": "^7.13.0 || ^8.0.0",
|
|
95
96
|
"@rsbuild/core": "^2.0.0"
|
|
96
97
|
},
|
|
98
|
+
"peerDependenciesMeta": {
|
|
99
|
+
"@react-router/dev": {
|
|
100
|
+
"optional": true
|
|
101
|
+
}
|
|
102
|
+
},
|
|
97
103
|
"publishConfig": {
|
|
98
104
|
"access": "public",
|
|
99
105
|
"provenance": false,
|
|
@@ -39,6 +39,7 @@ type RegisterBuildOutputTransformsOptions = {
|
|
|
39
39
|
ssr: boolean;
|
|
40
40
|
isSpaMode: boolean;
|
|
41
41
|
rootRoutePath: string;
|
|
42
|
+
isDevHmrEnabled?: () => boolean;
|
|
42
43
|
};
|
|
43
44
|
|
|
44
45
|
export const registerBuildOutputTransforms = ({
|
|
@@ -61,6 +62,7 @@ export const registerBuildOutputTransforms = ({
|
|
|
61
62
|
ssr,
|
|
62
63
|
isSpaMode,
|
|
63
64
|
rootRoutePath,
|
|
65
|
+
isDevHmrEnabled = () => false,
|
|
64
66
|
}: RegisterBuildOutputTransformsOptions): void => {
|
|
65
67
|
const transformRouteModule = async (args: Parameters<TransformHandler>[0]) =>
|
|
66
68
|
performanceProfiler.record(
|
|
@@ -81,6 +83,7 @@ export const registerBuildOutputTransforms = ({
|
|
|
81
83
|
isBuild,
|
|
82
84
|
isSpaMode,
|
|
83
85
|
rootRoutePath,
|
|
86
|
+
devHmr: isDevHmrEnabled(),
|
|
84
87
|
})
|
|
85
88
|
);
|
|
86
89
|
|
|
@@ -158,6 +161,8 @@ export const registerBuildOutputTransforms = ({
|
|
|
158
161
|
environmentName: args.environment?.name,
|
|
159
162
|
isBuild,
|
|
160
163
|
routeChunkConfig,
|
|
164
|
+
routeId: routeByFilePath.get(args.resourcePath)?.id,
|
|
165
|
+
devHmr: isDevHmrEnabled(),
|
|
161
166
|
})
|
|
162
167
|
)
|
|
163
168
|
);
|