@tanstack/start-plugin-core 1.132.0-alpha.8 → 1.132.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.
Files changed (85) hide show
  1. package/dist/esm/constants.d.ts +2 -1
  2. package/dist/esm/constants.js +3 -2
  3. package/dist/esm/constants.js.map +1 -1
  4. package/dist/esm/create-server-fn-plugin/compiler.d.ts +64 -0
  5. package/dist/esm/create-server-fn-plugin/compiler.js +364 -0
  6. package/dist/esm/create-server-fn-plugin/compiler.js.map +1 -0
  7. package/dist/esm/create-server-fn-plugin/handleCreateMiddleware.d.ts +5 -0
  8. package/dist/esm/{start-compiler-plugin/middleware.js → create-server-fn-plugin/handleCreateMiddleware.js} +11 -11
  9. package/dist/esm/create-server-fn-plugin/handleCreateMiddleware.js.map +1 -0
  10. package/dist/esm/create-server-fn-plugin/handleCreateServerFn.d.ts +6 -0
  11. package/dist/esm/{start-compiler-plugin/serverFn.js → create-server-fn-plugin/handleCreateServerFn.js} +15 -17
  12. package/dist/esm/create-server-fn-plugin/handleCreateServerFn.js.map +1 -0
  13. package/dist/esm/create-server-fn-plugin/plugin.d.ts +3 -0
  14. package/dist/esm/create-server-fn-plugin/plugin.js +128 -0
  15. package/dist/esm/create-server-fn-plugin/plugin.js.map +1 -0
  16. package/dist/esm/dev-server-plugin/plugin.d.ts +4 -2
  17. package/dist/esm/dev-server-plugin/plugin.js +6 -2
  18. package/dist/esm/dev-server-plugin/plugin.js.map +1 -1
  19. package/dist/esm/plugin.d.ts +12 -6
  20. package/dist/esm/plugin.js +56 -68
  21. package/dist/esm/plugin.js.map +1 -1
  22. package/dist/esm/resolve-entries.d.ts +0 -1
  23. package/dist/esm/resolve-entries.js +1 -1
  24. package/dist/esm/resolve-entries.js.map +1 -1
  25. package/dist/esm/schema.d.ts +375 -308
  26. package/dist/esm/schema.js +23 -11
  27. package/dist/esm/schema.js.map +1 -1
  28. package/dist/esm/start-compiler-plugin/compilers.js +17 -55
  29. package/dist/esm/start-compiler-plugin/compilers.js.map +1 -1
  30. package/dist/esm/start-compiler-plugin/constants.d.ts +1 -1
  31. package/dist/esm/start-compiler-plugin/constants.js +1 -6
  32. package/dist/esm/start-compiler-plugin/constants.js.map +1 -1
  33. package/dist/esm/start-compiler-plugin/plugin.d.ts +1 -8
  34. package/dist/esm/start-compiler-plugin/plugin.js +6 -13
  35. package/dist/esm/start-compiler-plugin/plugin.js.map +1 -1
  36. package/dist/esm/start-router-plugin/constants.d.ts +1 -0
  37. package/dist/esm/start-router-plugin/constants.js +5 -0
  38. package/dist/esm/start-router-plugin/constants.js.map +1 -0
  39. package/dist/esm/start-router-plugin/generator-plugins/routes-manifest-plugin.js +3 -9
  40. package/dist/esm/start-router-plugin/generator-plugins/routes-manifest-plugin.js.map +1 -1
  41. package/dist/esm/start-router-plugin/plugin.d.ts +3 -2
  42. package/dist/esm/start-router-plugin/plugin.js +191 -31
  43. package/dist/esm/start-router-plugin/plugin.js.map +1 -1
  44. package/dist/esm/start-router-plugin/pruneServerOnlySubtrees.d.ts +8 -0
  45. package/dist/esm/start-router-plugin/pruneServerOnlySubtrees.js +34 -0
  46. package/dist/esm/start-router-plugin/pruneServerOnlySubtrees.js.map +1 -0
  47. package/package.json +8 -8
  48. package/src/constants.ts +3 -2
  49. package/src/create-server-fn-plugin/compiler.ts +498 -0
  50. package/src/{start-compiler-plugin/middleware.ts → create-server-fn-plugin/handleCreateMiddleware.ts} +15 -12
  51. package/src/{start-compiler-plugin/serverFn.ts → create-server-fn-plugin/handleCreateServerFn.ts} +32 -39
  52. package/src/create-server-fn-plugin/plugin.ts +153 -0
  53. package/src/dev-server-plugin/plugin.ts +6 -3
  54. package/src/plugin.ts +78 -87
  55. package/src/resolve-entries.ts +1 -2
  56. package/src/schema.ts +31 -14
  57. package/src/start-compiler-plugin/compilers.ts +18 -57
  58. package/src/start-compiler-plugin/constants.ts +0 -5
  59. package/src/start-compiler-plugin/plugin.ts +7 -22
  60. package/src/start-router-plugin/constants.ts +1 -0
  61. package/src/start-router-plugin/generator-plugins/routes-manifest-plugin.ts +3 -9
  62. package/src/start-router-plugin/plugin.ts +233 -45
  63. package/src/start-router-plugin/pruneServerOnlySubtrees.ts +51 -0
  64. package/dist/esm/debug.js +0 -5
  65. package/dist/esm/debug.js.map +0 -1
  66. package/dist/esm/start-compiler-plugin/middleware.d.ts +0 -4
  67. package/dist/esm/start-compiler-plugin/middleware.js.map +0 -1
  68. package/dist/esm/start-compiler-plugin/serverFileRoute.d.ts +0 -4
  69. package/dist/esm/start-compiler-plugin/serverFileRoute.js +0 -38
  70. package/dist/esm/start-compiler-plugin/serverFileRoute.js.map +0 -1
  71. package/dist/esm/start-compiler-plugin/serverFn.d.ts +0 -4
  72. package/dist/esm/start-compiler-plugin/serverFn.js.map +0 -1
  73. package/dist/esm/start-router-plugin/generator-plugins/server-routes-plugin.d.ts +0 -2
  74. package/dist/esm/start-router-plugin/generator-plugins/server-routes-plugin.js +0 -119
  75. package/dist/esm/start-router-plugin/generator-plugins/server-routes-plugin.js.map +0 -1
  76. package/dist/esm/start-router-plugin/route-tree-client-plugin.d.ts +0 -6
  77. package/dist/esm/start-router-plugin/route-tree-client-plugin.js +0 -56
  78. package/dist/esm/start-router-plugin/route-tree-client-plugin.js.map +0 -1
  79. package/dist/esm/start-router-plugin/virtual-route-tree-plugin.d.ts +0 -3
  80. package/dist/esm/start-router-plugin/virtual-route-tree-plugin.js +0 -29
  81. package/dist/esm/start-router-plugin/virtual-route-tree-plugin.js.map +0 -1
  82. package/src/start-compiler-plugin/serverFileRoute.ts +0 -59
  83. package/src/start-router-plugin/generator-plugins/server-routes-plugin.ts +0 -138
  84. package/src/start-router-plugin/route-tree-client-plugin.ts +0 -77
  85. package/src/start-router-plugin/virtual-route-tree-plugin.ts +0 -29
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.js","sources":["../../../src/start-router-plugin/plugin.ts"],"sourcesContent":["/*\nwhat is this plugin doing, especially compared to one already existing in the @tanstack/router-plugin package?\n\nit configures:\n1. the generator to generate both the render-route-tree as well as the server-route-tree\n2. the code-splitter plugin, so it could possibly be enabled per environment (e.g. disable on the server)\n3. the auto import plugin for both environments\n4. the route tree client plugin, which removes the server part from the generated route tree\n5. the virtual route tree plugin, which provides the route tree to the server\n*/\n\nimport {\n tanStackRouterCodeSplitter,\n tanstackRouterAutoImport,\n tanstackRouterGenerator,\n} from '@tanstack/router-plugin/vite'\nimport { VITE_ENVIRONMENT_NAMES } from '../constants'\nimport { routeTreeClientPlugin } from './route-tree-client-plugin'\nimport { virtualRouteTreePlugin } from './virtual-route-tree-plugin'\nimport { routesManifestPlugin } from './generator-plugins/routes-manifest-plugin'\nimport { serverRoutesPlugin } from './generator-plugins/server-routes-plugin'\nimport type { PluginOption } from 'vite'\nimport type { Config } from '@tanstack/router-plugin'\n\nexport function tanStackStartRouter(config: Config): Array<PluginOption> {\n return [\n tanstackRouterGenerator({\n ...config,\n plugins: [serverRoutesPlugin(), routesManifestPlugin()],\n plugin: {\n vite: { environmentName: VITE_ENVIRONMENT_NAMES.client },\n },\n }),\n tanStackRouterCodeSplitter({\n ...config,\n codeSplittingOptions: {\n ...config.codeSplittingOptions,\n deleteNodes: ['ssr'],\n addHmr: true,\n },\n plugin: {\n vite: { environmentName: VITE_ENVIRONMENT_NAMES.client },\n },\n }),\n tanStackRouterCodeSplitter({\n ...config,\n codeSplittingOptions: {\n ...config.codeSplittingOptions,\n addHmr: false,\n },\n plugin: {\n vite: { environmentName: VITE_ENVIRONMENT_NAMES.server },\n },\n }),\n tanstackRouterAutoImport(config),\n routeTreeClientPlugin(config),\n virtualRouteTreePlugin(config),\n ]\n}\n"],"names":[],"mappings":";;;;;;AAwBO,SAAS,oBAAoB,QAAqC;AACvE,SAAO;AAAA,IACL,wBAAwB;AAAA,MACtB,GAAG;AAAA,MACH,SAAS,CAAC,sBAAsB,sBAAsB;AAAA,MACtD,QAAQ;AAAA,QACN,MAAM,EAAE,iBAAiB,uBAAuB,OAAA;AAAA,MAAO;AAAA,IACzD,CACD;AAAA,IACD,2BAA2B;AAAA,MACzB,GAAG;AAAA,MACH,sBAAsB;AAAA,QACpB,GAAG,OAAO;AAAA,QACV,aAAa,CAAC,KAAK;AAAA,QACnB,QAAQ;AAAA,MAAA;AAAA,MAEV,QAAQ;AAAA,QACN,MAAM,EAAE,iBAAiB,uBAAuB,OAAA;AAAA,MAAO;AAAA,IACzD,CACD;AAAA,IACD,2BAA2B;AAAA,MACzB,GAAG;AAAA,MACH,sBAAsB;AAAA,QACpB,GAAG,OAAO;AAAA,QACV,QAAQ;AAAA,MAAA;AAAA,MAEV,QAAQ;AAAA,QACN,MAAM,EAAE,iBAAiB,uBAAuB,OAAA;AAAA,MAAO;AAAA,IACzD,CACD;AAAA,IACD,yBAAyB,MAAM;AAAA,IAC/B,sBAAsB,MAAM;AAAA,IAC5B,uBAAuB,MAAM;AAAA,EAAA;AAEjC;"}
1
+ {"version":3,"file":"plugin.js","sources":["../../../src/start-router-plugin/plugin.ts"],"sourcesContent":["import {\n tanStackRouterCodeSplitter,\n tanstackRouterAutoImport,\n tanstackRouterGenerator,\n} from '@tanstack/router-plugin/vite'\nimport { normalizePath } from 'vite'\nimport path from 'pathe'\nimport { VITE_ENVIRONMENT_NAMES } from '../constants'\nimport { routesManifestPlugin } from './generator-plugins/routes-manifest-plugin'\nimport { pruneServerOnlySubtrees } from './pruneServerOnlySubtrees'\nimport { SERVER_PROP } from './constants'\nimport type {\n Generator,\n GeneratorPlugin,\n RouteNode,\n} from '@tanstack/router-generator'\nimport type { DevEnvironment, Plugin, PluginOption } from 'vite'\nimport type { TanStackStartInputConfig } from '../schema'\nimport type { GetConfigFn, TanStackStartVitePluginCoreOptions } from '../plugin'\n\nfunction isServerOnlyNode(node: RouteNode | undefined) {\n if (!node?.createFileRouteProps) {\n return false\n }\n return (\n node.createFileRouteProps.has(SERVER_PROP) &&\n node.createFileRouteProps.size === 1\n )\n}\n\nfunction moduleDeclaration({\n startFilePath,\n routerFilePath,\n corePluginOpts,\n generatedRouteTreePath,\n}: {\n startFilePath: string | undefined\n routerFilePath: string\n corePluginOpts: TanStackStartVitePluginCoreOptions\n generatedRouteTreePath: string\n}): string {\n function getImportPath(absolutePath: string) {\n let relativePath = path.relative(\n path.dirname(generatedRouteTreePath),\n absolutePath,\n )\n\n if (!relativePath.startsWith('.')) {\n relativePath = './' + relativePath\n }\n\n // convert to POSIX-style for ESM imports (important on Windows)\n relativePath = relativePath.split(path.sep).join('/')\n return relativePath\n }\n\n const result: Array<string> = [\n `import type { getRouter } from '${getImportPath(routerFilePath)}'`,\n ]\n if (startFilePath) {\n result.push(\n `import type { startInstance } from '${getImportPath(startFilePath)}'`,\n )\n }\n // make sure we import something from start to get the server route declaration merge\n else {\n result.push(\n `import type { createStart } from '@tanstack/${corePluginOpts.framework}-start'`,\n )\n }\n result.push(\n `declare module '@tanstack/${corePluginOpts.framework}-start' {\n interface Register {\n router: Awaited<ReturnType<typeof getRouter>>`,\n )\n if (startFilePath) {\n result.push(\n ` config: Awaited<ReturnType<typeof startInstance.getOptions>>`,\n )\n }\n result.push(` }\n}`)\n\n return result.join('\\n')\n}\n\nexport function tanStackStartRouter(\n startPluginOpts: TanStackStartInputConfig,\n getConfig: GetConfigFn,\n corePluginOpts: TanStackStartVitePluginCoreOptions,\n): Array<PluginOption> {\n const getGeneratedRouteTreePath = () => {\n const { startConfig } = getConfig()\n return path.resolve(startConfig.router.generatedRouteTree)\n }\n\n let clientEnvironment: DevEnvironment | null = null\n function invalidate() {\n if (!clientEnvironment) {\n return\n }\n\n const mod = clientEnvironment.moduleGraph.getModuleById(\n getGeneratedRouteTreePath(),\n )\n if (mod) {\n clientEnvironment.moduleGraph.invalidateModule(mod)\n }\n clientEnvironment.hot.send({ type: 'full-reload', path: '*' })\n }\n\n let generatorInstance: Generator | null = null\n\n const clientTreeGeneratorPlugin: GeneratorPlugin = {\n name: 'start-client-tree-plugin',\n init({ generator }) {\n generatorInstance = generator\n },\n afterTransform({ node, prevNode }) {\n if (isServerOnlyNode(node) !== isServerOnlyNode(prevNode)) {\n invalidate()\n }\n },\n }\n\n let routeTreeFileFooter: Array<string> | null = null\n\n function getRouteTreeFileFooter() {\n if (routeTreeFileFooter) {\n return routeTreeFileFooter\n }\n const { startConfig, resolvedStartConfig } = getConfig()\n const ogRouteTreeFileFooter = startConfig.router.routeTreeFileFooter\n if (ogRouteTreeFileFooter) {\n if (Array.isArray(ogRouteTreeFileFooter)) {\n routeTreeFileFooter = ogRouteTreeFileFooter\n } else {\n routeTreeFileFooter = ogRouteTreeFileFooter()\n }\n }\n routeTreeFileFooter = [\n moduleDeclaration({\n generatedRouteTreePath: getGeneratedRouteTreePath(),\n corePluginOpts,\n startFilePath: resolvedStartConfig.startFilePath,\n routerFilePath: resolvedStartConfig.routerFilePath,\n }),\n ...(routeTreeFileFooter ?? []),\n ]\n return routeTreeFileFooter\n }\n\n let resolvedGeneratedRouteTreePath: string | null = null\n const clientTreePlugin: Plugin = {\n name: 'tanstack-start:route-tree-client-plugin',\n enforce: 'pre',\n applyToEnvironment: (env) => env.name === VITE_ENVIRONMENT_NAMES.client,\n configureServer(server) {\n clientEnvironment = server.environments[VITE_ENVIRONMENT_NAMES.client]\n },\n config() {\n type LoadObjectHook = Extract<\n typeof clientTreePlugin.load,\n { filter?: unknown }\n >\n resolvedGeneratedRouteTreePath = normalizePath(\n getGeneratedRouteTreePath(),\n )\n ;(clientTreePlugin.load as LoadObjectHook).filter = {\n id: { include: new RegExp(resolvedGeneratedRouteTreePath) },\n }\n },\n\n load: {\n filter: {\n // this will be set in the config hook above since it relies on `config` hook being called first\n },\n async handler() {\n if (!generatorInstance) {\n throw new Error('Generator instance not initialized')\n }\n const crawlingResult = await generatorInstance.getCrawlingResult()\n if (!crawlingResult) {\n throw new Error('Crawling result not available')\n }\n const prunedAcc = pruneServerOnlySubtrees(crawlingResult)\n const acc = {\n ...crawlingResult.acc,\n ...prunedAcc,\n }\n const buildResult = generatorInstance.buildRouteTree({\n ...crawlingResult,\n acc,\n config: {\n // importRoutesUsingAbsolutePaths: true,\n // addExtensions: true,\n disableTypes: true,\n enableRouteTreeFormatting: false,\n routeTreeFileHeader: [],\n routeTreeFileFooter: [],\n },\n })\n return { code: buildResult.routeTreeContent, map: null }\n },\n },\n }\n return [\n clientTreePlugin,\n tanstackRouterGenerator(() => {\n const routerConfig = getConfig().startConfig.router\n return {\n ...routerConfig,\n target: corePluginOpts.framework,\n routeTreeFileFooter: getRouteTreeFileFooter,\n plugins: [clientTreeGeneratorPlugin, routesManifestPlugin()],\n }\n }),\n tanStackRouterCodeSplitter(() => {\n const routerConfig = getConfig().startConfig.router\n return {\n ...routerConfig,\n codeSplittingOptions: {\n ...routerConfig.codeSplittingOptions,\n deleteNodes: ['ssr', 'server'],\n addHmr: true,\n },\n plugin: {\n vite: { environmentName: VITE_ENVIRONMENT_NAMES.client },\n },\n }\n }),\n tanStackRouterCodeSplitter(() => {\n const routerConfig = getConfig().startConfig.router\n return {\n ...routerConfig,\n codeSplittingOptions: {\n ...routerConfig.codeSplittingOptions,\n addHmr: false,\n },\n plugin: {\n vite: { environmentName: VITE_ENVIRONMENT_NAMES.server },\n },\n }\n }),\n tanstackRouterAutoImport(startPluginOpts?.router),\n ]\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import { HandleNodeAccumulator, RouteNode } from '@tanstack/router-generator';
2
+ export declare function pruneServerOnlySubtrees({ rootRouteNode, acc, }: {
3
+ rootRouteNode: RouteNode;
4
+ acc: HandleNodeAccumulator;
5
+ }): {
6
+ routeTree: RouteNode[];
7
+ routeNodes: RouteNode[];
8
+ };
@@ -0,0 +1,34 @@
1
+ import { SERVER_PROP } from "./constants.js";
2
+ function pruneServerOnlySubtrees({
3
+ rootRouteNode,
4
+ acc
5
+ }) {
6
+ const routeNodes = [];
7
+ const routeTree = prune({ ...rootRouteNode, children: acc.routeTree }, routeNodes)?.children || [];
8
+ routeNodes.pop();
9
+ return {
10
+ routeTree,
11
+ routeNodes
12
+ };
13
+ }
14
+ function prune(node, collectedRouteNodes) {
15
+ const newChildren = [];
16
+ let allChildrenServerOnly = true;
17
+ for (const child of node.children || []) {
18
+ const newChild = prune(child, collectedRouteNodes);
19
+ if (newChild) {
20
+ newChildren.push(newChild);
21
+ allChildrenServerOnly = false;
22
+ }
23
+ }
24
+ const allServerOnly = node.createFileRouteProps?.has(SERVER_PROP) && node.createFileRouteProps.size === 1 && allChildrenServerOnly;
25
+ if (allServerOnly) {
26
+ return null;
27
+ }
28
+ collectedRouteNodes.push(node);
29
+ return { ...node, children: newChildren };
30
+ }
31
+ export {
32
+ pruneServerOnlySubtrees
33
+ };
34
+ //# sourceMappingURL=pruneServerOnlySubtrees.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pruneServerOnlySubtrees.js","sources":["../../../src/start-router-plugin/pruneServerOnlySubtrees.ts"],"sourcesContent":["import { SERVER_PROP } from './constants'\nimport type {\n HandleNodeAccumulator,\n RouteNode,\n} from '@tanstack/router-generator'\n\nexport function pruneServerOnlySubtrees({\n rootRouteNode,\n acc,\n}: {\n rootRouteNode: RouteNode\n acc: HandleNodeAccumulator\n}) {\n const routeNodes: Array<RouteNode> = []\n const routeTree =\n prune({ ...rootRouteNode, children: acc.routeTree }, routeNodes)\n ?.children || []\n // remove root node from routeNodes\n routeNodes.pop()\n return {\n routeTree,\n routeNodes,\n }\n}\nfunction prune(\n node: RouteNode,\n collectedRouteNodes: Array<RouteNode>,\n): RouteNode | null {\n const newChildren: Array<RouteNode> = []\n let allChildrenServerOnly = true\n\n for (const child of node.children || []) {\n const newChild = prune(child, collectedRouteNodes)\n if (newChild) {\n newChildren.push(newChild)\n // at least one child survived pruning\n allChildrenServerOnly = false\n }\n }\n\n const allServerOnly =\n node.createFileRouteProps?.has(SERVER_PROP) &&\n node.createFileRouteProps.size === 1 &&\n allChildrenServerOnly\n // prune this subtree\n if (allServerOnly) {\n return null\n }\n collectedRouteNodes.push(node)\n return { ...node, children: newChildren }\n}\n"],"names":[],"mappings":";AAMO,SAAS,wBAAwB;AAAA,EACtC;AAAA,EACA;AACF,GAGG;AACD,QAAM,aAA+B,CAAA;AACrC,QAAM,YACJ,MAAM,EAAE,GAAG,eAAe,UAAU,IAAI,UAAA,GAAa,UAAU,GAC3D,YAAY,CAAA;AAElB,aAAW,IAAA;AACX,SAAO;AAAA,IACL;AAAA,IACA;AAAA,EAAA;AAEJ;AACA,SAAS,MACP,MACA,qBACkB;AAClB,QAAM,cAAgC,CAAA;AACtC,MAAI,wBAAwB;AAE5B,aAAW,SAAS,KAAK,YAAY,CAAA,GAAI;AACvC,UAAM,WAAW,MAAM,OAAO,mBAAmB;AACjD,QAAI,UAAU;AACZ,kBAAY,KAAK,QAAQ;AAEzB,8BAAwB;AAAA,IAC1B;AAAA,EACF;AAEA,QAAM,gBACJ,KAAK,sBAAsB,IAAI,WAAW,KAC1C,KAAK,qBAAqB,SAAS,KACnC;AAEF,MAAI,eAAe;AACjB,WAAO;AAAA,EACT;AACA,sBAAoB,KAAK,IAAI;AAC7B,SAAO,EAAE,GAAG,MAAM,UAAU,YAAA;AAC9B;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/start-plugin-core",
3
- "version": "1.132.0-alpha.8",
3
+ "version": "1.132.0",
4
4
  "description": "Modern and scalable routing for React applications",
5
5
  "author": "Tanner Linsley",
6
6
  "license": "MIT",
@@ -42,7 +42,7 @@
42
42
  "src"
43
43
  ],
44
44
  "engines": {
45
- "node": ">=12"
45
+ "node": ">=22.12.0"
46
46
  },
47
47
  "dependencies": {
48
48
  "@babel/code-frame": "7.26.2",
@@ -57,12 +57,12 @@
57
57
  "vitefu": "^1.1.1",
58
58
  "xmlbuilder2": "^3.1.1",
59
59
  "zod": "^3.24.2",
60
- "@tanstack/router-core": "1.132.0-alpha.8",
61
- "@tanstack/router-generator": "1.132.0-alpha.8",
62
- "@tanstack/router-utils": "1.132.0-alpha.1",
63
- "@tanstack/router-plugin": "1.132.0-alpha.8",
64
- "@tanstack/server-functions-plugin": "1.132.0-alpha.3",
65
- "@tanstack/start-server-core": "1.132.0-alpha.8"
60
+ "@tanstack/router-core": "1.132.0",
61
+ "@tanstack/router-plugin": "1.132.0",
62
+ "@tanstack/router-generator": "1.132.0",
63
+ "@tanstack/server-functions-plugin": "1.132.0",
64
+ "@tanstack/router-utils": "1.132.0",
65
+ "@tanstack/start-server-core": "1.132.0"
66
66
  },
67
67
  "devDependencies": {
68
68
  "@types/babel__code-frame": "^7.0.6",
package/src/constants.ts CHANGED
@@ -14,6 +14,7 @@ export type ViteEnvironmentNames =
14
14
  export const ENTRY_POINTS = {
15
15
  client: 'virtual:tanstack-start-client-entry',
16
16
  server: 'virtual:tanstack-start-server-request-entry',
17
- // the router entry point must always be provided by the user
18
- router: '#tanstack-start-router-entry',
17
+ // the start entry point must always be provided by the user
18
+ start: '#tanstack-start-entry',
19
+ router: '#tanstack-router-entry',
19
20
  } as const
@@ -0,0 +1,498 @@
1
+ /* eslint-disable import/no-commonjs */
2
+ import * as t from '@babel/types'
3
+ import { generateFromAst, parseAst } from '@tanstack/router-utils'
4
+ import babel from '@babel/core'
5
+ import {
6
+ deadCodeElimination,
7
+ findReferencedIdentifiers,
8
+ } from 'babel-dead-code-elimination'
9
+ import { handleCreateServerFn } from './handleCreateServerFn'
10
+ import { handleCreateMiddleware } from './handleCreateMiddleware'
11
+
12
+ type Binding =
13
+ | {
14
+ type: 'import'
15
+ source: string
16
+ importedName: string
17
+ resolvedKind?: Kind
18
+ }
19
+ | {
20
+ type: 'var'
21
+ init: t.Expression | null
22
+ resolvedKind?: Kind
23
+ }
24
+
25
+ type ExportEntry =
26
+ | { tag: 'Normal'; name: string }
27
+ | { tag: 'Default'; name: string }
28
+ | { tag: 'Namespace'; name: string; targetId: string } // for `export * as ns from './x'`
29
+
30
+ type Kind = 'None' | `Root` | `Builder` | LookupKind
31
+
32
+ type LookupKind = 'ServerFn' | 'Middleware'
33
+
34
+ const validLookupKinds: Array<LookupKind> = ['ServerFn', 'Middleware']
35
+ const candidateCallIdentifier = ['handler', 'server', 'client']
36
+ export type LookupConfig = {
37
+ libName: string
38
+ rootExport: string
39
+ }
40
+ interface ModuleInfo {
41
+ id: string
42
+ code: string
43
+ ast: ReturnType<typeof parseAst>
44
+ bindings: Map<string, Binding>
45
+ exports: Map<string, ExportEntry>
46
+ }
47
+
48
+ export class ServerFnCompiler {
49
+ private moduleCache = new Map<string, ModuleInfo>()
50
+ private initialized = false
51
+ constructor(
52
+ private options: {
53
+ env: 'client' | 'server'
54
+ lookupConfigurations: Array<LookupConfig>
55
+ loadModule: (id: string) => Promise<void>
56
+ resolveId: (id: string, importer?: string) => Promise<string | null>
57
+ },
58
+ ) {}
59
+
60
+ private async init(id: string) {
61
+ await Promise.all(
62
+ this.options.lookupConfigurations.map(async (config) => {
63
+ const libId = await this.options.resolveId(config.libName, id)
64
+ if (!libId) {
65
+ throw new Error(`could not resolve "${config.libName}"`)
66
+ }
67
+ let rootModule = this.moduleCache.get(libId)
68
+ if (!rootModule) {
69
+ // insert root binding
70
+ rootModule = {
71
+ ast: null as any,
72
+ bindings: new Map(),
73
+ exports: new Map(),
74
+ code: '',
75
+ id: libId,
76
+ }
77
+ this.moduleCache.set(libId, rootModule)
78
+ }
79
+
80
+ rootModule.exports.set(config.rootExport, {
81
+ tag: 'Normal',
82
+ name: config.rootExport,
83
+ })
84
+ rootModule.exports.set('*', {
85
+ tag: 'Namespace',
86
+ name: config.rootExport,
87
+ targetId: libId,
88
+ })
89
+ rootModule.bindings.set(config.rootExport, {
90
+ type: 'var',
91
+ init: t.identifier(config.rootExport),
92
+ resolvedKind: `Root` satisfies Kind,
93
+ })
94
+ this.moduleCache.set(libId, rootModule)
95
+ }),
96
+ )
97
+
98
+ this.initialized = true
99
+ }
100
+
101
+ public ingestModule({ code, id }: { code: string; id: string }) {
102
+ const ast = parseAst({ code })
103
+
104
+ const bindings = new Map<string, Binding>()
105
+ const exports = new Map<string, ExportEntry>()
106
+
107
+ // we are only interested in top-level bindings, hence we don't traverse the AST
108
+ // instead we only iterate over the program body
109
+ for (const node of ast.program.body) {
110
+ if (t.isImportDeclaration(node)) {
111
+ const source = node.source.value
112
+ for (const s of node.specifiers) {
113
+ if (t.isImportSpecifier(s)) {
114
+ const importedName = t.isIdentifier(s.imported)
115
+ ? s.imported.name
116
+ : s.imported.value
117
+ bindings.set(s.local.name, { type: 'import', source, importedName })
118
+ } else if (t.isImportDefaultSpecifier(s)) {
119
+ bindings.set(s.local.name, {
120
+ type: 'import',
121
+ source,
122
+ importedName: 'default',
123
+ })
124
+ } else if (t.isImportNamespaceSpecifier(s)) {
125
+ bindings.set(s.local.name, {
126
+ type: 'import',
127
+ source,
128
+ importedName: '*',
129
+ })
130
+ }
131
+ }
132
+ } else if (t.isVariableDeclaration(node)) {
133
+ for (const decl of node.declarations) {
134
+ if (t.isIdentifier(decl.id)) {
135
+ bindings.set(decl.id.name, {
136
+ type: 'var',
137
+ init: decl.init ?? null,
138
+ })
139
+ }
140
+ }
141
+ } else if (t.isExportNamedDeclaration(node)) {
142
+ // export const foo = ...
143
+ if (node.declaration) {
144
+ if (t.isVariableDeclaration(node.declaration)) {
145
+ for (const d of node.declaration.declarations) {
146
+ if (t.isIdentifier(d.id)) {
147
+ exports.set(d.id.name, { tag: 'Normal', name: d.id.name })
148
+ bindings.set(d.id.name, { type: 'var', init: d.init ?? null })
149
+ }
150
+ }
151
+ }
152
+ }
153
+ for (const sp of node.specifiers) {
154
+ if (t.isExportNamespaceSpecifier(sp)) {
155
+ exports.set(sp.exported.name, {
156
+ tag: 'Namespace',
157
+ name: sp.exported.name,
158
+ targetId: node.source?.value || '',
159
+ })
160
+ }
161
+ // export { local as exported }
162
+ else if (t.isExportSpecifier(sp)) {
163
+ const local = sp.local.name
164
+ const exported = t.isIdentifier(sp.exported)
165
+ ? sp.exported.name
166
+ : sp.exported.value
167
+ exports.set(exported, { tag: 'Normal', name: local })
168
+ }
169
+ }
170
+ } else if (t.isExportDefaultDeclaration(node)) {
171
+ const d = node.declaration
172
+ if (t.isIdentifier(d)) {
173
+ exports.set('default', { tag: 'Default', name: d.name })
174
+ } else {
175
+ const synth = '__default_export__'
176
+ bindings.set(synth, { type: 'var', init: d as t.Expression })
177
+ exports.set('default', { tag: 'Default', name: synth })
178
+ }
179
+ }
180
+ }
181
+
182
+ const info: ModuleInfo = { code, id, ast, bindings, exports }
183
+ this.moduleCache.set(id, info)
184
+ return info
185
+ }
186
+
187
+ public invalidateModule(id: string) {
188
+ return this.moduleCache.delete(id)
189
+ }
190
+
191
+ public async compile({ code, id }: { code: string; id: string }) {
192
+ if (!this.initialized) {
193
+ await this.init(id)
194
+ }
195
+ const { bindings, ast } = this.ingestModule({ code, id })
196
+ const candidates = this.collectCandidates(bindings)
197
+ if (candidates.length === 0) {
198
+ // this hook will only be invoked if there is `.handler(` | `.server(` | `.client(` in the code,
199
+ // so not discovering a handler candidate is rather unlikely, but maybe possible?
200
+ return null
201
+ }
202
+
203
+ // let's find out which of the candidates are actually server functions
204
+ const toRewrite: Array<{
205
+ callExpression: t.CallExpression
206
+ kind: LookupKind
207
+ }> = []
208
+ for (const handler of candidates) {
209
+ const kind = await this.resolveExprKind(handler, id)
210
+ if (validLookupKinds.includes(kind as LookupKind)) {
211
+ toRewrite.push({ callExpression: handler, kind: kind as LookupKind })
212
+ }
213
+ }
214
+ if (toRewrite.length === 0) {
215
+ return null
216
+ }
217
+
218
+ const pathsToRewrite: Array<{
219
+ nodePath: babel.NodePath<t.CallExpression>
220
+ kind: LookupKind
221
+ }> = []
222
+ babel.traverse(ast, {
223
+ CallExpression(path) {
224
+ const found = toRewrite.findIndex((h) => path.node === h.callExpression)
225
+ if (found !== -1) {
226
+ pathsToRewrite.push({ nodePath: path, kind: toRewrite[found]!.kind })
227
+ // delete from toRewrite
228
+ toRewrite.splice(found, 1)
229
+ }
230
+ },
231
+ })
232
+
233
+ if (toRewrite.length > 0) {
234
+ throw new Error(
235
+ `Internal error: could not find all paths to rewrite. please file an issue`,
236
+ )
237
+ }
238
+
239
+ const refIdents = findReferencedIdentifiers(ast)
240
+
241
+ pathsToRewrite.map((p) => {
242
+ if (p.kind === 'ServerFn') {
243
+ handleCreateServerFn(p.nodePath, { env: this.options.env, code })
244
+ } else {
245
+ handleCreateMiddleware(p.nodePath, { env: this.options.env })
246
+ }
247
+ })
248
+
249
+ deadCodeElimination(ast, refIdents)
250
+
251
+ return generateFromAst(ast, {
252
+ sourceMaps: true,
253
+ sourceFileName: id,
254
+ filename: id,
255
+ })
256
+ }
257
+
258
+ // collects all candidate CallExpressions at top-level
259
+ private collectCandidates(bindings: Map<string, Binding>) {
260
+ const candidates: Array<t.CallExpression> = []
261
+
262
+ for (const binding of bindings.values()) {
263
+ if (binding.type === 'var') {
264
+ const handler = isCandidateCallExpression(binding.init)
265
+ if (handler) {
266
+ candidates.push(handler)
267
+ }
268
+ }
269
+ }
270
+ return candidates
271
+ }
272
+
273
+ private async resolveIdentifierKind(
274
+ ident: string,
275
+ id: string,
276
+ visited = new Set<string>(),
277
+ ): Promise<Kind> {
278
+ const info = await this.getModuleInfo(id)
279
+
280
+ const binding = info.bindings.get(ident)
281
+ if (!binding) {
282
+ return 'None'
283
+ }
284
+ if (binding.resolvedKind) {
285
+ return binding.resolvedKind
286
+ }
287
+
288
+ // TODO improve cycle detection? should we throw here instead of returning 'None'?
289
+ // prevent cycles
290
+ const vKey = `${id}:${ident}`
291
+ if (visited.has(vKey)) {
292
+ return 'None'
293
+ }
294
+ visited.add(vKey)
295
+
296
+ const resolvedKind = await this.resolveBindingKind(binding, id, visited)
297
+ binding.resolvedKind = resolvedKind
298
+ return resolvedKind
299
+ }
300
+
301
+ private async resolveBindingKind(
302
+ binding: Binding,
303
+ fileId: string,
304
+ visited = new Set<string>(),
305
+ ): Promise<Kind> {
306
+ if (binding.resolvedKind) {
307
+ return binding.resolvedKind
308
+ }
309
+ if (binding.type === 'import') {
310
+ const target = await this.options.resolveId(binding.source, fileId)
311
+ if (!target) {
312
+ return 'None'
313
+ }
314
+
315
+ const importedModule = await this.getModuleInfo(target)
316
+
317
+ const moduleExport = importedModule.exports.get(binding.importedName)
318
+ if (!moduleExport) {
319
+ return 'None'
320
+ }
321
+ const importedBinding = importedModule.bindings.get(moduleExport.name)
322
+ if (!importedBinding) {
323
+ return 'None'
324
+ }
325
+ if (importedBinding.resolvedKind) {
326
+ return importedBinding.resolvedKind
327
+ }
328
+
329
+ const resolvedKind = await this.resolveBindingKind(
330
+ importedBinding,
331
+ importedModule.id,
332
+ visited,
333
+ )
334
+ importedBinding.resolvedKind = resolvedKind
335
+ return resolvedKind
336
+ }
337
+
338
+ const resolvedKind = await this.resolveExprKind(
339
+ binding.init,
340
+ fileId,
341
+ visited,
342
+ )
343
+ binding.resolvedKind = resolvedKind
344
+ return resolvedKind
345
+ }
346
+
347
+ private async resolveExprKind(
348
+ expr: t.Expression | null,
349
+ fileId: string,
350
+ visited = new Set<string>(),
351
+ ): Promise<Kind> {
352
+ if (!expr) {
353
+ return 'None'
354
+ }
355
+
356
+ let result: Kind = 'None'
357
+
358
+ if (t.isCallExpression(expr)) {
359
+ if (!t.isExpression(expr.callee)) {
360
+ return 'None'
361
+ }
362
+ const calleeKind = await this.resolveCalleeKind(
363
+ expr.callee,
364
+ fileId,
365
+ visited,
366
+ )
367
+ if (calleeKind !== 'None') {
368
+ if (calleeKind === `Root` || calleeKind === `Builder`) {
369
+ return `Builder`
370
+ }
371
+ for (const kind of validLookupKinds) {
372
+ if (calleeKind === kind) {
373
+ return kind
374
+ }
375
+ }
376
+ }
377
+ } else if (t.isMemberExpression(expr) && t.isIdentifier(expr.property)) {
378
+ result = await this.resolveCalleeKind(expr.object, fileId, visited)
379
+ }
380
+
381
+ if (result === 'None' && t.isIdentifier(expr)) {
382
+ result = await this.resolveIdentifierKind(expr.name, fileId, visited)
383
+ }
384
+
385
+ if (result === 'None' && t.isTSAsExpression(expr)) {
386
+ result = await this.resolveExprKind(expr.expression, fileId, visited)
387
+ }
388
+ if (result === 'None' && t.isTSNonNullExpression(expr)) {
389
+ result = await this.resolveExprKind(expr.expression, fileId, visited)
390
+ }
391
+ if (result === 'None' && t.isParenthesizedExpression(expr)) {
392
+ result = await this.resolveExprKind(expr.expression, fileId, visited)
393
+ }
394
+
395
+ return result
396
+ }
397
+
398
+ private async resolveCalleeKind(
399
+ callee: t.Expression,
400
+ fileId: string,
401
+ visited = new Set<string>(),
402
+ ): Promise<Kind> {
403
+ if (t.isIdentifier(callee)) {
404
+ return this.resolveIdentifierKind(callee.name, fileId, visited)
405
+ }
406
+
407
+ if (t.isMemberExpression(callee) && t.isIdentifier(callee.property)) {
408
+ const prop = callee.property.name
409
+
410
+ if (prop === 'handler') {
411
+ const base = await this.resolveExprKind(callee.object, fileId, visited)
412
+ if (base === 'Root' || base === 'Builder') {
413
+ return 'ServerFn'
414
+ }
415
+ return 'None'
416
+ } else if (
417
+ prop === 'client' ||
418
+ prop === 'server' ||
419
+ prop === 'createMiddleware'
420
+ ) {
421
+ const base = await this.resolveExprKind(callee.object, fileId, visited)
422
+ if (base === 'Root' || base === 'Builder' || base === 'Middleware') {
423
+ return 'Middleware'
424
+ }
425
+ return 'None'
426
+ }
427
+ // Check if the object is a namespace import
428
+ if (t.isIdentifier(callee.object)) {
429
+ const info = await this.getModuleInfo(fileId)
430
+ const binding = info.bindings.get(callee.object.name)
431
+ if (
432
+ binding &&
433
+ binding.type === 'import' &&
434
+ binding.importedName === '*'
435
+ ) {
436
+ // resolve the property from the target module
437
+ const targetModuleId = await this.options.resolveId(
438
+ binding.source,
439
+ fileId,
440
+ )
441
+ if (targetModuleId) {
442
+ const targetModule = await this.getModuleInfo(targetModuleId)
443
+ const exportEntry = targetModule.exports.get(callee.property.name)
444
+ if (exportEntry) {
445
+ const exportedBinding = targetModule.bindings.get(
446
+ exportEntry.name,
447
+ )
448
+ if (exportedBinding) {
449
+ return await this.resolveBindingKind(
450
+ exportedBinding,
451
+ targetModule.id,
452
+ visited,
453
+ )
454
+ }
455
+ }
456
+ } else {
457
+ return 'None'
458
+ }
459
+ }
460
+ }
461
+ return this.resolveExprKind(callee.object, fileId, visited)
462
+ }
463
+
464
+ // handle nested expressions
465
+ return this.resolveExprKind(callee, fileId, visited)
466
+ }
467
+
468
+ private async getModuleInfo(id: string) {
469
+ let cached = this.moduleCache.get(id)
470
+ if (cached) {
471
+ return cached
472
+ }
473
+
474
+ await this.options.loadModule(id)
475
+
476
+ cached = this.moduleCache.get(id)
477
+ if (!cached) {
478
+ throw new Error(`could not load module info for ${id}`)
479
+ }
480
+ return cached
481
+ }
482
+ }
483
+
484
+ function isCandidateCallExpression(
485
+ node: t.Node | null | undefined,
486
+ ): undefined | t.CallExpression {
487
+ if (!t.isCallExpression(node)) return undefined
488
+
489
+ const callee = node.callee
490
+ if (!t.isMemberExpression(callee) || !t.isIdentifier(callee.property)) {
491
+ return undefined
492
+ }
493
+ if (!candidateCallIdentifier.includes(callee.property.name)) {
494
+ return undefined
495
+ }
496
+
497
+ return node
498
+ }
@@ -1,12 +1,12 @@
1
1
  import * as t from '@babel/types'
2
- import { getRootCallExpression } from './utils'
2
+ import { getRootCallExpression } from '../start-compiler-plugin/utils'
3
3
  import type * as babel from '@babel/core'
4
4
 
5
- import type { CompileOptions } from './compilers'
6
-
7
- export function handleCreateMiddlewareCallExpression(
5
+ export function handleCreateMiddleware(
8
6
  path: babel.NodePath<t.CallExpression>,
9
- opts: CompileOptions,
7
+ opts: {
8
+ env: 'client' | 'server'
9
+ },
10
10
  ) {
11
11
  const rootCallExpression = getRootCallExpression(path)
12
12
 
@@ -18,7 +18,7 @@ export function handleCreateMiddlewareCallExpression(
18
18
 
19
19
  const callExpressionPaths = {
20
20
  middleware: null as babel.NodePath<t.CallExpression> | null,
21
- validator: null as babel.NodePath<t.CallExpression> | null,
21
+ inputValidator: null as babel.NodePath<t.CallExpression> | null,
22
22
  client: null as babel.NodePath<t.CallExpression> | null,
23
23
  server: null as babel.NodePath<t.CallExpression> | null,
24
24
  }
@@ -41,20 +41,23 @@ export function handleCreateMiddlewareCallExpression(
41
41
  },
42
42
  })
43
43
 
44
- if (callExpressionPaths.validator) {
45
- const innerInputExpression = callExpressionPaths.validator.node.arguments[0]
44
+ if (callExpressionPaths.inputValidator) {
45
+ const innerInputExpression =
46
+ callExpressionPaths.inputValidator.node.arguments[0]
46
47
 
47
48
  if (!innerInputExpression) {
48
49
  throw new Error(
49
- 'createMiddleware().validator() must be called with a validator!',
50
+ 'createMiddleware().inputValidator() must be called with a validator!',
50
51
  )
51
52
  }
52
53
 
53
54
  // If we're on the client, remove the validator call expression
54
55
  if (opts.env === 'client') {
55
- if (t.isMemberExpression(callExpressionPaths.validator.node.callee)) {
56
- callExpressionPaths.validator.replaceWith(
57
- callExpressionPaths.validator.node.callee.object,
56
+ if (
57
+ t.isMemberExpression(callExpressionPaths.inputValidator.node.callee)
58
+ ) {
59
+ callExpressionPaths.inputValidator.replaceWith(
60
+ callExpressionPaths.inputValidator.node.callee.object,
58
61
  )
59
62
  }
60
63
  }