rsbuild-plugin-react-router 0.3.1 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +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 -29
- package/dist/index.d.ts +1 -0
- package/dist/index.js +346 -22
- 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 -0
- 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}`);
|
|
@@ -9530,7 +9849,7 @@ let shouldParallelizeEnvironmentBuilds = ({ isBuild, spareCoreCount = getDefault
|
|
|
9530
9849
|
}), api.modifyEnvironmentConfig(async (config, { name, mergeEnvironmentConfig })=>'web' !== name && 'node' !== name ? config : mergeEnvironmentConfig(config, {
|
|
9531
9850
|
tools: {
|
|
9532
9851
|
rspack: (rspackConfig)=>{
|
|
9533
|
-
|
|
9852
|
+
(pluginOptions.federation && ((rspackConfig)=>{
|
|
9534
9853
|
if (rspackConfig?.plugins?.length) for (let plugin of rspackConfig.plugins){
|
|
9535
9854
|
if (!plugin || 'object' != typeof plugin || 'ModuleFederationPlugin' !== plugin.name) continue;
|
|
9536
9855
|
let pluginOptions = plugin._options ?? plugin.options;
|
|
@@ -9539,7 +9858,8 @@ let shouldParallelizeEnvironmentBuilds = ({ isBuild, spareCoreCount = getDefault
|
|
|
9539
9858
|
asyncStartup: !0
|
|
9540
9859
|
});
|
|
9541
9860
|
}
|
|
9542
|
-
})(rspackConfig), '
|
|
9861
|
+
})(rspackConfig), 'web' === name) && (devHmrEnabled = !isBuild && void 0 !== devHmrRefreshRuntimePath && 'development' === config.mode && config.dev?.hmr !== !1 && readRuleSetSwcRefresh(rspackConfig.module?.rules)) && devHdrSignal?.ensure();
|
|
9862
|
+
if ('node' === name) {
|
|
9543
9863
|
let output = rspackConfig.output;
|
|
9544
9864
|
if (output) {
|
|
9545
9865
|
let library = output.library, libraryOptions = library && 'object' == typeof library && !Array.isArray(library) ? library : {};
|
|
@@ -9649,7 +9969,7 @@ let shouldParallelizeEnvironmentBuilds = ({ isBuild, spareCoreCount = getDefault
|
|
|
9649
9969
|
isBuild || devRuntime.captureWeb(compilation, manifestsByEntryName);
|
|
9650
9970
|
});
|
|
9651
9971
|
}
|
|
9652
|
-
}), (({ api, resolvedServerOutput, performanceProfiler, getLatestServerManifest, getLatestServerManifestByBundleId, routes, pluginOptions, getClientStats, appDirectory, getAssetPrefix, routeChunkOptions, routeTransformExecutor, routeByFilePath, routeChunkConfig, isBuild, splitRouteModules, ssr, isSpaMode, rootRoutePath })=>{
|
|
9972
|
+
}), (({ api, resolvedServerOutput, performanceProfiler, getLatestServerManifest, getLatestServerManifestByBundleId, routes, pluginOptions, getClientStats, appDirectory, getAssetPrefix, routeChunkOptions, routeTransformExecutor, routeByFilePath, routeChunkConfig, isBuild, splitRouteModules, ssr, isSpaMode, rootRoutePath, isDevHmrEnabled = ()=>!1 })=>{
|
|
9653
9973
|
let transformRouteModule = async (args)=>performanceProfiler.record(args.environment?.name, 'route:module', args.resource, async ()=>routeTransformExecutor.run({
|
|
9654
9974
|
kind: 'routeModule',
|
|
9655
9975
|
code: args.code,
|
|
@@ -9660,7 +9980,8 @@ let shouldParallelizeEnvironmentBuilds = ({ isBuild, spareCoreCount = getDefault
|
|
|
9660
9980
|
ssr,
|
|
9661
9981
|
isBuild,
|
|
9662
9982
|
isSpaMode,
|
|
9663
|
-
rootRoutePath
|
|
9983
|
+
rootRoutePath,
|
|
9984
|
+
devHmr: isDevHmrEnabled()
|
|
9664
9985
|
}));
|
|
9665
9986
|
api.processAssets({
|
|
9666
9987
|
stage: 'additional',
|
|
@@ -9690,7 +10011,9 @@ let shouldParallelizeEnvironmentBuilds = ({ isBuild, spareCoreCount = getDefault
|
|
|
9690
10011
|
resourcePath: args.resourcePath,
|
|
9691
10012
|
environmentName: args.environment?.name,
|
|
9692
10013
|
isBuild,
|
|
9693
|
-
routeChunkConfig
|
|
10014
|
+
routeChunkConfig,
|
|
10015
|
+
routeId: routeByFilePath.get(args.resourcePath)?.id,
|
|
10016
|
+
devHmr: isDevHmrEnabled()
|
|
9694
10017
|
}))), api.transform({
|
|
9695
10018
|
resourceQuery: /route-chunk=/,
|
|
9696
10019
|
environments: [
|
|
@@ -9767,7 +10090,8 @@ let shouldParallelizeEnvironmentBuilds = ({ isBuild, spareCoreCount = getDefault
|
|
|
9767
10090
|
splitRouteModules: !!splitRouteModules,
|
|
9768
10091
|
ssr,
|
|
9769
10092
|
isSpaMode,
|
|
9770
|
-
rootRoutePath
|
|
10093
|
+
rootRoutePath,
|
|
10094
|
+
isDevHmrEnabled: ()=>devHmrEnabled
|
|
9771
10095
|
});
|
|
9772
10096
|
}
|
|
9773
10097
|
});
|
|
@@ -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.0",
|
|
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
|
);
|