rsbuild-plugin-react-router
Version:
React Router plugin for Rsbuild
885 lines (879 loc) • 72.7 kB
JavaScript
import { basename, dirname, normalize, relative, resolve } from "pathe";
import { existsSync, readFileSync, statSync } from "node:fs";
import { langFromPath, parse, walk as external_yuku_parser_walk } from "yuku-parser";
import { Analyzer } from "yuku-analyzer";
import { print } from "yuku-codegen";
import { readFile, stat } from "node:fs/promises";
import { rspack } from "@rsbuild/core";
import { createRequire } from "node:module";
let PLUGIN_NAME = 'rsbuild:react-router', JS_EXTENSIONS = [
'.tsx',
'.ts',
'.jsx',
'.js',
'.mjs',
'.mts'
], BUILD_CLIENT_ROUTE_QUERY_STRING = '?__react-router-build-client-route', SERVER_ONLY_ROUTE_EXPORTS = [
'loader',
'action',
'middleware',
'headers'
], SERVER_ONLY_ROUTE_EXPORTS_SET = new Set(SERVER_ONLY_ROUTE_EXPORTS), CLIENT_ROUTE_EXPORTS_SET = new Set([
'clientAction',
'clientLoader',
'clientMiddleware',
'handle',
'meta',
'links',
'shouldRevalidate',
'default',
'ErrorBoundary',
'HydrateFallback',
'Layout'
]), NAMED_COMPONENT_EXPORTS_SET = new Set([
'HydrateFallback',
'ErrorBoundary'
]), SERVER_EXPORTS = {
loader: 'loader',
action: 'action',
middleware: 'middleware',
headers: 'headers'
}, CLIENT_EXPORTS = {
clientAction: 'clientAction',
clientLoader: 'clientLoader',
clientMiddleware: 'clientMiddleware',
default: 'default',
ErrorBoundary: 'ErrorBoundary',
handle: 'handle',
HydrateFallback: 'HydrateFallback',
Layout: 'Layout',
links: 'links',
meta: 'meta',
shouldRevalidate: 'shouldRevalidate'
}, getPatternIdentifierNames = (pattern, names = new Set())=>{
if (!pattern) return names;
if ('Identifier' === pattern.type) return names.add(pattern.name), names;
if ('RestElement' === pattern.type) return getPatternIdentifierNames(pattern.argument, names);
if ('AssignmentPattern' === pattern.type) return getPatternIdentifierNames(pattern.left, names);
if ('ArrayPattern' === pattern.type) {
for (let element of pattern.elements ?? [])getPatternIdentifierNames(element, names);
return names;
}
if ('ObjectPattern' === pattern.type) for (let property of pattern.properties ?? [])'RestElement' === property.type ? getPatternIdentifierNames(property.argument, names) : getPatternIdentifierNames(property.value, names);
return names;
}, getIdentifierNamesFromPattern = (pattern)=>Array.from(getPatternIdentifierNames(pattern)), getExportedName = (node)=>{
let exported = node?.exported ?? node;
return exported ? 'Identifier' === exported.type ? exported.name : 'Literal' === exported.type || 'StringLiteral' === exported.type ? String(exported.value) : null : null;
}, identifier = (name)=>({
type: 'Identifier',
start: 0,
end: 0,
name,
decorators: [],
optional: !1,
typeAnnotation: null
}), callExpression = (callee, args)=>({
type: 'CallExpression',
start: 0,
end: 0,
callee,
arguments: args,
optional: !1
}), importDeclaration = (specifiers, source)=>({
type: 'ImportDeclaration',
start: 0,
end: 0,
specifiers: specifiers.map((specifier)=>({
type: 'ImportSpecifier',
start: 0,
end: 0,
imported: identifier(specifier.imported),
local: identifier(specifier.local),
importKind: 'value'
})),
source: {
type: 'Literal',
start: 0,
end: 0,
value: source,
raw: JSON.stringify(source)
},
attributes: [],
phase: null,
importKind: 'value'
}), exportSpecifier = (local, exported)=>({
type: 'ExportSpecifier',
start: 0,
end: 0,
local: identifier(local),
exported: identifier(exported),
exportKind: 'value'
}), exportNamedDeclaration = (specifiers)=>({
type: 'ExportNamedDeclaration',
start: 0,
end: 0,
declaration: null,
specifiers,
source: null,
attributes: [],
exportKind: 'value'
}), variableDeclaration = (name, init)=>({
type: 'VariableDeclaration',
start: 0,
end: 0,
kind: 'const',
declare: !1,
declarations: [
{
type: 'VariableDeclarator',
start: 0,
end: 0,
id: identifier(name),
init,
definite: !1
}
]
}), removeFromArray = (array, value)=>{
let index = array.indexOf(value);
index >= 0 && array.splice(index, 1);
}, assertAllowedBindingName = (name, exportsToRemove)=>{
if (exportsToRemove.has(name)) throw Error(`Cannot remove destructured export "${name}"`);
}, validateRestElement = (element, exportsToRemove)=>{
element.argument?.type === 'Identifier' && element.argument.name && assertAllowedBindingName(element.argument.name, exportsToRemove);
}, validateObjectProperty = (property, exportsToRemove)=>{
'RestElement' === property.type ? validateRestElement(property, exportsToRemove) : 'Property' === property.type && validateBindingTarget(property.value, exportsToRemove);
}, validateBindingTarget = (node, exportsToRemove)=>{
if (node) switch(node.type){
case 'Identifier':
node.name && assertAllowedBindingName(node.name, exportsToRemove);
return;
case 'AssignmentPattern':
validateBindingTarget(node.left, exportsToRemove);
return;
case 'ArrayPattern':
for (let element of node.elements ?? [])element?.type === 'RestElement' ? validateRestElement(element, exportsToRemove) : validateBindingTarget(element, exportsToRemove);
return;
case 'ObjectPattern':
for (let property of node.properties ?? [])validateObjectProperty(property, exportsToRemove);
}
}, getDeclaredNames = (node)=>{
let names = new Set();
if ('VariableDeclaration' === node.type) for (let declarator of node.declarations ?? [])getPatternIdentifierNames(declarator.id, names);
else if (('FunctionDeclaration' === node.type || 'ClassDeclaration' === node.type) && node.id?.name) names.add(node.id.name);
else if ('ImportDeclaration' === node.type) for (let specifier of node.specifiers ?? [])specifier.local?.name && names.add(specifier.local.name);
return names;
}, collectReferencedNames = (node)=>{
let referenced = new Set();
return external_yuku_parser_walk(node, {
Identifier (node, ctx) {
let parent = ctx.parent;
!(parent && 'Identifier' === node.type && (((node, parent)=>{
if (!parent || 'Identifier' !== node.type) return !1;
if (('FunctionDeclaration' === parent.type || 'FunctionExpression' === parent.type || 'ClassDeclaration' === parent.type || 'ClassExpression' === parent.type) && parent.id === node) return !0;
if ('VariableDeclarator' === parent.type) return !!(node.name && getPatternIdentifierNames(parent.id).has(node.name));
if (('ImportSpecifier' === parent.type || 'ImportDefaultSpecifier' === parent.type || 'ImportNamespaceSpecifier' === parent.type) && parent.local === node) return !0;
if (('FunctionDeclaration' === parent.type || 'FunctionExpression' === parent.type || 'ArrowFunctionExpression' === parent.type) && node.name) {
let name = node.name;
return (parent.params ?? []).some((param)=>getPatternIdentifierNames(param).has(name));
}
return !1;
})(node, parent) || 'MemberExpression' === parent.type && parent.property === node && !parent.computed || 'Property' === parent.type && parent.key === node && !parent.computed && !parent.shorthand || 'MethodDefinition' === parent.type && parent.key === node && !parent.computed || 'ExportSpecifier' === parent.type || 'ExportDefaultSpecifier' === parent.type || 'ExportNamespaceSpecifier' === parent.type || 'ImportSpecifier' === parent.type && parent.imported === node || 'LabeledStatement' === parent.type || 'BreakStatement' === parent.type || 'ContinueStatement' === parent.type)) && node.name && referenced.add(node.name);
},
JSXIdentifier (node, ctx) {
let name, parent = ctx.parent;
if (parent) {
if ('JSXMemberExpression' === parent.type && parent.object === node) {
node.name && referenced.add(node.name);
return;
}
if (node.name && (name = node.name, /^[A-Z]/.test(name)) && ('JSXOpeningElement' === parent.type || 'JSXClosingElement' === parent.type) && parent.name === node) return void referenced.add(node.name);
}
},
ExportSpecifier (node, ctx) {
let declaration = ctx.parent;
!declaration?.source && declaration?.exportKind !== 'type' && node.local?.name && 'type' !== node.exportKind && referenced.add(node.local.name);
}
}), referenced;
}, collectLiveTopLevelDeclarations = (program, graph)=>{
let pendingNames = [];
for (let statement of program.body ?? [])if ('VariableDeclaration' !== statement.type && !graph.declarationsByNode.has(statement)) for (let name of collectReferencedNames(statement))pendingNames.push(name);
let visitedNames = new Set(), liveDeclarations = new Set();
for(; pendingNames.length > 0;){
let name = pendingNames.pop();
if (!(!name || visitedNames.has(name))) {
for (let declaration of (visitedNames.add(name), graph.declarationsByName.get(name) ?? []))if (!liveDeclarations.has(declaration)) for (let referencedName of (liveDeclarations.add(declaration), declaration.referencedNames))pendingNames.push(referencedName);
}
}
return liveDeclarations;
}, declarationReferencesName = (declaration, names, graph, cache, visitedNames = new Set())=>{
let cached = cache.get(declaration);
if (void 0 !== cached) return cached;
for (let referencedName of declaration.referencedNames){
if (names.has(referencedName)) return cache.set(declaration, !0), !0;
if (!visitedNames.has(referencedName)) {
for (let referencedDeclaration of (visitedNames.add(referencedName), graph.declarationsByName.get(referencedName) ?? []))if (declarationReferencesName(referencedDeclaration, names, graph, cache, visitedNames)) return cache.set(declaration, !0), !0;
}
}
return cache.set(declaration, !1), !1;
};
function toFunctionExpression(decl) {
return {
...decl,
type: 'FunctionExpression',
declare: void 0
};
}
function toClassExpression(decl) {
return {
...decl,
type: 'ClassExpression',
declare: void 0
};
}
let getComponentExportName = (exportedName)=>{
var name;
return 'default' === exportedName ? 'Component' : (name = exportedName, NAMED_COMPONENT_EXPORTS_SET.has(name)) ? exportedName : null;
}, declarationIncludesName = (declaration, name)=>'VariableDeclaration' === declaration.type ? (declaration.declarations ?? []).some((declarator)=>{
let pattern;
return pattern = declarator.id, getPatternIdentifierNames(pattern).has(name);
}) : ('FunctionDeclaration' === declaration.type || 'ClassDeclaration' === declaration.type || 'TSEnumDeclaration' === declaration.type) && declaration.id?.name ? declaration.id.name === name : 'ImportDeclaration' === declaration.type && (declaration.specifiers ?? []).some((specifier)=>specifier.local?.name === name), hasTopLevelBindingName = (program, name)=>{
for (let statement of program.body ?? []){
if ('ImportDeclaration' === statement.type) {
if (declarationIncludesName(statement, name)) return !0;
continue;
}
if ('ExportDefaultDeclaration' === statement.type) {
if (statement.declaration?.id?.name === name) return !0;
continue;
}
let declaration = 'ExportNamedDeclaration' === statement.type ? statement.declaration : statement;
if (declaration && declarationIncludesName(declaration, name)) return !0;
}
return !1;
};
function combineURLs(baseURL, relativeURL) {
return relativeURL ? `${baseURL.replace(/\/+$/, '')}/${relativeURL.replace(/^\/+/, '')}` : baseURL;
}
function normalizeAssetPrefix(assetPrefix) {
return assetPrefix && 'auto' !== assetPrefix ? assetPrefix.endsWith('/') ? assetPrefix : `${assetPrefix}/` : '/';
}
function createRouteId(file) {
return normalize(file.replace(/\.[^/.]+$/, ''));
}
function findEntryFile(basePath) {
for (let ext of JS_EXTENSIONS){
let filePath = `${basePath}${ext}`;
if (existsSync(filePath)) return filePath;
}
return `${basePath}.tsx`;
}
function generateWithProps() {
return `
import { createElement as h } from "react";
import { useActionData, useLoaderData, useMatches, useParams, useRouteError } from "react-router";
export function withComponentProps(Component) {
return function Wrapped() {
const props = {
params: useParams(),
loaderData: useLoaderData(),
actionData: useActionData(),
matches: useMatches(),
};
return h(Component, props);
};
}
export function withHydrateFallbackProps(HydrateFallback) {
return function Wrapped() {
const props = {
params: useParams(),
};
return h(HydrateFallback, props);
};
}
export function withErrorBoundaryProps(ErrorBoundary) {
return function Wrapped() {
const props = {
params: useParams(),
loaderData: useLoaderData(),
actionData: useActionData(),
error: useRouteError(),
};
return h(ErrorBoundary, props);
};
}
`;
}
let routeChunkExportNames = [
'clientAction',
'clientLoader',
'clientMiddleware',
'HydrateFallback'
];
[
...routeChunkExportNames
];
let mightContainRouteChunkExportName = (source)=>routeChunkExportNames.some((exportName)=>source.includes(exportName)), createRouteChunkExportMap = (getValue)=>Object.fromEntries(routeChunkExportNames.map((exportName)=>[
exportName,
getValue(exportName)
])), routeChunkQueryStringPrefix = '?route-chunk=', routeChunkQueryStrings = {
main: `${routeChunkQueryStringPrefix}main`,
clientAction: `${routeChunkQueryStringPrefix}clientAction`,
clientLoader: `${routeChunkQueryStringPrefix}clientLoader`,
clientMiddleware: `${routeChunkQueryStringPrefix}clientMiddleware`,
HydrateFallback: `${routeChunkQueryStringPrefix}HydrateFallback`
}, routeChunkEntrySuffix = {
clientAction: 'client-action',
clientLoader: 'client-loader',
clientMiddleware: 'client-middleware',
HydrateFallback: 'hydrate-fallback'
}, invariant = (value, message)=>{
if (!value) throw Error(message);
}, getOrSetFromCache = (cache, key, version, getValue)=>{
let entry = cache.get(key);
if (entry?.version === version) return entry.value;
let value = getValue();
return cache.set(key, {
value,
version
}), value;
}, analyzeCode = (code, cache, cacheKey)=>getOrSetFromCache(cache, `${cacheKey}::analyzeCode`, code, ()=>{
let module = new Analyzer().addFile(cacheKey, code, {
lang: 'tsx',
sourceType: 'module',
attachComments: !0
}), errors = module.diagnostics.filter((diagnostic)=>'error' === diagnostic.severity);
if (errors.length > 0) throw Error(errors.map((error)=>error.message).join('\n'));
return {
module,
program: module.ast
};
}), route_chunks_getExportedName = (exported)=>'Identifier' === exported.type ? exported.name : String(exported.value), setsIntersect = (set1, set2)=>{
let smallerSet = set1, largerSet = set2;
for (let element of (set1.size > set2.size && (smallerSet = set2, largerSet = set1), smallerSet))if (largerSet.has(element)) return !0;
return !1;
}, getExportDependencies = (code, cache, cacheKey)=>getOrSetFromCache(cache, `${cacheKey}::getExportDependencies`, code, ()=>{
let { module } = analyzeCode(code, cache, cacheKey), exportDependencies = new Map(), topLevelStatementCache = new Map(), variableDeclaratorCache = new Map(), getCachedTopLevelStatementForNode = (node)=>{
let cached = topLevelStatementCache.get(node);
if (cached) return cached;
let statement = ((module, node)=>{
let current = node, parent = module.parentOf(current);
for(; parent && 'Program' !== parent.type;)current = parent, parent = module.parentOf(current);
return invariant(parent?.type === 'Program', 'Expected node to be within Program'), current;
})(module, node);
return topLevelStatementCache.set(node, statement), statement;
}, getCachedVariableDeclaratorForNode = (node)=>{
if (variableDeclaratorCache.has(node)) return variableDeclaratorCache.get(node) ?? null;
let declarator = ((module, node)=>{
let current = node;
for(; current;){
if ('VariableDeclarator' === current.type) return current;
current = module.parentOf(current);
}
return null;
})(module, node);
return variableDeclaratorCache.set(node, declarator), declarator;
}, addCachedTopLevelStatement = (dependencies, node)=>{
let statement = getCachedTopLevelStatementForNode(node);
return dependencies.topLevelStatements.add(statement), 'ImportDeclaration' === statement.type || statement.type.startsWith('Export') || dependencies.topLevelNonModuleStatements.add(statement), statement;
}, handleExport = (exportName, exportNode, localSymbol)=>{
let dependencies = {
topLevelStatements: new Set(),
topLevelNonModuleStatements: new Set(),
importedIdentifierNames: new Set(),
exportedVariableDeclarators: new Set()
}, visitedSymbols = new Set(), scannedNodes = new Set(), scanNode = (node)=>{
scannedNodes.has(node) || (scannedNodes.add(node), external_yuku_parser_walk(node, {
Identifier (node) {
let reference = module.referenceOf(node);
reference?.symbol && visitSymbol(reference.symbol);
}
}));
}, visitSymbol = (symbol)=>{
if (!visitedSymbols.has(symbol)) {
for (let declaration of (visitedSymbols.add(symbol), symbol.declarations)){
let statement = addCachedTopLevelStatement(dependencies, declaration);
'ImportDeclaration' === statement.type && dependencies.importedIdentifierNames.add(symbol.name);
let declarator = getCachedVariableDeclaratorForNode(declaration);
declarator && 'ExportNamedDeclaration' === getCachedTopLevelStatementForNode(declarator).type && dependencies.exportedVariableDeclarators.add(declarator), scanNode(declarator ?? statement);
}
for (let reference of symbol.references){
let statement = addCachedTopLevelStatement(dependencies, reference.node);
scanNode(getCachedVariableDeclaratorForNode(reference.node) ?? statement);
}
}
};
addCachedTopLevelStatement(dependencies, exportNode), localSymbol ? visitSymbol(localSymbol) : scanNode(getCachedTopLevelStatementForNode(exportNode)), exportDependencies.set(exportName, dependencies);
};
for (let exp of module.exports)exp.typeOnly || exp.isStar || exp.isExportEquals || handleExport(exp.name, exp.node, exp.local ?? null);
return exportDependencies;
}), getChunkableExportMap = (code, cache, cacheKey)=>getOrSetFromCache(cache, `${cacheKey}::getChunkableExportMap`, code, ()=>{
let exportDependencies = getExportDependencies(code, cache, cacheKey);
return createRouteChunkExportMap((exportName)=>((exportName, exportDependencies)=>{
let dependencies = exportDependencies.get(exportName);
if (!dependencies) return !1;
for (let [currentExportName, currentDependencies] of exportDependencies)if (currentExportName !== exportName && setsIntersect(currentDependencies.topLevelNonModuleStatements, dependencies.topLevelNonModuleStatements)) return !1;
if (dependencies.exportedVariableDeclarators.size > 1) return !1;
if (dependencies.exportedVariableDeclarators.size > 0) {
for (let [currentExportName, currentDependencies] of exportDependencies)if (currentExportName !== exportName && setsIntersect(currentDependencies.exportedVariableDeclarators, dependencies.exportedVariableDeclarators)) return !1;
}
return !0;
})(exportName, exportDependencies));
}), generateCode = (program)=>{
if (0 === program.body.length) return;
let result = print(program, {
comments: !0
});
if (result.errors.length > 0) throw Error(result.errors.map((error)=>error.message).join('\n'));
return result.code;
}, filterImportSpecifiers = (node, shouldKeep)=>{
if (0 === node.specifiers.length) return node;
let specifiers = node.specifiers.filter((specifier)=>shouldKeep(specifier.local.name));
return specifiers.length > 0 ? {
...node,
specifiers
} : null;
}, getChunkedExport = (code, exportName, cache, cacheKey)=>getOrSetFromCache(cache, `${cacheKey}::getChunkedExport::${exportName}`, code, ()=>{
if (!routeChunkExportNames.includes(exportName) || !getChunkableExportMap(code, cache, cacheKey)[exportName]) return;
let dependencies = getExportDependencies(code, cache, cacheKey).get(exportName);
invariant(dependencies, 'Expected export to have dependencies');
let program = analyzeCode(code, cache, cacheKey).program, body = program.body.filter((node)=>dependencies.topLevelStatements.has(node)).map((node)=>'ImportDeclaration' !== node.type ? node : 0 === dependencies.importedIdentifierNames.size ? null : filterImportSpecifiers(node, (importedName)=>dependencies.importedIdentifierNames.has(importedName))).map((node)=>{
if (!node || !node.type.startsWith('Export')) return node;
if ('ExportAllDeclaration' === node.type) return null;
if ('ExportDefaultDeclaration' === node.type) return 'default' === exportName ? node : null;
let { declaration } = node;
if (declaration?.type === 'VariableDeclaration') {
let declarations = declaration.declarations.filter((declarationNode)=>dependencies.exportedVariableDeclarators.has(declarationNode));
return declarations.length > 0 ? {
...node,
declaration: {
...declaration,
declarations
}
} : null;
}
if (declaration?.type === 'FunctionDeclaration' || declaration?.type === 'ClassDeclaration') return declaration.id?.name === exportName ? node : null;
if ('ExportNamedDeclaration' === node.type) {
let specifiers = node.specifiers.filter((specifier)=>route_chunks_getExportedName(specifier.exported) === exportName);
return specifiers.length > 0 ? {
...node,
specifiers
} : null;
}
throw Error('Unknown export node type');
}).filter(Boolean);
return generateCode({
...program,
body
});
}), hasCachedChunkedExport = (code, exportName, cache, cacheKey)=>{
let key;
return key = `${cacheKey}::getChunkedExport::${exportName}`, cache.get(key)?.version === code;
}, getRouteChunkModuleId = (filePath, chunkName)=>`${filePath}${routeChunkQueryStrings[chunkName]}`, normalizeRelativeFilePath = (file, appDirectory)=>{
let fullPath = resolve(appDirectory, file);
return normalize(relative(appDirectory, fullPath)).split('?')[0];
}, isRootRouteModuleId = (config, id)=>normalizeRelativeFilePath(id, config.appDirectory) === config.rootRouteFile, shouldAnalyzeRouteChunks = (config, id, code)=>!!config.splitRouteModules && mightContainRouteChunkExportName(code) && !isRootRouteModuleId(config, id), createEmptyRouteChunkByExportName = ()=>createRouteChunkExportMap(()=>!1), buildManifestChunkValidity = (exportNames, hasRouteChunkByExportName)=>createRouteChunkExportMap((exportName)=>!exportNames.has(exportName) || hasRouteChunkByExportName[exportName]), detectRouteChunksIfEnabled = async (cache, config, id, code)=>{
let analysisCache, exportDependencies, hasRouteChunkByExportName, chunkedExports, hasRouteChunks;
if (!shouldAnalyzeRouteChunks(config, id, code)) return {
exportNames: [],
chunkedExports: [],
hasRouteChunks: !1,
hasRouteChunkByExportName: createEmptyRouteChunkByExportName()
};
let cacheKey = normalizeRelativeFilePath(id, config.appDirectory);
return exportDependencies = getExportDependencies(code, analysisCache = cache ?? new Map(), cacheKey), hasRouteChunks = (chunkedExports = Object.entries(hasRouteChunkByExportName = getChunkableExportMap(code, analysisCache, cacheKey)).filter(([, isChunked])=>isChunked).map(([exportName])=>exportName)).length > 0, {
exportNames: Array.from(exportDependencies.keys()),
hasRouteChunks,
hasRouteChunkByExportName,
chunkedExports
};
}, getRouteChunkIfEnabled = async (cache, config, id, chunkName, code)=>{
if (!config.splitRouteModules) return null;
if ('main' === chunkName) {
if (!mightContainRouteChunkExportName(code)) return code;
} else if (!code.includes(chunkName)) return null;
return ((code, chunkName, cache, cacheKey)=>{
let analysisCache = cache ?? new Map();
if ('main' === chunkName) return getOrSetFromCache(analysisCache, `${cacheKey}::omitChunkedExports::${routeChunkExportNames.join(',')}`, code, ()=>{
let chunkableExportMap = getChunkableExportMap(code, analysisCache, cacheKey), exportNameSet = new Set(routeChunkExportNames), isOmitted = (exportName)=>exportNameSet.has(exportName) && !!chunkableExportMap[exportName], exportDependencies = getExportDependencies(code, analysisCache, cacheKey), allExportNames = Array.from(exportDependencies.keys()), omittedExportNames = allExportNames.filter(isOmitted), retainedExportNames = allExportNames.filter((exportName)=>!isOmitted(exportName)), omittedStatements = new Set(), omittedExportedVariableDeclarators = new Set(), retainedImportedIdentifierNames = new Set(), omittedImportedIdentifierNames = new Set();
for (let omittedExportName of omittedExportNames){
let dependencies = exportDependencies.get(omittedExportName);
for (let statement of (invariant(dependencies, `Expected dependencies for ${omittedExportName}`), dependencies.topLevelNonModuleStatements))omittedStatements.add(statement);
for (let declarator of dependencies.exportedVariableDeclarators)omittedExportedVariableDeclarators.add(declarator);
for (let importedName of dependencies.importedIdentifierNames)omittedImportedIdentifierNames.add(importedName);
}
for (let retainedExportName of retainedExportNames){
let dependencies = exportDependencies.get(retainedExportName);
if (dependencies) for (let importedName of dependencies.importedIdentifierNames)retainedImportedIdentifierNames.add(importedName);
}
let program = analyzeCode(code, analysisCache, cacheKey).program, body = program.body.filter((node)=>!omittedStatements.has(node)).map((node)=>'ImportDeclaration' !== node.type ? node : filterImportSpecifiers(node, (importedName)=>!!retainedImportedIdentifierNames.has(importedName) || !omittedImportedIdentifierNames.has(importedName))).map((node)=>{
if (!node || !node.type.startsWith('Export') || 'ExportAllDeclaration' === node.type) return node;
if ('ExportDefaultDeclaration' === node.type) return isOmitted('default') ? null : node;
if (node.declaration?.type === 'VariableDeclaration') {
let declarations = node.declaration.declarations.filter((declarationNode)=>!omittedExportedVariableDeclarators.has(declarationNode));
return declarations.length > 0 ? {
...node,
declaration: {
...node.declaration,
declarations
}
} : null;
}
if (node.declaration?.type === 'FunctionDeclaration' || node.declaration?.type === 'ClassDeclaration') return isOmitted(node.declaration.id.name) ? null : node;
if ('ExportNamedDeclaration' === node.type) {
let specifiers = node.specifiers.filter((specifier)=>!isOmitted(route_chunks_getExportedName(specifier.exported)));
return specifiers.length > 0 || node.declaration ? {
...node,
specifiers
} : null;
}
throw Error('Unknown node type');
}).filter(Boolean);
return generateCode({
...program,
body
});
});
return hasCachedChunkedExport(code, chunkName, analysisCache, cacheKey) || ((code, cache, cacheKey)=>{
let chunkableExportMap = getChunkableExportMap(code, cache, cacheKey);
for (let exportName of routeChunkExportNames)chunkableExportMap[exportName] && (hasCachedChunkedExport(code, exportName, cache, cacheKey) || getChunkedExport(code, exportName, cache, cacheKey));
})(code, analysisCache, cacheKey), getChunkedExport(code, chunkName, analysisCache, cacheKey);
})(code, chunkName, cache, normalizeRelativeFilePath(id, config.appDirectory)) ?? null;
}, validateRouteChunks = ({ config, id, valid })=>{
if (isRootRouteModuleId(config, id)) return;
let invalidChunks = Object.entries(valid).filter(([, isValid])=>!isValid).map(([chunkName])=>chunkName);
if (0 === invalidChunks.length) return;
let plural = invalidChunks.length > 1;
throw Error([
`Error splitting route module: ${normalizeRelativeFilePath(id, config.appDirectory)}`,
invalidChunks.map((name)=>`- ${name}`).join('\n'),
`${plural ? 'These exports' : 'This export'} could not be split into ${plural ? 'their own chunks' : 'its own chunk'} because ${plural ? 'they share' : 'it shares'} code with other exports. You should extract any shared code into its own module and then import it within the route module.`
].join('\n\n'));
}, getRouteChunkEntryName = (routeId, chunkName)=>`${routeId}-${routeChunkEntrySuffix[chunkName]}`, setBoundedCacheEntry = (cache, key, value, maxEntries)=>{
if (maxEntries <= 0) return void cache.clear();
if (!cache.has(key) && cache.size >= maxEntries) {
let oldestEntry = cache.keys().next();
oldestEntry.done || cache.delete(oldestEntry.value);
}
cache.set(key, value);
}, exportInfoCache = new Map(), routeModuleAnalysisCache = new Map(), getParseErrors = (result)=>result.diagnostics.filter((diagnostic)=>'error' === diagnostic.severity), getParseErrorMessage = (errors)=>errors.map((error)=>error.message).join('\n'), parseProgram = (code, resourcePath)=>{
let sourcePath = resourcePath ? resourcePath.replace(/[?#].*$/, '') : void 0, lang = sourcePath ? langFromPath(sourcePath) : 'tsx', result = parse(code, {
sourceType: 'module',
lang
}), errors = getParseErrors(result);
if (0 === errors.length) return result.program ?? result;
if (!sourcePath || 'ts' !== lang && 'tsx' !== lang) throw Error(getParseErrorMessage(errors));
let normalizedResult = parse(rspack.experiments.swc.transformSync(code, {
filename: sourcePath,
jsc: {
parser: {
syntax: "typescript",
tsx: 'tsx' === lang
}
}
}).code, {
sourceType: 'module',
lang: 'js'
}), normalizedErrors = getParseErrors(normalizedResult);
if (normalizedErrors.length > 0) throw Error(getParseErrorMessage(normalizedErrors));
return normalizedResult.program ?? normalizedResult;
}, cachePromiseOnReject = (promise, invalidate)=>promise.catch((error)=>{
throw invalidate(), error;
}), isTypeOnlyExport = (node)=>'type' === node.exportKind || 'TSExportAssignment' === node.type || node.declaration?.declare === !0 || 'ExportDefaultDeclaration' === node.type && node.declaration?.type === 'TSInterfaceDeclaration', collectProgramExportNames = (program)=>{
let exportNames = new Set();
for (let statement of program.body ?? []){
if (isTypeOnlyExport(statement)) continue;
if ('ExportAllDeclaration' === statement.type) {
let exported = getExportedName(statement.exported);
exported && exportNames.add(exported);
continue;
}
if ('ExportDefaultDeclaration' === statement.type) {
exportNames.add('default');
continue;
}
if ('ExportNamedDeclaration' !== statement.type) continue;
let declaration = statement.declaration;
if (declaration) {
if ('VariableDeclaration' === declaration.type) for (let declarator of declaration.declarations ?? [])for (let name of getIdentifierNamesFromPattern(declarator.id))exportNames.add(name);
else ('FunctionDeclaration' === declaration.type || 'ClassDeclaration' === declaration.type || 'TSEnumDeclaration' === declaration.type) && declaration.id?.name && exportNames.add(declaration.id.name);
continue;
}
for (let specifier of statement.specifiers ?? []){
if ('type' === specifier.exportKind) continue;
let exported = getExportedName(specifier.exported);
exported && exportNames.add(exported);
}
}
return Array.from(exportNames);
}, collectExportAllModules = (program)=>{
let modules = [];
for (let statement of program.body ?? []){
if ('ExportAllDeclaration' !== statement.type || isTypeOnlyExport(statement) || statement.exported) continue;
let source = statement.source?.value;
'string' == typeof source && modules.push(source);
}
return modules;
}, getExportNames = async (code, resourcePath)=>(await getExportNamesAndExportAll(code, resourcePath)).exportNames, getExportNamesAndExportAll = async (code, resourcePath)=>{
var code1, resourcePath1;
let lang, trackedExportInfo, cacheKey = (code1 = code, lang = (resourcePath1 = resourcePath) ? langFromPath(resourcePath1.replace(/[?#].*$/, '')) : 'inline', `${lang}\0${code1}`), cached = exportInfoCache.get(cacheKey);
return cached || (trackedExportInfo = cachePromiseOnReject((async ()=>{
let program = parseProgram(code, resourcePath);
return {
exportNames: collectProgramExportNames(program),
exportAllModules: collectExportAllModules(program)
};
})(), ()=>{
exportInfoCache.get(cacheKey) === trackedExportInfo && exportInfoCache.delete(cacheKey);
}), setBoundedCacheEntry(exportInfoCache, cacheKey, trackedExportInfo, 2048), trackedExportInfo);
}, getRouteModuleAnalysis = async (resourcePath)=>{
let trackedAnalysis, stats = await stat(resourcePath), cached = routeModuleAnalysisCache.get(resourcePath);
return cached?.mtimeMs === stats.mtimeMs && cached.size === stats.size ? cached.analysis : (trackedAnalysis = cachePromiseOnReject((async ()=>{
let source = await readFile(resourcePath, 'utf8'), program = parseProgram(source, resourcePath);
return {
code: source,
exports: collectProgramExportNames(program),
exportAllModules: collectExportAllModules(program)
};
})(), ()=>{
routeModuleAnalysisCache.get(resourcePath)?.analysis === trackedAnalysis && routeModuleAnalysisCache.delete(resourcePath);
}), setBoundedCacheEntry(routeModuleAnalysisCache, resourcePath, {
mtimeMs: stats.mtimeMs,
size: stats.size,
analysis: trackedAnalysis
}, 2048), trackedAnalysis);
}, tryStat = (path)=>statSync(path, {
throwIfNoEntry: !1
}) ?? null, PACKAGE_IMPORT_CONDITIONS = new Set([
'import',
'node'
]), PACKAGE_RESOLUTION_NOT_APPLICABLE = Symbol('package resolution not applicable'), resolveIndexFile = (dirPath)=>{
for (let ext of JS_EXTENSIONS){
let candidate = resolve(dirPath, `index${ext}`), stats = tryStat(candidate);
if (stats?.isFile()) return candidate;
}
return null;
}, resolvePathWithExtensions = (basePath)=>{
let stats = tryStat(basePath);
if (stats?.isFile()) return basePath;
if (stats?.isDirectory()) return resolveIndexFile(basePath);
for (let ext of JS_EXTENSIONS){
let candidate = `${basePath}${ext}`, candidateStats = tryStat(candidate);
if (candidateStats?.isFile()) return candidate;
}
return resolveIndexFile(basePath);
}, resolvePackageExportTarget = (target)=>{
if (!target) return null;
if ('string' == typeof target) return target;
if (Array.isArray(target)) {
for (let nestedTarget of target){
let resolvedTarget = resolvePackageExportTarget(nestedTarget);
if (resolvedTarget) return resolvedTarget;
}
return null;
}
for (let [condition, nestedTarget] of Object.entries(target))if ('default' === condition || PACKAGE_IMPORT_CONDITIONS.has(condition)) {
let resolvedTarget = resolvePackageExportTarget(nestedTarget);
if (resolvedTarget) return resolvedTarget;
}
return null;
}, resolveExportAllModule = (specifier, importerPath)=>{
if (specifier.startsWith('.') || specifier.startsWith('/')) {
let resolvedPath = resolvePathWithExtensions(specifier.startsWith('/') ? specifier : resolve(dirname(importerPath), specifier));
if (resolvedPath) return resolvedPath;
}
let importResolvedPath = ((specifier, importerPath)=>{
let parsedSpecifier = ((specifier)=>{
if (specifier.startsWith('.') || specifier.startsWith('/') || specifier.startsWith('#')) return null;
let parts = specifier.split('/'), packageName = specifier.startsWith('@') ? parts.slice(0, 2).join('/') : parts[0], packagePathParts = specifier.startsWith('@') ? parts.slice(2) : parts.slice(1);
return {
packageName,
packageSubpath: packagePathParts.length > 0 ? `./${packagePathParts.join('/')}` : '.'
};
})(specifier);
if (!parsedSpecifier) return PACKAGE_RESOLUTION_NOT_APPLICABLE;
let packageDirectory = ((importerPath, packageName)=>{
let currentDirectory = dirname(importerPath);
for(;;){
let packageDirectory = resolve(currentDirectory, 'node_modules', packageName), packageJsonPath = resolve(packageDirectory, 'package.json');
if (tryStat(packageJsonPath)?.isFile()) return packageDirectory;
let parentDirectory = dirname(currentDirectory);
if (parentDirectory === currentDirectory) return null;
currentDirectory = parentDirectory;
}
})(importerPath, parsedSpecifier.packageName);
if (!packageDirectory) return PACKAGE_RESOLUTION_NOT_APPLICABLE;
let packageJson = ((packageDirectory)=>{
try {
return JSON.parse(readFileSync(resolve(packageDirectory, 'package.json'), 'utf8'));
} catch {
return null;
}
})(packageDirectory);
return packageJson && (void 0 !== packageJson.exports || packageJson.module || packageJson.main) ? ((packageDirectory, packageSubpath, packageJson)=>{
let exports = packageJson.exports;
if (!exports) {
let entry = packageJson.module ?? packageJson.main;
return entry ? resolvePathWithExtensions(resolve(packageDirectory, entry)) : null;
}
let resolvedTarget = resolvePackageExportTarget('object' == typeof exports && !Array.isArray(exports) && Object.keys(exports).some((key)=>key.startsWith('.')) ? exports[packageSubpath] : '.' === packageSubpath ? exports : void 0);
return resolvedTarget && resolvedTarget.startsWith('./') ? resolvePathWithExtensions(resolve(packageDirectory, resolvedTarget)) : null;
})(packageDirectory, parsedSpecifier.packageSubpath, packageJson) : PACKAGE_RESOLUTION_NOT_APPLICABLE;
})(specifier, importerPath);
if (importResolvedPath !== PACKAGE_RESOLUTION_NOT_APPLICABLE) return importResolvedPath;
try {
return createRequire(importerPath).resolve(specifier);
} catch {
return null;
}
}, createBundlerRouteExportResolver = (resolveModule)=>(specifier, importerPath)=>new Promise((resolvePromise)=>{
resolveModule(dirname(importerPath), specifier, (error, resolved)=>{
resolvePromise(error || !resolved ? null : resolved);
});
}), collectClientOnlyStubExportNames = async (code, resourcePath, resolveModule = resolveExportAllModule)=>{
let { exportNames: directExportNames, exportAllModules } = await getExportNamesAndExportAll(code, resourcePath), exportNames = new Set(directExportNames), unresolvedExportAll = new Set(), visitedModules = new Set(), collectExportNamesFromModule = async (modulePath)=>{
if (visitedModules.has(modulePath)) return;
visitedModules.add(modulePath);
let { exports: moduleExportNames, exportAllModules: moduleExportAll } = await getRouteModuleAnalysis(modulePath);
for (let name of moduleExportNames)'default' !== name && exportNames.add(name);
for (let nestedSpecifier of moduleExportAll){
let nestedPath = await resolveModule(nestedSpecifier, modulePath);
if (!nestedPath) {
unresolvedExportAll.add(nestedSpecifier);
continue;
}
await collectExportNamesFromModule(nestedPath);
}
};
for (let specifier of exportAllModules){
let resolvedPath = await resolveModule(specifier, resourcePath);
if (!resolvedPath) {
unresolvedExportAll.add(specifier);
continue;
}
await collectExportNamesFromModule(resolvedPath);
}
if (unresolvedExportAll.size > 0) throw Error(`[${PLUGIN_NAME}] Client-only module uses \`export * from\` with unresolvable specifier(s): ${Array.from(unresolvedExportAll).map((spec)=>`\`${spec}\``).join(', ')}. Please explicitly re-export named bindings in \`${relative(process.cwd(), resourcePath)}\`.`);
return exportNames;
}, HMR_PATCHABLE_ROUTE_FLAGS = [
'hasAction',
'hasClientAction',
'hasClientLoader',
'hasClientMiddleware',
'hasErrorBoundary',
'hasLoader'
], HMR_FLAG_EXPORT_NAME = {
hasAction: SERVER_EXPORTS.action,
hasClientAction: CLIENT_EXPORTS.clientAction,
hasClientLoader: CLIENT_EXPORTS.clientLoader,
hasClientMiddleware: CLIENT_EXPORTS.clientMiddleware,
hasErrorBoundary: CLIENT_EXPORTS.ErrorBoundary,
hasLoader: SERVER_EXPORTS.loader
}, createRouteClientEntryArtifact = async ({ code, resourcePath, environmentName, isBuild, routeChunkCache, routeChunkConfig, routeId, devHmr })=>{
let isServer = 'node' === environmentName, routeChunkInfo = !isServer && isBuild && shouldAnalyzeRouteChunks(routeChunkConfig, resourcePath, code) ? await detectRouteChunksIfEnabled(routeChunkCache, routeChunkConfig, resourcePath, code) : null;
return {
code: (({ exportNames, chunkedExports, isServer, resourcePath, routeId, devHmr })=>{
let exports, flags, chunkedExportSet = chunkedExports.length > 0 ? new Set(chunkedExports) : void 0, reexports = exportNames.filter((exp)=>!chunkedExportSet?.has(exp) && (CLIENT_ROUTE_EXPORTS_SET.has(exp) || isServer && SERVER_ONLY_ROUTE_EXPORTS_SET.has(exp))).sort(), target = `${resourcePath}?react-router-route`, reexportCode = `export { ${reexports.join(', ')} } from ${JSON.stringify(target)};`;
return !devHmr || isServer || void 0 === routeId ? reexportCode : reexportCode + (({ routeId, target, acceptTarget, flags })=>{
let targetJson = JSON.stringify(target), acceptTargetJson = JSON.stringify(acceptTarget);
return `
import * as __rrm from ${targetJson};
import {
registerReactRouterRouteExports as __rrr,
scheduleReactRouterRouteUpdate as __rru,
} from "virtual/react-router/hmr-runtime";
const __rrid = ${JSON.stringify(routeId)};
const __rrf = ${flags};
const __rrg = () => __rrm;
const __rru0 = () => {
__rrr(__rrid, __rrm);
__rru(__rrid, __rrf, __rrg);
};
__rrr(__rrid, __rrm);
if (import.meta.webpackHot) {
const __rrh = import.meta.webpackHot;
__rrh.accept(${acceptTargetJson}, __rru0);
__rrh.accept();
__rrh.dispose(data => { data.__rr = true; });
if (__rrh.data && __rrh.data.__rr) __rru0();
}
`;
})({
routeId,
target,
acceptTarget: `./${basename(resourcePath)}?react-router-route`,
flags: (exports = new Set(exportNames), flags = 0, HMR_PATCHABLE_ROUTE_FLAGS.forEach((flag, index)=>{
exports.has(HMR_FLAG_EXPORT_NAME[flag]) && (flags |= 1 << index);
}), flags)
});
})({
exportNames: routeChunkInfo?.exportNames ?? await getExportNames(code, resourcePath),
chunkedExports: routeChunkInfo?.chunkedExports ?? [],
isServer,
resourcePath,
routeId,
devHmr: devHmr && !isBuild
})
};
}, createRouteChunkArtifact = async ({ code, resource, resourcePath, isBuild, routeChunkCache, routeChunkConfig })=>{
let splitRouteModules = routeChunkConfig.splitRouteModules;
if (!isBuild || !splitRouteModules) return {
code: 'export {};',
map: null
};
let chunkName = ((id)=>{
let queryIndex = id.indexOf(routeChunkQueryStringPrefix);
if (-1 === queryIndex) return null;
let chunkNameStart = queryIndex + routeChunkQueryStringPrefix.length, chunkNameEnd = id.indexOf('&', chunkNameStart), chunkName = id.slice(chunkNameStart, -1 === chunkNameEnd ? void 0 : chunkNameEnd);
return 'main' === chunkName || routeChunkExportNames.includes(chunkName) ? chunkName : null;
})(resource);
if (!chunkName) throw Error(`Invalid route chunk name in "${resource}"`);
if ('main' !== chunkName && !code.includes(chunkName)) return {
code: 'export {};',
map: null
};
let chunk = await getRouteChunkIfEnabled(routeChunkCache, routeChunkConfig, resourcePath, chunkName, code);
if ('enforce' === splitRouteModules && 'main' === chunkName && chunk) {
let exportNameSet, exportNames = await getExportNames(chunk, resourcePath);
validateRouteChunks({
config: routeChunkConfig,
id: resourcePath,
valid: (exportNameSet = new Set(exportNames), createRouteChunkExportMap((exportName)=>!exportNameSet.has(exportName)))
});
}
return {
code: chunk ?? 'export {};',
map: null
};
}, defaultRouteChunkCache = new Map(), getRouteChunkCache = (options)=>options?.routeChunkCache ?? defaultRouteChunkCache, splitRouteExports = async (task, options)=>{
let { exportNames, hasRouteChunks, chunkedExports } = await detectRouteChunksIfEnabled(getRouteChunkCache(options), task.routeChunkConfig, task.resourcePath, task.code);
if (!hasRouteChunks) return {
code: task.code,
map: null
};
let chunkedExportSet = new Set(chunkedExports), mainChunkReexports = exportNames.filter((name)=>!chunkedExportSet.has(name)).join(', '), chunkBasePath = `./${basename(task.resourcePath)}`;
return {
code: [
mainChunkReexports ? `export { ${mainChunkReexports} } from "${getRouteChunkModuleId(chunkBasePath, 'main')}";` : null,
...chunkedExports.map((exportName)=>`export { ${exportName} } from "${getRouteChunkModuleId(chunkBasePath, exportName)}";`)
].filter(Boolean).join('\n'),
map: null
};
}, createClientOnlyStub = async (task)=>({
code: Array.from(await collectClientOnlyStubExportNames(task.code, task.resourcePath, task.resolveExportAllModule)).map((name)=>'default' === name ? 'export default undefined;' : `export const ${name} = undefined;`).join('\n'),
map: null
}), callResolvesToComponent = (node)=>{
let args = node.arguments ?? [];
if (0 === args.length) return !1;
let callee = node.callee;
if (!callee || 'Import' === callee.type) return !1;
if ('Identifier' === callee.type) {
let calleeName = callee.name ?? '';
if (calleeName.startsWith('require') || calleeName.startsWith('import')) return !1;
} else if ('MemberExpression' !== callee.type) return !1;
var node1 = args[0];
switch(node1?.type){
case 'FunctionExpression':
return !0;
case 'ArrowFunctionExpression':
return node1.body?.type !== 'ArrowFunctionExpression';
case '