UNPKG

@tanstack/router-plugin

Version:

Modern and scalable routing for React applications

1 lines 17.5 kB
{"version":3,"file":"router-code-splitter-plugin.cjs","names":[],"sources":["../../../src/core/router-code-splitter-plugin.ts"],"sourcesContent":["/**\n * It is important to familiarize yourself with how the code-splitting works in this plugin.\n * https://github.com/TanStack/router/pull/3355\n */\n\nimport { fileURLToPath, pathToFileURL } from 'node:url'\nimport { decodeIdentifier, logDiff } from '@tanstack/router-utils'\nimport { getConfig, splitGroupingsSchema } from './config'\nimport {\n compileCodeSplitReferenceRoute,\n compileCodeSplitSharedRoute,\n compileCodeSplitVirtualRoute,\n computeSharedBindings,\n detectCodeSplitGroupingsFromRoute,\n} from './code-splitter/compilers'\nimport { getReferenceRouteCompilerPlugins } from './code-splitter/plugins/framework-plugins'\nimport {\n defaultCodeSplitGroupings,\n splitRouteIdentNodes,\n tsrShared,\n tsrSplit,\n} from './constants'\nimport { debug, normalizePath, routeFactoryCallCodeFilter } from './utils'\nimport { createRouterPluginContext } from './router-plugin-context'\nimport type { CodeSplitGroupings, SplitRouteIdentNodes } from './constants'\nimport type { GetRoutesByFileMapResultValue } from '@tanstack/router-generator'\nimport type { Config } from './config'\nimport type { RouterPluginContext } from './router-plugin-context'\nimport type {\n UnpluginFactory,\n TransformResult as UnpluginTransformResult,\n} from 'unplugin'\n\nconst CODE_SPLITTER_PLUGIN_NAME =\n 'tanstack-router:code-splitter:compile-reference-file'\n\ntype TransformationPluginInfo = {\n pluginNames: Array<string>\n pkg: string\n usage: string\n}\n\n/**\n * JSX transformation plugins grouped by framework.\n * These plugins must come AFTER the TanStack Router plugin in the Vite config.\n */\nconst TRANSFORMATION_PLUGINS_BY_FRAMEWORK: Record<\n string,\n Array<TransformationPluginInfo>\n> = {\n react: [\n {\n // Babel-based React plugin\n pluginNames: ['vite:react-babel', 'vite:react-refresh'],\n pkg: '@vitejs/plugin-react',\n usage: 'react()',\n },\n {\n // SWC-based React plugin\n pluginNames: ['vite:react-swc', 'vite:react-swc:resolve-runtime'],\n pkg: '@vitejs/plugin-react-swc',\n usage: 'reactSwc()',\n },\n {\n // OXC-based React plugin (deprecated but should still be handled)\n pluginNames: ['vite:react-oxc:config', 'vite:react-oxc:refresh-runtime'],\n pkg: '@vitejs/plugin-react-oxc',\n usage: 'reactOxc()',\n },\n ],\n solid: [\n {\n pluginNames: ['solid'],\n pkg: 'vite-plugin-solid',\n usage: 'solid()',\n },\n ],\n}\n\nexport function createRouterCodeSplitterPlugin(\n options: Partial<Config | (() => Config)> | undefined = {},\n routerPluginContext: RouterPluginContext,\n): ReturnType<UnpluginFactory<Partial<Config | (() => Config)> | undefined>> {\n let ROOT: string = process.cwd()\n let userConfig: Config\n\n function initUserConfig() {\n if (typeof options === 'function') {\n userConfig = options()\n } else {\n userConfig = getConfig(options, ROOT)\n }\n }\n const isProduction = process.env.NODE_ENV === 'production'\n // Map from normalized route file path → set of shared binding names.\n // Populated by the reference compiler, consumed by virtual and shared compilers.\n const sharedBindingsMap = new Map<string, Set<string>>()\n\n const getGlobalCodeSplitGroupings = () => {\n return (\n userConfig.codeSplittingOptions?.defaultBehavior ||\n defaultCodeSplitGroupings\n )\n }\n const getShouldSplitFn = () => {\n return userConfig.codeSplittingOptions?.splitBehavior\n }\n\n const handleCompilingReferenceFile = (\n code: string,\n id: string,\n generatorNodeInfo: GetRoutesByFileMapResultValue,\n ): UnpluginTransformResult => {\n if (debug) console.info('Compiling Route: ', id)\n\n const fromCode = detectCodeSplitGroupingsFromRoute({\n code,\n filename: id,\n })\n\n if (fromCode.groupings !== undefined) {\n const res = splitGroupingsSchema.safeParse(fromCode.groupings)\n if (!res.success) {\n const message = res.error.issues.map((e) => e.message).join('. ')\n throw new Error(\n `The groupings for the route \"${id}\" are invalid.\\n${message}`,\n )\n }\n }\n\n const userShouldSplitFn = getShouldSplitFn()\n\n const pluginSplitBehavior = userShouldSplitFn?.({\n routeId: generatorNodeInfo.routeId,\n }) as CodeSplitGroupings | undefined\n\n if (pluginSplitBehavior) {\n const res = splitGroupingsSchema.safeParse(pluginSplitBehavior)\n if (!res.success) {\n const message = res.error.issues.map((e) => e.message).join('. ')\n throw new Error(\n `The groupings returned when using \\`splitBehavior\\` for the route \"${id}\" are invalid.\\n${message}`,\n )\n }\n }\n\n const splitGroupings: CodeSplitGroupings =\n fromCode.groupings ?? pluginSplitBehavior ?? getGlobalCodeSplitGroupings()\n\n // Compute shared bindings before compiling the reference route\n const sharedBindings = computeSharedBindings({\n code,\n filename: id,\n codeSplitGroupings: splitGroupings,\n })\n if (sharedBindings.size > 0) {\n sharedBindingsMap.set(id, sharedBindings)\n } else {\n sharedBindingsMap.delete(id)\n }\n\n const addHmr =\n (userConfig.codeSplittingOptions?.addHmr ?? true) && !isProduction\n const hmrStyle = userConfig.plugin?.hmr?.style ?? 'vite'\n\n const compiledReferenceRoute = compileCodeSplitReferenceRoute({\n code,\n codeSplitGroupings: splitGroupings,\n targetFramework: userConfig.target,\n filename: id,\n id,\n deleteNodes: userConfig.codeSplittingOptions?.deleteNodes\n ? new Set(userConfig.codeSplittingOptions.deleteNodes)\n : undefined,\n addHmr,\n hmrStyle,\n hmrRouteId: generatorNodeInfo.routeId,\n sharedBindings: sharedBindings.size > 0 ? sharedBindings : undefined,\n compilerPlugins: [\n ...(getReferenceRouteCompilerPlugins({\n targetFramework: userConfig.target,\n addHmr,\n hmrStyle,\n }) ?? []),\n ...(userConfig.codeSplittingOptions?.compilerPlugins ?? []),\n ],\n })\n\n if (compiledReferenceRoute === null) {\n if (debug) {\n console.info(\n `No changes made to route \"${id}\", skipping code-splitting.`,\n )\n }\n return null\n }\n if (debug) {\n logDiff(code, compiledReferenceRoute.code)\n console.log('Output:\\n', compiledReferenceRoute.code + '\\n\\n')\n }\n\n return compiledReferenceRoute\n }\n\n const handleCompilingVirtualFile = (\n code: string,\n id: string,\n ): UnpluginTransformResult => {\n if (debug) console.info('Splitting Route: ', id)\n\n const [_, ...pathnameParts] = id.split('?')\n\n const searchParams = new URLSearchParams(pathnameParts.join('?'))\n const splitValue = searchParams.get(tsrSplit)\n\n if (!splitValue) {\n throw new Error(\n `The split value for the virtual route \"${id}\" was not found.`,\n )\n }\n\n const rawGrouping = decodeIdentifier(splitValue)\n const grouping = [...new Set(rawGrouping)].filter((p) =>\n splitRouteIdentNodes.includes(p as any),\n ) as Array<SplitRouteIdentNodes>\n\n const baseId = id.split('?')[0]!\n const resolvedSharedBindings = sharedBindingsMap.get(baseId)\n\n const result = compileCodeSplitVirtualRoute({\n code,\n filename: id,\n splitTargets: grouping,\n sharedBindings: resolvedSharedBindings,\n })\n\n if (debug) {\n logDiff(code, result.code)\n console.log('Output:\\n', result.code + '\\n\\n')\n }\n\n return result\n }\n\n return [\n {\n name: 'tanstack-router:code-splitter:compile-reference-file',\n enforce: 'pre',\n\n transform: {\n filter: {\n id: {\n exclude: [tsrSplit, tsrShared],\n // this is necessary for webpack / rspack to avoid matching .html files\n include: /\\.(m|c)?(j|t)sx?$/,\n },\n code: {\n include: routeFactoryCallCodeFilter,\n },\n },\n handler(code, id) {\n const normalizedId = normalizePath(id)\n const generatorFileInfo =\n routerPluginContext.routesByFile.get(normalizedId)\n if (generatorFileInfo) {\n return handleCompilingReferenceFile(\n code,\n normalizedId,\n generatorFileInfo,\n )\n }\n\n return null\n },\n },\n\n vite: {\n configResolved(config) {\n ROOT = config.root\n initUserConfig()\n\n // Validate plugin order - router must come before JSX transformation plugins\n const routerPluginIndex = config.plugins.findIndex(\n (p) => p.name === CODE_SPLITTER_PLUGIN_NAME,\n )\n\n if (routerPluginIndex === -1) return\n\n const frameworkPlugins =\n TRANSFORMATION_PLUGINS_BY_FRAMEWORK[userConfig.target]\n if (!frameworkPlugins) return\n\n for (const transformPlugin of frameworkPlugins) {\n const transformPluginIndex = config.plugins.findIndex((p) =>\n transformPlugin.pluginNames.includes(p.name),\n )\n\n if (\n transformPluginIndex !== -1 &&\n transformPluginIndex < routerPluginIndex\n ) {\n throw new Error(\n `Plugin order error: '${transformPlugin.pkg}' is placed before '@tanstack/router-plugin'.\\n\\n` +\n `The TanStack Router plugin must come BEFORE JSX transformation plugins.\\n\\n` +\n `Please update your Vite config:\\n\\n` +\n ` plugins: [\\n` +\n ` tanstackRouter(),\\n` +\n ` ${transformPlugin.usage},\\n` +\n ` ]\\n`,\n )\n }\n }\n },\n applyToEnvironment(environment) {\n if (userConfig.plugin?.vite?.environmentName) {\n return userConfig.plugin.vite.environmentName === environment.name\n }\n return true\n },\n },\n\n rspack() {\n ROOT = process.cwd()\n initUserConfig()\n },\n\n webpack() {\n ROOT = process.cwd()\n initUserConfig()\n },\n },\n {\n name: 'tanstack-router:code-splitter:compile-virtual-file',\n enforce: 'pre',\n\n transform: {\n filter: {\n id: /tsr-split/,\n },\n handler(code, id) {\n const url = pathToFileURL(id)\n url.searchParams.delete('v')\n const normalizedId = normalizePath(fileURLToPath(url))\n return handleCompilingVirtualFile(code, normalizedId)\n },\n },\n\n vite: {\n applyToEnvironment(environment) {\n if (userConfig.plugin?.vite?.environmentName) {\n return userConfig.plugin.vite.environmentName === environment.name\n }\n return true\n },\n },\n },\n {\n name: 'tanstack-router:code-splitter:compile-shared-file',\n enforce: 'pre',\n\n transform: {\n filter: {\n id: /tsr-shared/,\n },\n handler(code, id) {\n const url = pathToFileURL(id)\n url.searchParams.delete('v')\n const normalizedId = normalizePath(fileURLToPath(url))\n const [baseId] = normalizedId.split('?')\n\n if (!baseId) return null\n\n const sharedBindings = sharedBindingsMap.get(baseId)\n if (!sharedBindings || sharedBindings.size === 0) return null\n\n if (debug) console.info('Compiling Shared Module: ', id)\n\n const result = compileCodeSplitSharedRoute({\n code,\n sharedBindings,\n filename: normalizedId,\n })\n\n if (debug) {\n logDiff(code, result.code)\n console.log('Output:\\n', result.code + '\\n\\n')\n }\n\n return result\n },\n },\n\n vite: {\n applyToEnvironment(environment) {\n if (userConfig.plugin?.vite?.environmentName) {\n return userConfig.plugin.vite.environmentName === environment.name\n }\n return true\n },\n },\n },\n ]\n}\n\nexport const unpluginRouterCodeSplitterFactory: UnpluginFactory<\n Partial<Config | (() => Config)> | undefined\n> = (options = {}) => {\n return createRouterCodeSplitterPlugin(options, createRouterPluginContext())\n}\n"],"mappings":"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"}