UNPKG

@tanstack/router-plugin

Version:

Modern and scalable routing for React applications

738 lines (737 loc) 29.6 kB
"use strict"; Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" }); const t = require("@babel/types"); const babel = require("@babel/core"); const template = require("@babel/template"); const babelDeadCodeElimination = require("babel-dead-code-elimination"); const routerUtils = require("@tanstack/router-utils"); const constants = require("../constants.cjs"); const routeHmrStatement = require("../route-hmr-statement.cjs"); const pathIds = require("./path-ids.cjs"); const frameworkOptions = require("./framework-options.cjs"); function _interopNamespaceDefault(e) { const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } }); if (e) { for (const k in e) { if (k !== "default") { const d = Object.getOwnPropertyDescriptor(e, k); Object.defineProperty(n, k, d.get ? d : { enumerable: true, get: () => e[k] }); } } } n.default = e; return Object.freeze(n); } const t__namespace = /* @__PURE__ */ _interopNamespaceDefault(t); const template__namespace = /* @__PURE__ */ _interopNamespaceDefault(template); const SPLIT_NODES_CONFIG = /* @__PURE__ */ new Map([ [ "loader", { routeIdent: "loader", localImporterIdent: "$$splitLoaderImporter", // const $$splitLoaderImporter = () => import('...') splitStrategy: "lazyFn", localExporterIdent: "SplitLoader", // const SplitLoader = ... exporterIdent: "loader" // export { SplitLoader as loader } } ], [ "component", { routeIdent: "component", localImporterIdent: "$$splitComponentImporter", // const $$splitComponentImporter = () => import('...') splitStrategy: "lazyRouteComponent", localExporterIdent: "SplitComponent", // const SplitComponent = ... exporterIdent: "component" // export { SplitComponent as component } } ], [ "pendingComponent", { routeIdent: "pendingComponent", localImporterIdent: "$$splitPendingComponentImporter", // const $$splitPendingComponentImporter = () => import('...') splitStrategy: "lazyRouteComponent", localExporterIdent: "SplitPendingComponent", // const SplitPendingComponent = ... exporterIdent: "pendingComponent" // export { SplitPendingComponent as pendingComponent } } ], [ "errorComponent", { routeIdent: "errorComponent", localImporterIdent: "$$splitErrorComponentImporter", // const $$splitErrorComponentImporter = () => import('...') splitStrategy: "lazyRouteComponent", localExporterIdent: "SplitErrorComponent", // const SplitErrorComponent = ... exporterIdent: "errorComponent" // export { SplitErrorComponent as errorComponent } } ], [ "notFoundComponent", { routeIdent: "notFoundComponent", localImporterIdent: "$$splitNotFoundComponentImporter", // const $$splitNotFoundComponentImporter = () => import('...') splitStrategy: "lazyRouteComponent", localExporterIdent: "SplitNotFoundComponent", // const SplitNotFoundComponent = ... exporterIdent: "notFoundComponent" // export { SplitNotFoundComponent as notFoundComponent } } ] ]); const KNOWN_SPLIT_ROUTE_IDENTS = [...SPLIT_NODES_CONFIG.keys()]; function addSplitSearchParamToFilename(filename, grouping) { const [bareFilename] = filename.split("?"); const params = new URLSearchParams(); params.append(constants.tsrSplit, pathIds.createIdentifier(grouping)); return `${bareFilename}?${params.toString()}`; } function removeSplitSearchParamFromFilename(filename) { const [bareFilename] = filename.split("?"); return bareFilename; } function compileCodeSplitReferenceRoute(opts) { const ast = routerUtils.parseAst(opts); const refIdents = babelDeadCodeElimination.findReferencedIdentifiers(ast); function findIndexForSplitNode(str) { return opts.codeSplitGroupings.findIndex( (group) => group.includes(str) ); } const frameworkOptions$1 = frameworkOptions.getFrameworkOptions(opts.targetFramework); const PACKAGE = frameworkOptions$1.package; const LAZY_ROUTE_COMPONENT_IDENT = frameworkOptions$1.idents.lazyRouteComponent; const LAZY_FN_IDENT = frameworkOptions$1.idents.lazyFn; babel.traverse(ast, { Program: { enter(programPath) { const removableImportPaths = /* @__PURE__ */ new Set([]); programPath.traverse({ CallExpression: (path) => { if (!t__namespace.isIdentifier(path.node.callee)) { return; } if (!(path.node.callee.name === "createRoute" || path.node.callee.name === "createFileRoute")) { return; } function babelHandleReference(routeOptions) { const hasImportedOrDefinedIdentifier = (name) => { return programPath.scope.hasBinding(name); }; if (t__namespace.isObjectExpression(routeOptions)) { routeOptions.properties.forEach((prop) => { if (t__namespace.isObjectProperty(prop)) { if (t__namespace.isIdentifier(prop.key)) { const codeSplitGroupingByKey = findIndexForSplitNode( prop.key.name ); if (codeSplitGroupingByKey === -1) { return; } const codeSplitGroup = [ ...new Set( opts.codeSplitGroupings[codeSplitGroupingByKey] ) ]; const key = prop.key.name; const isNodeConfigAvailable = SPLIT_NODES_CONFIG.has( key ); if (!isNodeConfigAvailable) { return; } const splitNodeMeta = SPLIT_NODES_CONFIG.get(key); const splitUrl = addSplitSearchParamToFilename( opts.filename, codeSplitGroup ); if (splitNodeMeta.splitStrategy === "lazyRouteComponent") { const value = prop.value; let shouldSplit = true; if (t__namespace.isIdentifier(value)) { const existingImportPath = getImportSpecifierAndPathFromLocalName( programPath, value.name ).path; if (existingImportPath) { removableImportPaths.add(existingImportPath); } const isExported = hasExport(ast, value); shouldSplit = !isExported; if (shouldSplit) { removeIdentifierLiteral(path, value); } } if (!shouldSplit) { return; } if (!hasImportedOrDefinedIdentifier( LAZY_ROUTE_COMPONENT_IDENT )) { programPath.unshiftContainer("body", [ template__namespace.statement( `import { ${LAZY_ROUTE_COMPONENT_IDENT} } from '${PACKAGE}'` )() ]); } if (!hasImportedOrDefinedIdentifier( splitNodeMeta.localImporterIdent )) { programPath.unshiftContainer("body", [ template__namespace.statement( `const ${splitNodeMeta.localImporterIdent} = () => import('${splitUrl}')` )() ]); } if (key === "component") { prop.value = template__namespace.expression( `${LAZY_ROUTE_COMPONENT_IDENT}(${splitNodeMeta.localImporterIdent}, '${splitNodeMeta.exporterIdent}', () => Route.ssr)` )(); } else { prop.value = template__namespace.expression( `${LAZY_ROUTE_COMPONENT_IDENT}(${splitNodeMeta.localImporterIdent}, '${splitNodeMeta.exporterIdent}')` )(); } if (opts.runtimeEnv !== "prod") { programPath.pushContainer("body", routeHmrStatement.routeHmrStatement); } } if (splitNodeMeta.splitStrategy === "lazyFn") { const value = prop.value; let shouldSplit = true; if (t__namespace.isIdentifier(value)) { const existingImportPath = getImportSpecifierAndPathFromLocalName( programPath, value.name ).path; if (existingImportPath) { removableImportPaths.add(existingImportPath); } const isExported = hasExport(ast, value); shouldSplit = !isExported; if (shouldSplit) { removeIdentifierLiteral(path, value); } } if (!shouldSplit) { return; } if (!hasImportedOrDefinedIdentifier(LAZY_FN_IDENT)) { programPath.unshiftContainer( "body", template__namespace.smart( `import { ${LAZY_FN_IDENT} } from '${PACKAGE}'` )() ); } if (!hasImportedOrDefinedIdentifier( splitNodeMeta.localImporterIdent )) { programPath.unshiftContainer("body", [ template__namespace.statement( `const ${splitNodeMeta.localImporterIdent} = () => import('${splitUrl}')` )() ]); } prop.value = template__namespace.expression( `${LAZY_FN_IDENT}(${splitNodeMeta.localImporterIdent}, '${splitNodeMeta.exporterIdent}')` )(); } } } programPath.scope.crawl(); }); } } if (t__namespace.isCallExpression(path.parentPath.node)) { const options = resolveIdentifier( path, path.parentPath.node.arguments[0] ); babelHandleReference(options); } else if (t__namespace.isVariableDeclarator(path.parentPath.node)) { const caller = resolveIdentifier(path, path.parentPath.node.init); if (t__namespace.isCallExpression(caller)) { const options = resolveIdentifier(path, caller.arguments[0]); babelHandleReference(options); } } } }); if (removableImportPaths.size > 0) { programPath.traverse({ ImportDeclaration(path) { if (path.node.specifiers.length > 0) return; if (removableImportPaths.has(path.node.source.value)) { path.remove(); } } }); } } } }); babelDeadCodeElimination.deadCodeElimination(ast, refIdents); return routerUtils.generateFromAst(ast, { sourceMaps: true, sourceFileName: opts.filename, filename: opts.filename }); } function compileCodeSplitVirtualRoute(opts) { const ast = routerUtils.parseAst(opts); const refIdents = babelDeadCodeElimination.findReferencedIdentifiers(ast); const intendedSplitNodes = new Set(opts.splitTargets); const knownExportedIdents = /* @__PURE__ */ new Set(); babel.traverse(ast, { Program: { enter(programPath) { const trackedNodesToSplitByType = { component: void 0, loader: void 0, pendingComponent: void 0, errorComponent: void 0, notFoundComponent: void 0 }; programPath.traverse({ CallExpression: (path) => { if (!t__namespace.isIdentifier(path.node.callee)) { return; } if (!(path.node.callee.name === "createRoute" || path.node.callee.name === "createFileRoute")) { return; } function babelHandleVirtual(options) { if (t__namespace.isObjectExpression(options)) { options.properties.forEach((prop) => { if (t__namespace.isObjectProperty(prop)) { KNOWN_SPLIT_ROUTE_IDENTS.forEach((splitType) => { if (!t__namespace.isIdentifier(prop.key) || prop.key.name !== splitType) { return; } const value = prop.value; let isExported = false; if (t__namespace.isIdentifier(value)) { isExported = hasExport(ast, value); if (isExported) { knownExportedIdents.add(value.name); } } if (isExported && t__namespace.isIdentifier(value)) { removeExports(ast, value); } else { const meta = SPLIT_NODES_CONFIG.get(splitType); trackedNodesToSplitByType[splitType] = { node: prop.value, meta }; } }); } }); options.properties = []; } } if (t__namespace.isCallExpression(path.parentPath.node)) { const options = resolveIdentifier( path, path.parentPath.node.arguments[0] ); babelHandleVirtual(options); } else if (t__namespace.isVariableDeclarator(path.parentPath.node)) { const caller = resolveIdentifier(path, path.parentPath.node.init); if (t__namespace.isCallExpression(caller)) { const options = resolveIdentifier(path, caller.arguments[0]); babelHandleVirtual(options); } } } }); intendedSplitNodes.forEach((SPLIT_TYPE) => { const splitKey = trackedNodesToSplitByType[SPLIT_TYPE]; if (!splitKey) { return; } let splitNode = splitKey.node; const splitMeta = splitKey.meta; while (t__namespace.isIdentifier(splitNode)) { const binding = programPath.scope.getBinding(splitNode.name); splitNode = binding == null ? void 0 : binding.path.node; } if (splitNode) { if (t__namespace.isFunctionDeclaration(splitNode)) { programPath.pushContainer( "body", t__namespace.variableDeclaration("const", [ t__namespace.variableDeclarator( t__namespace.identifier(splitMeta.localExporterIdent), t__namespace.functionExpression( splitNode.id || null, // Anonymize the function expression splitNode.params, splitNode.body, splitNode.generator, splitNode.async ) ) ]) ); } else if (t__namespace.isFunctionExpression(splitNode) || t__namespace.isArrowFunctionExpression(splitNode)) { programPath.pushContainer( "body", t__namespace.variableDeclaration("const", [ t__namespace.variableDeclarator( t__namespace.identifier(splitMeta.localExporterIdent), splitNode ) ]) ); } else if (t__namespace.isImportSpecifier(splitNode) || t__namespace.isImportDefaultSpecifier(splitNode)) { programPath.pushContainer( "body", t__namespace.variableDeclaration("const", [ t__namespace.variableDeclarator( t__namespace.identifier(splitMeta.localExporterIdent), splitNode.local ) ]) ); } else if (t__namespace.isVariableDeclarator(splitNode)) { programPath.pushContainer( "body", t__namespace.variableDeclaration("const", [ t__namespace.variableDeclarator( t__namespace.identifier(splitMeta.localExporterIdent), splitNode.init ) ]) ); } else if (t__namespace.isCallExpression(splitNode)) { const outputSplitNodeCode = routerUtils.generateFromAst(splitNode).code; const splitNodeAst = babel.parse(outputSplitNodeCode); if (!splitNodeAst) { throw new Error( `Failed to parse the generated code for "${SPLIT_TYPE}" in the node type "${splitNode.type}"` ); } const statement = splitNodeAst.program.body[0]; if (!statement) { throw new Error( `Failed to parse the generated code for "${SPLIT_TYPE}" in the node type "${splitNode.type}" as no statement was found in the program body` ); } if (t__namespace.isExpressionStatement(statement)) { const expression = statement.expression; programPath.pushContainer( "body", t__namespace.variableDeclaration("const", [ t__namespace.variableDeclarator( t__namespace.identifier(splitMeta.localExporterIdent), expression ) ]) ); } else { throw new Error( `Unexpected expression type encounter for "${SPLIT_TYPE}" in the node type "${splitNode.type}"` ); } } else if (t__namespace.isConditionalExpression(splitNode)) { programPath.pushContainer( "body", t__namespace.variableDeclaration("const", [ t__namespace.variableDeclarator( t__namespace.identifier(splitMeta.localExporterIdent), splitNode ) ]) ); } else if (t__namespace.isTSAsExpression(splitNode)) { splitNode = splitNode.expression; programPath.pushContainer( "body", t__namespace.variableDeclaration("const", [ t__namespace.variableDeclarator( t__namespace.identifier(splitMeta.localExporterIdent), splitNode ) ]) ); } else { console.info("Unexpected splitNode type:", splitNode); throw new Error(`Unexpected splitNode type ☝️: ${splitNode.type}`); } } programPath.node.body = programPath.node.body.filter((node) => { return node !== splitNode; }); programPath.pushContainer("body", [ t__namespace.exportNamedDeclaration(null, [ t__namespace.exportSpecifier( t__namespace.identifier(splitMeta.localExporterIdent), // local variable name t__namespace.identifier(splitMeta.exporterIdent) // as what name it should be exported as ) ]) ]); }); programPath.traverse({ ExportNamedDeclaration(path) { if (path.node.declaration) { if (t__namespace.isVariableDeclaration(path.node.declaration)) { path.replaceWith( t__namespace.importDeclaration( path.node.declaration.declarations.map( (decl) => t__namespace.importSpecifier( t__namespace.identifier(decl.id.name), t__namespace.identifier(decl.id.name) ) ), t__namespace.stringLiteral( removeSplitSearchParamFromFilename(opts.filename) ) ) ); } } } }); } } }); babelDeadCodeElimination.deadCodeElimination(ast, refIdents); if (knownExportedIdents.size > 0) { const list = Array.from(knownExportedIdents).reduce((str, ident) => { str += ` - ${ident}`; return str; }, ""); const warningMessage = `These exports from "${opts.filename}" are not being code-split and will increase your bundle size: ${list} These should either have their export statements removed or be imported from another file that is not a route.`; console.warn(warningMessage); if (process.env.NODE_ENV !== "production") { const warningTemplate = template__namespace.statement( `console.warn(${JSON.stringify(warningMessage)})` )(); ast.program.body.unshift(warningTemplate); } } return routerUtils.generateFromAst(ast, { sourceMaps: true, sourceFileName: opts.filename, filename: opts.filename }); } function detectCodeSplitGroupingsFromRoute(opts) { const ast = routerUtils.parseAst(opts); let codeSplitGroupings = void 0; babel.traverse(ast, { Program: { enter(programPath) { programPath.traverse({ CallExpression(path) { if (!t__namespace.isIdentifier(path.node.callee)) { return; } if (!(path.node.callee.name === "createRoute" || path.node.callee.name === "createFileRoute")) { return; } function babelHandleSplittingGroups(routeOptions) { if (t__namespace.isObjectExpression(routeOptions)) { routeOptions.properties.forEach((prop) => { if (t__namespace.isObjectProperty(prop)) { if (t__namespace.isIdentifier(prop.key)) { if (prop.key.name === "codeSplitGroupings") { const value = prop.value; if (t__namespace.isArrayExpression(value)) { codeSplitGroupings = value.elements.map((group) => { if (t__namespace.isArrayExpression(group)) { return group.elements.map((node) => { if (!t__namespace.isStringLiteral(node)) { throw new Error( "You must provide a string literal for the codeSplitGroupings" ); } return node.value; }); } throw new Error( "You must provide arrays with codeSplitGroupings options." ); }); } else { throw new Error( "You must provide an array of arrays for the codeSplitGroupings." ); } } } } }); } } if (t__namespace.isCallExpression(path.parentPath.node)) { const options = resolveIdentifier( path, path.parentPath.node.arguments[0] ); babelHandleSplittingGroups(options); } else if (t__namespace.isVariableDeclarator(path.parentPath.node)) { const caller = resolveIdentifier(path, path.parentPath.node.init); if (t__namespace.isCallExpression(caller)) { const options = resolveIdentifier(path, caller.arguments[0]); babelHandleSplittingGroups(options); } } } }); } } }); return { groupings: codeSplitGroupings }; } function getImportSpecifierAndPathFromLocalName(programPath, name) { let specifier = null; let path = null; programPath.traverse({ ImportDeclaration(importPath) { const found = importPath.node.specifiers.find( (targetSpecifier) => targetSpecifier.local.name === name ); if (found) { specifier = found; path = importPath.node.source.value; } } }); return { specifier, path }; } function resolveIdentifier(path, node) { if (t__namespace.isIdentifier(node)) { const binding = path.scope.getBinding(node.name); if (binding) { const declarator = binding.path.node; if (t__namespace.isObjectExpression(declarator.init)) { return declarator.init; } else if (t__namespace.isFunctionDeclaration(declarator.init)) { return declarator.init; } } return void 0; } return node; } function removeIdentifierLiteral(path, node) { if (t__namespace.isIdentifier(node)) { const binding = path.scope.getBinding(node.name); if (binding) { binding.path.remove(); } } } function hasExport(ast, node) { let found = false; babel.traverse(ast, { ExportNamedDeclaration(path) { if (path.node.declaration) { if (t__namespace.isVariableDeclaration(path.node.declaration)) { path.node.declaration.declarations.forEach((decl) => { if (t__namespace.isVariableDeclarator(decl)) { if (t__namespace.isIdentifier(decl.id)) { if (decl.id.name === node.name) { found = true; } } } }); } if (t__namespace.isFunctionDeclaration(path.node.declaration)) { if (t__namespace.isIdentifier(path.node.declaration.id)) { if (path.node.declaration.id.name === node.name) { found = true; } } } } }, ExportDefaultDeclaration(path) { if (t__namespace.isIdentifier(path.node.declaration)) { if (path.node.declaration.name === node.name) { found = true; } } if (t__namespace.isFunctionDeclaration(path.node.declaration)) { if (t__namespace.isIdentifier(path.node.declaration.id)) { if (path.node.declaration.id.name === node.name) { found = true; } } } } }); return found; } function removeExports(ast, node) { let removed = false; babel.traverse(ast, { ExportNamedDeclaration(path) { if (path.node.declaration) { if (t__namespace.isVariableDeclaration(path.node.declaration)) { path.node.declaration.declarations.forEach((decl) => { if (t__namespace.isVariableDeclarator(decl)) { if (t__namespace.isIdentifier(decl.id)) { if (decl.id.name === node.name) { path.remove(); removed = true; } } } }); } else if (t__namespace.isFunctionDeclaration(path.node.declaration)) { if (t__namespace.isIdentifier(path.node.declaration.id)) { if (path.node.declaration.id.name === node.name) { path.remove(); removed = true; } } } } }, ExportDefaultDeclaration(path) { if (t__namespace.isIdentifier(path.node.declaration)) { if (path.node.declaration.name === node.name) { path.remove(); removed = true; } } else if (t__namespace.isFunctionDeclaration(path.node.declaration)) { if (t__namespace.isIdentifier(path.node.declaration.id)) { if (path.node.declaration.id.name === node.name) { path.remove(); removed = true; } } } } }); return removed; } exports.compileCodeSplitReferenceRoute = compileCodeSplitReferenceRoute; exports.compileCodeSplitVirtualRoute = compileCodeSplitVirtualRoute; exports.detectCodeSplitGroupingsFromRoute = detectCodeSplitGroupingsFromRoute; //# sourceMappingURL=compilers.cjs.map