UNPKG

@tanstack/router-plugin

Version:

Modern and scalable routing for React applications

245 lines (241 loc) 8.36 kB
"use strict"; Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" }); const node_url = require("node:url"); const routerUtils = require("@tanstack/router-utils"); const config = require("./config.cjs"); const compilers = require("./code-splitter/compilers.cjs"); const constants = require("./constants.cjs"); const pathIds = require("./code-splitter/path-ids.cjs"); const utils = require("./utils.cjs"); const PLUGIN_NAME = "unplugin:router-code-splitter"; const CODE_SPLITTER_PLUGIN_NAME = "tanstack-router:code-splitter:compile-reference-file"; const TRANSFORMATION_PLUGINS_BY_FRAMEWORK = { react: [ { // Babel-based React plugin pluginNames: ["vite:react-babel", "vite:react-refresh"], pkg: "@vitejs/plugin-react", usage: "react()" }, { // SWC-based React plugin pluginNames: ["vite:react-swc", "vite:react-swc:resolve-runtime"], pkg: "@vitejs/plugin-react-swc", usage: "reactSwc()" }, { // OXC-based React plugin (deprecated but should still be handled) pluginNames: ["vite:react-oxc:config", "vite:react-oxc:refresh-runtime"], pkg: "@vitejs/plugin-react-oxc", usage: "reactOxc()" } ], solid: [ { pluginNames: ["solid"], pkg: "vite-plugin-solid", usage: "solid()" } ] }; const unpluginRouterCodeSplitterFactory = (options = {}, { framework: _framework }) => { let ROOT = process.cwd(); let userConfig; function initUserConfig() { if (typeof options === "function") { userConfig = options(); } else { userConfig = config.getConfig(options, ROOT); } } const isProduction = process.env.NODE_ENV === "production"; const getGlobalCodeSplitGroupings = () => { return userConfig.codeSplittingOptions?.defaultBehavior || constants.defaultCodeSplitGroupings; }; const getShouldSplitFn = () => { return userConfig.codeSplittingOptions?.splitBehavior; }; const handleCompilingReferenceFile = (code, id, generatorNodeInfo) => { if (utils.debug) console.info("Compiling Route: ", id); const fromCode = compilers.detectCodeSplitGroupingsFromRoute({ code }); if (fromCode.groupings) { const res = config.splitGroupingsSchema.safeParse(fromCode.groupings); if (!res.success) { const message = res.error.errors.map((e) => e.message).join(". "); throw new Error( `The groupings for the route "${id}" are invalid. ${message}` ); } } const userShouldSplitFn = getShouldSplitFn(); const pluginSplitBehavior = userShouldSplitFn?.({ routeId: generatorNodeInfo.routePath }); if (pluginSplitBehavior) { const res = config.splitGroupingsSchema.safeParse(pluginSplitBehavior); if (!res.success) { const message = res.error.errors.map((e) => e.message).join(". "); throw new Error( `The groupings returned when using \`splitBehavior\` for the route "${id}" are invalid. ${message}` ); } } const splitGroupings = fromCode.groupings || pluginSplitBehavior || getGlobalCodeSplitGroupings(); const compiledReferenceRoute = compilers.compileCodeSplitReferenceRoute({ code, codeSplitGroupings: splitGroupings, targetFramework: userConfig.target, filename: id, id, deleteNodes: userConfig.codeSplittingOptions?.deleteNodes ? new Set(userConfig.codeSplittingOptions.deleteNodes) : void 0, addHmr: (userConfig.codeSplittingOptions?.addHmr ?? true) && !isProduction }); if (compiledReferenceRoute === null) { if (utils.debug) { console.info( `No changes made to route "${id}", skipping code-splitting.` ); } return null; } if (utils.debug) { routerUtils.logDiff(code, compiledReferenceRoute.code); console.log("Output:\n", compiledReferenceRoute.code + "\n\n"); } return compiledReferenceRoute; }; const handleCompilingVirtualFile = (code, id) => { if (utils.debug) console.info("Splitting Route: ", id); const [_, ...pathnameParts] = id.split("?"); const searchParams = new URLSearchParams(pathnameParts.join("?")); const splitValue = searchParams.get(constants.tsrSplit); if (!splitValue) { throw new Error( `The split value for the virtual route "${id}" was not found.` ); } const rawGrouping = pathIds.decodeIdentifier(splitValue); const grouping = [...new Set(rawGrouping)].filter( (p) => constants.splitRouteIdentNodes.includes(p) ); const result = compilers.compileCodeSplitVirtualRoute({ code, filename: id, splitTargets: grouping }); if (utils.debug) { routerUtils.logDiff(code, result.code); console.log("Output:\n", result.code + "\n\n"); } return result; }; const includedCode = [ "createFileRoute(", "createRootRoute(", "createRootRouteWithContext(" ]; return [ { name: "tanstack-router:code-splitter:compile-reference-file", enforce: "pre", transform: { filter: { id: { exclude: constants.tsrSplit, // this is necessary for webpack / rspack to avoid matching .html files include: /\.(m|c)?(j|t)sx?$/ }, code: { include: includedCode } }, handler(code, id) { const normalizedId = utils.normalizePath(id); const generatorFileInfo = globalThis.TSR_ROUTES_BY_ID_MAP?.get(normalizedId); if (generatorFileInfo && includedCode.some((included) => code.includes(included))) { return handleCompilingReferenceFile( code, normalizedId, generatorFileInfo ); } return null; } }, vite: { configResolved(config2) { ROOT = config2.root; initUserConfig(); const routerPluginIndex = config2.plugins.findIndex( (p) => p.name === CODE_SPLITTER_PLUGIN_NAME ); if (routerPluginIndex === -1) return; const frameworkPlugins = TRANSFORMATION_PLUGINS_BY_FRAMEWORK[userConfig.target]; if (!frameworkPlugins) return; for (const transformPlugin of frameworkPlugins) { const transformPluginIndex = config2.plugins.findIndex( (p) => transformPlugin.pluginNames.includes(p.name) ); if (transformPluginIndex !== -1 && transformPluginIndex < routerPluginIndex) { throw new Error( `Plugin order error: '${transformPlugin.pkg}' is placed before '@tanstack/router-plugin'. The TanStack Router plugin must come BEFORE JSX transformation plugins. Please update your Vite config: plugins: [ tanstackRouter(), ${transformPlugin.usage}, ] ` ); } } }, applyToEnvironment(environment) { if (userConfig.plugin?.vite?.environmentName) { return userConfig.plugin.vite.environmentName === environment.name; } return true; } }, rspack(compiler) { ROOT = process.cwd(); initUserConfig(); if (compiler.options.mode === "production") { compiler.hooks.done.tap(PLUGIN_NAME, () => { console.info("✅ " + PLUGIN_NAME + ": code-splitting done!"); }); } }, webpack(compiler) { ROOT = process.cwd(); initUserConfig(); if (compiler.options.mode === "production") { compiler.hooks.done.tap(PLUGIN_NAME, () => { console.info("✅ " + PLUGIN_NAME + ": code-splitting done!"); }); } } }, { name: "tanstack-router:code-splitter:compile-virtual-file", enforce: "pre", transform: { filter: { id: /tsr-split/ }, handler(code, id) { const url = node_url.pathToFileURL(id); url.searchParams.delete("v"); const normalizedId = utils.normalizePath(node_url.fileURLToPath(url)); return handleCompilingVirtualFile(code, normalizedId); } } } ]; }; exports.unpluginRouterCodeSplitterFactory = unpluginRouterCodeSplitterFactory; //# sourceMappingURL=router-code-splitter-plugin.cjs.map