@tanstack/start-plugin-core 1.143.6 → 1.143.8

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.
@@ -11,17 +11,6 @@ type Binding = {
11
11
  init: t.Expression | null;
12
12
  resolvedKind?: Kind;
13
13
  };
14
- type ExportEntry = {
15
- tag: 'Normal';
16
- name: string;
17
- } | {
18
- tag: 'Default';
19
- name: string;
20
- } | {
21
- tag: 'Namespace';
22
- name: string;
23
- targetId: string;
24
- };
25
14
  type Kind = 'None' | `Root` | `Builder` | LookupKind;
26
15
  export type LookupKind = 'ServerFn' | 'Middleware' | 'IsomorphicFn' | 'ServerOnlyFn' | 'ClientOnlyFn' | 'ClientOnlyJSX';
27
16
  export declare const KindDetectionPatterns: Record<LookupKind, RegExp>;
@@ -39,7 +28,7 @@ export type LookupConfig = {
39
28
  interface ModuleInfo {
40
29
  id: string;
41
30
  bindings: Map<string, Binding>;
42
- exports: Map<string, ExportEntry>;
31
+ exports: Map<string, string>;
43
32
  reExportAllSources: Array<string>;
44
33
  }
45
34
  export declare class StartCompiler {
@@ -52,6 +41,7 @@ export declare class StartCompiler {
52
41
  private knownRootImports;
53
42
  private entryIdToFunctionId;
54
43
  private functionIds;
44
+ private _rootWithTrailingSlash;
55
45
  constructor(options: {
56
46
  env: 'client' | 'server';
57
47
  envName: string;
@@ -98,6 +88,7 @@ export declare class StartCompiler {
98
88
  */
99
89
  private generateFunctionId;
100
90
  private get mode();
91
+ private get rootWithTrailingSlash();
101
92
  private resolveIdCached;
102
93
  private getExportResolutionCache;
103
94
  private init;
@@ -127,6 +118,13 @@ export declare class StartCompiler {
127
118
  */
128
119
  private findExportInModule;
129
120
  private resolveBindingKind;
121
+ /**
122
+ * Checks if an identifier is a direct import from a known factory library.
123
+ * Returns true for imports like `import { createServerOnlyFn } from '@tanstack/react-start'`
124
+ * or renamed imports like `import { createServerOnlyFn as myFn } from '...'`.
125
+ * Returns false for local variables that hold the result of calling a factory.
126
+ */
127
+ private isKnownFactoryImport;
130
128
  private resolveExprKind;
131
129
  private resolveCalleeKind;
132
130
  private getModuleInfo;
@@ -19,16 +19,14 @@ const LookupSetup = {
19
19
  },
20
20
  IsomorphicFn: {
21
21
  type: "methodChain",
22
- candidateCallIdentifier: /* @__PURE__ */ new Set(["server", "client"]),
23
- allowRootAsCandidate: true
24
- // createIsomorphicFn() alone is valid (returns no-op)
22
+ candidateCallIdentifier: /* @__PURE__ */ new Set(["server", "client"])
25
23
  },
26
- ServerOnlyFn: { type: "directCall" },
27
- ClientOnlyFn: { type: "directCall" },
24
+ ServerOnlyFn: { type: "directCall", factoryName: "createServerOnlyFn" },
25
+ ClientOnlyFn: { type: "directCall", factoryName: "createClientOnlyFn" },
28
26
  ClientOnlyJSX: { type: "jsx", componentName: "ClientOnly" }
29
27
  };
30
28
  const KindDetectionPatterns = {
31
- ServerFn: /\.handler\s*\(/,
29
+ ServerFn: /\bcreateServerFn\b|\.\s*handler\s*\(/,
32
30
  Middleware: /createMiddleware/,
33
31
  IsomorphicFn: /createIsomorphicFn/,
34
32
  ServerOnlyFn: /createServerOnlyFn/,
@@ -60,18 +58,20 @@ const KindHandlers = {
60
58
  ClientOnlyFn: handleEnvOnlyFn
61
59
  // ClientOnlyJSX is handled separately via JSX traversal, not here
62
60
  };
61
+ const AllLookupKinds = Object.keys(LookupSetup);
63
62
  function detectKindsInCode(code, env) {
64
63
  const detected = /* @__PURE__ */ new Set();
65
64
  const validForEnv = LookupKindsPerEnv[env];
66
- for (const [kind, pattern] of Object.entries(KindDetectionPatterns)) {
67
- if (validForEnv.has(kind) && pattern.test(code)) {
65
+ for (const kind of AllLookupKinds) {
66
+ if (validForEnv.has(kind) && KindDetectionPatterns[kind].test(code)) {
68
67
  detected.add(kind);
69
68
  }
70
69
  }
71
70
  return detected;
72
71
  }
73
72
  const IdentifierToKinds = /* @__PURE__ */ new Map();
74
- for (const [kind, setup] of Object.entries(LookupSetup)) {
73
+ for (const kind of AllLookupKinds) {
74
+ const setup = LookupSetup[kind];
75
75
  if (setup.type === "methodChain") {
76
76
  for (const id of setup.candidateCallIdentifier) {
77
77
  let kinds = IdentifierToKinds.get(id);
@@ -83,15 +83,16 @@ for (const [kind, setup] of Object.entries(LookupSetup)) {
83
83
  }
84
84
  }
85
85
  }
86
- const DirectCallFactoryNames = /* @__PURE__ */ new Set([
87
- "createServerOnlyFn",
88
- "createClientOnlyFn",
89
- "createIsomorphicFn"
90
- ]);
86
+ const DirectCallFactoryNames = /* @__PURE__ */ new Set();
87
+ for (const kind of AllLookupKinds) {
88
+ const setup = LookupSetup[kind];
89
+ if (setup.type === "directCall") {
90
+ DirectCallFactoryNames.add(setup.factoryName);
91
+ }
92
+ }
91
93
  function needsDirectCallDetection(kinds) {
92
94
  for (const kind of kinds) {
93
- const setup = LookupSetup[kind];
94
- if (setup.type === "directCall" || setup.type === "methodChain" && setup.allowRootAsCandidate) {
95
+ if (LookupSetup[kind].type === "directCall") {
95
96
  return true;
96
97
  }
97
98
  }
@@ -102,23 +103,12 @@ function areAllKindsTopLevelOnly(kinds) {
102
103
  }
103
104
  function needsJSXDetection(kinds) {
104
105
  for (const kind of kinds) {
105
- const setup = LookupSetup[kind];
106
- if (setup.type === "jsx") {
106
+ if (LookupSetup[kind].type === "jsx") {
107
107
  return true;
108
108
  }
109
109
  }
110
110
  return false;
111
111
  }
112
- function getJSXComponentNames(kinds) {
113
- const names = /* @__PURE__ */ new Set();
114
- for (const kind of kinds) {
115
- const setup = LookupSetup[kind];
116
- if (setup.type === "jsx") {
117
- names.add(setup.componentName);
118
- }
119
- }
120
- return names;
121
- }
122
112
  function isNestedDirectCallCandidate(node) {
123
113
  let calleeName;
124
114
  if (t.isIdentifier(node.callee)) {
@@ -161,6 +151,8 @@ class StartCompiler {
161
151
  // For generating unique function IDs in production builds
162
152
  entryIdToFunctionId = /* @__PURE__ */ new Map();
163
153
  functionIds = /* @__PURE__ */ new Set();
154
+ // Cached root path with trailing slash for dev mode function ID generation
155
+ _rootWithTrailingSlash;
164
156
  /**
165
157
  * Generates a unique function ID for a server function.
166
158
  * In dev mode, uses a base64-encoded JSON with file path and export name.
@@ -168,10 +160,9 @@ class StartCompiler {
168
160
  */
169
161
  generateFunctionId(opts) {
170
162
  if (this.mode === "dev") {
171
- const rootWithTrailingSlash = this.options.root.endsWith("/") ? this.options.root : `${this.options.root}/`;
172
163
  let file = opts.extractedFilename;
173
- if (opts.extractedFilename.startsWith(rootWithTrailingSlash)) {
174
- file = opts.extractedFilename.slice(rootWithTrailingSlash.length);
164
+ if (opts.extractedFilename.startsWith(this.rootWithTrailingSlash)) {
165
+ file = opts.extractedFilename.slice(this.rootWithTrailingSlash.length);
175
166
  }
176
167
  file = `/@id/${file}`;
177
168
  const serverFn = {
@@ -208,6 +199,12 @@ class StartCompiler {
208
199
  get mode() {
209
200
  return this.options.mode ?? "dev";
210
201
  }
202
+ get rootWithTrailingSlash() {
203
+ if (this._rootWithTrailingSlash === void 0) {
204
+ this._rootWithTrailingSlash = this.options.root.endsWith("/") ? this.options.root : `${this.options.root}/`;
205
+ }
206
+ return this._rootWithTrailingSlash;
207
+ }
211
208
  async resolveIdCached(id, importer) {
212
209
  if (this.mode === "dev") {
213
210
  return this.options.resolveId(id, importer);
@@ -266,15 +263,8 @@ class StartCompiler {
266
263
  };
267
264
  this.moduleCache.set(libId, rootModule);
268
265
  }
269
- rootModule.exports.set(config.rootExport, {
270
- tag: "Normal",
271
- name: config.rootExport
272
- });
273
- rootModule.exports.set("*", {
274
- tag: "Namespace",
275
- name: config.rootExport,
276
- targetId: libId
277
- });
266
+ rootModule.exports.set(config.rootExport, config.rootExport);
267
+ rootModule.exports.set("*", config.rootExport);
278
268
  rootModule.bindings.set(config.rootExport, {
279
269
  type: "var",
280
270
  init: null,
@@ -329,7 +319,7 @@ class StartCompiler {
329
319
  if (t.isVariableDeclaration(node.declaration)) {
330
320
  for (const d of node.declaration.declarations) {
331
321
  if (t.isIdentifier(d.id)) {
332
- exports.set(d.id.name, { tag: "Normal", name: d.id.name });
322
+ exports.set(d.id.name, d.id.name);
333
323
  bindings.set(d.id.name, { type: "var", init: d.init ?? null });
334
324
  }
335
325
  }
@@ -337,15 +327,11 @@ class StartCompiler {
337
327
  }
338
328
  for (const sp of node.specifiers) {
339
329
  if (t.isExportNamespaceSpecifier(sp)) {
340
- exports.set(sp.exported.name, {
341
- tag: "Namespace",
342
- name: sp.exported.name,
343
- targetId: node.source?.value || ""
344
- });
330
+ exports.set(sp.exported.name, sp.exported.name);
345
331
  } else if (t.isExportSpecifier(sp)) {
346
332
  const local = sp.local.name;
347
333
  const exported = t.isIdentifier(sp.exported) ? sp.exported.name : sp.exported.value;
348
- exports.set(exported, { tag: "Normal", name: local });
334
+ exports.set(exported, local);
349
335
  if (node.source) {
350
336
  bindings.set(local, {
351
337
  type: "import",
@@ -358,11 +344,11 @@ class StartCompiler {
358
344
  } else if (t.isExportDefaultDeclaration(node)) {
359
345
  const d = node.declaration;
360
346
  if (t.isIdentifier(d)) {
361
- exports.set("default", { tag: "Default", name: d.name });
347
+ exports.set("default", d.name);
362
348
  } else {
363
349
  const synth = "__default_export__";
364
350
  bindings.set(synth, { type: "var", init: d });
365
- exports.set("default", { tag: "Default", name: synth });
351
+ exports.set("default", synth);
366
352
  }
367
353
  } else if (t.isExportAllDeclaration(node)) {
368
354
  reExportAllSources.push(node.source.value);
@@ -404,7 +390,6 @@ class StartCompiler {
404
390
  const chainCallPaths = /* @__PURE__ */ new Map();
405
391
  const jsxCandidatePaths = [];
406
392
  const checkJSX = needsJSXDetection(fileKinds);
407
- const jsxTargetComponentNames = checkJSX ? getJSXComponentNames(fileKinds) : null;
408
393
  const moduleInfo = this.moduleCache.get(id);
409
394
  if (canUseFastPath) {
410
395
  const candidateIndices = [];
@@ -477,19 +462,21 @@ class StartCompiler {
477
462
  }
478
463
  },
479
464
  // Pattern 3: JSX element pattern (e.g., <ClientOnly>)
480
- // Collect JSX elements where the component name matches a known import
481
- // that resolves to a target component (e.g., ClientOnly from @tanstack/react-router)
465
+ // Collect JSX elements where the component is imported from a known package
466
+ // and resolves to a JSX kind (e.g., ClientOnly from @tanstack/react-router)
482
467
  JSXElement: (path) => {
483
- if (!checkJSX || !jsxTargetComponentNames) return;
468
+ if (!checkJSX) return;
484
469
  const openingElement = path.node.openingElement;
485
470
  const nameNode = openingElement.name;
486
471
  if (!t.isJSXIdentifier(nameNode)) return;
487
472
  const componentName = nameNode.name;
488
473
  const binding = moduleInfo.bindings.get(componentName);
489
474
  if (!binding || binding.type !== "import") return;
490
- if (jsxTargetComponentNames.has(binding.importedName)) {
491
- jsxCandidatePaths.push(path);
492
- }
475
+ const knownExports = this.knownRootImports.get(binding.source);
476
+ if (!knownExports) return;
477
+ const kind = knownExports.get(binding.importedName);
478
+ if (kind !== "ClientOnlyJSX") return;
479
+ jsxCandidatePaths.push(path);
493
480
  }
494
481
  });
495
482
  }
@@ -577,16 +564,6 @@ class StartCompiler {
577
564
  handler(candidates, context, kind);
578
565
  }
579
566
  for (const jsxPath of jsxCandidatePaths) {
580
- const openingElement = jsxPath.node.openingElement;
581
- const nameNode = openingElement.name;
582
- if (!t.isJSXIdentifier(nameNode)) continue;
583
- const componentName = nameNode.name;
584
- const binding = moduleInfo.bindings.get(componentName);
585
- if (!binding || binding.type !== "import") continue;
586
- const knownExports = this.knownRootImports.get(binding.source);
587
- if (!knownExports) continue;
588
- const kind = knownExports.get(binding.importedName);
589
- if (kind !== "ClientOnlyJSX") continue;
590
567
  handleClientOnlyJSX(jsxPath);
591
568
  }
592
569
  deadCodeElimination(ast, refIdents);
@@ -633,9 +610,9 @@ class StartCompiler {
633
610
  return void 0;
634
611
  }
635
612
  visitedModules.add(moduleInfo.id);
636
- const directExport = moduleInfo.exports.get(exportName);
637
- if (directExport) {
638
- const binding = moduleInfo.bindings.get(directExport.name);
613
+ const localBindingName = moduleInfo.exports.get(exportName);
614
+ if (localBindingName) {
615
+ const binding = moduleInfo.bindings.get(localBindingName);
639
616
  if (binding) {
640
617
  const result = { moduleInfo, binding };
641
618
  if (isBuildMode) {
@@ -721,6 +698,21 @@ class StartCompiler {
721
698
  binding.resolvedKind = resolvedKind;
722
699
  return resolvedKind;
723
700
  }
701
+ /**
702
+ * Checks if an identifier is a direct import from a known factory library.
703
+ * Returns true for imports like `import { createServerOnlyFn } from '@tanstack/react-start'`
704
+ * or renamed imports like `import { createServerOnlyFn as myFn } from '...'`.
705
+ * Returns false for local variables that hold the result of calling a factory.
706
+ */
707
+ async isKnownFactoryImport(identName, fileId) {
708
+ const info = await this.getModuleInfo(fileId);
709
+ const binding = info.bindings.get(identName);
710
+ if (!binding || binding.type !== "import") {
711
+ return false;
712
+ }
713
+ const knownExports = this.knownRootImports.get(binding.source);
714
+ return knownExports !== void 0 && knownExports.has(binding.importedName);
715
+ }
724
716
  async resolveExprKind(expr, fileId, visited = /* @__PURE__ */ new Set()) {
725
717
  if (!expr) {
726
718
  return "None";
@@ -741,8 +733,19 @@ class StartCompiler {
741
733
  if (calleeKind === "Root" || calleeKind === "Builder") {
742
734
  return "Builder";
743
735
  }
744
- if (this.validLookupKinds.has(calleeKind)) {
745
- return calleeKind;
736
+ if (t.isMemberExpression(expr.callee)) {
737
+ if (this.validLookupKinds.has(calleeKind)) {
738
+ return calleeKind;
739
+ }
740
+ }
741
+ if (t.isIdentifier(expr.callee)) {
742
+ const isFactoryImport = await this.isKnownFactoryImport(
743
+ expr.callee.name,
744
+ fileId
745
+ );
746
+ if (isFactoryImport && this.validLookupKinds.has(calleeKind)) {
747
+ return calleeKind;
748
+ }
746
749
  }
747
750
  } else if (t.isMemberExpression(expr) && t.isIdentifier(expr.property)) {
748
751
  result = await this.resolveCalleeKind(expr.object, fileId, visited);
@@ -788,11 +791,11 @@ class StartCompiler {
788
791
  );
789
792
  if (targetModuleId) {
790
793
  const targetModule = await this.getModuleInfo(targetModuleId);
791
- const exportEntry = targetModule.exports.get(callee.property.name);
792
- if (exportEntry) {
793
- const exportedBinding = targetModule.bindings.get(
794
- exportEntry.name
795
- );
794
+ const localBindingName = targetModule.exports.get(
795
+ callee.property.name
796
+ );
797
+ if (localBindingName) {
798
+ const exportedBinding = targetModule.bindings.get(localBindingName);
796
799
  if (exportedBinding) {
797
800
  return await this.resolveBindingKind(
798
801
  exportedBinding,
@@ -1 +1 @@
1
- {"version":3,"file":"compiler.js","sources":["../../../src/start-compiler-plugin/compiler.ts"],"sourcesContent":["/* eslint-disable import/no-commonjs */\nimport crypto from 'node:crypto'\nimport * as t from '@babel/types'\nimport { generateFromAst, parseAst } from '@tanstack/router-utils'\nimport babel from '@babel/core'\nimport {\n deadCodeElimination,\n findReferencedIdentifiers,\n} from 'babel-dead-code-elimination'\nimport { handleCreateServerFn } from './handleCreateServerFn'\nimport { handleCreateMiddleware } from './handleCreateMiddleware'\nimport { handleCreateIsomorphicFn } from './handleCreateIsomorphicFn'\nimport { handleEnvOnlyFn } from './handleEnvOnly'\nimport { handleClientOnlyJSX } from './handleClientOnlyJSX'\nimport type {\n CompilationContext,\n MethodChainPaths,\n RewriteCandidate,\n ServerFn,\n} from './types'\nimport type { CompileStartFrameworkOptions } from '../types'\n\ntype Binding =\n | {\n type: 'import'\n source: string\n importedName: string\n resolvedKind?: Kind\n }\n | {\n type: 'var'\n init: t.Expression | null\n resolvedKind?: Kind\n }\n\ntype ExportEntry =\n | { tag: 'Normal'; name: string }\n | { tag: 'Default'; name: string }\n | { tag: 'Namespace'; name: string; targetId: string } // for `export * as ns from './x'`\n\ntype Kind = 'None' | `Root` | `Builder` | LookupKind\n\nexport type LookupKind =\n | 'ServerFn'\n | 'Middleware'\n | 'IsomorphicFn'\n | 'ServerOnlyFn'\n | 'ClientOnlyFn'\n | 'ClientOnlyJSX'\n\n// Detection strategy for each kind\ntype MethodChainSetup = {\n type: 'methodChain'\n candidateCallIdentifier: Set<string>\n // If true, a call to the root function (e.g., createIsomorphicFn()) is also a candidate\n // even without chained method calls. This is used for IsomorphicFn which can be\n // called without .client() or .server() (resulting in a no-op function).\n allowRootAsCandidate?: boolean\n}\ntype DirectCallSetup = { type: 'directCall' }\ntype JSXSetup = { type: 'jsx'; componentName: string }\n\nconst LookupSetup: Record<\n LookupKind,\n MethodChainSetup | DirectCallSetup | JSXSetup\n> = {\n ServerFn: {\n type: 'methodChain',\n candidateCallIdentifier: new Set(['handler']),\n },\n Middleware: {\n type: 'methodChain',\n candidateCallIdentifier: new Set(['server', 'client', 'createMiddlewares']),\n },\n IsomorphicFn: {\n type: 'methodChain',\n candidateCallIdentifier: new Set(['server', 'client']),\n allowRootAsCandidate: true, // createIsomorphicFn() alone is valid (returns no-op)\n },\n ServerOnlyFn: { type: 'directCall' },\n ClientOnlyFn: { type: 'directCall' },\n ClientOnlyJSX: { type: 'jsx', componentName: 'ClientOnly' },\n}\n\n// Single source of truth for detecting which kinds are present in code\n// These patterns are used for:\n// 1. Pre-scanning code to determine which kinds to look for (before AST parsing)\n// 2. Deriving the plugin's transform code filter\nexport const KindDetectionPatterns: Record<LookupKind, RegExp> = {\n ServerFn: /\\.handler\\s*\\(/,\n Middleware: /createMiddleware/,\n IsomorphicFn: /createIsomorphicFn/,\n ServerOnlyFn: /createServerOnlyFn/,\n ClientOnlyFn: /createClientOnlyFn/,\n ClientOnlyJSX: /<ClientOnly|import\\s*\\{[^}]*\\bClientOnly\\b/,\n}\n\n// Which kinds are valid for each environment\nexport const LookupKindsPerEnv: Record<'client' | 'server', Set<LookupKind>> = {\n client: new Set([\n 'Middleware',\n 'ServerFn',\n 'IsomorphicFn',\n 'ServerOnlyFn',\n 'ClientOnlyFn',\n ] as const),\n server: new Set([\n 'ServerFn',\n 'IsomorphicFn',\n 'ServerOnlyFn',\n 'ClientOnlyFn',\n 'ClientOnlyJSX', // Only transform on server to remove children\n ] as const),\n}\n\n/**\n * Handler type for processing candidates of a specific kind.\n * The kind is passed as the third argument to allow shared handlers (like handleEnvOnlyFn).\n */\ntype KindHandler = (\n candidates: Array<RewriteCandidate>,\n context: CompilationContext,\n kind: LookupKind,\n) => void\n\n/**\n * Registry mapping each LookupKind to its handler function.\n * When adding a new kind, add its handler here.\n */\nconst KindHandlers: Record<\n Exclude<LookupKind, 'ClientOnlyJSX'>,\n KindHandler\n> = {\n ServerFn: handleCreateServerFn,\n Middleware: handleCreateMiddleware,\n IsomorphicFn: handleCreateIsomorphicFn,\n ServerOnlyFn: handleEnvOnlyFn,\n ClientOnlyFn: handleEnvOnlyFn,\n // ClientOnlyJSX is handled separately via JSX traversal, not here\n}\n\n/**\n * Detects which LookupKinds are present in the code using string matching.\n * This is a fast pre-scan before AST parsing to limit the work done during compilation.\n */\nexport function detectKindsInCode(\n code: string,\n env: 'client' | 'server',\n): Set<LookupKind> {\n const detected = new Set<LookupKind>()\n const validForEnv = LookupKindsPerEnv[env]\n\n for (const [kind, pattern] of Object.entries(KindDetectionPatterns) as Array<\n [LookupKind, RegExp]\n >) {\n if (validForEnv.has(kind) && pattern.test(code)) {\n detected.add(kind)\n }\n }\n\n return detected\n}\n\n// Pre-computed map: identifier name -> Set<LookupKind> for fast candidate detection (method chain only)\n// Multiple kinds can share the same identifier (e.g., 'server' and 'client' are used by both Middleware and IsomorphicFn)\nconst IdentifierToKinds = new Map<string, Set<LookupKind>>()\nfor (const [kind, setup] of Object.entries(LookupSetup) as Array<\n [LookupKind, MethodChainSetup | DirectCallSetup]\n>) {\n if (setup.type === 'methodChain') {\n for (const id of setup.candidateCallIdentifier) {\n let kinds = IdentifierToKinds.get(id)\n if (!kinds) {\n kinds = new Set()\n IdentifierToKinds.set(id, kinds)\n }\n kinds.add(kind)\n }\n }\n}\n\n// Known factory function names for direct call and root-as-candidate patterns\n// These are the names that, when called directly, create a new function.\n// Used to filter nested candidates - we only want to include actual factory calls,\n// not invocations of already-created functions (e.g., `myServerFn()` should NOT be a candidate)\nconst DirectCallFactoryNames = new Set([\n 'createServerOnlyFn',\n 'createClientOnlyFn',\n 'createIsomorphicFn',\n])\n\nexport type LookupConfig = {\n libName: string\n rootExport: string\n kind: LookupKind | 'Root' // 'Root' for builder pattern, LookupKind for direct call\n}\n\ninterface ModuleInfo {\n id: string\n bindings: Map<string, Binding>\n exports: Map<string, ExportEntry>\n // Track `export * from './module'` declarations for re-export resolution\n reExportAllSources: Array<string>\n}\n\n/**\n * Computes whether any file kinds need direct-call candidate detection.\n * This includes both directCall types (ServerOnlyFn, ClientOnlyFn) and\n * allowRootAsCandidate types (IsomorphicFn).\n */\nfunction needsDirectCallDetection(kinds: Set<LookupKind>): boolean {\n for (const kind of kinds) {\n const setup = LookupSetup[kind]\n if (\n setup.type === 'directCall' ||\n (setup.type === 'methodChain' && setup.allowRootAsCandidate)\n ) {\n return true\n }\n }\n return false\n}\n\n/**\n * Checks if all kinds in the set are guaranteed to be top-level only.\n * Only ServerFn is always declared at module level (must be assigned to a variable).\n * Middleware, IsomorphicFn, ServerOnlyFn, ClientOnlyFn can be nested inside functions.\n * When all kinds are top-level-only, we can use a fast scan instead of full traversal.\n */\nfunction areAllKindsTopLevelOnly(kinds: Set<LookupKind>): boolean {\n return kinds.size === 1 && kinds.has('ServerFn')\n}\n\n/**\n * Checks if we need to detect JSX elements (e.g., <ClientOnly>).\n */\nfunction needsJSXDetection(kinds: Set<LookupKind>): boolean {\n for (const kind of kinds) {\n const setup = LookupSetup[kind]\n if (setup.type === 'jsx') {\n return true\n }\n }\n return false\n}\n\n/**\n * Gets the set of JSX component names to detect.\n */\nfunction getJSXComponentNames(kinds: Set<LookupKind>): Set<string> {\n const names = new Set<string>()\n for (const kind of kinds) {\n const setup = LookupSetup[kind]\n if (setup.type === 'jsx') {\n names.add(setup.componentName)\n }\n }\n return names\n}\n\n/**\n * Checks if a CallExpression is a direct-call candidate for NESTED detection.\n * Returns true if the callee is a known factory function name.\n * This is stricter than top-level detection because we need to filter out\n * invocations of existing server functions (e.g., `myServerFn()`).\n */\nfunction isNestedDirectCallCandidate(node: t.CallExpression): boolean {\n let calleeName: string | undefined\n if (t.isIdentifier(node.callee)) {\n calleeName = node.callee.name\n } else if (\n t.isMemberExpression(node.callee) &&\n t.isIdentifier(node.callee.property)\n ) {\n calleeName = node.callee.property.name\n }\n return calleeName !== undefined && DirectCallFactoryNames.has(calleeName)\n}\n\n/**\n * Checks if a CallExpression path is a top-level direct-call candidate.\n * Top-level means the call is the init of a VariableDeclarator at program level.\n * We accept any simple identifier call or namespace call at top level\n * (e.g., `isomorphicFn()`, `TanStackStart.createServerOnlyFn()`) and let\n * resolution verify it. This handles renamed imports.\n */\nfunction isTopLevelDirectCallCandidate(\n path: babel.NodePath<t.CallExpression>,\n): boolean {\n const node = path.node\n\n // Must be a simple identifier call or namespace call\n const isSimpleCall =\n t.isIdentifier(node.callee) ||\n (t.isMemberExpression(node.callee) &&\n t.isIdentifier(node.callee.object) &&\n t.isIdentifier(node.callee.property))\n\n if (!isSimpleCall) {\n return false\n }\n\n // Must be top-level: VariableDeclarator -> VariableDeclaration -> Program\n const parent = path.parent\n if (!t.isVariableDeclarator(parent) || parent.init !== node) {\n return false\n }\n const grandParent = path.parentPath.parent\n if (!t.isVariableDeclaration(grandParent)) {\n return false\n }\n return t.isProgram(path.parentPath.parentPath?.parent)\n}\n\nexport class StartCompiler {\n private moduleCache = new Map<string, ModuleInfo>()\n private initialized = false\n private validLookupKinds: Set<LookupKind>\n private resolveIdCache = new Map<string, string | null>()\n private exportResolutionCache = new Map<\n string,\n Map<string, { moduleInfo: ModuleInfo; binding: Binding } | null>\n >()\n // Fast lookup for direct imports from known libraries (e.g., '@tanstack/react-start')\n // Maps: libName → (exportName → Kind)\n // This allows O(1) resolution for the common case without async resolveId calls\n private knownRootImports = new Map<string, Map<string, Kind>>()\n\n // For generating unique function IDs in production builds\n private entryIdToFunctionId = new Map<string, string>()\n private functionIds = new Set<string>()\n\n constructor(\n private options: {\n env: 'client' | 'server'\n envName: string\n root: string\n lookupConfigurations: Array<LookupConfig>\n lookupKinds: Set<LookupKind>\n loadModule: (id: string) => Promise<void>\n resolveId: (id: string, importer?: string) => Promise<string | null>\n /**\n * In 'build' mode, resolution results are cached for performance.\n * In 'dev' mode (default), caching is disabled to avoid invalidation complexity with HMR.\n */\n mode?: 'dev' | 'build'\n /**\n * The framework being used (e.g., 'react', 'solid').\n */\n framework: CompileStartFrameworkOptions\n /**\n * The Vite environment name for the server function provider.\n */\n providerEnvName: string\n /**\n * Custom function ID generator (optional, defaults to hash-based).\n */\n generateFunctionId?: (opts: {\n filename: string\n functionName: string\n }) => string | undefined\n /**\n * Callback when server functions are discovered.\n * Called after each file is compiled with its new functions.\n */\n onServerFnsById?: (d: Record<string, ServerFn>) => void\n /**\n * Returns the currently known server functions from previous builds.\n * Used by server callers to look up canonical extracted filenames.\n */\n getKnownServerFns?: () => Record<string, ServerFn>\n },\n ) {\n this.validLookupKinds = options.lookupKinds\n }\n\n /**\n * Generates a unique function ID for a server function.\n * In dev mode, uses a base64-encoded JSON with file path and export name.\n * In build mode, uses SHA256 hash or custom generator.\n */\n private generateFunctionId(opts: {\n filename: string\n functionName: string\n extractedFilename: string\n }): string {\n if (this.mode === 'dev') {\n // In dev, encode the file path and export name for direct lookup\n const rootWithTrailingSlash = this.options.root.endsWith('/')\n ? this.options.root\n : `${this.options.root}/`\n let file = opts.extractedFilename\n if (opts.extractedFilename.startsWith(rootWithTrailingSlash)) {\n file = opts.extractedFilename.slice(rootWithTrailingSlash.length)\n }\n file = `/@id/${file}`\n\n const serverFn = {\n file,\n export: opts.functionName,\n }\n return Buffer.from(JSON.stringify(serverFn), 'utf8').toString('base64url')\n }\n\n // Production build: use custom generator or hash\n const entryId = `${opts.filename}--${opts.functionName}`\n let functionId = this.entryIdToFunctionId.get(entryId)\n if (functionId === undefined) {\n if (this.options.generateFunctionId) {\n functionId = this.options.generateFunctionId({\n filename: opts.filename,\n functionName: opts.functionName,\n })\n }\n if (!functionId) {\n functionId = crypto.createHash('sha256').update(entryId).digest('hex')\n }\n // Deduplicate in case the generated id conflicts with an existing id\n if (this.functionIds.has(functionId)) {\n let deduplicatedId\n let iteration = 0\n do {\n deduplicatedId = `${functionId}_${++iteration}`\n } while (this.functionIds.has(deduplicatedId))\n functionId = deduplicatedId\n }\n this.entryIdToFunctionId.set(entryId, functionId)\n this.functionIds.add(functionId)\n }\n return functionId\n }\n\n private get mode(): 'dev' | 'build' {\n return this.options.mode ?? 'dev'\n }\n\n private async resolveIdCached(id: string, importer?: string) {\n if (this.mode === 'dev') {\n return this.options.resolveId(id, importer)\n }\n\n const cacheKey = importer ? `${importer}::${id}` : id\n const cached = this.resolveIdCache.get(cacheKey)\n if (cached !== undefined) {\n return cached\n }\n const resolved = await this.options.resolveId(id, importer)\n this.resolveIdCache.set(cacheKey, resolved)\n return resolved\n }\n\n private getExportResolutionCache(moduleId: string) {\n let cache = this.exportResolutionCache.get(moduleId)\n if (!cache) {\n cache = new Map()\n this.exportResolutionCache.set(moduleId, cache)\n }\n return cache\n }\n\n private async init() {\n // Register internal stub package exports for recognition.\n // These don't need module resolution - only the knownRootImports fast path.\n this.knownRootImports.set(\n '@tanstack/start-fn-stubs',\n new Map<string, Kind>([\n ['createIsomorphicFn', 'IsomorphicFn'],\n ['createServerOnlyFn', 'ServerOnlyFn'],\n ['createClientOnlyFn', 'ClientOnlyFn'],\n ]),\n )\n\n await Promise.all(\n this.options.lookupConfigurations.map(async (config) => {\n // Populate the fast lookup map for direct imports (by package name)\n // This allows O(1) recognition of imports from known packages.\n let libExports = this.knownRootImports.get(config.libName)\n if (!libExports) {\n libExports = new Map()\n this.knownRootImports.set(config.libName, libExports)\n }\n libExports.set(config.rootExport, config.kind)\n\n // For JSX lookups (e.g., ClientOnlyJSX), we only need the knownRootImports\n // fast path to verify imports. Skip module resolution which may fail if\n // the package isn't a direct dependency (e.g., @tanstack/react-router from\n // within start-plugin-core).\n if (config.kind !== 'Root') {\n const setup = LookupSetup[config.kind]\n if (setup.type === 'jsx') {\n return\n }\n }\n\n const libId = await this.resolveIdCached(config.libName)\n if (!libId) {\n throw new Error(`could not resolve \"${config.libName}\"`)\n }\n let rootModule = this.moduleCache.get(libId)\n if (!rootModule) {\n // insert root binding\n rootModule = {\n bindings: new Map(),\n exports: new Map(),\n id: libId,\n reExportAllSources: [],\n }\n this.moduleCache.set(libId, rootModule)\n }\n\n rootModule.exports.set(config.rootExport, {\n tag: 'Normal',\n name: config.rootExport,\n })\n rootModule.exports.set('*', {\n tag: 'Namespace',\n name: config.rootExport,\n targetId: libId,\n })\n rootModule.bindings.set(config.rootExport, {\n type: 'var',\n init: null, // Not needed since resolvedKind is set\n resolvedKind: config.kind satisfies Kind,\n })\n this.moduleCache.set(libId, rootModule)\n }),\n )\n\n this.initialized = true\n }\n\n /**\n * Extracts bindings and exports from an already-parsed AST.\n * This is the core logic shared by ingestModule and ingestModuleFromAst.\n */\n private extractModuleInfo(\n ast: ReturnType<typeof parseAst>,\n id: string,\n ): ModuleInfo {\n const bindings = new Map<string, Binding>()\n const exports = new Map<string, ExportEntry>()\n const reExportAllSources: Array<string> = []\n\n // we are only interested in top-level bindings, hence we don't traverse the AST\n // instead we only iterate over the program body\n for (const node of ast.program.body) {\n if (t.isImportDeclaration(node)) {\n const source = node.source.value\n for (const s of node.specifiers) {\n if (t.isImportSpecifier(s)) {\n const importedName = t.isIdentifier(s.imported)\n ? s.imported.name\n : s.imported.value\n bindings.set(s.local.name, { type: 'import', source, importedName })\n } else if (t.isImportDefaultSpecifier(s)) {\n bindings.set(s.local.name, {\n type: 'import',\n source,\n importedName: 'default',\n })\n } else if (t.isImportNamespaceSpecifier(s)) {\n bindings.set(s.local.name, {\n type: 'import',\n source,\n importedName: '*',\n })\n }\n }\n } else if (t.isVariableDeclaration(node)) {\n for (const decl of node.declarations) {\n if (t.isIdentifier(decl.id)) {\n bindings.set(decl.id.name, {\n type: 'var',\n init: decl.init ?? null,\n })\n }\n }\n } else if (t.isExportNamedDeclaration(node)) {\n // export const foo = ...\n if (node.declaration) {\n if (t.isVariableDeclaration(node.declaration)) {\n for (const d of node.declaration.declarations) {\n if (t.isIdentifier(d.id)) {\n exports.set(d.id.name, { tag: 'Normal', name: d.id.name })\n bindings.set(d.id.name, { type: 'var', init: d.init ?? null })\n }\n }\n }\n }\n for (const sp of node.specifiers) {\n if (t.isExportNamespaceSpecifier(sp)) {\n exports.set(sp.exported.name, {\n tag: 'Namespace',\n name: sp.exported.name,\n targetId: node.source?.value || '',\n })\n }\n // export { local as exported }\n else if (t.isExportSpecifier(sp)) {\n const local = sp.local.name\n const exported = t.isIdentifier(sp.exported)\n ? sp.exported.name\n : sp.exported.value\n exports.set(exported, { tag: 'Normal', name: local })\n\n // When re-exporting from another module (export { foo } from './module'),\n // create an import binding so the server function can be resolved\n if (node.source) {\n bindings.set(local, {\n type: 'import',\n source: node.source.value,\n importedName: local,\n })\n }\n }\n }\n } else if (t.isExportDefaultDeclaration(node)) {\n const d = node.declaration\n if (t.isIdentifier(d)) {\n exports.set('default', { tag: 'Default', name: d.name })\n } else {\n const synth = '__default_export__'\n bindings.set(synth, { type: 'var', init: d as t.Expression })\n exports.set('default', { tag: 'Default', name: synth })\n }\n } else if (t.isExportAllDeclaration(node)) {\n // Handle `export * from './module'` syntax\n // Track the source so we can look up exports from it when needed\n reExportAllSources.push(node.source.value)\n }\n }\n\n const info: ModuleInfo = {\n id,\n bindings,\n exports,\n reExportAllSources,\n }\n this.moduleCache.set(id, info)\n return info\n }\n\n public ingestModule({ code, id }: { code: string; id: string }) {\n const ast = parseAst({ code })\n const info = this.extractModuleInfo(ast, id)\n return { info, ast }\n }\n\n public invalidateModule(id: string) {\n // Note: Resolution caches (resolveIdCache, exportResolutionCache) are only\n // used in build mode where there's no HMR. In dev mode, caching is disabled,\n // so we only need to invalidate the moduleCache here.\n return this.moduleCache.delete(id)\n }\n\n public async compile({\n code,\n id,\n detectedKinds,\n }: {\n code: string\n id: string\n /** Pre-detected kinds present in this file. If not provided, all valid kinds are checked. */\n detectedKinds?: Set<LookupKind>\n }) {\n if (!this.initialized) {\n await this.init()\n }\n\n // Use detected kinds if provided, otherwise fall back to all valid kinds for this env\n const fileKinds = detectedKinds\n ? new Set([...detectedKinds].filter((k) => this.validLookupKinds.has(k)))\n : this.validLookupKinds\n\n // Early exit if no kinds to process\n if (fileKinds.size === 0) {\n return null\n }\n\n const checkDirectCalls = needsDirectCallDetection(fileKinds)\n // Optimization: ServerFn is always a top-level declaration (must be assigned to a variable).\n // If the file only has ServerFn, we can skip full AST traversal and only visit\n // the specific top-level declarations that have candidates.\n const canUseFastPath = areAllKindsTopLevelOnly(fileKinds)\n\n // Always parse and extract module info upfront.\n // This ensures the module is cached for import resolution even if no candidates are found.\n const { ast } = this.ingestModule({ code, id })\n\n // Single-pass traversal to:\n // 1. Collect candidate paths (only candidates, not all CallExpressions)\n // 2. Build a map for looking up paths of nested calls in method chains\n const candidatePaths: Array<babel.NodePath<t.CallExpression>> = []\n // Map for nested chain lookup - only populated for CallExpressions that are\n // part of a method chain (callee.object is a CallExpression)\n const chainCallPaths = new Map<\n t.CallExpression,\n babel.NodePath<t.CallExpression>\n >()\n\n // JSX candidates (e.g., <ClientOnly>)\n const jsxCandidatePaths: Array<babel.NodePath<t.JSXElement>> = []\n const checkJSX = needsJSXDetection(fileKinds)\n // Get target component names from JSX setup (e.g., 'ClientOnly')\n const jsxTargetComponentNames = checkJSX\n ? getJSXComponentNames(fileKinds)\n : null\n // Get module info that was just cached by ingestModule\n const moduleInfo = this.moduleCache.get(id)!\n\n if (canUseFastPath) {\n // Fast path: only visit top-level statements that have potential candidates\n\n // Collect indices of top-level statements that contain candidates\n const candidateIndices: Array<number> = []\n for (let i = 0; i < ast.program.body.length; i++) {\n const node = ast.program.body[i]!\n let declarations: Array<t.VariableDeclarator> | undefined\n\n if (t.isVariableDeclaration(node)) {\n declarations = node.declarations\n } else if (t.isExportNamedDeclaration(node) && node.declaration) {\n if (t.isVariableDeclaration(node.declaration)) {\n declarations = node.declaration.declarations\n }\n }\n\n if (declarations) {\n for (const decl of declarations) {\n if (decl.init && t.isCallExpression(decl.init)) {\n if (isMethodChainCandidate(decl.init, fileKinds)) {\n candidateIndices.push(i)\n break // Only need to mark this statement once\n }\n }\n }\n }\n }\n\n // Early exit: no potential candidates found at top level\n if (candidateIndices.length === 0) {\n return null\n }\n\n // Targeted traversal: only visit the specific statements that have candidates\n // This is much faster than traversing the entire AST\n babel.traverse(ast, {\n Program(programPath) {\n const bodyPaths = programPath.get('body')\n for (const idx of candidateIndices) {\n const stmtPath = bodyPaths[idx]\n if (!stmtPath) continue\n\n // Traverse only this statement's subtree\n stmtPath.traverse({\n CallExpression(path) {\n const node = path.node\n const parent = path.parent\n\n // Check if this call is part of a larger chain (inner call)\n if (\n t.isMemberExpression(parent) &&\n t.isCallExpression(path.parentPath.parent)\n ) {\n chainCallPaths.set(node, path)\n return\n }\n\n // Method chain pattern\n if (isMethodChainCandidate(node, fileKinds)) {\n candidatePaths.push(path)\n }\n },\n })\n }\n // Stop traversal after processing Program\n programPath.stop()\n },\n })\n } else {\n // Normal path: full traversal for non-fast-path kinds\n babel.traverse(ast, {\n CallExpression: (path) => {\n const node = path.node\n const parent = path.parent\n\n // Check if this call is part of a larger chain (inner call)\n // If so, store it for method chain lookup but don't treat as candidate\n if (\n t.isMemberExpression(parent) &&\n t.isCallExpression(path.parentPath.parent)\n ) {\n // This is an inner call in a chain - store for later lookup\n chainCallPaths.set(node, path)\n return\n }\n\n // Pattern 1: Method chain pattern (.handler(), .server(), .client(), etc.)\n if (isMethodChainCandidate(node, fileKinds)) {\n candidatePaths.push(path)\n return\n }\n\n // Pattern 2: Direct call pattern\n if (checkDirectCalls) {\n if (isTopLevelDirectCallCandidate(path)) {\n candidatePaths.push(path)\n } else if (isNestedDirectCallCandidate(node)) {\n candidatePaths.push(path)\n }\n }\n },\n // Pattern 3: JSX element pattern (e.g., <ClientOnly>)\n // Collect JSX elements where the component name matches a known import\n // that resolves to a target component (e.g., ClientOnly from @tanstack/react-router)\n JSXElement: (path) => {\n if (!checkJSX || !jsxTargetComponentNames) return\n\n const openingElement = path.node.openingElement\n const nameNode = openingElement.name\n\n // Only handle simple identifier names (not namespaced or member expressions)\n if (!t.isJSXIdentifier(nameNode)) return\n\n const componentName = nameNode.name\n const binding = moduleInfo.bindings.get(componentName)\n\n // Must be an import binding\n if (!binding || binding.type !== 'import') return\n\n // Check if the original import name matches a target component\n if (jsxTargetComponentNames.has(binding.importedName)) {\n jsxCandidatePaths.push(path)\n }\n },\n })\n }\n\n if (candidatePaths.length === 0 && jsxCandidatePaths.length === 0) {\n return null\n }\n\n // Resolve all candidates in parallel to determine their kinds\n const resolvedCandidates = await Promise.all(\n candidatePaths.map(async (path) => ({\n path,\n kind: await this.resolveExprKind(path.node, id),\n })),\n )\n\n // Filter to valid candidates\n const validCandidates = resolvedCandidates.filter(({ kind }) =>\n this.validLookupKinds.has(kind as Exclude<LookupKind, 'ClientOnlyJSX'>),\n ) as Array<{\n path: babel.NodePath<t.CallExpression>\n kind: Exclude<LookupKind, 'ClientOnlyJSX'>\n }>\n\n if (validCandidates.length === 0 && jsxCandidatePaths.length === 0) {\n return null\n }\n\n // Process valid candidates to collect method chains\n const pathsToRewrite: Array<{\n path: babel.NodePath<t.CallExpression>\n kind: Exclude<LookupKind, 'ClientOnlyJSX'>\n methodChain: MethodChainPaths\n }> = []\n\n for (const { path, kind } of validCandidates) {\n const node = path.node\n\n // Collect method chain paths by walking DOWN from root through the chain\n const methodChain: MethodChainPaths = {\n middleware: null,\n inputValidator: null,\n handler: null,\n server: null,\n client: null,\n }\n\n // Walk down the call chain using nodes, look up paths from map\n let currentNode: t.CallExpression = node\n let currentPath: babel.NodePath<t.CallExpression> = path\n\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n while (true) {\n const callee = currentNode.callee\n if (!t.isMemberExpression(callee)) {\n break\n }\n\n // Record method chain path if it's a known method\n if (t.isIdentifier(callee.property)) {\n const name = callee.property.name as keyof MethodChainPaths\n if (name in methodChain) {\n // Get first argument path\n const args = currentPath.get('arguments')\n const firstArgPath =\n Array.isArray(args) && args.length > 0 ? (args[0] ?? null) : null\n methodChain[name] = {\n callPath: currentPath,\n firstArgPath,\n }\n }\n }\n\n // Move to the inner call (the object of the member expression)\n if (!t.isCallExpression(callee.object)) {\n break\n }\n currentNode = callee.object\n // Look up path from chain map, or use candidate path if not found\n const nextPath = chainCallPaths.get(currentNode)\n if (!nextPath) {\n break\n }\n currentPath = nextPath\n }\n\n pathsToRewrite.push({ path, kind, methodChain })\n }\n\n const refIdents = findReferencedIdentifiers(ast)\n\n const context: CompilationContext = {\n ast,\n id,\n code,\n env: this.options.env,\n envName: this.options.envName,\n root: this.options.root,\n framework: this.options.framework,\n providerEnvName: this.options.providerEnvName,\n\n generateFunctionId: (opts) => this.generateFunctionId(opts),\n getKnownServerFns: () => this.options.getKnownServerFns?.() ?? {},\n onServerFnsById: this.options.onServerFnsById,\n }\n\n // Group candidates by kind for batch processing\n const candidatesByKind = new Map<\n Exclude<LookupKind, 'ClientOnlyJSX'>,\n Array<RewriteCandidate>\n >()\n\n for (const { path: candidatePath, kind, methodChain } of pathsToRewrite) {\n const candidate: RewriteCandidate = { path: candidatePath, methodChain }\n const existing = candidatesByKind.get(kind)\n if (existing) {\n existing.push(candidate)\n } else {\n candidatesByKind.set(kind, [candidate])\n }\n }\n\n // Process each kind using its registered handler\n for (const [kind, candidates] of candidatesByKind) {\n const handler = KindHandlers[kind]\n handler(candidates, context, kind)\n }\n\n // Handle JSX candidates (e.g., <ClientOnly>)\n // Note: We only reach here on the server (ClientOnlyJSX is only in LookupKindsPerEnv.server)\n // Verify import source using knownRootImports (same as function call resolution)\n for (const jsxPath of jsxCandidatePaths) {\n const openingElement = jsxPath.node.openingElement\n const nameNode = openingElement.name\n if (!t.isJSXIdentifier(nameNode)) continue\n\n const componentName = nameNode.name\n const binding = moduleInfo.bindings.get(componentName)\n if (!binding || binding.type !== 'import') continue\n\n // Verify the import source is a known TanStack router package\n const knownExports = this.knownRootImports.get(binding.source)\n if (!knownExports) continue\n\n // Verify the imported name resolves to ClientOnlyJSX kind\n const kind = knownExports.get(binding.importedName)\n if (kind !== 'ClientOnlyJSX') continue\n\n handleClientOnlyJSX(jsxPath, { env: 'server' })\n }\n\n deadCodeElimination(ast, refIdents)\n\n return generateFromAst(ast, {\n sourceMaps: true,\n sourceFileName: id,\n filename: id,\n })\n }\n\n private async resolveIdentifierKind(\n ident: string,\n id: string,\n visited = new Set<string>(),\n ): Promise<Kind> {\n const info = await this.getModuleInfo(id)\n\n const binding = info.bindings.get(ident)\n if (!binding) {\n return 'None'\n }\n if (binding.resolvedKind) {\n return binding.resolvedKind\n }\n\n // TODO improve cycle detection? should we throw here instead of returning 'None'?\n // prevent cycles\n const vKey = `${id}:${ident}`\n if (visited.has(vKey)) {\n return 'None'\n }\n visited.add(vKey)\n\n const resolvedKind = await this.resolveBindingKind(binding, id, visited)\n binding.resolvedKind = resolvedKind\n return resolvedKind\n }\n\n /**\n * Recursively find an export in a module, following `export * from` chains.\n * Returns the module info and binding if found, or undefined if not found.\n */\n private async findExportInModule(\n moduleInfo: ModuleInfo,\n exportName: string,\n visitedModules = new Set<string>(),\n ): Promise<{ moduleInfo: ModuleInfo; binding: Binding } | undefined> {\n const isBuildMode = this.mode === 'build'\n\n // Check cache first (only for top-level calls in build mode)\n if (isBuildMode && visitedModules.size === 0) {\n const moduleCache = this.exportResolutionCache.get(moduleInfo.id)\n if (moduleCache) {\n const cached = moduleCache.get(exportName)\n if (cached !== undefined) {\n return cached ?? undefined\n }\n }\n }\n\n // Prevent infinite loops in circular re-exports\n if (visitedModules.has(moduleInfo.id)) {\n return undefined\n }\n visitedModules.add(moduleInfo.id)\n\n // First check direct exports\n const directExport = moduleInfo.exports.get(exportName)\n if (directExport) {\n const binding = moduleInfo.bindings.get(directExport.name)\n if (binding) {\n const result = { moduleInfo, binding }\n // Cache the result (build mode only)\n if (isBuildMode) {\n this.getExportResolutionCache(moduleInfo.id).set(exportName, result)\n }\n return result\n }\n }\n\n // If not found, recursively check re-export-all sources in parallel\n // Valid code won't have duplicate exports across chains, so first match wins\n if (moduleInfo.reExportAllSources.length > 0) {\n const results = await Promise.all(\n moduleInfo.reExportAllSources.map(async (reExportSource) => {\n const reExportTarget = await this.resolveIdCached(\n reExportSource,\n moduleInfo.id,\n )\n\n if (reExportTarget) {\n const reExportModule = await this.getModuleInfo(reExportTarget)\n return this.findExportInModule(\n reExportModule,\n exportName,\n visitedModules,\n )\n }\n return undefined\n }),\n )\n // Return the first valid result\n for (const result of results) {\n if (result) {\n // Cache the result (build mode only)\n if (isBuildMode) {\n this.getExportResolutionCache(moduleInfo.id).set(exportName, result)\n }\n return result\n }\n }\n }\n\n // Cache negative result (build mode only)\n if (isBuildMode) {\n this.getExportResolutionCache(moduleInfo.id).set(exportName, null)\n }\n return undefined\n }\n\n private async resolveBindingKind(\n binding: Binding,\n fileId: string,\n visited = new Set<string>(),\n ): Promise<Kind> {\n if (binding.resolvedKind) {\n return binding.resolvedKind\n }\n if (binding.type === 'import') {\n // Fast path: check if this is a direct import from a known library\n // (e.g., import { createServerFn } from '@tanstack/react-start')\n // This avoids async resolveId calls for the common case\n const knownExports = this.knownRootImports.get(binding.source)\n if (knownExports) {\n const kind = knownExports.get(binding.importedName)\n if (kind) {\n binding.resolvedKind = kind\n return kind\n }\n }\n\n // Slow path: resolve through the module graph\n const target = await this.resolveIdCached(binding.source, fileId)\n if (!target) {\n return 'None'\n }\n\n const importedModule = await this.getModuleInfo(target)\n\n // Find the export, recursively searching through export * from chains\n const found = await this.findExportInModule(\n importedModule,\n binding.importedName,\n )\n\n if (!found) {\n return 'None'\n }\n\n const { moduleInfo: foundModule, binding: foundBinding } = found\n\n if (foundBinding.resolvedKind) {\n return foundBinding.resolvedKind\n }\n\n const resolvedKind = await this.resolveBindingKind(\n foundBinding,\n foundModule.id,\n visited,\n )\n foundBinding.resolvedKind = resolvedKind\n return resolvedKind\n }\n\n const resolvedKind = await this.resolveExprKind(\n binding.init,\n fileId,\n visited,\n )\n binding.resolvedKind = resolvedKind\n return resolvedKind\n }\n\n private async resolveExprKind(\n expr: t.Expression | null,\n fileId: string,\n visited = new Set<string>(),\n ): Promise<Kind> {\n if (!expr) {\n return 'None'\n }\n\n // Unwrap common TypeScript/parenthesized wrappers first for efficiency\n while (\n t.isTSAsExpression(expr) ||\n t.isTSNonNullExpression(expr) ||\n t.isParenthesizedExpression(expr)\n ) {\n expr = expr.expression\n }\n\n let result: Kind = 'None'\n\n if (t.isCallExpression(expr)) {\n if (!t.isExpression(expr.callee)) {\n return 'None'\n }\n const calleeKind = await this.resolveCalleeKind(\n expr.callee,\n fileId,\n visited,\n )\n if (calleeKind === 'Root' || calleeKind === 'Builder') {\n return 'Builder'\n }\n // Use direct Set.has() instead of iterating\n if (this.validLookupKinds.has(calleeKind as LookupKind)) {\n return calleeKind\n }\n } else if (t.isMemberExpression(expr) && t.isIdentifier(expr.property)) {\n result = await this.resolveCalleeKind(expr.object, fileId, visited)\n }\n\n if (result === 'None' && t.isIdentifier(expr)) {\n result = await this.resolveIdentifierKind(expr.name, fileId, visited)\n }\n\n return result\n }\n\n private async resolveCalleeKind(\n callee: t.Expression,\n fileId: string,\n visited = new Set<string>(),\n ): Promise<Kind> {\n if (t.isIdentifier(callee)) {\n return this.resolveIdentifierKind(callee.name, fileId, visited)\n }\n\n if (t.isMemberExpression(callee) && t.isIdentifier(callee.property)) {\n const prop = callee.property.name\n\n // Check if this property matches any method chain pattern\n const possibleKinds = IdentifierToKinds.get(prop)\n if (possibleKinds) {\n // Resolve base expression ONCE and reuse for all pattern checks\n const base = await this.resolveExprKind(callee.object, fileId, visited)\n\n // Check each possible kind that uses this identifier\n for (const kind of possibleKinds) {\n if (!this.validLookupKinds.has(kind)) continue\n\n if (kind === 'ServerFn') {\n if (base === 'Root' || base === 'Builder') {\n return 'ServerFn'\n }\n } else if (kind === 'Middleware') {\n if (\n base === 'Root' ||\n base === 'Builder' ||\n base === 'Middleware'\n ) {\n return 'Middleware'\n }\n } else if (kind === 'IsomorphicFn') {\n if (\n base === 'Root' ||\n base === 'Builder' ||\n base === 'IsomorphicFn'\n ) {\n return 'IsomorphicFn'\n }\n }\n }\n }\n\n // Check if the object is a namespace import\n if (t.isIdentifier(callee.object)) {\n const info = await this.getModuleInfo(fileId)\n const binding = info.bindings.get(callee.object.name)\n if (\n binding &&\n binding.type === 'import' &&\n binding.importedName === '*'\n ) {\n // resolve the property from the target module\n const targetModuleId = await this.resolveIdCached(\n binding.source,\n fileId,\n )\n if (targetModuleId) {\n const targetModule = await this.getModuleInfo(targetModuleId)\n const exportEntry = targetModule.exports.get(callee.property.name)\n if (exportEntry) {\n const exportedBinding = targetModule.bindings.get(\n exportEntry.name,\n )\n if (exportedBinding) {\n return await this.resolveBindingKind(\n exportedBinding,\n targetModule.id,\n visited,\n )\n }\n }\n } else {\n return 'None'\n }\n }\n }\n return this.resolveExprKind(callee.object, fileId, visited)\n }\n\n // handle nested expressions\n return this.resolveExprKind(callee, fileId, visited)\n }\n\n private async getModuleInfo(id: string) {\n let cached = this.moduleCache.get(id)\n if (cached) {\n return cached\n }\n\n await this.options.loadModule(id)\n\n cached = this.moduleCache.get(id)\n if (!cached) {\n throw new Error(`could not load module info for ${id}`)\n }\n return cached\n }\n}\n\n/**\n * Checks if a CallExpression has a method chain pattern that matches any of the lookup kinds.\n * E.g., `.handler()`, `.server()`, `.client()`, `.createMiddlewares()`\n */\nfunction isMethodChainCandidate(\n node: t.CallExpression,\n lookupKinds: Set<LookupKind>,\n): boolean {\n const callee = node.callee\n if (!t.isMemberExpression(callee) || !t.isIdentifier(callee.property)) {\n return false\n }\n\n // Use pre-computed map for O(1) lookup\n // IdentifierToKinds maps identifier -> Set<LookupKind> to handle shared identifiers\n const possibleKinds = IdentifierToKinds.get(callee.property.name)\n if (possibleKinds) {\n // Check if any of the possible kinds are in the valid lookup kinds\n for (const kind of possibleKinds) {\n if (lookupKinds.has(kind)) {\n return true\n }\n }\n }\n\n return false\n}\n"],"names":["resolvedKind"],"mappings":"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"}
1
+ {"version":3,"file":"compiler.js","sources":["../../../src/start-compiler-plugin/compiler.ts"],"sourcesContent":["/* eslint-disable import/no-commonjs */\nimport crypto from 'node:crypto'\nimport * as t from '@babel/types'\nimport { generateFromAst, parseAst } from '@tanstack/router-utils'\nimport babel from '@babel/core'\nimport {\n deadCodeElimination,\n findReferencedIdentifiers,\n} from 'babel-dead-code-elimination'\nimport { handleCreateServerFn } from './handleCreateServerFn'\nimport { handleCreateMiddleware } from './handleCreateMiddleware'\nimport { handleCreateIsomorphicFn } from './handleCreateIsomorphicFn'\nimport { handleEnvOnlyFn } from './handleEnvOnly'\nimport { handleClientOnlyJSX } from './handleClientOnlyJSX'\nimport type {\n CompilationContext,\n MethodChainPaths,\n RewriteCandidate,\n ServerFn,\n} from './types'\nimport type { CompileStartFrameworkOptions } from '../types'\n\ntype Binding =\n | {\n type: 'import'\n source: string\n importedName: string\n resolvedKind?: Kind\n }\n | {\n type: 'var'\n init: t.Expression | null\n resolvedKind?: Kind\n }\n\ntype Kind = 'None' | `Root` | `Builder` | LookupKind\n\nexport type LookupKind =\n | 'ServerFn'\n | 'Middleware'\n | 'IsomorphicFn'\n | 'ServerOnlyFn'\n | 'ClientOnlyFn'\n | 'ClientOnlyJSX'\n\n// Detection strategy for each kind\ntype MethodChainSetup = {\n type: 'methodChain'\n candidateCallIdentifier: Set<string>\n}\ntype DirectCallSetup = {\n type: 'directCall'\n // The factory function name used to create this kind (e.g., 'createServerOnlyFn')\n factoryName: string\n}\ntype JSXSetup = { type: 'jsx'; componentName: string }\n\nconst LookupSetup: Record<\n LookupKind,\n MethodChainSetup | DirectCallSetup | JSXSetup\n> = {\n ServerFn: {\n type: 'methodChain',\n candidateCallIdentifier: new Set(['handler']),\n },\n Middleware: {\n type: 'methodChain',\n candidateCallIdentifier: new Set(['server', 'client', 'createMiddlewares']),\n },\n IsomorphicFn: {\n type: 'methodChain',\n candidateCallIdentifier: new Set(['server', 'client']),\n },\n ServerOnlyFn: { type: 'directCall', factoryName: 'createServerOnlyFn' },\n ClientOnlyFn: { type: 'directCall', factoryName: 'createClientOnlyFn' },\n ClientOnlyJSX: { type: 'jsx', componentName: 'ClientOnly' },\n}\n\n// Single source of truth for detecting which kinds are present in code\n// These patterns are used for:\n// 1. Pre-scanning code to determine which kinds to look for (before AST parsing)\n// 2. Deriving the plugin's transform code filter\nexport const KindDetectionPatterns: Record<LookupKind, RegExp> = {\n ServerFn: /\\bcreateServerFn\\b|\\.\\s*handler\\s*\\(/,\n Middleware: /createMiddleware/,\n IsomorphicFn: /createIsomorphicFn/,\n ServerOnlyFn: /createServerOnlyFn/,\n ClientOnlyFn: /createClientOnlyFn/,\n ClientOnlyJSX: /<ClientOnly|import\\s*\\{[^}]*\\bClientOnly\\b/,\n}\n\n// Which kinds are valid for each environment\nexport const LookupKindsPerEnv: Record<'client' | 'server', Set<LookupKind>> = {\n client: new Set([\n 'Middleware',\n 'ServerFn',\n 'IsomorphicFn',\n 'ServerOnlyFn',\n 'ClientOnlyFn',\n ] as const),\n server: new Set([\n 'ServerFn',\n 'IsomorphicFn',\n 'ServerOnlyFn',\n 'ClientOnlyFn',\n 'ClientOnlyJSX', // Only transform on server to remove children\n ] as const),\n}\n\n/**\n * Handler type for processing candidates of a specific kind.\n * The kind is passed as the third argument to allow shared handlers (like handleEnvOnlyFn).\n */\ntype KindHandler = (\n candidates: Array<RewriteCandidate>,\n context: CompilationContext,\n kind: LookupKind,\n) => void\n\n/**\n * Registry mapping each LookupKind to its handler function.\n * When adding a new kind, add its handler here.\n */\nconst KindHandlers: Record<\n Exclude<LookupKind, 'ClientOnlyJSX'>,\n KindHandler\n> = {\n ServerFn: handleCreateServerFn,\n Middleware: handleCreateMiddleware,\n IsomorphicFn: handleCreateIsomorphicFn,\n ServerOnlyFn: handleEnvOnlyFn,\n ClientOnlyFn: handleEnvOnlyFn,\n // ClientOnlyJSX is handled separately via JSX traversal, not here\n}\n\n// All lookup kinds as an array for iteration with proper typing\nconst AllLookupKinds = Object.keys(LookupSetup) as Array<LookupKind>\n\n/**\n * Detects which LookupKinds are present in the code using string matching.\n * This is a fast pre-scan before AST parsing to limit the work done during compilation.\n */\nexport function detectKindsInCode(\n code: string,\n env: 'client' | 'server',\n): Set<LookupKind> {\n const detected = new Set<LookupKind>()\n const validForEnv = LookupKindsPerEnv[env]\n\n for (const kind of AllLookupKinds) {\n if (validForEnv.has(kind) && KindDetectionPatterns[kind].test(code)) {\n detected.add(kind)\n }\n }\n\n return detected\n}\n\n// Pre-computed map: identifier name -> Set<LookupKind> for fast candidate detection (method chain only)\n// Multiple kinds can share the same identifier (e.g., 'server' and 'client' are used by both Middleware and IsomorphicFn)\nconst IdentifierToKinds = new Map<string, Set<LookupKind>>()\nfor (const kind of AllLookupKinds) {\n const setup = LookupSetup[kind]\n if (setup.type === 'methodChain') {\n for (const id of setup.candidateCallIdentifier) {\n let kinds = IdentifierToKinds.get(id)\n if (!kinds) {\n kinds = new Set()\n IdentifierToKinds.set(id, kinds)\n }\n kinds.add(kind)\n }\n }\n}\n\n// Factory function names for direct call patterns.\n// Used to filter nested candidates - we only want to include actual factory calls,\n// not invocations of already-created functions (e.g., `myServerFn()` should NOT be a candidate)\nconst DirectCallFactoryNames = new Set<string>()\nfor (const kind of AllLookupKinds) {\n const setup = LookupSetup[kind]\n if (setup.type === 'directCall') {\n DirectCallFactoryNames.add(setup.factoryName)\n }\n}\n\nexport type LookupConfig = {\n libName: string\n rootExport: string\n kind: LookupKind | 'Root' // 'Root' for builder pattern, LookupKind for direct call\n}\n\ninterface ModuleInfo {\n id: string\n bindings: Map<string, Binding>\n // Maps exported name → local binding name\n exports: Map<string, string>\n // Track `export * from './module'` declarations for re-export resolution\n reExportAllSources: Array<string>\n}\n\n/**\n * Computes whether any file kinds need direct-call candidate detection.\n * This applies to directCall types (ServerOnlyFn, ClientOnlyFn).\n */\nfunction needsDirectCallDetection(kinds: Set<LookupKind>): boolean {\n for (const kind of kinds) {\n if (LookupSetup[kind].type === 'directCall') {\n return true\n }\n }\n return false\n}\n\n/**\n * Checks if all kinds in the set are guaranteed to be top-level only.\n * Only ServerFn is always declared at module level (must be assigned to a variable).\n * Middleware, IsomorphicFn, ServerOnlyFn, ClientOnlyFn can be nested inside functions.\n * When all kinds are top-level-only, we can use a fast scan instead of full traversal.\n */\nfunction areAllKindsTopLevelOnly(kinds: Set<LookupKind>): boolean {\n return kinds.size === 1 && kinds.has('ServerFn')\n}\n\n/**\n * Checks if we need to detect JSX elements (e.g., <ClientOnly>).\n */\nfunction needsJSXDetection(kinds: Set<LookupKind>): boolean {\n for (const kind of kinds) {\n if (LookupSetup[kind].type === 'jsx') {\n return true\n }\n }\n return false\n}\n\n/**\n * Checks if a CallExpression is a direct-call candidate for NESTED detection.\n * Returns true if the callee is a known factory function name.\n * This is stricter than top-level detection because we need to filter out\n * invocations of existing server functions (e.g., `myServerFn()`).\n */\nfunction isNestedDirectCallCandidate(node: t.CallExpression): boolean {\n let calleeName: string | undefined\n if (t.isIdentifier(node.callee)) {\n calleeName = node.callee.name\n } else if (\n t.isMemberExpression(node.callee) &&\n t.isIdentifier(node.callee.property)\n ) {\n calleeName = node.callee.property.name\n }\n return calleeName !== undefined && DirectCallFactoryNames.has(calleeName)\n}\n\n/**\n * Checks if a CallExpression path is a top-level direct-call candidate.\n * Top-level means the call is the init of a VariableDeclarator at program level.\n * We accept any simple identifier call or namespace call at top level\n * (e.g., `createServerOnlyFn()`, `TanStackStart.createServerOnlyFn()`) and let\n * resolution verify it. This handles renamed imports.\n */\nfunction isTopLevelDirectCallCandidate(\n path: babel.NodePath<t.CallExpression>,\n): boolean {\n const node = path.node\n\n // Must be a simple identifier call or namespace call\n const isSimpleCall =\n t.isIdentifier(node.callee) ||\n (t.isMemberExpression(node.callee) &&\n t.isIdentifier(node.callee.object) &&\n t.isIdentifier(node.callee.property))\n\n if (!isSimpleCall) {\n return false\n }\n\n // Must be top-level: VariableDeclarator -> VariableDeclaration -> Program\n const parent = path.parent\n if (!t.isVariableDeclarator(parent) || parent.init !== node) {\n return false\n }\n const grandParent = path.parentPath.parent\n if (!t.isVariableDeclaration(grandParent)) {\n return false\n }\n return t.isProgram(path.parentPath.parentPath?.parent)\n}\n\nexport class StartCompiler {\n private moduleCache = new Map<string, ModuleInfo>()\n private initialized = false\n private validLookupKinds: Set<LookupKind>\n private resolveIdCache = new Map<string, string | null>()\n private exportResolutionCache = new Map<\n string,\n Map<string, { moduleInfo: ModuleInfo; binding: Binding } | null>\n >()\n // Fast lookup for direct imports from known libraries (e.g., '@tanstack/react-start')\n // Maps: libName → (exportName → Kind)\n // This allows O(1) resolution for the common case without async resolveId calls\n private knownRootImports = new Map<string, Map<string, Kind>>()\n\n // For generating unique function IDs in production builds\n private entryIdToFunctionId = new Map<string, string>()\n private functionIds = new Set<string>()\n\n // Cached root path with trailing slash for dev mode function ID generation\n private _rootWithTrailingSlash: string | undefined\n\n constructor(\n private options: {\n env: 'client' | 'server'\n envName: string\n root: string\n lookupConfigurations: Array<LookupConfig>\n lookupKinds: Set<LookupKind>\n loadModule: (id: string) => Promise<void>\n resolveId: (id: string, importer?: string) => Promise<string | null>\n /**\n * In 'build' mode, resolution results are cached for performance.\n * In 'dev' mode (default), caching is disabled to avoid invalidation complexity with HMR.\n */\n mode?: 'dev' | 'build'\n /**\n * The framework being used (e.g., 'react', 'solid').\n */\n framework: CompileStartFrameworkOptions\n /**\n * The Vite environment name for the server function provider.\n */\n providerEnvName: string\n /**\n * Custom function ID generator (optional, defaults to hash-based).\n */\n generateFunctionId?: (opts: {\n filename: string\n functionName: string\n }) => string | undefined\n /**\n * Callback when server functions are discovered.\n * Called after each file is compiled with its new functions.\n */\n onServerFnsById?: (d: Record<string, ServerFn>) => void\n /**\n * Returns the currently known server functions from previous builds.\n * Used by server callers to look up canonical extracted filenames.\n */\n getKnownServerFns?: () => Record<string, ServerFn>\n },\n ) {\n this.validLookupKinds = options.lookupKinds\n }\n\n /**\n * Generates a unique function ID for a server function.\n * In dev mode, uses a base64-encoded JSON with file path and export name.\n * In build mode, uses SHA256 hash or custom generator.\n */\n private generateFunctionId(opts: {\n filename: string\n functionName: string\n extractedFilename: string\n }): string {\n if (this.mode === 'dev') {\n // In dev, encode the file path and export name for direct lookup\n let file = opts.extractedFilename\n if (opts.extractedFilename.startsWith(this.rootWithTrailingSlash)) {\n file = opts.extractedFilename.slice(this.rootWithTrailingSlash.length)\n }\n file = `/@id/${file}`\n\n const serverFn = {\n file,\n export: opts.functionName,\n }\n return Buffer.from(JSON.stringify(serverFn), 'utf8').toString('base64url')\n }\n\n // Production build: use custom generator or hash\n const entryId = `${opts.filename}--${opts.functionName}`\n let functionId = this.entryIdToFunctionId.get(entryId)\n if (functionId === undefined) {\n if (this.options.generateFunctionId) {\n functionId = this.options.generateFunctionId({\n filename: opts.filename,\n functionName: opts.functionName,\n })\n }\n if (!functionId) {\n functionId = crypto.createHash('sha256').update(entryId).digest('hex')\n }\n // Deduplicate in case the generated id conflicts with an existing id\n if (this.functionIds.has(functionId)) {\n let deduplicatedId\n let iteration = 0\n do {\n deduplicatedId = `${functionId}_${++iteration}`\n } while (this.functionIds.has(deduplicatedId))\n functionId = deduplicatedId\n }\n this.entryIdToFunctionId.set(entryId, functionId)\n this.functionIds.add(functionId)\n }\n return functionId\n }\n\n private get mode(): 'dev' | 'build' {\n return this.options.mode ?? 'dev'\n }\n\n private get rootWithTrailingSlash(): string {\n if (this._rootWithTrailingSlash === undefined) {\n this._rootWithTrailingSlash = this.options.root.endsWith('/')\n ? this.options.root\n : `${this.options.root}/`\n }\n return this._rootWithTrailingSlash\n }\n\n private async resolveIdCached(id: string, importer?: string) {\n if (this.mode === 'dev') {\n return this.options.resolveId(id, importer)\n }\n\n const cacheKey = importer ? `${importer}::${id}` : id\n const cached = this.resolveIdCache.get(cacheKey)\n if (cached !== undefined) {\n return cached\n }\n const resolved = await this.options.resolveId(id, importer)\n this.resolveIdCache.set(cacheKey, resolved)\n return resolved\n }\n\n private getExportResolutionCache(moduleId: string) {\n let cache = this.exportResolutionCache.get(moduleId)\n if (!cache) {\n cache = new Map()\n this.exportResolutionCache.set(moduleId, cache)\n }\n return cache\n }\n\n private async init() {\n // Register internal stub package exports for recognition.\n // These don't need module resolution - only the knownRootImports fast path.\n this.knownRootImports.set(\n '@tanstack/start-fn-stubs',\n new Map<string, Kind>([\n ['createIsomorphicFn', 'IsomorphicFn'],\n ['createServerOnlyFn', 'ServerOnlyFn'],\n ['createClientOnlyFn', 'ClientOnlyFn'],\n ]),\n )\n\n await Promise.all(\n this.options.lookupConfigurations.map(async (config) => {\n // Populate the fast lookup map for direct imports (by package name)\n // This allows O(1) recognition of imports from known packages.\n let libExports = this.knownRootImports.get(config.libName)\n if (!libExports) {\n libExports = new Map()\n this.knownRootImports.set(config.libName, libExports)\n }\n libExports.set(config.rootExport, config.kind)\n\n // For JSX lookups (e.g., ClientOnlyJSX), we only need the knownRootImports\n // fast path to verify imports. Skip module resolution which may fail if\n // the package isn't a direct dependency (e.g., @tanstack/react-router from\n // within start-plugin-core).\n if (config.kind !== 'Root') {\n const setup = LookupSetup[config.kind]\n if (setup.type === 'jsx') {\n return\n }\n }\n\n const libId = await this.resolveIdCached(config.libName)\n if (!libId) {\n throw new Error(`could not resolve \"${config.libName}\"`)\n }\n let rootModule = this.moduleCache.get(libId)\n if (!rootModule) {\n // insert root binding\n rootModule = {\n bindings: new Map(),\n exports: new Map(),\n id: libId,\n reExportAllSources: [],\n }\n this.moduleCache.set(libId, rootModule)\n }\n\n rootModule.exports.set(config.rootExport, config.rootExport)\n rootModule.exports.set('*', config.rootExport)\n rootModule.bindings.set(config.rootExport, {\n type: 'var',\n init: null, // Not needed since resolvedKind is set\n resolvedKind: config.kind satisfies Kind,\n })\n this.moduleCache.set(libId, rootModule)\n }),\n )\n\n this.initialized = true\n }\n\n /**\n * Extracts bindings and exports from an already-parsed AST.\n * This is the core logic shared by ingestModule and ingestModuleFromAst.\n */\n private extractModuleInfo(\n ast: ReturnType<typeof parseAst>,\n id: string,\n ): ModuleInfo {\n const bindings = new Map<string, Binding>()\n const exports = new Map<string, string>()\n const reExportAllSources: Array<string> = []\n\n // we are only interested in top-level bindings, hence we don't traverse the AST\n // instead we only iterate over the program body\n for (const node of ast.program.body) {\n if (t.isImportDeclaration(node)) {\n const source = node.source.value\n for (const s of node.specifiers) {\n if (t.isImportSpecifier(s)) {\n const importedName = t.isIdentifier(s.imported)\n ? s.imported.name\n : s.imported.value\n bindings.set(s.local.name, { type: 'import', source, importedName })\n } else if (t.isImportDefaultSpecifier(s)) {\n bindings.set(s.local.name, {\n type: 'import',\n source,\n importedName: 'default',\n })\n } else if (t.isImportNamespaceSpecifier(s)) {\n bindings.set(s.local.name, {\n type: 'import',\n source,\n importedName: '*',\n })\n }\n }\n } else if (t.isVariableDeclaration(node)) {\n for (const decl of node.declarations) {\n if (t.isIdentifier(decl.id)) {\n bindings.set(decl.id.name, {\n type: 'var',\n init: decl.init ?? null,\n })\n }\n }\n } else if (t.isExportNamedDeclaration(node)) {\n // export const foo = ...\n if (node.declaration) {\n if (t.isVariableDeclaration(node.declaration)) {\n for (const d of node.declaration.declarations) {\n if (t.isIdentifier(d.id)) {\n exports.set(d.id.name, d.id.name)\n bindings.set(d.id.name, { type: 'var', init: d.init ?? null })\n }\n }\n }\n }\n for (const sp of node.specifiers) {\n if (t.isExportNamespaceSpecifier(sp)) {\n exports.set(sp.exported.name, sp.exported.name)\n }\n // export { local as exported }\n else if (t.isExportSpecifier(sp)) {\n const local = sp.local.name\n const exported = t.isIdentifier(sp.exported)\n ? sp.exported.name\n : sp.exported.value\n exports.set(exported, local)\n\n // When re-exporting from another module (export { foo } from './module'),\n // create an import binding so the server function can be resolved\n if (node.source) {\n bindings.set(local, {\n type: 'import',\n source: node.source.value,\n importedName: local,\n })\n }\n }\n }\n } else if (t.isExportDefaultDeclaration(node)) {\n const d = node.declaration\n if (t.isIdentifier(d)) {\n exports.set('default', d.name)\n } else {\n const synth = '__default_export__'\n bindings.set(synth, { type: 'var', init: d as t.Expression })\n exports.set('default', synth)\n }\n } else if (t.isExportAllDeclaration(node)) {\n // Handle `export * from './module'` syntax\n // Track the source so we can look up exports from it when needed\n reExportAllSources.push(node.source.value)\n }\n }\n\n const info: ModuleInfo = {\n id,\n bindings,\n exports,\n reExportAllSources,\n }\n this.moduleCache.set(id, info)\n return info\n }\n\n public ingestModule({ code, id }: { code: string; id: string }) {\n const ast = parseAst({ code })\n const info = this.extractModuleInfo(ast, id)\n return { info, ast }\n }\n\n public invalidateModule(id: string) {\n // Note: Resolution caches (resolveIdCache, exportResolutionCache) are only\n // used in build mode where there's no HMR. In dev mode, caching is disabled,\n // so we only need to invalidate the moduleCache here.\n return this.moduleCache.delete(id)\n }\n\n public async compile({\n code,\n id,\n detectedKinds,\n }: {\n code: string\n id: string\n /** Pre-detected kinds present in this file. If not provided, all valid kinds are checked. */\n detectedKinds?: Set<LookupKind>\n }) {\n if (!this.initialized) {\n await this.init()\n }\n\n // Use detected kinds if provided, otherwise fall back to all valid kinds for this env\n const fileKinds = detectedKinds\n ? new Set([...detectedKinds].filter((k) => this.validLookupKinds.has(k)))\n : this.validLookupKinds\n\n // Early exit if no kinds to process\n if (fileKinds.size === 0) {\n return null\n }\n\n const checkDirectCalls = needsDirectCallDetection(fileKinds)\n // Optimization: ServerFn is always a top-level declaration (must be assigned to a variable).\n // If the file only has ServerFn, we can skip full AST traversal and only visit\n // the specific top-level declarations that have candidates.\n const canUseFastPath = areAllKindsTopLevelOnly(fileKinds)\n\n // Always parse and extract module info upfront.\n // This ensures the module is cached for import resolution even if no candidates are found.\n const { ast } = this.ingestModule({ code, id })\n\n // Single-pass traversal to:\n // 1. Collect candidate paths (only candidates, not all CallExpressions)\n // 2. Build a map for looking up paths of nested calls in method chains\n const candidatePaths: Array<babel.NodePath<t.CallExpression>> = []\n // Map for nested chain lookup - only populated for CallExpressions that are\n // part of a method chain (callee.object is a CallExpression)\n const chainCallPaths = new Map<\n t.CallExpression,\n babel.NodePath<t.CallExpression>\n >()\n\n // JSX candidates (e.g., <ClientOnly>)\n const jsxCandidatePaths: Array<babel.NodePath<t.JSXElement>> = []\n const checkJSX = needsJSXDetection(fileKinds)\n // Get module info that was just cached by ingestModule\n const moduleInfo = this.moduleCache.get(id)!\n\n if (canUseFastPath) {\n // Fast path: only visit top-level statements that have potential candidates\n\n // Collect indices of top-level statements that contain candidates\n const candidateIndices: Array<number> = []\n for (let i = 0; i < ast.program.body.length; i++) {\n const node = ast.program.body[i]!\n let declarations: Array<t.VariableDeclarator> | undefined\n\n if (t.isVariableDeclaration(node)) {\n declarations = node.declarations\n } else if (t.isExportNamedDeclaration(node) && node.declaration) {\n if (t.isVariableDeclaration(node.declaration)) {\n declarations = node.declaration.declarations\n }\n }\n\n if (declarations) {\n for (const decl of declarations) {\n if (decl.init && t.isCallExpression(decl.init)) {\n if (isMethodChainCandidate(decl.init, fileKinds)) {\n candidateIndices.push(i)\n break // Only need to mark this statement once\n }\n }\n }\n }\n }\n\n // Early exit: no potential candidates found at top level\n if (candidateIndices.length === 0) {\n return null\n }\n\n // Targeted traversal: only visit the specific statements that have candidates\n // This is much faster than traversing the entire AST\n babel.traverse(ast, {\n Program(programPath) {\n const bodyPaths = programPath.get('body')\n for (const idx of candidateIndices) {\n const stmtPath = bodyPaths[idx]\n if (!stmtPath) continue\n\n // Traverse only this statement's subtree\n stmtPath.traverse({\n CallExpression(path) {\n const node = path.node\n const parent = path.parent\n\n // Check if this call is part of a larger chain (inner call)\n if (\n t.isMemberExpression(parent) &&\n t.isCallExpression(path.parentPath.parent)\n ) {\n chainCallPaths.set(node, path)\n return\n }\n\n // Method chain pattern\n if (isMethodChainCandidate(node, fileKinds)) {\n candidatePaths.push(path)\n }\n },\n })\n }\n // Stop traversal after processing Program\n programPath.stop()\n },\n })\n } else {\n // Normal path: full traversal for non-fast-path kinds\n babel.traverse(ast, {\n CallExpression: (path) => {\n const node = path.node\n const parent = path.parent\n\n // Check if this call is part of a larger chain (inner call)\n // If so, store it for method chain lookup but don't treat as candidate\n if (\n t.isMemberExpression(parent) &&\n t.isCallExpression(path.parentPath.parent)\n ) {\n // This is an inner call in a chain - store for later lookup\n chainCallPaths.set(node, path)\n return\n }\n\n // Pattern 1: Method chain pattern (.handler(), .server(), .client(), etc.)\n if (isMethodChainCandidate(node, fileKinds)) {\n candidatePaths.push(path)\n return\n }\n\n // Pattern 2: Direct call pattern\n if (checkDirectCalls) {\n if (isTopLevelDirectCallCandidate(path)) {\n candidatePaths.push(path)\n } else if (isNestedDirectCallCandidate(node)) {\n candidatePaths.push(path)\n }\n }\n },\n // Pattern 3: JSX element pattern (e.g., <ClientOnly>)\n // Collect JSX elements where the component is imported from a known package\n // and resolves to a JSX kind (e.g., ClientOnly from @tanstack/react-router)\n JSXElement: (path) => {\n if (!checkJSX) return\n\n const openingElement = path.node.openingElement\n const nameNode = openingElement.name\n\n // Only handle simple identifier names (not namespaced or member expressions)\n if (!t.isJSXIdentifier(nameNode)) return\n\n const componentName = nameNode.name\n const binding = moduleInfo.bindings.get(componentName)\n\n // Must be an import binding from a known package\n if (!binding || binding.type !== 'import') return\n\n // Verify the import source is a known TanStack router package\n const knownExports = this.knownRootImports.get(binding.source)\n if (!knownExports) return\n\n // Verify the imported name resolves to a JSX kind (e.g., ClientOnlyJSX)\n const kind = knownExports.get(binding.importedName)\n if (kind !== 'ClientOnlyJSX') return\n\n jsxCandidatePaths.push(path)\n },\n })\n }\n\n if (candidatePaths.length === 0 && jsxCandidatePaths.length === 0) {\n return null\n }\n\n // Resolve all candidates in parallel to determine their kinds\n const resolvedCandidates = await Promise.all(\n candidatePaths.map(async (path) => ({\n path,\n kind: await this.resolveExprKind(path.node, id),\n })),\n )\n\n // Filter to valid candidates\n const validCandidates = resolvedCandidates.filter(({ kind }) =>\n this.validLookupKinds.has(kind as Exclude<LookupKind, 'ClientOnlyJSX'>),\n ) as Array<{\n path: babel.NodePath<t.CallExpression>\n kind: Exclude<LookupKind, 'ClientOnlyJSX'>\n }>\n\n if (validCandidates.length === 0 && jsxCandidatePaths.length === 0) {\n return null\n }\n\n // Process valid candidates to collect method chains\n const pathsToRewrite: Array<{\n path: babel.NodePath<t.CallExpression>\n kind: Exclude<LookupKind, 'ClientOnlyJSX'>\n methodChain: MethodChainPaths\n }> = []\n\n for (const { path, kind } of validCandidates) {\n const node = path.node\n\n // Collect method chain paths by walking DOWN from root through the chain\n const methodChain: MethodChainPaths = {\n middleware: null,\n inputValidator: null,\n handler: null,\n server: null,\n client: null,\n }\n\n // Walk down the call chain using nodes, look up paths from map\n let currentNode: t.CallExpression = node\n let currentPath: babel.NodePath<t.CallExpression> = path\n\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n while (true) {\n const callee = currentNode.callee\n if (!t.isMemberExpression(callee)) {\n break\n }\n\n // Record method chain path if it's a known method\n if (t.isIdentifier(callee.property)) {\n const name = callee.property.name as keyof MethodChainPaths\n if (name in methodChain) {\n // Get first argument path\n const args = currentPath.get('arguments')\n const firstArgPath =\n Array.isArray(args) && args.length > 0 ? (args[0] ?? null) : null\n methodChain[name] = {\n callPath: currentPath,\n firstArgPath,\n }\n }\n }\n\n // Move to the inner call (the object of the member expression)\n if (!t.isCallExpression(callee.object)) {\n break\n }\n currentNode = callee.object\n // Look up path from chain map, or use candidate path if not found\n const nextPath = chainCallPaths.get(currentNode)\n if (!nextPath) {\n break\n }\n currentPath = nextPath\n }\n\n pathsToRewrite.push({ path, kind, methodChain })\n }\n\n const refIdents = findReferencedIdentifiers(ast)\n\n const context: CompilationContext = {\n ast,\n id,\n code,\n env: this.options.env,\n envName: this.options.envName,\n root: this.options.root,\n framework: this.options.framework,\n providerEnvName: this.options.providerEnvName,\n\n generateFunctionId: (opts) => this.generateFunctionId(opts),\n getKnownServerFns: () => this.options.getKnownServerFns?.() ?? {},\n onServerFnsById: this.options.onServerFnsById,\n }\n\n // Group candidates by kind for batch processing\n const candidatesByKind = new Map<\n Exclude<LookupKind, 'ClientOnlyJSX'>,\n Array<RewriteCandidate>\n >()\n\n for (const { path: candidatePath, kind, methodChain } of pathsToRewrite) {\n const candidate: RewriteCandidate = { path: candidatePath, methodChain }\n const existing = candidatesByKind.get(kind)\n if (existing) {\n existing.push(candidate)\n } else {\n candidatesByKind.set(kind, [candidate])\n }\n }\n\n // Process each kind using its registered handler\n for (const [kind, candidates] of candidatesByKind) {\n const handler = KindHandlers[kind]\n handler(candidates, context, kind)\n }\n\n // Handle JSX candidates (e.g., <ClientOnly>)\n // Validation was already done during traversal - just call the handler\n for (const jsxPath of jsxCandidatePaths) {\n handleClientOnlyJSX(jsxPath, { env: 'server' })\n }\n\n deadCodeElimination(ast, refIdents)\n\n return generateFromAst(ast, {\n sourceMaps: true,\n sourceFileName: id,\n filename: id,\n })\n }\n\n private async resolveIdentifierKind(\n ident: string,\n id: string,\n visited = new Set<string>(),\n ): Promise<Kind> {\n const info = await this.getModuleInfo(id)\n\n const binding = info.bindings.get(ident)\n if (!binding) {\n return 'None'\n }\n if (binding.resolvedKind) {\n return binding.resolvedKind\n }\n\n // TODO improve cycle detection? should we throw here instead of returning 'None'?\n // prevent cycles\n const vKey = `${id}:${ident}`\n if (visited.has(vKey)) {\n return 'None'\n }\n visited.add(vKey)\n\n const resolvedKind = await this.resolveBindingKind(binding, id, visited)\n binding.resolvedKind = resolvedKind\n return resolvedKind\n }\n\n /**\n * Recursively find an export in a module, following `export * from` chains.\n * Returns the module info and binding if found, or undefined if not found.\n */\n private async findExportInModule(\n moduleInfo: ModuleInfo,\n exportName: string,\n visitedModules = new Set<string>(),\n ): Promise<{ moduleInfo: ModuleInfo; binding: Binding } | undefined> {\n const isBuildMode = this.mode === 'build'\n\n // Check cache first (only for top-level calls in build mode)\n if (isBuildMode && visitedModules.size === 0) {\n const moduleCache = this.exportResolutionCache.get(moduleInfo.id)\n if (moduleCache) {\n const cached = moduleCache.get(exportName)\n if (cached !== undefined) {\n return cached ?? undefined\n }\n }\n }\n\n // Prevent infinite loops in circular re-exports\n if (visitedModules.has(moduleInfo.id)) {\n return undefined\n }\n visitedModules.add(moduleInfo.id)\n\n // First check direct exports\n const localBindingName = moduleInfo.exports.get(exportName)\n if (localBindingName) {\n const binding = moduleInfo.bindings.get(localBindingName)\n if (binding) {\n const result = { moduleInfo, binding }\n // Cache the result (build mode only)\n if (isBuildMode) {\n this.getExportResolutionCache(moduleInfo.id).set(exportName, result)\n }\n return result\n }\n }\n\n // If not found, recursively check re-export-all sources in parallel\n // Valid code won't have duplicate exports across chains, so first match wins\n if (moduleInfo.reExportAllSources.length > 0) {\n const results = await Promise.all(\n moduleInfo.reExportAllSources.map(async (reExportSource) => {\n const reExportTarget = await this.resolveIdCached(\n reExportSource,\n moduleInfo.id,\n )\n\n if (reExportTarget) {\n const reExportModule = await this.getModuleInfo(reExportTarget)\n return this.findExportInModule(\n reExportModule,\n exportName,\n visitedModules,\n )\n }\n return undefined\n }),\n )\n // Return the first valid result\n for (const result of results) {\n if (result) {\n // Cache the result (build mode only)\n if (isBuildMode) {\n this.getExportResolutionCache(moduleInfo.id).set(exportName, result)\n }\n return result\n }\n }\n }\n\n // Cache negative result (build mode only)\n if (isBuildMode) {\n this.getExportResolutionCache(moduleInfo.id).set(exportName, null)\n }\n return undefined\n }\n\n private async resolveBindingKind(\n binding: Binding,\n fileId: string,\n visited = new Set<string>(),\n ): Promise<Kind> {\n if (binding.resolvedKind) {\n return binding.resolvedKind\n }\n if (binding.type === 'import') {\n // Fast path: check if this is a direct import from a known library\n // (e.g., import { createServerFn } from '@tanstack/react-start')\n // This avoids async resolveId calls for the common case\n const knownExports = this.knownRootImports.get(binding.source)\n if (knownExports) {\n const kind = knownExports.get(binding.importedName)\n if (kind) {\n binding.resolvedKind = kind\n return kind\n }\n }\n\n // Slow path: resolve through the module graph\n const target = await this.resolveIdCached(binding.source, fileId)\n if (!target) {\n return 'None'\n }\n\n const importedModule = await this.getModuleInfo(target)\n\n // Find the export, recursively searching through export * from chains\n const found = await this.findExportInModule(\n importedModule,\n binding.importedName,\n )\n\n if (!found) {\n return 'None'\n }\n\n const { moduleInfo: foundModule, binding: foundBinding } = found\n\n if (foundBinding.resolvedKind) {\n return foundBinding.resolvedKind\n }\n\n const resolvedKind = await this.resolveBindingKind(\n foundBinding,\n foundModule.id,\n visited,\n )\n foundBinding.resolvedKind = resolvedKind\n return resolvedKind\n }\n\n const resolvedKind = await this.resolveExprKind(\n binding.init,\n fileId,\n visited,\n )\n binding.resolvedKind = resolvedKind\n return resolvedKind\n }\n\n /**\n * Checks if an identifier is a direct import from a known factory library.\n * Returns true for imports like `import { createServerOnlyFn } from '@tanstack/react-start'`\n * or renamed imports like `import { createServerOnlyFn as myFn } from '...'`.\n * Returns false for local variables that hold the result of calling a factory.\n */\n private async isKnownFactoryImport(\n identName: string,\n fileId: string,\n ): Promise<boolean> {\n const info = await this.getModuleInfo(fileId)\n const binding = info.bindings.get(identName)\n\n if (!binding || binding.type !== 'import') {\n return false\n }\n\n // Check if it's imported from a known library\n const knownExports = this.knownRootImports.get(binding.source)\n return knownExports !== undefined && knownExports.has(binding.importedName)\n }\n\n private async resolveExprKind(\n expr: t.Expression | null,\n fileId: string,\n visited = new Set<string>(),\n ): Promise<Kind> {\n if (!expr) {\n return 'None'\n }\n\n // Unwrap common TypeScript/parenthesized wrappers first for efficiency\n while (\n t.isTSAsExpression(expr) ||\n t.isTSNonNullExpression(expr) ||\n t.isParenthesizedExpression(expr)\n ) {\n expr = expr.expression\n }\n\n let result: Kind = 'None'\n\n if (t.isCallExpression(expr)) {\n if (!t.isExpression(expr.callee)) {\n return 'None'\n }\n const calleeKind = await this.resolveCalleeKind(\n expr.callee,\n fileId,\n visited,\n )\n if (calleeKind === 'Root' || calleeKind === 'Builder') {\n return 'Builder'\n }\n // For method chain patterns (callee is MemberExpression like .server() or .client()),\n // return the resolved kind if valid\n if (t.isMemberExpression(expr.callee)) {\n if (this.validLookupKinds.has(calleeKind as LookupKind)) {\n return calleeKind\n }\n }\n // For direct calls (callee is Identifier), only return the kind if the\n // callee is a direct import from a known library (e.g., createServerOnlyFn).\n // Calling a local variable that holds an already-built function (e.g., myServerOnlyFn())\n // should NOT be treated as a transformation candidate.\n if (t.isIdentifier(expr.callee)) {\n const isFactoryImport = await this.isKnownFactoryImport(\n expr.callee.name,\n fileId,\n )\n if (\n isFactoryImport &&\n this.validLookupKinds.has(calleeKind as LookupKind)\n ) {\n return calleeKind\n }\n }\n } else if (t.isMemberExpression(expr) && t.isIdentifier(expr.property)) {\n result = await this.resolveCalleeKind(expr.object, fileId, visited)\n }\n\n if (result === 'None' && t.isIdentifier(expr)) {\n result = await this.resolveIdentifierKind(expr.name, fileId, visited)\n }\n\n return result\n }\n\n private async resolveCalleeKind(\n callee: t.Expression,\n fileId: string,\n visited = new Set<string>(),\n ): Promise<Kind> {\n if (t.isIdentifier(callee)) {\n return this.resolveIdentifierKind(callee.name, fileId, visited)\n }\n\n if (t.isMemberExpression(callee) && t.isIdentifier(callee.property)) {\n const prop = callee.property.name\n\n // Check if this property matches any method chain pattern\n const possibleKinds = IdentifierToKinds.get(prop)\n if (possibleKinds) {\n // Resolve base expression ONCE and reuse for all pattern checks\n const base = await this.resolveExprKind(callee.object, fileId, visited)\n\n // Check each possible kind that uses this identifier\n for (const kind of possibleKinds) {\n if (!this.validLookupKinds.has(kind)) continue\n\n if (kind === 'ServerFn') {\n if (base === 'Root' || base === 'Builder') {\n return 'ServerFn'\n }\n } else if (kind === 'Middleware') {\n if (\n base === 'Root' ||\n base === 'Builder' ||\n base === 'Middleware'\n ) {\n return 'Middleware'\n }\n } else if (kind === 'IsomorphicFn') {\n if (\n base === 'Root' ||\n base === 'Builder' ||\n base === 'IsomorphicFn'\n ) {\n return 'IsomorphicFn'\n }\n }\n }\n }\n\n // Check if the object is a namespace import\n if (t.isIdentifier(callee.object)) {\n const info = await this.getModuleInfo(fileId)\n const binding = info.bindings.get(callee.object.name)\n if (\n binding &&\n binding.type === 'import' &&\n binding.importedName === '*'\n ) {\n // resolve the property from the target module\n const targetModuleId = await this.resolveIdCached(\n binding.source,\n fileId,\n )\n if (targetModuleId) {\n const targetModule = await this.getModuleInfo(targetModuleId)\n const localBindingName = targetModule.exports.get(\n callee.property.name,\n )\n if (localBindingName) {\n const exportedBinding =\n targetModule.bindings.get(localBindingName)\n if (exportedBinding) {\n return await this.resolveBindingKind(\n exportedBinding,\n targetModule.id,\n visited,\n )\n }\n }\n } else {\n return 'None'\n }\n }\n }\n return this.resolveExprKind(callee.object, fileId, visited)\n }\n\n // handle nested expressions\n return this.resolveExprKind(callee, fileId, visited)\n }\n\n private async getModuleInfo(id: string) {\n let cached = this.moduleCache.get(id)\n if (cached) {\n return cached\n }\n\n await this.options.loadModule(id)\n\n cached = this.moduleCache.get(id)\n if (!cached) {\n throw new Error(`could not load module info for ${id}`)\n }\n return cached\n }\n}\n\n/**\n * Checks if a CallExpression has a method chain pattern that matches any of the lookup kinds.\n * E.g., `.handler()`, `.server()`, `.client()`, `.createMiddlewares()`\n */\nfunction isMethodChainCandidate(\n node: t.CallExpression,\n lookupKinds: Set<LookupKind>,\n): boolean {\n const callee = node.callee\n if (!t.isMemberExpression(callee) || !t.isIdentifier(callee.property)) {\n return false\n }\n\n // Use pre-computed map for O(1) lookup\n // IdentifierToKinds maps identifier -> Set<LookupKind> to handle shared identifiers\n const possibleKinds = IdentifierToKinds.get(callee.property.name)\n if (possibleKinds) {\n // Check if any of the possible kinds are in the valid lookup kinds\n for (const kind of possibleKinds) {\n if (lookupKinds.has(kind)) {\n return true\n }\n }\n }\n\n return false\n}\n"],"names":["resolvedKind"],"mappings":"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"}
@@ -3,17 +3,6 @@ function handleCreateIsomorphicFn(candidates, context) {
3
3
  for (const candidate of candidates) {
4
4
  const { path, methodChain } = candidate;
5
5
  const envCallInfo = context.env === "client" ? methodChain.client : methodChain.server;
6
- if (!methodChain.client && !methodChain.server) {
7
- const variableId = path.parentPath.isVariableDeclarator() ? path.parentPath.node.id : null;
8
- console.warn(
9
- "createIsomorphicFn called without a client or server implementation!",
10
- "This will result in a no-op function.",
11
- "Variable name:",
12
- t.isIdentifier(variableId) ? variableId.name : "unknown"
13
- );
14
- path.replaceWith(t.arrowFunctionExpression([], t.blockStatement([])));
15
- continue;
16
- }
17
6
  if (!envCallInfo) {
18
7
  path.replaceWith(t.arrowFunctionExpression([], t.blockStatement([])));
19
8
  continue;
@@ -1 +1 @@
1
- {"version":3,"file":"handleCreateIsomorphicFn.js","sources":["../../../src/start-compiler-plugin/handleCreateIsomorphicFn.ts"],"sourcesContent":["import * as t from '@babel/types'\nimport type { CompilationContext, RewriteCandidate } from './types'\n\n/**\n * Handles createIsomorphicFn transformations for a batch of candidates.\n *\n * @param candidates - All IsomorphicFn candidates to process\n * @param context - The compilation context\n */\nexport function handleCreateIsomorphicFn(\n candidates: Array<RewriteCandidate>,\n context: CompilationContext,\n): void {\n for (const candidate of candidates) {\n const { path, methodChain } = candidate\n\n // Get the environment-specific call (.client() or .server())\n const envCallInfo =\n context.env === 'client' ? methodChain.client : methodChain.server\n\n // Check if we have any implementation at all\n if (!methodChain.client && !methodChain.server) {\n // No implementations provided - warn and replace with no-op\n const variableId = path.parentPath.isVariableDeclarator()\n ? path.parentPath.node.id\n : null\n console.warn(\n 'createIsomorphicFn called without a client or server implementation!',\n 'This will result in a no-op function.',\n 'Variable name:',\n t.isIdentifier(variableId) ? variableId.name : 'unknown',\n )\n path.replaceWith(t.arrowFunctionExpression([], t.blockStatement([])))\n continue\n }\n\n if (!envCallInfo) {\n // No implementation for this environment - replace with no-op\n path.replaceWith(t.arrowFunctionExpression([], t.blockStatement([])))\n continue\n }\n\n // Extract the function argument from the environment-specific call\n const innerFn = envCallInfo.firstArgPath?.node\n\n if (!t.isExpression(innerFn)) {\n throw new Error(\n `createIsomorphicFn().${context.env}(func) must be called with a function!`,\n )\n }\n\n path.replaceWith(innerFn)\n }\n}\n"],"names":[],"mappings":";AASO,SAAS,yBACd,YACA,SACM;AACN,aAAW,aAAa,YAAY;AAClC,UAAM,EAAE,MAAM,YAAA,IAAgB;AAG9B,UAAM,cACJ,QAAQ,QAAQ,WAAW,YAAY,SAAS,YAAY;AAG9D,QAAI,CAAC,YAAY,UAAU,CAAC,YAAY,QAAQ;AAE9C,YAAM,aAAa,KAAK,WAAW,qBAAA,IAC/B,KAAK,WAAW,KAAK,KACrB;AACJ,cAAQ;AAAA,QACN;AAAA,QACA;AAAA,QACA;AAAA,QACA,EAAE,aAAa,UAAU,IAAI,WAAW,OAAO;AAAA,MAAA;AAEjD,WAAK,YAAY,EAAE,wBAAwB,CAAA,GAAI,EAAE,eAAe,CAAA,CAAE,CAAC,CAAC;AACpE;AAAA,IACF;AAEA,QAAI,CAAC,aAAa;AAEhB,WAAK,YAAY,EAAE,wBAAwB,CAAA,GAAI,EAAE,eAAe,CAAA,CAAE,CAAC,CAAC;AACpE;AAAA,IACF;AAGA,UAAM,UAAU,YAAY,cAAc;AAE1C,QAAI,CAAC,EAAE,aAAa,OAAO,GAAG;AAC5B,YAAM,IAAI;AAAA,QACR,wBAAwB,QAAQ,GAAG;AAAA,MAAA;AAAA,IAEvC;AAEA,SAAK,YAAY,OAAO;AAAA,EAC1B;AACF;"}
1
+ {"version":3,"file":"handleCreateIsomorphicFn.js","sources":["../../../src/start-compiler-plugin/handleCreateIsomorphicFn.ts"],"sourcesContent":["import * as t from '@babel/types'\nimport type { CompilationContext, RewriteCandidate } from './types'\n\n/**\n * Handles createIsomorphicFn transformations for a batch of candidates.\n *\n * @param candidates - All IsomorphicFn candidates to process\n * @param context - The compilation context\n */\nexport function handleCreateIsomorphicFn(\n candidates: Array<RewriteCandidate>,\n context: CompilationContext,\n): void {\n for (const candidate of candidates) {\n const { path, methodChain } = candidate\n\n // Get the environment-specific call (.client() or .server())\n const envCallInfo =\n context.env === 'client' ? methodChain.client : methodChain.server\n\n if (!envCallInfo) {\n // No implementation for this environment - replace with no-op\n path.replaceWith(t.arrowFunctionExpression([], t.blockStatement([])))\n continue\n }\n\n // Extract the function argument from the environment-specific call\n const innerFn = envCallInfo.firstArgPath?.node\n\n if (!t.isExpression(innerFn)) {\n throw new Error(\n `createIsomorphicFn().${context.env}(func) must be called with a function!`,\n )\n }\n\n path.replaceWith(innerFn)\n }\n}\n"],"names":[],"mappings":";AASO,SAAS,yBACd,YACA,SACM;AACN,aAAW,aAAa,YAAY;AAClC,UAAM,EAAE,MAAM,YAAA,IAAgB;AAG9B,UAAM,cACJ,QAAQ,QAAQ,WAAW,YAAY,SAAS,YAAY;AAE9D,QAAI,CAAC,aAAa;AAEhB,WAAK,YAAY,EAAE,wBAAwB,CAAA,GAAI,EAAE,eAAe,CAAA,CAAE,CAAC,CAAC;AACpE;AAAA,IACF;AAGA,UAAM,UAAU,YAAY,cAAc;AAE1C,QAAI,CAAC,EAAE,aAAa,OAAO,GAAG;AAC5B,YAAM,IAAI;AAAA,QACR,wBAAwB,QAAQ,GAAG;AAAA,MAAA;AAAA,IAEvC;AAEA,SAAK,YAAY,OAAO;AAAA,EAC1B;AACF;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/start-plugin-core",
3
- "version": "1.143.6",
3
+ "version": "1.143.8",
4
4
  "description": "Modern and scalable routing for React applications",
5
5
  "author": "Tanner Linsley",
6
6
  "license": "MIT",
@@ -59,12 +59,12 @@
59
59
  "vitefu": "^1.1.1",
60
60
  "xmlbuilder2": "^4.0.0",
61
61
  "zod": "^3.24.2",
62
- "@tanstack/router-core": "1.143.6",
63
62
  "@tanstack/router-generator": "1.143.6",
63
+ "@tanstack/router-core": "1.143.6",
64
64
  "@tanstack/router-plugin": "1.143.6",
65
- "@tanstack/start-client-core": "1.143.6",
66
65
  "@tanstack/router-utils": "1.141.0",
67
- "@tanstack/start-server-core": "1.143.6"
66
+ "@tanstack/start-client-core": "1.143.8",
67
+ "@tanstack/start-server-core": "1.143.8"
68
68
  },
69
69
  "devDependencies": {
70
70
  "@types/babel__code-frame": "^7.0.6",
@@ -33,11 +33,6 @@ type Binding =
33
33
  resolvedKind?: Kind
34
34
  }
35
35
 
36
- type ExportEntry =
37
- | { tag: 'Normal'; name: string }
38
- | { tag: 'Default'; name: string }
39
- | { tag: 'Namespace'; name: string; targetId: string } // for `export * as ns from './x'`
40
-
41
36
  type Kind = 'None' | `Root` | `Builder` | LookupKind
42
37
 
43
38
  export type LookupKind =
@@ -52,12 +47,12 @@ export type LookupKind =
52
47
  type MethodChainSetup = {
53
48
  type: 'methodChain'
54
49
  candidateCallIdentifier: Set<string>
55
- // If true, a call to the root function (e.g., createIsomorphicFn()) is also a candidate
56
- // even without chained method calls. This is used for IsomorphicFn which can be
57
- // called without .client() or .server() (resulting in a no-op function).
58
- allowRootAsCandidate?: boolean
59
50
  }
60
- type DirectCallSetup = { type: 'directCall' }
51
+ type DirectCallSetup = {
52
+ type: 'directCall'
53
+ // The factory function name used to create this kind (e.g., 'createServerOnlyFn')
54
+ factoryName: string
55
+ }
61
56
  type JSXSetup = { type: 'jsx'; componentName: string }
62
57
 
63
58
  const LookupSetup: Record<
@@ -75,10 +70,9 @@ const LookupSetup: Record<
75
70
  IsomorphicFn: {
76
71
  type: 'methodChain',
77
72
  candidateCallIdentifier: new Set(['server', 'client']),
78
- allowRootAsCandidate: true, // createIsomorphicFn() alone is valid (returns no-op)
79
73
  },
80
- ServerOnlyFn: { type: 'directCall' },
81
- ClientOnlyFn: { type: 'directCall' },
74
+ ServerOnlyFn: { type: 'directCall', factoryName: 'createServerOnlyFn' },
75
+ ClientOnlyFn: { type: 'directCall', factoryName: 'createClientOnlyFn' },
82
76
  ClientOnlyJSX: { type: 'jsx', componentName: 'ClientOnly' },
83
77
  }
84
78
 
@@ -87,7 +81,7 @@ const LookupSetup: Record<
87
81
  // 1. Pre-scanning code to determine which kinds to look for (before AST parsing)
88
82
  // 2. Deriving the plugin's transform code filter
89
83
  export const KindDetectionPatterns: Record<LookupKind, RegExp> = {
90
- ServerFn: /\.handler\s*\(/,
84
+ ServerFn: /\bcreateServerFn\b|\.\s*handler\s*\(/,
91
85
  Middleware: /createMiddleware/,
92
86
  IsomorphicFn: /createIsomorphicFn/,
93
87
  ServerOnlyFn: /createServerOnlyFn/,
@@ -139,6 +133,9 @@ const KindHandlers: Record<
139
133
  // ClientOnlyJSX is handled separately via JSX traversal, not here
140
134
  }
141
135
 
136
+ // All lookup kinds as an array for iteration with proper typing
137
+ const AllLookupKinds = Object.keys(LookupSetup) as Array<LookupKind>
138
+
142
139
  /**
143
140
  * Detects which LookupKinds are present in the code using string matching.
144
141
  * This is a fast pre-scan before AST parsing to limit the work done during compilation.
@@ -150,10 +147,8 @@ export function detectKindsInCode(
150
147
  const detected = new Set<LookupKind>()
151
148
  const validForEnv = LookupKindsPerEnv[env]
152
149
 
153
- for (const [kind, pattern] of Object.entries(KindDetectionPatterns) as Array<
154
- [LookupKind, RegExp]
155
- >) {
156
- if (validForEnv.has(kind) && pattern.test(code)) {
150
+ for (const kind of AllLookupKinds) {
151
+ if (validForEnv.has(kind) && KindDetectionPatterns[kind].test(code)) {
157
152
  detected.add(kind)
158
153
  }
159
154
  }
@@ -164,9 +159,8 @@ export function detectKindsInCode(
164
159
  // Pre-computed map: identifier name -> Set<LookupKind> for fast candidate detection (method chain only)
165
160
  // Multiple kinds can share the same identifier (e.g., 'server' and 'client' are used by both Middleware and IsomorphicFn)
166
161
  const IdentifierToKinds = new Map<string, Set<LookupKind>>()
167
- for (const [kind, setup] of Object.entries(LookupSetup) as Array<
168
- [LookupKind, MethodChainSetup | DirectCallSetup]
169
- >) {
162
+ for (const kind of AllLookupKinds) {
163
+ const setup = LookupSetup[kind]
170
164
  if (setup.type === 'methodChain') {
171
165
  for (const id of setup.candidateCallIdentifier) {
172
166
  let kinds = IdentifierToKinds.get(id)
@@ -179,15 +173,16 @@ for (const [kind, setup] of Object.entries(LookupSetup) as Array<
179
173
  }
180
174
  }
181
175
 
182
- // Known factory function names for direct call and root-as-candidate patterns
183
- // These are the names that, when called directly, create a new function.
176
+ // Factory function names for direct call patterns.
184
177
  // Used to filter nested candidates - we only want to include actual factory calls,
185
178
  // not invocations of already-created functions (e.g., `myServerFn()` should NOT be a candidate)
186
- const DirectCallFactoryNames = new Set([
187
- 'createServerOnlyFn',
188
- 'createClientOnlyFn',
189
- 'createIsomorphicFn',
190
- ])
179
+ const DirectCallFactoryNames = new Set<string>()
180
+ for (const kind of AllLookupKinds) {
181
+ const setup = LookupSetup[kind]
182
+ if (setup.type === 'directCall') {
183
+ DirectCallFactoryNames.add(setup.factoryName)
184
+ }
185
+ }
191
186
 
192
187
  export type LookupConfig = {
193
188
  libName: string
@@ -198,23 +193,19 @@ export type LookupConfig = {
198
193
  interface ModuleInfo {
199
194
  id: string
200
195
  bindings: Map<string, Binding>
201
- exports: Map<string, ExportEntry>
196
+ // Maps exported name → local binding name
197
+ exports: Map<string, string>
202
198
  // Track `export * from './module'` declarations for re-export resolution
203
199
  reExportAllSources: Array<string>
204
200
  }
205
201
 
206
202
  /**
207
203
  * Computes whether any file kinds need direct-call candidate detection.
208
- * This includes both directCall types (ServerOnlyFn, ClientOnlyFn) and
209
- * allowRootAsCandidate types (IsomorphicFn).
204
+ * This applies to directCall types (ServerOnlyFn, ClientOnlyFn).
210
205
  */
211
206
  function needsDirectCallDetection(kinds: Set<LookupKind>): boolean {
212
207
  for (const kind of kinds) {
213
- const setup = LookupSetup[kind]
214
- if (
215
- setup.type === 'directCall' ||
216
- (setup.type === 'methodChain' && setup.allowRootAsCandidate)
217
- ) {
208
+ if (LookupSetup[kind].type === 'directCall') {
218
209
  return true
219
210
  }
220
211
  }
@@ -236,28 +227,13 @@ function areAllKindsTopLevelOnly(kinds: Set<LookupKind>): boolean {
236
227
  */
237
228
  function needsJSXDetection(kinds: Set<LookupKind>): boolean {
238
229
  for (const kind of kinds) {
239
- const setup = LookupSetup[kind]
240
- if (setup.type === 'jsx') {
230
+ if (LookupSetup[kind].type === 'jsx') {
241
231
  return true
242
232
  }
243
233
  }
244
234
  return false
245
235
  }
246
236
 
247
- /**
248
- * Gets the set of JSX component names to detect.
249
- */
250
- function getJSXComponentNames(kinds: Set<LookupKind>): Set<string> {
251
- const names = new Set<string>()
252
- for (const kind of kinds) {
253
- const setup = LookupSetup[kind]
254
- if (setup.type === 'jsx') {
255
- names.add(setup.componentName)
256
- }
257
- }
258
- return names
259
- }
260
-
261
237
  /**
262
238
  * Checks if a CallExpression is a direct-call candidate for NESTED detection.
263
239
  * Returns true if the callee is a known factory function name.
@@ -281,7 +257,7 @@ function isNestedDirectCallCandidate(node: t.CallExpression): boolean {
281
257
  * Checks if a CallExpression path is a top-level direct-call candidate.
282
258
  * Top-level means the call is the init of a VariableDeclarator at program level.
283
259
  * We accept any simple identifier call or namespace call at top level
284
- * (e.g., `isomorphicFn()`, `TanStackStart.createServerOnlyFn()`) and let
260
+ * (e.g., `createServerOnlyFn()`, `TanStackStart.createServerOnlyFn()`) and let
285
261
  * resolution verify it. This handles renamed imports.
286
262
  */
287
263
  function isTopLevelDirectCallCandidate(
@@ -330,6 +306,9 @@ export class StartCompiler {
330
306
  private entryIdToFunctionId = new Map<string, string>()
331
307
  private functionIds = new Set<string>()
332
308
 
309
+ // Cached root path with trailing slash for dev mode function ID generation
310
+ private _rootWithTrailingSlash: string | undefined
311
+
333
312
  constructor(
334
313
  private options: {
335
314
  env: 'client' | 'server'
@@ -386,12 +365,9 @@ export class StartCompiler {
386
365
  }): string {
387
366
  if (this.mode === 'dev') {
388
367
  // In dev, encode the file path and export name for direct lookup
389
- const rootWithTrailingSlash = this.options.root.endsWith('/')
390
- ? this.options.root
391
- : `${this.options.root}/`
392
368
  let file = opts.extractedFilename
393
- if (opts.extractedFilename.startsWith(rootWithTrailingSlash)) {
394
- file = opts.extractedFilename.slice(rootWithTrailingSlash.length)
369
+ if (opts.extractedFilename.startsWith(this.rootWithTrailingSlash)) {
370
+ file = opts.extractedFilename.slice(this.rootWithTrailingSlash.length)
395
371
  }
396
372
  file = `/@id/${file}`
397
373
 
@@ -434,6 +410,15 @@ export class StartCompiler {
434
410
  return this.options.mode ?? 'dev'
435
411
  }
436
412
 
413
+ private get rootWithTrailingSlash(): string {
414
+ if (this._rootWithTrailingSlash === undefined) {
415
+ this._rootWithTrailingSlash = this.options.root.endsWith('/')
416
+ ? this.options.root
417
+ : `${this.options.root}/`
418
+ }
419
+ return this._rootWithTrailingSlash
420
+ }
421
+
437
422
  private async resolveIdCached(id: string, importer?: string) {
438
423
  if (this.mode === 'dev') {
439
424
  return this.options.resolveId(id, importer)
@@ -508,15 +493,8 @@ export class StartCompiler {
508
493
  this.moduleCache.set(libId, rootModule)
509
494
  }
510
495
 
511
- rootModule.exports.set(config.rootExport, {
512
- tag: 'Normal',
513
- name: config.rootExport,
514
- })
515
- rootModule.exports.set('*', {
516
- tag: 'Namespace',
517
- name: config.rootExport,
518
- targetId: libId,
519
- })
496
+ rootModule.exports.set(config.rootExport, config.rootExport)
497
+ rootModule.exports.set('*', config.rootExport)
520
498
  rootModule.bindings.set(config.rootExport, {
521
499
  type: 'var',
522
500
  init: null, // Not needed since resolvedKind is set
@@ -538,7 +516,7 @@ export class StartCompiler {
538
516
  id: string,
539
517
  ): ModuleInfo {
540
518
  const bindings = new Map<string, Binding>()
541
- const exports = new Map<string, ExportEntry>()
519
+ const exports = new Map<string, string>()
542
520
  const reExportAllSources: Array<string> = []
543
521
 
544
522
  // we are only interested in top-level bindings, hence we don't traverse the AST
@@ -581,7 +559,7 @@ export class StartCompiler {
581
559
  if (t.isVariableDeclaration(node.declaration)) {
582
560
  for (const d of node.declaration.declarations) {
583
561
  if (t.isIdentifier(d.id)) {
584
- exports.set(d.id.name, { tag: 'Normal', name: d.id.name })
562
+ exports.set(d.id.name, d.id.name)
585
563
  bindings.set(d.id.name, { type: 'var', init: d.init ?? null })
586
564
  }
587
565
  }
@@ -589,11 +567,7 @@ export class StartCompiler {
589
567
  }
590
568
  for (const sp of node.specifiers) {
591
569
  if (t.isExportNamespaceSpecifier(sp)) {
592
- exports.set(sp.exported.name, {
593
- tag: 'Namespace',
594
- name: sp.exported.name,
595
- targetId: node.source?.value || '',
596
- })
570
+ exports.set(sp.exported.name, sp.exported.name)
597
571
  }
598
572
  // export { local as exported }
599
573
  else if (t.isExportSpecifier(sp)) {
@@ -601,7 +575,7 @@ export class StartCompiler {
601
575
  const exported = t.isIdentifier(sp.exported)
602
576
  ? sp.exported.name
603
577
  : sp.exported.value
604
- exports.set(exported, { tag: 'Normal', name: local })
578
+ exports.set(exported, local)
605
579
 
606
580
  // When re-exporting from another module (export { foo } from './module'),
607
581
  // create an import binding so the server function can be resolved
@@ -617,11 +591,11 @@ export class StartCompiler {
617
591
  } else if (t.isExportDefaultDeclaration(node)) {
618
592
  const d = node.declaration
619
593
  if (t.isIdentifier(d)) {
620
- exports.set('default', { tag: 'Default', name: d.name })
594
+ exports.set('default', d.name)
621
595
  } else {
622
596
  const synth = '__default_export__'
623
597
  bindings.set(synth, { type: 'var', init: d as t.Expression })
624
- exports.set('default', { tag: 'Default', name: synth })
598
+ exports.set('default', synth)
625
599
  }
626
600
  } else if (t.isExportAllDeclaration(node)) {
627
601
  // Handle `export * from './module'` syntax
@@ -701,10 +675,6 @@ export class StartCompiler {
701
675
  // JSX candidates (e.g., <ClientOnly>)
702
676
  const jsxCandidatePaths: Array<babel.NodePath<t.JSXElement>> = []
703
677
  const checkJSX = needsJSXDetection(fileKinds)
704
- // Get target component names from JSX setup (e.g., 'ClientOnly')
705
- const jsxTargetComponentNames = checkJSX
706
- ? getJSXComponentNames(fileKinds)
707
- : null
708
678
  // Get module info that was just cached by ingestModule
709
679
  const moduleInfo = this.moduleCache.get(id)!
710
680
 
@@ -811,10 +781,10 @@ export class StartCompiler {
811
781
  }
812
782
  },
813
783
  // Pattern 3: JSX element pattern (e.g., <ClientOnly>)
814
- // Collect JSX elements where the component name matches a known import
815
- // that resolves to a target component (e.g., ClientOnly from @tanstack/react-router)
784
+ // Collect JSX elements where the component is imported from a known package
785
+ // and resolves to a JSX kind (e.g., ClientOnly from @tanstack/react-router)
816
786
  JSXElement: (path) => {
817
- if (!checkJSX || !jsxTargetComponentNames) return
787
+ if (!checkJSX) return
818
788
 
819
789
  const openingElement = path.node.openingElement
820
790
  const nameNode = openingElement.name
@@ -825,13 +795,18 @@ export class StartCompiler {
825
795
  const componentName = nameNode.name
826
796
  const binding = moduleInfo.bindings.get(componentName)
827
797
 
828
- // Must be an import binding
798
+ // Must be an import binding from a known package
829
799
  if (!binding || binding.type !== 'import') return
830
800
 
831
- // Check if the original import name matches a target component
832
- if (jsxTargetComponentNames.has(binding.importedName)) {
833
- jsxCandidatePaths.push(path)
834
- }
801
+ // Verify the import source is a known TanStack router package
802
+ const knownExports = this.knownRootImports.get(binding.source)
803
+ if (!knownExports) return
804
+
805
+ // Verify the imported name resolves to a JSX kind (e.g., ClientOnlyJSX)
806
+ const kind = knownExports.get(binding.importedName)
807
+ if (kind !== 'ClientOnlyJSX') return
808
+
809
+ jsxCandidatePaths.push(path)
835
810
  },
836
811
  })
837
812
  }
@@ -961,25 +936,8 @@ export class StartCompiler {
961
936
  }
962
937
 
963
938
  // Handle JSX candidates (e.g., <ClientOnly>)
964
- // Note: We only reach here on the server (ClientOnlyJSX is only in LookupKindsPerEnv.server)
965
- // Verify import source using knownRootImports (same as function call resolution)
939
+ // Validation was already done during traversal - just call the handler
966
940
  for (const jsxPath of jsxCandidatePaths) {
967
- const openingElement = jsxPath.node.openingElement
968
- const nameNode = openingElement.name
969
- if (!t.isJSXIdentifier(nameNode)) continue
970
-
971
- const componentName = nameNode.name
972
- const binding = moduleInfo.bindings.get(componentName)
973
- if (!binding || binding.type !== 'import') continue
974
-
975
- // Verify the import source is a known TanStack router package
976
- const knownExports = this.knownRootImports.get(binding.source)
977
- if (!knownExports) continue
978
-
979
- // Verify the imported name resolves to ClientOnlyJSX kind
980
- const kind = knownExports.get(binding.importedName)
981
- if (kind !== 'ClientOnlyJSX') continue
982
-
983
941
  handleClientOnlyJSX(jsxPath, { env: 'server' })
984
942
  }
985
943
 
@@ -1049,9 +1007,9 @@ export class StartCompiler {
1049
1007
  visitedModules.add(moduleInfo.id)
1050
1008
 
1051
1009
  // First check direct exports
1052
- const directExport = moduleInfo.exports.get(exportName)
1053
- if (directExport) {
1054
- const binding = moduleInfo.bindings.get(directExport.name)
1010
+ const localBindingName = moduleInfo.exports.get(exportName)
1011
+ if (localBindingName) {
1012
+ const binding = moduleInfo.bindings.get(localBindingName)
1055
1013
  if (binding) {
1056
1014
  const result = { moduleInfo, binding }
1057
1015
  // Cache the result (build mode only)
@@ -1165,6 +1123,28 @@ export class StartCompiler {
1165
1123
  return resolvedKind
1166
1124
  }
1167
1125
 
1126
+ /**
1127
+ * Checks if an identifier is a direct import from a known factory library.
1128
+ * Returns true for imports like `import { createServerOnlyFn } from '@tanstack/react-start'`
1129
+ * or renamed imports like `import { createServerOnlyFn as myFn } from '...'`.
1130
+ * Returns false for local variables that hold the result of calling a factory.
1131
+ */
1132
+ private async isKnownFactoryImport(
1133
+ identName: string,
1134
+ fileId: string,
1135
+ ): Promise<boolean> {
1136
+ const info = await this.getModuleInfo(fileId)
1137
+ const binding = info.bindings.get(identName)
1138
+
1139
+ if (!binding || binding.type !== 'import') {
1140
+ return false
1141
+ }
1142
+
1143
+ // Check if it's imported from a known library
1144
+ const knownExports = this.knownRootImports.get(binding.source)
1145
+ return knownExports !== undefined && knownExports.has(binding.importedName)
1146
+ }
1147
+
1168
1148
  private async resolveExprKind(
1169
1149
  expr: t.Expression | null,
1170
1150
  fileId: string,
@@ -1197,9 +1177,28 @@ export class StartCompiler {
1197
1177
  if (calleeKind === 'Root' || calleeKind === 'Builder') {
1198
1178
  return 'Builder'
1199
1179
  }
1200
- // Use direct Set.has() instead of iterating
1201
- if (this.validLookupKinds.has(calleeKind as LookupKind)) {
1202
- return calleeKind
1180
+ // For method chain patterns (callee is MemberExpression like .server() or .client()),
1181
+ // return the resolved kind if valid
1182
+ if (t.isMemberExpression(expr.callee)) {
1183
+ if (this.validLookupKinds.has(calleeKind as LookupKind)) {
1184
+ return calleeKind
1185
+ }
1186
+ }
1187
+ // For direct calls (callee is Identifier), only return the kind if the
1188
+ // callee is a direct import from a known library (e.g., createServerOnlyFn).
1189
+ // Calling a local variable that holds an already-built function (e.g., myServerOnlyFn())
1190
+ // should NOT be treated as a transformation candidate.
1191
+ if (t.isIdentifier(expr.callee)) {
1192
+ const isFactoryImport = await this.isKnownFactoryImport(
1193
+ expr.callee.name,
1194
+ fileId,
1195
+ )
1196
+ if (
1197
+ isFactoryImport &&
1198
+ this.validLookupKinds.has(calleeKind as LookupKind)
1199
+ ) {
1200
+ return calleeKind
1201
+ }
1203
1202
  }
1204
1203
  } else if (t.isMemberExpression(expr) && t.isIdentifier(expr.property)) {
1205
1204
  result = await this.resolveCalleeKind(expr.object, fileId, visited)
@@ -1274,11 +1273,12 @@ export class StartCompiler {
1274
1273
  )
1275
1274
  if (targetModuleId) {
1276
1275
  const targetModule = await this.getModuleInfo(targetModuleId)
1277
- const exportEntry = targetModule.exports.get(callee.property.name)
1278
- if (exportEntry) {
1279
- const exportedBinding = targetModule.bindings.get(
1280
- exportEntry.name,
1281
- )
1276
+ const localBindingName = targetModule.exports.get(
1277
+ callee.property.name,
1278
+ )
1279
+ if (localBindingName) {
1280
+ const exportedBinding =
1281
+ targetModule.bindings.get(localBindingName)
1282
1282
  if (exportedBinding) {
1283
1283
  return await this.resolveBindingKind(
1284
1284
  exportedBinding,
@@ -18,22 +18,6 @@ export function handleCreateIsomorphicFn(
18
18
  const envCallInfo =
19
19
  context.env === 'client' ? methodChain.client : methodChain.server
20
20
 
21
- // Check if we have any implementation at all
22
- if (!methodChain.client && !methodChain.server) {
23
- // No implementations provided - warn and replace with no-op
24
- const variableId = path.parentPath.isVariableDeclarator()
25
- ? path.parentPath.node.id
26
- : null
27
- console.warn(
28
- 'createIsomorphicFn called without a client or server implementation!',
29
- 'This will result in a no-op function.',
30
- 'Variable name:',
31
- t.isIdentifier(variableId) ? variableId.name : 'unknown',
32
- )
33
- path.replaceWith(t.arrowFunctionExpression([], t.blockStatement([])))
34
- continue
35
- }
36
-
37
21
  if (!envCallInfo) {
38
22
  // No implementation for this environment - replace with no-op
39
23
  path.replaceWith(t.arrowFunctionExpression([], t.blockStatement([])))