rsbuild-plugin-react-router 0.6.6 → 0.7.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 +22 -0
- package/dist/511.js +10 -3
- package/dist/environment-output.d.ts +16 -2
- package/dist/index.cjs +100 -71
- package/dist/index.js +85 -64
- package/dist/manifest.d.ts +2 -0
- package/dist/mode-plan.d.ts +1 -1
- package/dist/plugin-utils.d.ts +19 -13
- package/dist/rsc-virtual-modules.d.ts +1 -3
- package/package.json +1 -1
- package/src/environment-output.ts +50 -1
- package/src/index.ts +43 -61
- package/src/manifest.ts +26 -20
- package/src/mode-plan.ts +6 -12
- package/src/modify-browser-manifest.ts +4 -4
- package/src/plugin-utils.ts +37 -23
- package/src/rsc-route-transforms.ts +8 -12
- package/src/rsc-virtual-modules.ts +51 -8
package/dist/index.js
CHANGED
|
@@ -7356,7 +7356,7 @@ let createReactRouterManifestOptions = ({ routeChunks, routeModuleAnalysis })=>{
|
|
|
7356
7356
|
routeModuleAnalysis
|
|
7357
7357
|
} : {}
|
|
7358
7358
|
};
|
|
7359
|
-
}, collectManifestFilesByName = (items, names, getFiles)=>{
|
|
7359
|
+
}, isManifestJsAsset = (asset)=>/(?<!\.hot-update)\.[cm]?js(?:\?.*)?$/.test(asset), isManifestCssAsset = (asset)=>/\.css(?:\?.*)?$/.test(asset), collectManifestFilesByName = (items, names, getFiles)=>{
|
|
7360
7360
|
let filesByName = {};
|
|
7361
7361
|
if (!names) {
|
|
7362
7362
|
for (let [name, item] of items)null != item && (filesByName[name] = getFiles(name, item));
|
|
@@ -7372,15 +7372,7 @@ let createReactRouterManifestOptions = ({ routeChunks, routeModuleAnalysis })=>{
|
|
|
7372
7372
|
return filesByName;
|
|
7373
7373
|
}, createReactRouterManifestStats = (compilation, chunkNames)=>{
|
|
7374
7374
|
if (!compilation) return;
|
|
7375
|
-
let assetsByChunkName = collectManifestFilesByName(compilation.namedChunks, chunkNames, (
|
|
7376
|
-
var files;
|
|
7377
|
-
let ownChunkAsset, ownFileIndex;
|
|
7378
|
-
return files = Array.from(chunk.files ?? []), ownChunkAsset = `${chunkName}.js`, (ownFileIndex = files.findIndex((file)=>file.endsWith(ownChunkAsset))) <= 0 ? files : [
|
|
7379
|
-
files[ownFileIndex],
|
|
7380
|
-
...files.slice(0, ownFileIndex),
|
|
7381
|
-
...files.slice(ownFileIndex + 1)
|
|
7382
|
-
];
|
|
7383
|
-
}), entrypointFilesByName = compilation.entrypoints ? collectManifestFilesByName(compilation.entrypoints, chunkNames, (_name, entrypoint)=>Array.from(entrypoint.getFiles?.() ?? [])) : {};
|
|
7375
|
+
let assetsByChunkName = collectManifestFilesByName(compilation.namedChunks, chunkNames, (_chunkName, chunk)=>Array.from(chunk.files ?? [])), entrypointFilesByName = compilation.entrypoints ? collectManifestFilesByName(compilation.entrypoints, chunkNames, (_name, entrypoint)=>Array.from(entrypoint.getFiles?.() ?? [])) : {};
|
|
7384
7376
|
return Object.keys(entrypointFilesByName).length > 0 ? {
|
|
7385
7377
|
assetsByChunkName,
|
|
7386
7378
|
entrypointFilesByName
|
|
@@ -7416,9 +7408,9 @@ async function generateReactRouterManifestForDev(routes, _options, clientStats,
|
|
|
7416
7408
|
return chunkAssetsByName.set(chunkName, result), result;
|
|
7417
7409
|
}
|
|
7418
7410
|
let cssAssets = new Set(), jsAssets = new Set();
|
|
7419
|
-
for (let asset of assets)asset
|
|
7420
|
-
for (let asset of clientStats?.entrypointFilesByName?.[chunkName] ?? [])asset
|
|
7421
|
-
0 === jsAssets.size
|
|
7411
|
+
for (let asset of assets)isManifestCssAsset(asset) ? cssAssets.add(asset) : isManifestJsAsset(asset) && jsAssets.add(asset);
|
|
7412
|
+
for (let asset of clientStats?.entrypointFilesByName?.[chunkName] ?? [])isManifestCssAsset(asset) ? cssAssets.add(asset) : isBuild && isManifestJsAsset(asset) && jsAssets.add(asset);
|
|
7413
|
+
if (0 === jsAssets.size) throw Error(`[react-router] Chunk "${chunkName}" emitted no JavaScript asset the browser manifest can reference (files: ${assets.join(', ') || 'none'}). Check the web \`output.filename.js\` scheme.`);
|
|
7422
7414
|
let result = {
|
|
7423
7415
|
js: [
|
|
7424
7416
|
...jsAssets
|
|
@@ -7906,7 +7898,7 @@ let redirectStatusCodes = new Set([
|
|
|
7906
7898
|
}
|
|
7907
7899
|
};
|
|
7908
7900
|
}, BROWSER_MANIFEST_ASSET = 'static/js/virtual/react-router/browser-manifest.js', ABSOLUTE_URL_RE = /^[a-zA-Z][a-zA-Z\d+\-.]*:/, addIntegrity = (sri, assetPrefix, assetName, integrity)=>{
|
|
7909
|
-
'string' == typeof assetName && assetName
|
|
7901
|
+
'string' == typeof assetName && isManifestJsAsset(assetName) && 'string' == typeof integrity && (sri[ABSOLUTE_URL_RE.test(assetName) || assetName.startsWith('//') || assetName.startsWith('/') ? assetName : combineURLs(assetPrefix, assetName)] = integrity);
|
|
7910
7902
|
}, computeSubresourceIntegrity = (source)=>{
|
|
7911
7903
|
if (source) return `sha384-${createHash('sha384').update(source.source()).digest('base64')}`;
|
|
7912
7904
|
};
|
|
@@ -8596,12 +8588,14 @@ export function EnsureClientRouteModuleForHMR___() { return ___EnsureClientRoute
|
|
|
8596
8588
|
}, createServerRouteEntry = async (options)=>{
|
|
8597
8589
|
let plan = await createRscRouteExportPlan(options);
|
|
8598
8590
|
validateRscRouteExportPlan(plan, options);
|
|
8599
|
-
let lines = [], needsReactImport = !1, needsEnsureHmrImport = !1, needsStyleEntryImport = !1,
|
|
8591
|
+
let lines = [], needsReactImport = !1, needsEnsureHmrImport = !1, needsStyleEntryImport = !1, pushStylesheetLinks = (entryCssFilesExpression)=>{
|
|
8592
|
+
lines.push(` ...(${entryCssFilesExpression} ?? []).map(href =>`), lines.push(' React.createElement("link", { key: href, rel: "stylesheet", href: href, precedence: "default" })),');
|
|
8593
|
+
}, streamsClientRouteCss = !plan.exportNames.some(isServerComponentExport) && plan.exportNames.includes('default') && programHasSideEffectStyleImports(getProgram(yuku_parse(options.code, {
|
|
8600
8594
|
sourceType: 'module'
|
|
8601
8595
|
})));
|
|
8602
8596
|
for (let exportName of plan.exportNames){
|
|
8603
8597
|
if (streamsClientRouteCss && 'default' === exportName) {
|
|
8604
|
-
needsReactImport = !0, needsStyleEntryImport = !0, lines.push(`import RscClientRouteDefault___ from ${JSON.stringify(plan.clientTargetFor('default'))};`), lines.push('export default function RscClientRouteWithStyles___(props) {'), lines.push(' return React.createElement(React.Fragment, null,'),
|
|
8598
|
+
needsReactImport = !0, needsStyleEntryImport = !0, lines.push(`import RscClientRouteDefault___ from ${JSON.stringify(plan.clientTargetFor('default'))};`), lines.push('export default function RscClientRouteWithStyles___(props) {'), lines.push(' return React.createElement(React.Fragment, null,'), pushStylesheetLinks(`${RSC_ROUTE_STYLE_ENTRY_EXPORT}.entryCssFiles`), lines.push(' React.createElement(RscClientRouteDefault___, props),'), lines.push(' );'), lines.push('}');
|
|
8605
8599
|
continue;
|
|
8606
8600
|
}
|
|
8607
8601
|
if (isClientRouteExport(exportName)) {
|
|
@@ -8609,7 +8603,7 @@ export function EnsureClientRouteModuleForHMR___() { return ___EnsureClientRoute
|
|
|
8609
8603
|
continue;
|
|
8610
8604
|
}
|
|
8611
8605
|
if (isServerComponentExport(exportName)) {
|
|
8612
|
-
needsReactImport = !0, needsEnsureHmrImport = !0, lines.push(`import { ${exportName} as ${exportName}WithoutClientChunk } from ${JSON.stringify(plan.serverTarget)};`), lines.push(`export function ${exportName}(props) {`), lines.push(' return React.createElement(React.Fragment, null,'),
|
|
8606
|
+
needsReactImport = !0, needsEnsureHmrImport = !0, lines.push(`import { ${exportName} as ${exportName}WithoutClientChunk } from ${JSON.stringify(plan.serverTarget)};`), lines.push(`export function ${exportName}(props) {`), lines.push(' return React.createElement(React.Fragment, null,'), pushStylesheetLinks(`${exportName}WithoutClientChunk.entryCssFiles`), lines.push(' React.createElement(EnsureClientRouteModuleForHMR___, null),'), lines.push(` React.createElement(${exportName}WithoutClientChunk, props),`), lines.push(' );'), lines.push('}');
|
|
8613
8607
|
continue;
|
|
8614
8608
|
}
|
|
8615
8609
|
lines.push(createReexport(exportName, plan.serverTarget));
|
|
@@ -8803,6 +8797,7 @@ export function EnsureClientRouteModuleForHMR___() { return ___EnsureClientRoute
|
|
|
8803
8797
|
'allowed-action-origins',
|
|
8804
8798
|
'client-version',
|
|
8805
8799
|
'react-router-serve-config',
|
|
8800
|
+
'manifest-prefix',
|
|
8806
8801
|
"bootstrap-scripts",
|
|
8807
8802
|
'server-manifest'
|
|
8808
8803
|
], setupReactRouterRscPlugin = async ({ api, entryRscPath, entrySsrPath, pluginName, rsc })=>{
|
|
@@ -9528,8 +9523,8 @@ export function EnsureClientRouteModuleForHMR___() { return ___EnsureClientRoute
|
|
|
9528
9523
|
layer: RSC_LAYERS.rsc
|
|
9529
9524
|
}
|
|
9530
9525
|
},
|
|
9531
|
-
createVirtualModules: (publicPath
|
|
9532
|
-
let rscAssetsBuildDirectory = relative(external_pathe_resolve(buildDirectory, 'server'), outputClientPath),
|
|
9526
|
+
createVirtualModules: (publicPath)=>(({ allowedActionOrigins, appDirectory, basename, buildDirectory, isBuild, outputClientPath, publicPath, routeDiscovery, routes, ssr })=>{
|
|
9527
|
+
let rscAssetsBuildDirectory = relative(external_pathe_resolve(buildDirectory, 'server'), outputClientPath), serverPublicPath = normalizeAssetPrefix(publicPath);
|
|
9533
9528
|
return {
|
|
9534
9529
|
'virtual/react-router/unstable_rsc/routes': (({ appDirectory, routes })=>{
|
|
9535
9530
|
let componentResolutionCode = RSC_ROUTE_COMPONENT_EXPORTS.map(({ routeProperty, clientExport, serverExport })=>{
|
|
@@ -9625,9 +9620,33 @@ export default [`;
|
|
|
9625
9620
|
assetsBuildDirectory: rscAssetsBuildDirectory,
|
|
9626
9621
|
publicPath
|
|
9627
9622
|
}),
|
|
9628
|
-
|
|
9629
|
-
|
|
9630
|
-
|
|
9623
|
+
'virtual/react-router/unstable_rsc/manifest-prefix': `const manifest = __webpack_require__.rscM;
|
|
9624
|
+
const serverPrefix = ${JSON.stringify(serverPublicPath)};
|
|
9625
|
+
const appliedPrefix = manifest?.moduleLoading?.prefix;
|
|
9626
|
+
if (appliedPrefix && appliedPrefix !== serverPrefix) {
|
|
9627
|
+
const rewrite = url =>
|
|
9628
|
+
typeof url === "string" && url.startsWith(appliedPrefix)
|
|
9629
|
+
? serverPrefix + url.slice(appliedPrefix.length)
|
|
9630
|
+
: url;
|
|
9631
|
+
const rewriteAll = list => {
|
|
9632
|
+
if (Array.isArray(list)) for (let i = 0; i < list.length; i++) list[i] = rewrite(list[i]);
|
|
9633
|
+
};
|
|
9634
|
+
manifest.moduleLoading.prefix = serverPrefix;
|
|
9635
|
+
rewriteAll(manifest.entryJsFiles);
|
|
9636
|
+
for (const files of Object.values(manifest.entryCssFiles ?? {})) rewriteAll(files);
|
|
9637
|
+
for (const reference of Object.values(manifest.clientManifest ?? {})) rewriteAll(reference.cssFiles);
|
|
9638
|
+
}
|
|
9639
|
+
export const rscManifest = manifest;
|
|
9640
|
+
`,
|
|
9641
|
+
"virtual/react-router/unstable_rsc/bootstrap-scripts": `import { rscManifest } from "virtual/react-router/unstable_rsc/manifest-prefix";
|
|
9642
|
+
const entryJsFiles = rscManifest?.entryJsFiles;
|
|
9643
|
+
if (!entryJsFiles?.length) {
|
|
9644
|
+
throw new Error(
|
|
9645
|
+
"[rsbuild-plugin-react-router] The rspack RSC manifest lists no browser entry script (entryJsFiles is empty), so the server cannot render bootstrap scripts. Rspack only records entry files whose name ends in \\".js\\"; web output.filename.js values with a query (for example \\"[name].js?v=[contenthash:8]\\") or another extension are not supported in RSC mode."
|
|
9646
|
+
);
|
|
9647
|
+
}
|
|
9648
|
+
export default entryJsFiles;
|
|
9649
|
+
`,
|
|
9631
9650
|
'virtual/react-router/unstable_rsc/server-manifest': `export default function getServerManifest() {
|
|
9632
9651
|
return __webpack_require__.rscM?.serverManifest;
|
|
9633
9652
|
}
|
|
@@ -9667,7 +9686,6 @@ export {
|
|
|
9667
9686
|
basename,
|
|
9668
9687
|
buildDirectory,
|
|
9669
9688
|
isBuild,
|
|
9670
|
-
jsDistPath,
|
|
9671
9689
|
outputClientPath,
|
|
9672
9690
|
publicPath,
|
|
9673
9691
|
routeDiscovery,
|
|
@@ -9849,7 +9867,7 @@ export {
|
|
|
9849
9867
|
defaultEntryName,
|
|
9850
9868
|
serverBundleEntries: artifacts.serverBundleEntries
|
|
9851
9869
|
}),
|
|
9852
|
-
createVirtualModules: (publicPath
|
|
9870
|
+
createVirtualModules: (publicPath)=>(({ allowedActionOrigins, appDirectory, assetsBuildDirectory, basename, devHmrRuntimeModule, entryServerPath, future, isSpaMode, prerenderPaths, publicPath, routeDiscovery, routes, routesByServerBundleId, ssr })=>{
|
|
9853
9871
|
let serverBuildOptions = {
|
|
9854
9872
|
entryServerPath,
|
|
9855
9873
|
assetsBuildDirectory,
|
|
@@ -9938,10 +9956,7 @@ export {
|
|
|
9938
9956
|
library: {
|
|
9939
9957
|
type: 'module'
|
|
9940
9958
|
},
|
|
9941
|
-
module: !0
|
|
9942
|
-
...isBuild ? {
|
|
9943
|
-
chunkFilename: 'static/js/async/[id]-[contenthash:16].js'
|
|
9944
|
-
} : {}
|
|
9959
|
+
module: !0
|
|
9945
9960
|
},
|
|
9946
9961
|
webOptimization: {
|
|
9947
9962
|
avoidEntryIife: !0,
|
|
@@ -10105,11 +10120,14 @@ export {
|
|
|
10105
10120
|
(isSourceMapEnabled(sourceMapSetting) || !0 === devtoolSetting || ('string' == typeof devtoolSetting ? devtoolSetting.includes('source-map') : null !== devtoolSetting && 'object' == typeof devtoolSetting)) && warn("\n WARNING: Source maps are enabled in production\n This makes your server code publicly visible in the browser.\n This is highly discouraged! If you insist, ensure that you are using\n environment variables for secrets and not hard-coding them in your source code.\n");
|
|
10106
10121
|
}(api.getNormalizedConfig(), (msg)=>api.logger.warn(msg), 'web');
|
|
10107
10122
|
}), api.onBeforeCreateCompiler(()=>{
|
|
10108
|
-
let
|
|
10123
|
+
let root = api.getNormalizedConfig(), web = root.environments.web;
|
|
10109
10124
|
assetPrefix = resolveEffectiveAssetPrefix({
|
|
10110
|
-
dev:
|
|
10111
|
-
output:
|
|
10125
|
+
dev: web?.dev,
|
|
10126
|
+
output: web?.output,
|
|
10112
10127
|
isBuild: 'build' === api.context.action
|
|
10128
|
+
}, {
|
|
10129
|
+
dev: root.dev,
|
|
10130
|
+
output: root.output
|
|
10113
10131
|
});
|
|
10114
10132
|
});
|
|
10115
10133
|
let configPath = findEntryFile(external_pathe_resolve('react-router.config')), configExists = existsSync(configPath), configWatchPaths = configExists ? configPath : JS_EXTENSIONS.map((extension)=>external_pathe_resolve(`react-router.config${extension}`)), reactRouterUserConfig1 = {};
|
|
@@ -10934,16 +10952,19 @@ if (typeof window !== 'undefined' && import.meta.webpackHot) {
|
|
|
10934
10952
|
basename
|
|
10935
10953
|
})))), api.modifyRsbuildConfig(async (config, { mergeRsbuildConfig })=>{
|
|
10936
10954
|
var existing;
|
|
10937
|
-
let publicPath,
|
|
10938
|
-
|
|
10939
|
-
|
|
10955
|
+
let publicPath, webConfig = config.environments?.web, webJsFilename = webConfig?.output?.filename?.js ?? config.output?.filename?.js;
|
|
10956
|
+
if (isRscMode && 'string' == typeof webJsFilename && !/\.js$/.test(webJsFilename)) throw Error(`[${PLUGIN_NAME}] RSC mode requires web \`output.filename.js\` to end in ".js" (got ${JSON.stringify(webJsFilename)}): rspack's RSC manifest omits entry files with a query or another extension, so the server could not render bootstrap scripts.`);
|
|
10957
|
+
let vmodPlugin = (publicPath = resolveEffectiveAssetPrefix({
|
|
10958
|
+
dev: webConfig?.dev,
|
|
10959
|
+
output: webConfig?.output,
|
|
10940
10960
|
isBuild
|
|
10941
|
-
}
|
|
10961
|
+
}, {
|
|
10962
|
+
dev: config.dev,
|
|
10963
|
+
output: config.output
|
|
10964
|
+
}), new rspack.experiments.VirtualModulesPlugin(Object.fromEntries(Object.entries(modePlan.createVirtualModules(publicPath)).map(([moduleId, contents])=>[
|
|
10942
10965
|
getVirtualModuleFilePath(moduleId),
|
|
10943
10966
|
contents
|
|
10944
|
-
])))),
|
|
10945
|
-
'module' === resolvedServerOutput ? nodeChunkLoading = 'import' : useAsyncNodeChunkLoading && (nodeChunkLoading = 'async-node');
|
|
10946
|
-
let guardedLazyCompilation = (({ lazyCompilation, entryClientPath, prewarmReactRouterModules = !1 })=>{
|
|
10967
|
+
])))), guardedLazyCompilation = (({ lazyCompilation, entryClientPath, prewarmReactRouterModules = !1 })=>{
|
|
10947
10968
|
if (void 0 === lazyCompilation || !1 === lazyCompilation) return lazyCompilation;
|
|
10948
10969
|
let options = !0 === lazyCompilation ? {
|
|
10949
10970
|
entries: !0,
|
|
@@ -11035,9 +11056,6 @@ if (typeof window !== 'undefined' && import.meta.webpackHot) {
|
|
|
11035
11056
|
}
|
|
11036
11057
|
},
|
|
11037
11058
|
output: {
|
|
11038
|
-
filename: {
|
|
11039
|
-
js: '[name].js'
|
|
11040
|
-
},
|
|
11041
11059
|
distPath: {
|
|
11042
11060
|
root: outputClientPath
|
|
11043
11061
|
}
|
|
@@ -11056,13 +11074,6 @@ if (typeof window !== 'undefined' && import.meta.webpackHot) {
|
|
|
11056
11074
|
]
|
|
11057
11075
|
},
|
|
11058
11076
|
externalsType: modePlan.webExternalsType,
|
|
11059
|
-
output: {
|
|
11060
|
-
...modePlan.webOutput,
|
|
11061
|
-
publicPath: assetPrefix,
|
|
11062
|
-
...options.federation ? {
|
|
11063
|
-
chunkLoading: 'import'
|
|
11064
|
-
} : {}
|
|
11065
|
-
},
|
|
11066
11077
|
optimization: modePlan.webOptimization
|
|
11067
11078
|
}
|
|
11068
11079
|
}
|
|
@@ -11100,24 +11111,33 @@ if (typeof window !== 'undefined' && import.meta.webpackHot) {
|
|
|
11100
11111
|
},
|
|
11101
11112
|
externals: modePlan.nodeExternals,
|
|
11102
11113
|
...modePlan.nodeDependencies,
|
|
11103
|
-
externalsType: resolvedServerOutput
|
|
11104
|
-
output: {
|
|
11105
|
-
chunkFormat: resolvedServerOutput,
|
|
11106
|
-
chunkLoading: nodeChunkLoading,
|
|
11107
|
-
devtoolModuleFilenameTemplate: '[absolute-resource-path]',
|
|
11108
|
-
devtoolFallbackModuleFilenameTemplate: '[absolute-resource-path]?[hash]',
|
|
11109
|
-
workerChunkLoading: nodeChunkLoading,
|
|
11110
|
-
wasmLoading: 'fetch',
|
|
11111
|
-
module: 'module' === resolvedServerOutput,
|
|
11112
|
-
chunkFilename: 'static/js/async/[name].js'
|
|
11113
|
-
}
|
|
11114
|
+
externalsType: resolvedServerOutput
|
|
11114
11115
|
}
|
|
11115
11116
|
}
|
|
11116
11117
|
}
|
|
11117
11118
|
}
|
|
11118
11119
|
});
|
|
11119
|
-
}), (({ api, federation, resolvedServerOutput })=>{
|
|
11120
|
-
|
|
11120
|
+
}), (({ api, federation, resolvedServerOutput, webOutput })=>{
|
|
11121
|
+
let nodeChunkLoading = 'module' === resolvedServerOutput ? 'import' : federation ? 'async-node' : 'require';
|
|
11122
|
+
api.modifyRspackConfig((rspackConfig, { environment, mergeConfig })=>'web' === environment.name ? mergeConfig(rspackConfig, {
|
|
11123
|
+
output: {
|
|
11124
|
+
...webOutput,
|
|
11125
|
+
...federation ? {
|
|
11126
|
+
chunkLoading: 'import'
|
|
11127
|
+
} : {}
|
|
11128
|
+
}
|
|
11129
|
+
}) : 'node' === environment.name ? mergeConfig(rspackConfig, {
|
|
11130
|
+
output: {
|
|
11131
|
+
chunkFormat: resolvedServerOutput,
|
|
11132
|
+
chunkLoading: nodeChunkLoading,
|
|
11133
|
+
devtoolModuleFilenameTemplate: '[absolute-resource-path]',
|
|
11134
|
+
devtoolFallbackModuleFilenameTemplate: '[absolute-resource-path]?[hash]',
|
|
11135
|
+
workerChunkLoading: nodeChunkLoading,
|
|
11136
|
+
wasmLoading: 'fetch',
|
|
11137
|
+
module: 'module' === resolvedServerOutput,
|
|
11138
|
+
chunkFilename: 'static/js/async/[name].js'
|
|
11139
|
+
}
|
|
11140
|
+
}) : rspackConfig), api.modifyEnvironmentConfig(async (config, { name, mergeEnvironmentConfig })=>'web' !== name && 'node' !== name ? config : mergeEnvironmentConfig(config, {
|
|
11121
11141
|
tools: {
|
|
11122
11142
|
rspack: (rspackConfig)=>{
|
|
11123
11143
|
if (federation && ((rspackConfig)=>{
|
|
@@ -11149,7 +11169,8 @@ if (typeof window !== 'undefined' && import.meta.webpackHot) {
|
|
|
11149
11169
|
})({
|
|
11150
11170
|
api,
|
|
11151
11171
|
federation: pluginOptions.federation,
|
|
11152
|
-
resolvedServerOutput
|
|
11172
|
+
resolvedServerOutput,
|
|
11173
|
+
webOutput: modePlan.webOutput
|
|
11153
11174
|
}), 'classic' === modePlan.kind && useRouteModuleTransformLoader && api.modifyEnvironmentConfig(async (config, { name, mergeEnvironmentConfig })=>'web' !== name && 'node' !== name ? config : mergeEnvironmentConfig(config, {
|
|
11154
11175
|
tools: {
|
|
11155
11176
|
rspack: (rspackConfig)=>{
|
|
@@ -11286,7 +11307,7 @@ if (typeof window !== 'undefined' && import.meta.webpackHot) {
|
|
|
11286
11307
|
compilation.updateAsset(BROWSER_MANIFEST_ASSET, new sources.RawSource(newSource));
|
|
11287
11308
|
}
|
|
11288
11309
|
if (isBuild) {
|
|
11289
|
-
let entryAssets = stats?.assetsByChunkName?.['entry.client'], entryJsAssets = entryAssets?.filter(
|
|
11310
|
+
let entryAssets = stats?.assetsByChunkName?.['entry.client'], entryJsAssets = entryAssets?.filter(isManifestJsAsset) || [], manifestPath = getReactRouterManifestPath({
|
|
11290
11311
|
version: manifest.version,
|
|
11291
11312
|
isBuild: !0,
|
|
11292
11313
|
entryModulePath: entryJsAssets[0]
|
|
@@ -11298,7 +11319,7 @@ if (typeof window !== 'undefined' && import.meta.webpackHot) {
|
|
|
11298
11319
|
let sri = withSri ? ((stats, compilation, assetPrefix = '/')=>{
|
|
11299
11320
|
let sri = {};
|
|
11300
11321
|
for (let asset of stats?.assets ?? [])addIntegrity(sri, assetPrefix, asset.name, asset.integrity);
|
|
11301
|
-
if ('function' == typeof compilation?.getAssets) for (let asset of compilation.getAssets())asset.name
|
|
11322
|
+
if ('function' == typeof compilation?.getAssets) for (let asset of compilation.getAssets())isManifestJsAsset(asset.name) && addIntegrity(sri, assetPrefix, asset.name, computeSubresourceIntegrity(asset.source) ?? asset.info?.integrity);
|
|
11302
11323
|
return Object.keys(sri).length > 0 ? sri : void 0;
|
|
11303
11324
|
})(compilation.getStats().toJson({
|
|
11304
11325
|
all: !1,
|
package/dist/manifest.d.ts
CHANGED
|
@@ -64,6 +64,8 @@ type ReactRouterManifestStatsCompilation = {
|
|
|
64
64
|
namedChunks: ReactRouterManifestStatsLookup<ReactRouterManifestStatsChunk>;
|
|
65
65
|
entrypoints?: ReactRouterManifestStatsLookup<ReactRouterManifestStatsEntrypoint>;
|
|
66
66
|
};
|
|
67
|
+
export declare const isManifestJsAsset: (asset: string) => boolean;
|
|
68
|
+
export declare const isManifestCssAsset: (asset: string) => boolean;
|
|
67
69
|
export declare const createReactRouterManifestStats: (compilation: ReactRouterManifestStatsCompilation | undefined, chunkNames?: ReadonlySet<string>) => ReactRouterManifestStats | undefined;
|
|
68
70
|
export type RouteManifestModuleExports = Record<string, readonly string[]>;
|
|
69
71
|
export type ReactRouterManifestGenerationResult = {
|
package/dist/mode-plan.d.ts
CHANGED
|
@@ -10,7 +10,7 @@ type CommonModePlan = {
|
|
|
10
10
|
manifestChunkNames: Set<string>;
|
|
11
11
|
webEntries: Record<string, string | RsbuildEntryDescription>;
|
|
12
12
|
nodeEntries: Record<string, string | RsbuildEntryDescription>;
|
|
13
|
-
createVirtualModules(publicPath: string
|
|
13
|
+
createVirtualModules(publicPath: string): Record<string, string>;
|
|
14
14
|
createResolveConfig(rootPath: string): Rspack.Configuration['resolve'];
|
|
15
15
|
server: RsbuildConfig['server'] | undefined;
|
|
16
16
|
webExternalsType: 'module' | undefined;
|
package/dist/plugin-utils.d.ts
CHANGED
|
@@ -7,26 +7,32 @@ export declare const getPackageVersion: (packageName: string, resolvePackagePath
|
|
|
7
7
|
export declare const escapeHtml: (value: string) => string;
|
|
8
8
|
export declare function combineURLs(baseURL: string, relativeURL: string): string;
|
|
9
9
|
export declare function normalizeAssetPrefix(assetPrefix?: string): string;
|
|
10
|
-
|
|
11
|
-
* Resolve the asset prefix Rsbuild applies to emitted asset URLs for the given
|
|
12
|
-
* mode. In development the effective prefix is `dev.assetPrefix` (which Rsbuild
|
|
13
|
-
* defaults from `server.base`, falling back to `output.assetPrefix`); in a
|
|
14
|
-
* production build it is `output.assetPrefix`. Both fields are already resolved
|
|
15
|
-
* on the normalized config, so this mirrors Rsbuild's own precedence rather than
|
|
16
|
-
* re-deriving it from `server.base`.
|
|
17
|
-
*
|
|
18
|
-
* `dev.assetPrefix` may be a boolean on the raw config (`false` disables it);
|
|
19
|
-
* boolean/`'auto'`/empty values normalize to the root prefix `'/'`.
|
|
20
|
-
*/
|
|
21
|
-
export declare function resolveEffectiveAssetPrefix(config: {
|
|
10
|
+
type AssetPrefixConfig = {
|
|
22
11
|
dev?: {
|
|
23
12
|
assetPrefix?: unknown;
|
|
24
13
|
};
|
|
25
14
|
output?: {
|
|
26
15
|
assetPrefix?: unknown;
|
|
27
16
|
};
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Resolve the absolute asset prefix the server build and browser manifest use
|
|
20
|
+
* for asset URLs. In development the effective prefix is `dev.assetPrefix`
|
|
21
|
+
* (which Rsbuild defaults from `server.base`, falling back to
|
|
22
|
+
* `output.assetPrefix`); in a production build it is `output.assetPrefix`.
|
|
23
|
+
*
|
|
24
|
+
* `fallbacks` are consulted in order (e.g. the root config after the web
|
|
25
|
+
* environment) when the preceding config only offers a prefix the server
|
|
26
|
+
* cannot use: `'auto'` and empty values are browser-runtime-only, so a root
|
|
27
|
+
* CDN prefix must survive a web `'auto'`. The choice happens before
|
|
28
|
+
* normalization so that `'auto'` is not first folded into `'/'`.
|
|
29
|
+
*
|
|
30
|
+
* `dev.assetPrefix` may be a boolean on the raw config (`false` disables it);
|
|
31
|
+
* boolean/`'auto'`/empty values ultimately normalize to the root prefix `'/'`.
|
|
32
|
+
*/
|
|
33
|
+
export declare function resolveEffectiveAssetPrefix(config: AssetPrefixConfig & {
|
|
28
34
|
isBuild: boolean;
|
|
29
|
-
}): string;
|
|
35
|
+
}, ...fallbacks: AssetPrefixConfig[]): string;
|
|
30
36
|
export declare function createRouteId(file: string): string;
|
|
31
37
|
export declare function findEntryFile(basePath: string): string;
|
|
32
38
|
export declare function generateWithProps(): string;
|
|
@@ -6,8 +6,6 @@ type RscVirtualModulesOptions = {
|
|
|
6
6
|
basename: string;
|
|
7
7
|
buildDirectory: string;
|
|
8
8
|
isBuild: boolean;
|
|
9
|
-
/** Resolved web `output.distPath.js` segment, e.g. `static/js`. */
|
|
10
|
-
jsDistPath: string;
|
|
11
9
|
outputClientPath: string;
|
|
12
10
|
publicPath: string;
|
|
13
11
|
routeDiscovery: Config['routeDiscovery'];
|
|
@@ -17,5 +15,5 @@ type RscVirtualModulesOptions = {
|
|
|
17
15
|
export declare const createReactRouterRscResolveAliases: (rootPath: string, options?: {
|
|
18
16
|
entrySsrPath?: string;
|
|
19
17
|
}) => Record<string, string>;
|
|
20
|
-
export declare const createReactRouterRscVirtualModules: ({ allowedActionOrigins, appDirectory, basename, buildDirectory, isBuild,
|
|
18
|
+
export declare const createReactRouterRscVirtualModules: ({ allowedActionOrigins, appDirectory, basename, buildDirectory, isBuild, outputClientPath, publicPath, routeDiscovery, routes, ssr, }: RscVirtualModulesOptions) => Record<string, string>;
|
|
21
19
|
export {};
|
package/package.json
CHANGED
|
@@ -1,15 +1,64 @@
|
|
|
1
|
-
import type { RsbuildPluginAPI } from '@rsbuild/core';
|
|
1
|
+
import type { RsbuildPluginAPI, Rspack } from '@rsbuild/core';
|
|
2
2
|
import { ensureFederationAsyncStartup } from './federation.js';
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* Rspack `output` policy for the web and node environments, in two tiers:
|
|
6
|
+
*
|
|
7
|
+
* 1. Overridable defaults, registered through `modifyRspackConfig`. Rsbuild
|
|
8
|
+
* runs the user's `tools.rspack` (object or function form) after this hook,
|
|
9
|
+
* so user output settings such as `chunkFilename` take precedence
|
|
10
|
+
* (#129, #130). Neither `filename` nor `publicPath` is set here: Rsbuild
|
|
11
|
+
* derives them from `output.filename`/`output.filenameHash`/`output.distPath`
|
|
12
|
+
* and the environment's `output.assetPrefix`, which keeps `'auto'` intact.
|
|
13
|
+
* 2. Enforced invariants, registered through a `tools.rspack` function that
|
|
14
|
+
* runs after the user's own `tools.rspack`: the server library type must
|
|
15
|
+
* match the server module format, and federation builds need async startup.
|
|
16
|
+
*/
|
|
4
17
|
export const registerReactRouterEnvironmentOutput = ({
|
|
5
18
|
api,
|
|
6
19
|
federation,
|
|
7
20
|
resolvedServerOutput,
|
|
21
|
+
webOutput,
|
|
8
22
|
}: {
|
|
9
23
|
api: RsbuildPluginAPI;
|
|
10
24
|
federation: boolean | undefined;
|
|
11
25
|
resolvedServerOutput: 'commonjs' | 'module';
|
|
26
|
+
webOutput: NonNullable<Rspack.Configuration['output']>;
|
|
12
27
|
}): void => {
|
|
28
|
+
const nodeChunkLoading =
|
|
29
|
+
resolvedServerOutput === 'module'
|
|
30
|
+
? 'import'
|
|
31
|
+
: federation
|
|
32
|
+
? 'async-node'
|
|
33
|
+
: 'require';
|
|
34
|
+
|
|
35
|
+
api.modifyRspackConfig((rspackConfig, { environment, mergeConfig }) => {
|
|
36
|
+
if (environment.name === 'web') {
|
|
37
|
+
return mergeConfig(rspackConfig, {
|
|
38
|
+
output: {
|
|
39
|
+
...webOutput,
|
|
40
|
+
...(federation ? { chunkLoading: 'import' } : {}),
|
|
41
|
+
},
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
if (environment.name === 'node') {
|
|
45
|
+
return mergeConfig(rspackConfig, {
|
|
46
|
+
output: {
|
|
47
|
+
chunkFormat: resolvedServerOutput,
|
|
48
|
+
chunkLoading: nodeChunkLoading,
|
|
49
|
+
devtoolModuleFilenameTemplate: '[absolute-resource-path]',
|
|
50
|
+
devtoolFallbackModuleFilenameTemplate:
|
|
51
|
+
'[absolute-resource-path]?[hash]',
|
|
52
|
+
workerChunkLoading: nodeChunkLoading,
|
|
53
|
+
wasmLoading: 'fetch',
|
|
54
|
+
module: resolvedServerOutput === 'module',
|
|
55
|
+
chunkFilename: 'static/js/async/[name].js',
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
return rspackConfig;
|
|
60
|
+
});
|
|
61
|
+
|
|
13
62
|
api.modifyEnvironmentConfig(
|
|
14
63
|
async (config, { name, mergeEnvironmentConfig }) => {
|
|
15
64
|
if (name !== 'web' && name !== 'node') {
|
package/src/index.ts
CHANGED
|
@@ -6,11 +6,7 @@ import { rspack, type RsbuildPlugin, type Rspack } from '@rsbuild/core';
|
|
|
6
6
|
import { relative, resolve } from 'pathe';
|
|
7
7
|
|
|
8
8
|
import { getDefaultConcurrency } from './concurrency.js';
|
|
9
|
-
import {
|
|
10
|
-
DEFAULT_JS_DIST_PATH,
|
|
11
|
-
JS_EXTENSIONS,
|
|
12
|
-
PLUGIN_NAME,
|
|
13
|
-
} from './constants.js';
|
|
9
|
+
import { JS_EXTENSIONS, PLUGIN_NAME } from './constants.js';
|
|
14
10
|
import { guardReactRouterLazyCompilation } from './lazy-compilation.js';
|
|
15
11
|
import {
|
|
16
12
|
findEntryFile,
|
|
@@ -222,13 +218,25 @@ export const pluginReactRouter = (
|
|
|
222
218
|
warnOnClientSourceMaps(normalized, msg => api.logger.warn(msg), 'web');
|
|
223
219
|
});
|
|
224
220
|
|
|
221
|
+
// The manifest / server `publicPath` follows the web environment's asset
|
|
222
|
+
// prefix because that is where the browser assets are served from. A web
|
|
223
|
+
// prefix the server cannot use (`'auto'`) falls back to the root prefix,
|
|
224
|
+
// so `output.assetPrefix: 'https://cdn/'` + web `'auto'` still emits CDN
|
|
225
|
+
// URLs from the server.
|
|
225
226
|
api.onBeforeCreateCompiler(() => {
|
|
226
|
-
const
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
227
|
+
const root = api.getNormalizedConfig();
|
|
228
|
+
// `getNormalizedConfig({ environment: 'web' })` throws when the build was
|
|
229
|
+
// narrowed to other environments (`--environment node`), so look the web
|
|
230
|
+
// environment up on the root config instead.
|
|
231
|
+
const web = root.environments.web;
|
|
232
|
+
assetPrefix = resolveEffectiveAssetPrefix(
|
|
233
|
+
{
|
|
234
|
+
dev: web?.dev,
|
|
235
|
+
output: web?.output,
|
|
236
|
+
isBuild: api.context.action === 'build',
|
|
237
|
+
},
|
|
238
|
+
{ dev: root.dev, output: root.output }
|
|
239
|
+
);
|
|
232
240
|
});
|
|
233
241
|
|
|
234
242
|
const configPath = findEntryFile(resolve('react-router.config'));
|
|
@@ -832,39 +840,35 @@ export const pluginReactRouter = (
|
|
|
832
840
|
}
|
|
833
841
|
|
|
834
842
|
// Public requests stay bare while Rspack resolves seeded virtual files.
|
|
835
|
-
const createVirtualModulePlugin = (
|
|
836
|
-
publicPath: string,
|
|
837
|
-
jsDistPath: string
|
|
838
|
-
) => {
|
|
843
|
+
const createVirtualModulePlugin = (publicPath: string) => {
|
|
839
844
|
return new rspack.experiments.VirtualModulesPlugin(
|
|
840
|
-
mapVirtualModules(modePlan.createVirtualModules(publicPath
|
|
845
|
+
mapVirtualModules(modePlan.createVirtualModules(publicPath))
|
|
841
846
|
);
|
|
842
847
|
};
|
|
843
848
|
|
|
844
849
|
api.modifyRsbuildConfig(async (config, { mergeRsbuildConfig }) => {
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
options.federation && resolvedServerOutput === 'commonjs';
|
|
862
|
-
let nodeChunkLoading: 'import' | 'async-node' | 'require' = 'require';
|
|
863
|
-
if (resolvedServerOutput === 'module') {
|
|
864
|
-
nodeChunkLoading = 'import';
|
|
865
|
-
} else if (useAsyncNodeChunkLoading) {
|
|
866
|
-
nodeChunkLoading = 'async-node';
|
|
850
|
+
const webConfig = config.environments?.web;
|
|
851
|
+
const webJsFilename =
|
|
852
|
+
webConfig?.output?.filename?.js ?? config.output?.filename?.js;
|
|
853
|
+
// Rspack's RSC manifest only records browser entry files named `*.js`
|
|
854
|
+
// (`entryJsFiles`), and the server renders its bootstrap scripts from
|
|
855
|
+
// that list. Reject filename schemes it would silently drop up front.
|
|
856
|
+
if (
|
|
857
|
+
isRscMode &&
|
|
858
|
+
typeof webJsFilename === 'string' &&
|
|
859
|
+
!/\.js$/.test(webJsFilename)
|
|
860
|
+
) {
|
|
861
|
+
throw new Error(
|
|
862
|
+
`[${PLUGIN_NAME}] RSC mode requires web \`output.filename.js\` to end in ".js" (got ${JSON.stringify(
|
|
863
|
+
webJsFilename
|
|
864
|
+
)}): rspack's RSC manifest omits entry files with a query or another extension, so the server could not render bootstrap scripts.`
|
|
865
|
+
);
|
|
867
866
|
}
|
|
867
|
+
const assetPrefix = resolveEffectiveAssetPrefix(
|
|
868
|
+
{ dev: webConfig?.dev, output: webConfig?.output, isBuild },
|
|
869
|
+
{ dev: config.dev, output: config.output }
|
|
870
|
+
);
|
|
871
|
+
const vmodPlugin = createVirtualModulePlugin(assetPrefix);
|
|
868
872
|
const configuredLazyCompilation = Object.prototype.hasOwnProperty.call(
|
|
869
873
|
options,
|
|
870
874
|
'lazyCompilation'
|
|
@@ -964,9 +968,6 @@ export const pluginReactRouter = (
|
|
|
964
968
|
}),
|
|
965
969
|
},
|
|
966
970
|
output: {
|
|
967
|
-
filename: {
|
|
968
|
-
js: '[name].js',
|
|
969
|
-
},
|
|
970
971
|
distPath: {
|
|
971
972
|
root: outputClientPath,
|
|
972
973
|
},
|
|
@@ -985,15 +986,6 @@ export const pluginReactRouter = (
|
|
|
985
986
|
],
|
|
986
987
|
},
|
|
987
988
|
externalsType: modePlan.webExternalsType,
|
|
988
|
-
output: {
|
|
989
|
-
...modePlan.webOutput,
|
|
990
|
-
publicPath: assetPrefix,
|
|
991
|
-
...(options.federation
|
|
992
|
-
? {
|
|
993
|
-
chunkLoading: 'import',
|
|
994
|
-
}
|
|
995
|
-
: {}),
|
|
996
|
-
},
|
|
997
989
|
optimization: modePlan.webOptimization,
|
|
998
990
|
},
|
|
999
991
|
},
|
|
@@ -1038,17 +1030,6 @@ export const pluginReactRouter = (
|
|
|
1038
1030
|
externals: modePlan.nodeExternals,
|
|
1039
1031
|
...modePlan.nodeDependencies,
|
|
1040
1032
|
externalsType: resolvedServerOutput,
|
|
1041
|
-
output: {
|
|
1042
|
-
chunkFormat: resolvedServerOutput,
|
|
1043
|
-
chunkLoading: nodeChunkLoading,
|
|
1044
|
-
devtoolModuleFilenameTemplate: '[absolute-resource-path]',
|
|
1045
|
-
devtoolFallbackModuleFilenameTemplate:
|
|
1046
|
-
'[absolute-resource-path]?[hash]',
|
|
1047
|
-
workerChunkLoading: nodeChunkLoading,
|
|
1048
|
-
wasmLoading: 'fetch',
|
|
1049
|
-
module: resolvedServerOutput === 'module',
|
|
1050
|
-
chunkFilename: 'static/js/async/[name].js',
|
|
1051
|
-
},
|
|
1052
1033
|
},
|
|
1053
1034
|
},
|
|
1054
1035
|
},
|
|
@@ -1060,6 +1041,7 @@ export const pluginReactRouter = (
|
|
|
1060
1041
|
api,
|
|
1061
1042
|
federation: pluginOptions.federation,
|
|
1062
1043
|
resolvedServerOutput,
|
|
1044
|
+
webOutput: modePlan.webOutput,
|
|
1063
1045
|
});
|
|
1064
1046
|
|
|
1065
1047
|
if (modePlan.kind === 'classic' && useRouteModuleTransformLoader) {
|