@tanstack/start-plugin-core 1.142.13 → 1.143.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -21,7 +21,7 @@ type ExportEntry = {
21
21
  targetId: string;
22
22
  };
23
23
  type Kind = 'None' | `Root` | `Builder` | LookupKind;
24
- export type LookupKind = 'ServerFn' | 'Middleware' | 'IsomorphicFn' | 'ServerOnlyFn' | 'ClientOnlyFn';
24
+ export type LookupKind = 'ServerFn' | 'Middleware' | 'IsomorphicFn' | 'ServerOnlyFn' | 'ClientOnlyFn' | 'ClientOnlyJSX';
25
25
  export declare const KindDetectionPatterns: Record<LookupKind, RegExp>;
26
26
  export declare const LookupKindsPerEnv: Record<'client' | 'server', Set<LookupKind>>;
27
27
  /**
@@ -6,6 +6,7 @@ import { handleCreateServerFn } from "./handleCreateServerFn.js";
6
6
  import { handleCreateMiddleware } from "./handleCreateMiddleware.js";
7
7
  import { handleCreateIsomorphicFn } from "./handleCreateIsomorphicFn.js";
8
8
  import { handleEnvOnlyFn } from "./handleEnvOnly.js";
9
+ import { handleClientOnlyJSX } from "./handleClientOnlyJSX.js";
9
10
  const LookupSetup = {
10
11
  ServerFn: {
11
12
  type: "methodChain",
@@ -22,14 +23,16 @@ const LookupSetup = {
22
23
  // createIsomorphicFn() alone is valid (returns no-op)
23
24
  },
24
25
  ServerOnlyFn: { type: "directCall" },
25
- ClientOnlyFn: { type: "directCall" }
26
+ ClientOnlyFn: { type: "directCall" },
27
+ ClientOnlyJSX: { type: "jsx", componentName: "ClientOnly" }
26
28
  };
27
29
  const KindDetectionPatterns = {
28
30
  ServerFn: /\.handler\s*\(/,
29
31
  Middleware: /createMiddleware/,
30
32
  IsomorphicFn: /createIsomorphicFn/,
31
33
  ServerOnlyFn: /createServerOnlyFn/,
32
- ClientOnlyFn: /createClientOnlyFn/
34
+ ClientOnlyFn: /createClientOnlyFn/,
35
+ ClientOnlyJSX: /<ClientOnly|import\s*\{[^}]*\bClientOnly\b/
33
36
  };
34
37
  const LookupKindsPerEnv = {
35
38
  client: /* @__PURE__ */ new Set([
@@ -43,7 +46,9 @@ const LookupKindsPerEnv = {
43
46
  "ServerFn",
44
47
  "IsomorphicFn",
45
48
  "ServerOnlyFn",
46
- "ClientOnlyFn"
49
+ "ClientOnlyFn",
50
+ "ClientOnlyJSX"
51
+ // Only transform on server to remove children
47
52
  ])
48
53
  };
49
54
  function detectKindsInCode(code, env) {
@@ -77,7 +82,7 @@ const DirectCallFactoryNames = /* @__PURE__ */ new Set([
77
82
  function needsDirectCallDetection(kinds) {
78
83
  for (const kind of kinds) {
79
84
  const setup = LookupSetup[kind];
80
- if (setup.type === "directCall" || setup.allowRootAsCandidate) {
85
+ if (setup.type === "directCall" || setup.type === "methodChain" && setup.allowRootAsCandidate) {
81
86
  return true;
82
87
  }
83
88
  }
@@ -86,6 +91,25 @@ function needsDirectCallDetection(kinds) {
86
91
  function areAllKindsTopLevelOnly(kinds) {
87
92
  return kinds.size === 1 && kinds.has("ServerFn");
88
93
  }
94
+ function needsJSXDetection(kinds) {
95
+ for (const kind of kinds) {
96
+ const setup = LookupSetup[kind];
97
+ if (setup.type === "jsx") {
98
+ return true;
99
+ }
100
+ }
101
+ return false;
102
+ }
103
+ function getJSXComponentNames(kinds) {
104
+ const names = /* @__PURE__ */ new Set();
105
+ for (const kind of kinds) {
106
+ const setup = LookupSetup[kind];
107
+ if (setup.type === "jsx") {
108
+ names.add(setup.componentName);
109
+ }
110
+ }
111
+ return names;
112
+ }
89
113
  function isNestedDirectCallCandidate(node) {
90
114
  let calleeName;
91
115
  if (t.isIdentifier(node.callee)) {
@@ -166,6 +190,12 @@ class ServerFnCompiler {
166
190
  this.knownRootImports.set(config.libName, libExports);
167
191
  }
168
192
  libExports.set(config.rootExport, config.kind);
193
+ if (config.kind !== "Root") {
194
+ const setup = LookupSetup[config.kind];
195
+ if (setup.type === "jsx") {
196
+ return;
197
+ }
198
+ }
169
199
  const libId = await this.resolveIdCached(config.libName);
170
200
  if (!libId) {
171
201
  throw new Error(`could not resolve "${config.libName}"`);
@@ -317,6 +347,10 @@ class ServerFnCompiler {
317
347
  const { ast } = this.ingestModule({ code, id });
318
348
  const candidatePaths = [];
319
349
  const chainCallPaths = /* @__PURE__ */ new Map();
350
+ const jsxCandidatePaths = [];
351
+ const checkJSX = needsJSXDetection(fileKinds);
352
+ const jsxTargetComponentNames = checkJSX ? getJSXComponentNames(fileKinds) : null;
353
+ const moduleInfo = this.moduleCache.get(id);
320
354
  if (canUseFastPath) {
321
355
  const candidateIndices = [];
322
356
  for (let i = 0; i < ast.program.body.length; i++) {
@@ -386,10 +420,25 @@ class ServerFnCompiler {
386
420
  candidatePaths.push(path);
387
421
  }
388
422
  }
423
+ },
424
+ // Pattern 3: JSX element pattern (e.g., <ClientOnly>)
425
+ // Collect JSX elements where the component name matches a known import
426
+ // that resolves to a target component (e.g., ClientOnly from @tanstack/react-router)
427
+ JSXElement: (path) => {
428
+ if (!checkJSX || !jsxTargetComponentNames) return;
429
+ const openingElement = path.node.openingElement;
430
+ const nameNode = openingElement.name;
431
+ if (!t.isJSXIdentifier(nameNode)) return;
432
+ const componentName = nameNode.name;
433
+ const binding = moduleInfo.bindings.get(componentName);
434
+ if (!binding || binding.type !== "import") return;
435
+ if (jsxTargetComponentNames.has(binding.importedName)) {
436
+ jsxCandidatePaths.push(path);
437
+ }
389
438
  }
390
439
  });
391
440
  }
392
- if (candidatePaths.length === 0) {
441
+ if (candidatePaths.length === 0 && jsxCandidatePaths.length === 0) {
393
442
  return null;
394
443
  }
395
444
  const resolvedCandidates = await Promise.all(
@@ -401,7 +450,7 @@ class ServerFnCompiler {
401
450
  const validCandidates = resolvedCandidates.filter(
402
451
  ({ kind }) => this.validLookupKinds.has(kind)
403
452
  );
404
- if (validCandidates.length === 0) {
453
+ if (validCandidates.length === 0 && jsxCandidatePaths.length === 0) {
405
454
  return null;
406
455
  }
407
456
  const pathsToRewrite = [];
@@ -469,6 +518,19 @@ class ServerFnCompiler {
469
518
  });
470
519
  }
471
520
  }
521
+ for (const jsxPath of jsxCandidatePaths) {
522
+ const openingElement = jsxPath.node.openingElement;
523
+ const nameNode = openingElement.name;
524
+ if (!t.isJSXIdentifier(nameNode)) continue;
525
+ const componentName = nameNode.name;
526
+ const binding = moduleInfo.bindings.get(componentName);
527
+ if (!binding || binding.type !== "import") continue;
528
+ const knownExports = this.knownRootImports.get(binding.source);
529
+ if (!knownExports) continue;
530
+ const kind = knownExports.get(binding.importedName);
531
+ if (kind !== "ClientOnlyJSX") continue;
532
+ handleClientOnlyJSX(jsxPath);
533
+ }
472
534
  deadCodeElimination(ast, refIdents);
473
535
  return generateFromAst(ast, {
474
536
  sourceMaps: true,
@@ -1 +1 @@
1
- {"version":3,"file":"compiler.js","sources":["../../../src/create-server-fn-plugin/compiler.ts"],"sourcesContent":["/* eslint-disable import/no-commonjs */\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 type { MethodChainPaths, RewriteCandidate } 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\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' }\n\nconst LookupSetup: Record<LookupKind, MethodChainSetup | DirectCallSetup> = {\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}\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}\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 ] as const),\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 (setup.type === 'directCall' || setup.allowRootAsCandidate) {\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 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 ServerFnCompiler {\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 constructor(\n private options: {\n env: 'client' | 'server'\n directive: 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 ) {\n this.validLookupKinds = options.lookupKinds\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 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 isProviderFile,\n detectedKinds,\n }: {\n code: string\n id: string\n isProviderFile: boolean\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 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 })\n }\n\n if (candidatePaths.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 LookupKind),\n ) as Array<{ path: babel.NodePath<t.CallExpression>; kind: LookupKind }>\n\n if (validCandidates.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: LookupKind\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 for (const { path, kind, methodChain } of pathsToRewrite) {\n const candidate: RewriteCandidate = { path, methodChain }\n if (kind === 'ServerFn') {\n handleCreateServerFn(candidate, {\n env: this.options.env,\n code,\n directive: this.options.directive,\n isProviderFile,\n })\n } else if (kind === 'Middleware') {\n handleCreateMiddleware(candidate, {\n env: this.options.env,\n })\n } else if (kind === 'IsomorphicFn') {\n handleCreateIsomorphicFn(candidate, {\n env: this.options.env,\n })\n } else {\n // ServerOnlyFn or ClientOnlyFn\n handleEnvOnlyFn(candidate, {\n env: this.options.env,\n kind,\n })\n }\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":";;;;;;;;AAoDA,MAAM,cAAsE;AAAA,EAC1E,UAAU;AAAA,IACR,MAAM;AAAA,IACN,yBAAyB,oBAAI,IAAI,CAAC,SAAS,CAAC;AAAA,EAAA;AAAA,EAE9C,YAAY;AAAA,IACV,MAAM;AAAA,IACN,yBAAyB,oBAAI,IAAI,CAAC,UAAU,UAAU,mBAAmB,CAAC;AAAA,EAAA;AAAA,EAE5E,cAAc;AAAA,IACZ,MAAM;AAAA,IACN,yBAAyB,oBAAI,IAAI,CAAC,UAAU,QAAQ,CAAC;AAAA,IACrD,sBAAsB;AAAA;AAAA,EAAA;AAAA,EAExB,cAAc,EAAE,MAAM,aAAA;AAAA,EACtB,cAAc,EAAE,MAAM,aAAA;AACxB;AAMO,MAAM,wBAAoD;AAAA,EAC/D,UAAU;AAAA,EACV,YAAY;AAAA,EACZ,cAAc;AAAA,EACd,cAAc;AAAA,EACd,cAAc;AAChB;AAGO,MAAM,oBAAkE;AAAA,EAC7E,4BAAY,IAAI;AAAA,IACd;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EAAA,CACQ;AAAA,EACV,4BAAY,IAAI;AAAA,IACd;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EAAA,CACQ;AACZ;AAMO,SAAS,kBACd,MACA,KACiB;AACjB,QAAM,+BAAe,IAAA;AACrB,QAAM,cAAc,kBAAkB,GAAG;AAEzC,aAAW,CAAC,MAAM,OAAO,KAAK,OAAO,QAAQ,qBAAqB,GAE/D;AACD,QAAI,YAAY,IAAI,IAAI,KAAK,QAAQ,KAAK,IAAI,GAAG;AAC/C,eAAS,IAAI,IAAI;AAAA,IACnB;AAAA,EACF;AAEA,SAAO;AACT;AAIA,MAAM,wCAAwB,IAAA;AAC9B,WAAW,CAAC,MAAM,KAAK,KAAK,OAAO,QAAQ,WAAW,GAEnD;AACD,MAAI,MAAM,SAAS,eAAe;AAChC,eAAW,MAAM,MAAM,yBAAyB;AAC9C,UAAI,QAAQ,kBAAkB,IAAI,EAAE;AACpC,UAAI,CAAC,OAAO;AACV,oCAAY,IAAA;AACZ,0BAAkB,IAAI,IAAI,KAAK;AAAA,MACjC;AACA,YAAM,IAAI,IAAI;AAAA,IAChB;AAAA,EACF;AACF;AAMA,MAAM,6CAA6B,IAAI;AAAA,EACrC;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAqBD,SAAS,yBAAyB,OAAiC;AACjE,aAAW,QAAQ,OAAO;AACxB,UAAM,QAAQ,YAAY,IAAI;AAC9B,QAAI,MAAM,SAAS,gBAAgB,MAAM,sBAAsB;AAC7D,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO;AACT;AAQA,SAAS,wBAAwB,OAAiC;AAChE,SAAO,MAAM,SAAS,KAAK,MAAM,IAAI,UAAU;AACjD;AAQA,SAAS,4BAA4B,MAAiC;AACpE,MAAI;AACJ,MAAI,EAAE,aAAa,KAAK,MAAM,GAAG;AAC/B,iBAAa,KAAK,OAAO;AAAA,EAC3B,WACE,EAAE,mBAAmB,KAAK,MAAM,KAChC,EAAE,aAAa,KAAK,OAAO,QAAQ,GACnC;AACA,iBAAa,KAAK,OAAO,SAAS;AAAA,EACpC;AACA,SAAO,eAAe,UAAa,uBAAuB,IAAI,UAAU;AAC1E;AASA,SAAS,8BACP,MACS;AACT,QAAM,OAAO,KAAK;AAGlB,QAAM,eACJ,EAAE,aAAa,KAAK,MAAM,KACzB,EAAE,mBAAmB,KAAK,MAAM,KAC/B,EAAE,aAAa,KAAK,OAAO,MAAM,KACjC,EAAE,aAAa,KAAK,OAAO,QAAQ;AAEvC,MAAI,CAAC,cAAc;AACjB,WAAO;AAAA,EACT;AAGA,QAAM,SAAS,KAAK;AACpB,MAAI,CAAC,EAAE,qBAAqB,MAAM,KAAK,OAAO,SAAS,MAAM;AAC3D,WAAO;AAAA,EACT;AACA,QAAM,cAAc,KAAK,WAAW;AACpC,MAAI,CAAC,EAAE,sBAAsB,WAAW,GAAG;AACzC,WAAO;AAAA,EACT;AACA,SAAO,EAAE,UAAU,KAAK,WAAW,YAAY,MAAM;AACvD;AAEO,MAAM,iBAAiB;AAAA,EAa5B,YACU,SAaR;AAbQ,SAAA,UAAA;AAcR,SAAK,mBAAmB,QAAQ;AAAA,EAClC;AAAA,EA5BQ,kCAAkB,IAAA;AAAA,EAClB,cAAc;AAAA,EACd;AAAA,EACA,qCAAqB,IAAA;AAAA,EACrB,4CAA4B,IAAA;AAAA;AAAA;AAAA;AAAA,EAO5B,uCAAuB,IAAA;AAAA,EAmB/B,IAAY,OAAwB;AAClC,WAAO,KAAK,QAAQ,QAAQ;AAAA,EAC9B;AAAA,EAEA,MAAc,gBAAgB,IAAY,UAAmB;AAC3D,QAAI,KAAK,SAAS,OAAO;AACvB,aAAO,KAAK,QAAQ,UAAU,IAAI,QAAQ;AAAA,IAC5C;AAEA,UAAM,WAAW,WAAW,GAAG,QAAQ,KAAK,EAAE,KAAK;AACnD,UAAM,SAAS,KAAK,eAAe,IAAI,QAAQ;AAC/C,QAAI,WAAW,QAAW;AACxB,aAAO;AAAA,IACT;AACA,UAAM,WAAW,MAAM,KAAK,QAAQ,UAAU,IAAI,QAAQ;AAC1D,SAAK,eAAe,IAAI,UAAU,QAAQ;AAC1C,WAAO;AAAA,EACT;AAAA,EAEQ,yBAAyB,UAAkB;AACjD,QAAI,QAAQ,KAAK,sBAAsB,IAAI,QAAQ;AACnD,QAAI,CAAC,OAAO;AACV,kCAAY,IAAA;AACZ,WAAK,sBAAsB,IAAI,UAAU,KAAK;AAAA,IAChD;AACA,WAAO;AAAA,EACT;AAAA,EAEA,MAAc,OAAO;AAGnB,SAAK,iBAAiB;AAAA,MACpB;AAAA,0BACI,IAAkB;AAAA,QACpB,CAAC,sBAAsB,cAAc;AAAA,QACrC,CAAC,sBAAsB,cAAc;AAAA,QACrC,CAAC,sBAAsB,cAAc;AAAA,MAAA,CACtC;AAAA,IAAA;AAGH,UAAM,QAAQ;AAAA,MACZ,KAAK,QAAQ,qBAAqB,IAAI,OAAO,WAAW;AAGtD,YAAI,aAAa,KAAK,iBAAiB,IAAI,OAAO,OAAO;AACzD,YAAI,CAAC,YAAY;AACf,2CAAiB,IAAA;AACjB,eAAK,iBAAiB,IAAI,OAAO,SAAS,UAAU;AAAA,QACtD;AACA,mBAAW,IAAI,OAAO,YAAY,OAAO,IAAI;AAE7C,cAAM,QAAQ,MAAM,KAAK,gBAAgB,OAAO,OAAO;AACvD,YAAI,CAAC,OAAO;AACV,gBAAM,IAAI,MAAM,sBAAsB,OAAO,OAAO,GAAG;AAAA,QACzD;AACA,YAAI,aAAa,KAAK,YAAY,IAAI,KAAK;AAC3C,YAAI,CAAC,YAAY;AAEf,uBAAa;AAAA,YACX,8BAAc,IAAA;AAAA,YACd,6BAAa,IAAA;AAAA,YACb,IAAI;AAAA,YACJ,oBAAoB,CAAA;AAAA,UAAC;AAEvB,eAAK,YAAY,IAAI,OAAO,UAAU;AAAA,QACxC;AAEA,mBAAW,QAAQ,IAAI,OAAO,YAAY;AAAA,UACxC,KAAK;AAAA,UACL,MAAM,OAAO;AAAA,QAAA,CACd;AACD,mBAAW,QAAQ,IAAI,KAAK;AAAA,UAC1B,KAAK;AAAA,UACL,MAAM,OAAO;AAAA,UACb,UAAU;AAAA,QAAA,CACX;AACD,mBAAW,SAAS,IAAI,OAAO,YAAY;AAAA,UACzC,MAAM;AAAA,UACN,MAAM;AAAA;AAAA,UACN,cAAc,OAAO;AAAA,QAAA,CACtB;AACD,aAAK,YAAY,IAAI,OAAO,UAAU;AAAA,MACxC,CAAC;AAAA,IAAA;AAGH,SAAK,cAAc;AAAA,EACrB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,kBACN,KACA,IACY;AACZ,UAAM,+BAAe,IAAA;AACrB,UAAM,8BAAc,IAAA;AACpB,UAAM,qBAAoC,CAAA;AAI1C,eAAW,QAAQ,IAAI,QAAQ,MAAM;AACnC,UAAI,EAAE,oBAAoB,IAAI,GAAG;AAC/B,cAAM,SAAS,KAAK,OAAO;AAC3B,mBAAW,KAAK,KAAK,YAAY;AAC/B,cAAI,EAAE,kBAAkB,CAAC,GAAG;AAC1B,kBAAM,eAAe,EAAE,aAAa,EAAE,QAAQ,IAC1C,EAAE,SAAS,OACX,EAAE,SAAS;AACf,qBAAS,IAAI,EAAE,MAAM,MAAM,EAAE,MAAM,UAAU,QAAQ,cAAc;AAAA,UACrE,WAAW,EAAE,yBAAyB,CAAC,GAAG;AACxC,qBAAS,IAAI,EAAE,MAAM,MAAM;AAAA,cACzB,MAAM;AAAA,cACN;AAAA,cACA,cAAc;AAAA,YAAA,CACf;AAAA,UACH,WAAW,EAAE,2BAA2B,CAAC,GAAG;AAC1C,qBAAS,IAAI,EAAE,MAAM,MAAM;AAAA,cACzB,MAAM;AAAA,cACN;AAAA,cACA,cAAc;AAAA,YAAA,CACf;AAAA,UACH;AAAA,QACF;AAAA,MACF,WAAW,EAAE,sBAAsB,IAAI,GAAG;AACxC,mBAAW,QAAQ,KAAK,cAAc;AACpC,cAAI,EAAE,aAAa,KAAK,EAAE,GAAG;AAC3B,qBAAS,IAAI,KAAK,GAAG,MAAM;AAAA,cACzB,MAAM;AAAA,cACN,MAAM,KAAK,QAAQ;AAAA,YAAA,CACpB;AAAA,UACH;AAAA,QACF;AAAA,MACF,WAAW,EAAE,yBAAyB,IAAI,GAAG;AAE3C,YAAI,KAAK,aAAa;AACpB,cAAI,EAAE,sBAAsB,KAAK,WAAW,GAAG;AAC7C,uBAAW,KAAK,KAAK,YAAY,cAAc;AAC7C,kBAAI,EAAE,aAAa,EAAE,EAAE,GAAG;AACxB,wBAAQ,IAAI,EAAE,GAAG,MAAM,EAAE,KAAK,UAAU,MAAM,EAAE,GAAG,KAAA,CAAM;AACzD,yBAAS,IAAI,EAAE,GAAG,MAAM,EAAE,MAAM,OAAO,MAAM,EAAE,QAAQ,KAAA,CAAM;AAAA,cAC/D;AAAA,YACF;AAAA,UACF;AAAA,QACF;AACA,mBAAW,MAAM,KAAK,YAAY;AAChC,cAAI,EAAE,2BAA2B,EAAE,GAAG;AACpC,oBAAQ,IAAI,GAAG,SAAS,MAAM;AAAA,cAC5B,KAAK;AAAA,cACL,MAAM,GAAG,SAAS;AAAA,cAClB,UAAU,KAAK,QAAQ,SAAS;AAAA,YAAA,CACjC;AAAA,UACH,WAES,EAAE,kBAAkB,EAAE,GAAG;AAChC,kBAAM,QAAQ,GAAG,MAAM;AACvB,kBAAM,WAAW,EAAE,aAAa,GAAG,QAAQ,IACvC,GAAG,SAAS,OACZ,GAAG,SAAS;AAChB,oBAAQ,IAAI,UAAU,EAAE,KAAK,UAAU,MAAM,OAAO;AAIpD,gBAAI,KAAK,QAAQ;AACf,uBAAS,IAAI,OAAO;AAAA,gBAClB,MAAM;AAAA,gBACN,QAAQ,KAAK,OAAO;AAAA,gBACpB,cAAc;AAAA,cAAA,CACf;AAAA,YACH;AAAA,UACF;AAAA,QACF;AAAA,MACF,WAAW,EAAE,2BAA2B,IAAI,GAAG;AAC7C,cAAM,IAAI,KAAK;AACf,YAAI,EAAE,aAAa,CAAC,GAAG;AACrB,kBAAQ,IAAI,WAAW,EAAE,KAAK,WAAW,MAAM,EAAE,MAAM;AAAA,QACzD,OAAO;AACL,gBAAM,QAAQ;AACd,mBAAS,IAAI,OAAO,EAAE,MAAM,OAAO,MAAM,GAAmB;AAC5D,kBAAQ,IAAI,WAAW,EAAE,KAAK,WAAW,MAAM,OAAO;AAAA,QACxD;AAAA,MACF,WAAW,EAAE,uBAAuB,IAAI,GAAG;AAGzC,2BAAmB,KAAK,KAAK,OAAO,KAAK;AAAA,MAC3C;AAAA,IACF;AAEA,UAAM,OAAmB;AAAA,MACvB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IAAA;AAEF,SAAK,YAAY,IAAI,IAAI,IAAI;AAC7B,WAAO;AAAA,EACT;AAAA,EAEO,aAAa,EAAE,MAAM,MAAoC;AAC9D,UAAM,MAAM,SAAS,EAAE,MAAM;AAC7B,UAAM,OAAO,KAAK,kBAAkB,KAAK,EAAE;AAC3C,WAAO,EAAE,MAAM,IAAA;AAAA,EACjB;AAAA,EAEO,iBAAiB,IAAY;AAIlC,WAAO,KAAK,YAAY,OAAO,EAAE;AAAA,EACnC;AAAA,EAEA,MAAa,QAAQ;AAAA,IACnB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EAAA,GAOC;AACD,QAAI,CAAC,KAAK,aAAa;AACrB,YAAM,KAAK,KAAA;AAAA,IACb;AAGA,UAAM,YAAY,gBACd,IAAI,IAAI,CAAC,GAAG,aAAa,EAAE,OAAO,CAAC,MAAM,KAAK,iBAAiB,IAAI,CAAC,CAAC,CAAC,IACtE,KAAK;AAGT,QAAI,UAAU,SAAS,GAAG;AACxB,aAAO;AAAA,IACT;AAEA,UAAM,mBAAmB,yBAAyB,SAAS;AAI3D,UAAM,iBAAiB,wBAAwB,SAAS;AAIxD,UAAM,EAAE,QAAQ,KAAK,aAAa,EAAE,MAAM,IAAI;AAK9C,UAAM,iBAA0D,CAAA;AAGhE,UAAM,qCAAqB,IAAA;AAK3B,QAAI,gBAAgB;AAIlB,YAAM,mBAAkC,CAAA;AACxC,eAAS,IAAI,GAAG,IAAI,IAAI,QAAQ,KAAK,QAAQ,KAAK;AAChD,cAAM,OAAO,IAAI,QAAQ,KAAK,CAAC;AAC/B,YAAI;AAEJ,YAAI,EAAE,sBAAsB,IAAI,GAAG;AACjC,yBAAe,KAAK;AAAA,QACtB,WAAW,EAAE,yBAAyB,IAAI,KAAK,KAAK,aAAa;AAC/D,cAAI,EAAE,sBAAsB,KAAK,WAAW,GAAG;AAC7C,2BAAe,KAAK,YAAY;AAAA,UAClC;AAAA,QACF;AAEA,YAAI,cAAc;AAChB,qBAAW,QAAQ,cAAc;AAC/B,gBAAI,KAAK,QAAQ,EAAE,iBAAiB,KAAK,IAAI,GAAG;AAC9C,kBAAI,uBAAuB,KAAK,MAAM,SAAS,GAAG;AAChD,iCAAiB,KAAK,CAAC;AACvB;AAAA,cACF;AAAA,YACF;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAGA,UAAI,iBAAiB,WAAW,GAAG;AACjC,eAAO;AAAA,MACT;AAIA,YAAM,SAAS,KAAK;AAAA,QAClB,QAAQ,aAAa;AACnB,gBAAM,YAAY,YAAY,IAAI,MAAM;AACxC,qBAAW,OAAO,kBAAkB;AAClC,kBAAM,WAAW,UAAU,GAAG;AAC9B,gBAAI,CAAC,SAAU;AAGf,qBAAS,SAAS;AAAA,cAChB,eAAe,MAAM;AACnB,sBAAM,OAAO,KAAK;AAClB,sBAAM,SAAS,KAAK;AAGpB,oBACE,EAAE,mBAAmB,MAAM,KAC3B,EAAE,iBAAiB,KAAK,WAAW,MAAM,GACzC;AACA,iCAAe,IAAI,MAAM,IAAI;AAC7B;AAAA,gBACF;AAGA,oBAAI,uBAAuB,MAAM,SAAS,GAAG;AAC3C,iCAAe,KAAK,IAAI;AAAA,gBAC1B;AAAA,cACF;AAAA,YAAA,CACD;AAAA,UACH;AAEA,sBAAY,KAAA;AAAA,QACd;AAAA,MAAA,CACD;AAAA,IACH,OAAO;AAEL,YAAM,SAAS,KAAK;AAAA,QAClB,gBAAgB,CAAC,SAAS;AACxB,gBAAM,OAAO,KAAK;AAClB,gBAAM,SAAS,KAAK;AAIpB,cACE,EAAE,mBAAmB,MAAM,KAC3B,EAAE,iBAAiB,KAAK,WAAW,MAAM,GACzC;AAEA,2BAAe,IAAI,MAAM,IAAI;AAC7B;AAAA,UACF;AAGA,cAAI,uBAAuB,MAAM,SAAS,GAAG;AAC3C,2BAAe,KAAK,IAAI;AACxB;AAAA,UACF;AAGA,cAAI,kBAAkB;AACpB,gBAAI,8BAA8B,IAAI,GAAG;AACvC,6BAAe,KAAK,IAAI;AAAA,YAC1B,WAAW,4BAA4B,IAAI,GAAG;AAC5C,6BAAe,KAAK,IAAI;AAAA,YAC1B;AAAA,UACF;AAAA,QACF;AAAA,MAAA,CACD;AAAA,IACH;AAEA,QAAI,eAAe,WAAW,GAAG;AAC/B,aAAO;AAAA,IACT;AAGA,UAAM,qBAAqB,MAAM,QAAQ;AAAA,MACvC,eAAe,IAAI,OAAO,UAAU;AAAA,QAClC;AAAA,QACA,MAAM,MAAM,KAAK,gBAAgB,KAAK,MAAM,EAAE;AAAA,MAAA,EAC9C;AAAA,IAAA;AAIJ,UAAM,kBAAkB,mBAAmB;AAAA,MAAO,CAAC,EAAE,KAAA,MACnD,KAAK,iBAAiB,IAAI,IAAkB;AAAA,IAAA;AAG9C,QAAI,gBAAgB,WAAW,GAAG;AAChC,aAAO;AAAA,IACT;AAGA,UAAM,iBAID,CAAA;AAEL,eAAW,EAAE,MAAM,KAAA,KAAU,iBAAiB;AAC5C,YAAM,OAAO,KAAK;AAGlB,YAAM,cAAgC;AAAA,QACpC,YAAY;AAAA,QACZ,gBAAgB;AAAA,QAChB,SAAS;AAAA,QACT,QAAQ;AAAA,QACR,QAAQ;AAAA,MAAA;AAIV,UAAI,cAAgC;AACpC,UAAI,cAAgD;AAGpD,aAAO,MAAM;AACX,cAAM,SAAS,YAAY;AAC3B,YAAI,CAAC,EAAE,mBAAmB,MAAM,GAAG;AACjC;AAAA,QACF;AAGA,YAAI,EAAE,aAAa,OAAO,QAAQ,GAAG;AACnC,gBAAM,OAAO,OAAO,SAAS;AAC7B,cAAI,QAAQ,aAAa;AAEvB,kBAAM,OAAO,YAAY,IAAI,WAAW;AACxC,kBAAM,eACJ,MAAM,QAAQ,IAAI,KAAK,KAAK,SAAS,IAAK,KAAK,CAAC,KAAK,OAAQ;AAC/D,wBAAY,IAAI,IAAI;AAAA,cAClB,UAAU;AAAA,cACV;AAAA,YAAA;AAAA,UAEJ;AAAA,QACF;AAGA,YAAI,CAAC,EAAE,iBAAiB,OAAO,MAAM,GAAG;AACtC;AAAA,QACF;AACA,sBAAc,OAAO;AAErB,cAAM,WAAW,eAAe,IAAI,WAAW;AAC/C,YAAI,CAAC,UAAU;AACb;AAAA,QACF;AACA,sBAAc;AAAA,MAChB;AAEA,qBAAe,KAAK,EAAE,MAAM,MAAM,aAAa;AAAA,IACjD;AAEA,UAAM,YAAY,0BAA0B,GAAG;AAE/C,eAAW,EAAE,MAAM,MAAM,YAAA,KAAiB,gBAAgB;AACxD,YAAM,YAA8B,EAAE,MAAM,YAAA;AAC5C,UAAI,SAAS,YAAY;AACvB,6BAAqB,WAAW;AAAA,UAC9B,KAAK,KAAK,QAAQ;AAAA,UAClB;AAAA,UACA,WAAW,KAAK,QAAQ;AAAA,UACxB;AAAA,QAAA,CACD;AAAA,MACH,WAAW,SAAS,cAAc;AAChC,+BAAuB,WAAW;AAAA,UAChC,KAAK,KAAK,QAAQ;AAAA,QAAA,CACnB;AAAA,MACH,WAAW,SAAS,gBAAgB;AAClC,iCAAyB,WAAW;AAAA,UAClC,KAAK,KAAK,QAAQ;AAAA,QAAA,CACnB;AAAA,MACH,OAAO;AAEL,wBAAgB,WAAW;AAAA,UACzB,KAAK,KAAK,QAAQ;AAAA,UAClB;AAAA,QAAA,CACD;AAAA,MACH;AAAA,IACF;AAEA,wBAAoB,KAAK,SAAS;AAElC,WAAO,gBAAgB,KAAK;AAAA,MAC1B,YAAY;AAAA,MACZ,gBAAgB;AAAA,MAChB,UAAU;AAAA,IAAA,CACX;AAAA,EACH;AAAA,EAEA,MAAc,sBACZ,OACA,IACA,UAAU,oBAAI,OACC;AACf,UAAM,OAAO,MAAM,KAAK,cAAc,EAAE;AAExC,UAAM,UAAU,KAAK,SAAS,IAAI,KAAK;AACvC,QAAI,CAAC,SAAS;AACZ,aAAO;AAAA,IACT;AACA,QAAI,QAAQ,cAAc;AACxB,aAAO,QAAQ;AAAA,IACjB;AAIA,UAAM,OAAO,GAAG,EAAE,IAAI,KAAK;AAC3B,QAAI,QAAQ,IAAI,IAAI,GAAG;AACrB,aAAO;AAAA,IACT;AACA,YAAQ,IAAI,IAAI;AAEhB,UAAM,eAAe,MAAM,KAAK,mBAAmB,SAAS,IAAI,OAAO;AACvE,YAAQ,eAAe;AACvB,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAc,mBACZ,YACA,YACA,iBAAiB,oBAAI,OAC8C;AACnE,UAAM,cAAc,KAAK,SAAS;AAGlC,QAAI,eAAe,eAAe,SAAS,GAAG;AAC5C,YAAM,cAAc,KAAK,sBAAsB,IAAI,WAAW,EAAE;AAChE,UAAI,aAAa;AACf,cAAM,SAAS,YAAY,IAAI,UAAU;AACzC,YAAI,WAAW,QAAW;AACxB,iBAAO,UAAU;AAAA,QACnB;AAAA,MACF;AAAA,IACF;AAGA,QAAI,eAAe,IAAI,WAAW,EAAE,GAAG;AACrC,aAAO;AAAA,IACT;AACA,mBAAe,IAAI,WAAW,EAAE;AAGhC,UAAM,eAAe,WAAW,QAAQ,IAAI,UAAU;AACtD,QAAI,cAAc;AAChB,YAAM,UAAU,WAAW,SAAS,IAAI,aAAa,IAAI;AACzD,UAAI,SAAS;AACX,cAAM,SAAS,EAAE,YAAY,QAAA;AAE7B,YAAI,aAAa;AACf,eAAK,yBAAyB,WAAW,EAAE,EAAE,IAAI,YAAY,MAAM;AAAA,QACrE;AACA,eAAO;AAAA,MACT;AAAA,IACF;AAIA,QAAI,WAAW,mBAAmB,SAAS,GAAG;AAC5C,YAAM,UAAU,MAAM,QAAQ;AAAA,QAC5B,WAAW,mBAAmB,IAAI,OAAO,mBAAmB;AAC1D,gBAAM,iBAAiB,MAAM,KAAK;AAAA,YAChC;AAAA,YACA,WAAW;AAAA,UAAA;AAGb,cAAI,gBAAgB;AAClB,kBAAM,iBAAiB,MAAM,KAAK,cAAc,cAAc;AAC9D,mBAAO,KAAK;AAAA,cACV;AAAA,cACA;AAAA,cACA;AAAA,YAAA;AAAA,UAEJ;AACA,iBAAO;AAAA,QACT,CAAC;AAAA,MAAA;AAGH,iBAAW,UAAU,SAAS;AAC5B,YAAI,QAAQ;AAEV,cAAI,aAAa;AACf,iBAAK,yBAAyB,WAAW,EAAE,EAAE,IAAI,YAAY,MAAM;AAAA,UACrE;AACA,iBAAO;AAAA,QACT;AAAA,MACF;AAAA,IACF;AAGA,QAAI,aAAa;AACf,WAAK,yBAAyB,WAAW,EAAE,EAAE,IAAI,YAAY,IAAI;AAAA,IACnE;AACA,WAAO;AAAA,EACT;AAAA,EAEA,MAAc,mBACZ,SACA,QACA,UAAU,oBAAI,OACC;AACf,QAAI,QAAQ,cAAc;AACxB,aAAO,QAAQ;AAAA,IACjB;AACA,QAAI,QAAQ,SAAS,UAAU;AAI7B,YAAM,eAAe,KAAK,iBAAiB,IAAI,QAAQ,MAAM;AAC7D,UAAI,cAAc;AAChB,cAAM,OAAO,aAAa,IAAI,QAAQ,YAAY;AAClD,YAAI,MAAM;AACR,kBAAQ,eAAe;AACvB,iBAAO;AAAA,QACT;AAAA,MACF;AAGA,YAAM,SAAS,MAAM,KAAK,gBAAgB,QAAQ,QAAQ,MAAM;AAChE,UAAI,CAAC,QAAQ;AACX,eAAO;AAAA,MACT;AAEA,YAAM,iBAAiB,MAAM,KAAK,cAAc,MAAM;AAGtD,YAAM,QAAQ,MAAM,KAAK;AAAA,QACvB;AAAA,QACA,QAAQ;AAAA,MAAA;AAGV,UAAI,CAAC,OAAO;AACV,eAAO;AAAA,MACT;AAEA,YAAM,EAAE,YAAY,aAAa,SAAS,iBAAiB;AAE3D,UAAI,aAAa,cAAc;AAC7B,eAAO,aAAa;AAAA,MACtB;AAEA,YAAMA,gBAAe,MAAM,KAAK;AAAA,QAC9B;AAAA,QACA,YAAY;AAAA,QACZ;AAAA,MAAA;AAEF,mBAAa,eAAeA;AAC5B,aAAOA;AAAAA,IACT;AAEA,UAAM,eAAe,MAAM,KAAK;AAAA,MAC9B,QAAQ;AAAA,MACR;AAAA,MACA;AAAA,IAAA;AAEF,YAAQ,eAAe;AACvB,WAAO;AAAA,EACT;AAAA,EAEA,MAAc,gBACZ,MACA,QACA,UAAU,oBAAI,OACC;AACf,QAAI,CAAC,MAAM;AACT,aAAO;AAAA,IACT;AAGA,WACE,EAAE,iBAAiB,IAAI,KACvB,EAAE,sBAAsB,IAAI,KAC5B,EAAE,0BAA0B,IAAI,GAChC;AACA,aAAO,KAAK;AAAA,IACd;AAEA,QAAI,SAAe;AAEnB,QAAI,EAAE,iBAAiB,IAAI,GAAG;AAC5B,UAAI,CAAC,EAAE,aAAa,KAAK,MAAM,GAAG;AAChC,eAAO;AAAA,MACT;AACA,YAAM,aAAa,MAAM,KAAK;AAAA,QAC5B,KAAK;AAAA,QACL;AAAA,QACA;AAAA,MAAA;AAEF,UAAI,eAAe,UAAU,eAAe,WAAW;AACrD,eAAO;AAAA,MACT;AAEA,UAAI,KAAK,iBAAiB,IAAI,UAAwB,GAAG;AACvD,eAAO;AAAA,MACT;AAAA,IACF,WAAW,EAAE,mBAAmB,IAAI,KAAK,EAAE,aAAa,KAAK,QAAQ,GAAG;AACtE,eAAS,MAAM,KAAK,kBAAkB,KAAK,QAAQ,QAAQ,OAAO;AAAA,IACpE;AAEA,QAAI,WAAW,UAAU,EAAE,aAAa,IAAI,GAAG;AAC7C,eAAS,MAAM,KAAK,sBAAsB,KAAK,MAAM,QAAQ,OAAO;AAAA,IACtE;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,MAAc,kBACZ,QACA,QACA,UAAU,oBAAI,OACC;AACf,QAAI,EAAE,aAAa,MAAM,GAAG;AAC1B,aAAO,KAAK,sBAAsB,OAAO,MAAM,QAAQ,OAAO;AAAA,IAChE;AAEA,QAAI,EAAE,mBAAmB,MAAM,KAAK,EAAE,aAAa,OAAO,QAAQ,GAAG;AACnE,YAAM,OAAO,OAAO,SAAS;AAG7B,YAAM,gBAAgB,kBAAkB,IAAI,IAAI;AAChD,UAAI,eAAe;AAEjB,cAAM,OAAO,MAAM,KAAK,gBAAgB,OAAO,QAAQ,QAAQ,OAAO;AAGtE,mBAAW,QAAQ,eAAe;AAChC,cAAI,CAAC,KAAK,iBAAiB,IAAI,IAAI,EAAG;AAEtC,cAAI,SAAS,YAAY;AACvB,gBAAI,SAAS,UAAU,SAAS,WAAW;AACzC,qBAAO;AAAA,YACT;AAAA,UACF,WAAW,SAAS,cAAc;AAChC,gBACE,SAAS,UACT,SAAS,aACT,SAAS,cACT;AACA,qBAAO;AAAA,YACT;AAAA,UACF,WAAW,SAAS,gBAAgB;AAClC,gBACE,SAAS,UACT,SAAS,aACT,SAAS,gBACT;AACA,qBAAO;AAAA,YACT;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAGA,UAAI,EAAE,aAAa,OAAO,MAAM,GAAG;AACjC,cAAM,OAAO,MAAM,KAAK,cAAc,MAAM;AAC5C,cAAM,UAAU,KAAK,SAAS,IAAI,OAAO,OAAO,IAAI;AACpD,YACE,WACA,QAAQ,SAAS,YACjB,QAAQ,iBAAiB,KACzB;AAEA,gBAAM,iBAAiB,MAAM,KAAK;AAAA,YAChC,QAAQ;AAAA,YACR;AAAA,UAAA;AAEF,cAAI,gBAAgB;AAClB,kBAAM,eAAe,MAAM,KAAK,cAAc,cAAc;AAC5D,kBAAM,cAAc,aAAa,QAAQ,IAAI,OAAO,SAAS,IAAI;AACjE,gBAAI,aAAa;AACf,oBAAM,kBAAkB,aAAa,SAAS;AAAA,gBAC5C,YAAY;AAAA,cAAA;AAEd,kBAAI,iBAAiB;AACnB,uBAAO,MAAM,KAAK;AAAA,kBAChB;AAAA,kBACA,aAAa;AAAA,kBACb;AAAA,gBAAA;AAAA,cAEJ;AAAA,YACF;AAAA,UACF,OAAO;AACL,mBAAO;AAAA,UACT;AAAA,QACF;AAAA,MACF;AACA,aAAO,KAAK,gBAAgB,OAAO,QAAQ,QAAQ,OAAO;AAAA,IAC5D;AAGA,WAAO,KAAK,gBAAgB,QAAQ,QAAQ,OAAO;AAAA,EACrD;AAAA,EAEA,MAAc,cAAc,IAAY;AACtC,QAAI,SAAS,KAAK,YAAY,IAAI,EAAE;AACpC,QAAI,QAAQ;AACV,aAAO;AAAA,IACT;AAEA,UAAM,KAAK,QAAQ,WAAW,EAAE;AAEhC,aAAS,KAAK,YAAY,IAAI,EAAE;AAChC,QAAI,CAAC,QAAQ;AACX,YAAM,IAAI,MAAM,kCAAkC,EAAE,EAAE;AAAA,IACxD;AACA,WAAO;AAAA,EACT;AACF;AAMA,SAAS,uBACP,MACA,aACS;AACT,QAAM,SAAS,KAAK;AACpB,MAAI,CAAC,EAAE,mBAAmB,MAAM,KAAK,CAAC,EAAE,aAAa,OAAO,QAAQ,GAAG;AACrE,WAAO;AAAA,EACT;AAIA,QAAM,gBAAgB,kBAAkB,IAAI,OAAO,SAAS,IAAI;AAChE,MAAI,eAAe;AAEjB,eAAW,QAAQ,eAAe;AAChC,UAAI,YAAY,IAAI,IAAI,GAAG;AACzB,eAAO;AAAA,MACT;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;"}
1
+ {"version":3,"file":"compiler.js","sources":["../../../src/create-server-fn-plugin/compiler.ts"],"sourcesContent":["/* eslint-disable import/no-commonjs */\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 { MethodChainPaths, RewriteCandidate } 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 * 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 ServerFnCompiler {\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 constructor(\n private options: {\n env: 'client' | 'server'\n directive: 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 ) {\n this.validLookupKinds = options.lookupKinds\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 isProviderFile,\n detectedKinds,\n }: {\n code: string\n id: string\n isProviderFile: boolean\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 LookupKind),\n ) as Array<{ path: babel.NodePath<t.CallExpression>; kind: LookupKind }>\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: LookupKind\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 for (const { path, kind, methodChain } of pathsToRewrite) {\n const candidate: RewriteCandidate = { path, methodChain }\n if (kind === 'ServerFn') {\n handleCreateServerFn(candidate, {\n env: this.options.env,\n code,\n directive: this.options.directive,\n isProviderFile,\n })\n } else if (kind === 'Middleware') {\n handleCreateMiddleware(candidate, {\n env: this.options.env,\n })\n } else if (kind === 'IsomorphicFn') {\n handleCreateIsomorphicFn(candidate, {\n env: this.options.env,\n })\n } else {\n // ServerOnlyFn or ClientOnlyFn\n handleEnvOnlyFn(candidate, {\n env: this.options.env,\n kind,\n })\n }\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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"}
@@ -0,0 +1,21 @@
1
+ import type * as t from '@babel/types';
2
+ import type * as babel from '@babel/core';
3
+ /**
4
+ * Handles <ClientOnly> JSX elements on the server side.
5
+ *
6
+ * On the server, the children of <ClientOnly> should be removed since they
7
+ * are client-only code. Only the fallback prop (if present) will be rendered.
8
+ *
9
+ * Transform:
10
+ * <ClientOnly fallback={<Loading />}>{clientOnlyContent}</ClientOnly>
11
+ * Into:
12
+ * <ClientOnly fallback={<Loading />} />
13
+ *
14
+ * Or if no fallback:
15
+ * <ClientOnly>{clientOnlyContent}</ClientOnly>
16
+ * Into:
17
+ * <ClientOnly />
18
+ */
19
+ export declare function handleClientOnlyJSX(path: babel.NodePath<t.JSXElement>, _opts: {
20
+ env: 'server';
21
+ }): void;
@@ -0,0 +1,10 @@
1
+ function handleClientOnlyJSX(path, _opts) {
2
+ const element = path.node;
3
+ element.children = [];
4
+ element.openingElement.selfClosing = true;
5
+ element.closingElement = null;
6
+ }
7
+ export {
8
+ handleClientOnlyJSX
9
+ };
10
+ //# sourceMappingURL=handleClientOnlyJSX.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handleClientOnlyJSX.js","sources":["../../../src/create-server-fn-plugin/handleClientOnlyJSX.ts"],"sourcesContent":["import type * as t from '@babel/types'\nimport type * as babel from '@babel/core'\n\n/**\n * Handles <ClientOnly> JSX elements on the server side.\n *\n * On the server, the children of <ClientOnly> should be removed since they\n * are client-only code. Only the fallback prop (if present) will be rendered.\n *\n * Transform:\n * <ClientOnly fallback={<Loading />}>{clientOnlyContent}</ClientOnly>\n * Into:\n * <ClientOnly fallback={<Loading />} />\n *\n * Or if no fallback:\n * <ClientOnly>{clientOnlyContent}</ClientOnly>\n * Into:\n * <ClientOnly />\n */\nexport function handleClientOnlyJSX(\n path: babel.NodePath<t.JSXElement>,\n _opts: { env: 'server' },\n): void {\n const element = path.node\n\n // Remove all children - they are client-only code\n element.children = []\n\n // Make it a self-closing element since there are no children\n element.openingElement.selfClosing = true\n element.closingElement = null\n}\n"],"names":[],"mappings":"AAmBO,SAAS,oBACd,MACA,OACM;AACN,QAAM,UAAU,KAAK;AAGrB,UAAQ,WAAW,CAAA;AAGnB,UAAQ,eAAe,cAAc;AACrC,UAAQ,iBAAiB;AAC3B;"}
@@ -55,7 +55,14 @@ const getLookupConfigurationsForEnv = (env, framework) => {
55
55
  ...commonConfigs
56
56
  ];
57
57
  } else {
58
- return commonConfigs;
58
+ return [
59
+ ...commonConfigs,
60
+ {
61
+ libName: `@tanstack/${framework}-router`,
62
+ rootExport: "ClientOnly",
63
+ kind: "ClientOnlyJSX"
64
+ }
65
+ ];
59
66
  }
60
67
  };
61
68
  const SERVER_FN_LOOKUP = "server-fn-module-lookup";
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.js","sources":["../../../src/create-server-fn-plugin/plugin.ts"],"sourcesContent":["import { TRANSFORM_ID_REGEX } from '../constants'\nimport {\n KindDetectionPatterns,\n LookupKindsPerEnv,\n ServerFnCompiler,\n detectKindsInCode,\n} from './compiler'\nimport type { CompileStartFrameworkOptions } from '../types'\nimport type { LookupConfig, LookupKind } from './compiler'\nimport type { PluginOption } from 'vite'\n\nfunction cleanId(id: string): string {\n // Remove null byte prefix used by Vite/Rollup for virtual modules\n if (id.startsWith('\\0')) {\n id = id.slice(1)\n }\n const queryIndex = id.indexOf('?')\n return queryIndex === -1 ? id : id.substring(0, queryIndex)\n}\n\n// Derive transform code filter from KindDetectionPatterns (single source of truth)\nfunction getTransformCodeFilterForEnv(env: 'client' | 'server'): Array<RegExp> {\n const validKinds = LookupKindsPerEnv[env]\n const patterns: Array<RegExp> = []\n for (const [kind, pattern] of Object.entries(KindDetectionPatterns) as Array<\n [LookupKind, RegExp]\n >) {\n if (validKinds.has(kind)) {\n patterns.push(pattern)\n }\n }\n return patterns\n}\n\nconst getLookupConfigurationsForEnv = (\n env: 'client' | 'server',\n framework: CompileStartFrameworkOptions,\n): Array<LookupConfig> => {\n // Common configs for all environments\n const commonConfigs: Array<LookupConfig> = [\n {\n libName: `@tanstack/${framework}-start`,\n rootExport: 'createServerFn',\n kind: 'Root',\n },\n {\n libName: `@tanstack/${framework}-start`,\n rootExport: 'createIsomorphicFn',\n kind: 'IsomorphicFn',\n },\n {\n libName: `@tanstack/${framework}-start`,\n rootExport: 'createServerOnlyFn',\n kind: 'ServerOnlyFn',\n },\n {\n libName: `@tanstack/${framework}-start`,\n rootExport: 'createClientOnlyFn',\n kind: 'ClientOnlyFn',\n },\n ]\n\n if (env === 'client') {\n return [\n {\n libName: `@tanstack/${framework}-start`,\n rootExport: 'createMiddleware',\n kind: 'Root',\n },\n {\n libName: `@tanstack/${framework}-start`,\n rootExport: 'createStart',\n kind: 'Root',\n },\n ...commonConfigs,\n ]\n } else {\n return commonConfigs\n }\n}\nconst SERVER_FN_LOOKUP = 'server-fn-module-lookup'\n\nfunction buildDirectiveSplitParam(directive: string) {\n return `tsr-directive-${directive.replace(/[^a-zA-Z0-9]/g, '-')}`\n}\n\nexport function createServerFnPlugin(opts: {\n framework: CompileStartFrameworkOptions\n directive: string\n environments: Array<{ name: string; type: 'client' | 'server' }>\n}): PluginOption {\n const compilers: Record<string /* envName */, ServerFnCompiler> = {}\n const directiveSplitParam = buildDirectiveSplitParam(opts.directive)\n\n function perEnvServerFnPlugin(environment: {\n name: string\n type: 'client' | 'server'\n }): PluginOption {\n // Derive transform code filter from KindDetectionPatterns (single source of truth)\n const transformCodeFilter = getTransformCodeFilterForEnv(environment.type)\n\n return {\n name: `tanstack-start-core::server-fn:${environment.name}`,\n enforce: 'pre',\n applyToEnvironment(env) {\n return env.name === environment.name\n },\n transform: {\n filter: {\n id: {\n exclude: new RegExp(`${SERVER_FN_LOOKUP}$`),\n include: TRANSFORM_ID_REGEX,\n },\n code: {\n include: transformCodeFilter,\n },\n },\n async handler(code, id) {\n let compiler = compilers[this.environment.name]\n if (!compiler) {\n // Default to 'dev' mode for unknown environments (conservative: no caching)\n const mode =\n this.environment.mode === 'build' ? 'build' : ('dev' as const)\n compiler = new ServerFnCompiler({\n env: environment.type,\n directive: opts.directive,\n lookupKinds: LookupKindsPerEnv[environment.type],\n lookupConfigurations: getLookupConfigurationsForEnv(\n environment.type,\n opts.framework,\n ),\n mode,\n loadModule: async (id: string) => {\n if (this.environment.mode === 'build') {\n const loaded = await this.load({ id })\n if (!loaded.code) {\n throw new Error(`could not load module ${id}`)\n }\n compiler!.ingestModule({ code: loaded.code, id })\n } else if (this.environment.mode === 'dev') {\n /**\n * in dev, vite does not return code from `ctx.load()`\n * so instead, we need to take a different approach\n * we must force vite to load the module and run it through the vite plugin pipeline\n * we can do this by using the `fetchModule` method\n * the `captureServerFnModuleLookupPlugin` captures the module code via its transform hook and invokes analyzeModuleAST\n */\n await this.environment.fetchModule(\n id + '?' + SERVER_FN_LOOKUP,\n )\n } else {\n throw new Error(\n `could not load module ${id}: unknown environment mode ${this.environment.mode}`,\n )\n }\n },\n resolveId: async (source: string, importer?: string) => {\n const r = await this.resolve(source, importer)\n if (r) {\n if (!r.external) {\n return cleanId(r.id)\n }\n }\n return null\n },\n })\n compilers[this.environment.name] = compiler\n }\n\n const isProviderFile = id.includes(directiveSplitParam)\n\n // Detect which kinds are present in this file before parsing\n const detectedKinds = detectKindsInCode(code, environment.type)\n\n id = cleanId(id)\n const result = await compiler.compile({\n id,\n code,\n isProviderFile,\n detectedKinds,\n })\n return result\n },\n },\n\n hotUpdate(ctx) {\n const compiler = compilers[this.environment.name]\n\n ctx.modules.forEach((m) => {\n if (m.id) {\n const deleted = compiler?.invalidateModule(m.id)\n if (deleted) {\n m.importers.forEach((importer) => {\n if (importer.id) {\n compiler?.invalidateModule(importer.id)\n }\n })\n }\n }\n })\n },\n }\n }\n\n return [\n ...opts.environments.map(perEnvServerFnPlugin),\n {\n name: 'tanstack-start-core:capture-server-fn-module-lookup',\n // we only need this plugin in dev mode\n apply: 'serve',\n applyToEnvironment(env) {\n return !!opts.environments.find((e) => e.name === env.name)\n },\n transform: {\n filter: {\n id: new RegExp(`${SERVER_FN_LOOKUP}$`),\n },\n handler(code, id) {\n const compiler = compilers[this.environment.name]\n compiler?.ingestModule({ code, id: cleanId(id) })\n },\n },\n },\n ]\n}\n"],"names":["id"],"mappings":"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"}
1
+ {"version":3,"file":"plugin.js","sources":["../../../src/create-server-fn-plugin/plugin.ts"],"sourcesContent":["import { TRANSFORM_ID_REGEX } from '../constants'\nimport {\n KindDetectionPatterns,\n LookupKindsPerEnv,\n ServerFnCompiler,\n detectKindsInCode,\n} from './compiler'\nimport type { CompileStartFrameworkOptions } from '../types'\nimport type { LookupConfig, LookupKind } from './compiler'\nimport type { PluginOption } from 'vite'\n\nfunction cleanId(id: string): string {\n // Remove null byte prefix used by Vite/Rollup for virtual modules\n if (id.startsWith('\\0')) {\n id = id.slice(1)\n }\n const queryIndex = id.indexOf('?')\n return queryIndex === -1 ? id : id.substring(0, queryIndex)\n}\n\n// Derive transform code filter from KindDetectionPatterns (single source of truth)\nfunction getTransformCodeFilterForEnv(env: 'client' | 'server'): Array<RegExp> {\n const validKinds = LookupKindsPerEnv[env]\n const patterns: Array<RegExp> = []\n for (const [kind, pattern] of Object.entries(KindDetectionPatterns) as Array<\n [LookupKind, RegExp]\n >) {\n if (validKinds.has(kind)) {\n patterns.push(pattern)\n }\n }\n return patterns\n}\n\nconst getLookupConfigurationsForEnv = (\n env: 'client' | 'server',\n framework: CompileStartFrameworkOptions,\n): Array<LookupConfig> => {\n // Common configs for all environments\n const commonConfigs: Array<LookupConfig> = [\n {\n libName: `@tanstack/${framework}-start`,\n rootExport: 'createServerFn',\n kind: 'Root',\n },\n {\n libName: `@tanstack/${framework}-start`,\n rootExport: 'createIsomorphicFn',\n kind: 'IsomorphicFn',\n },\n {\n libName: `@tanstack/${framework}-start`,\n rootExport: 'createServerOnlyFn',\n kind: 'ServerOnlyFn',\n },\n {\n libName: `@tanstack/${framework}-start`,\n rootExport: 'createClientOnlyFn',\n kind: 'ClientOnlyFn',\n },\n ]\n\n if (env === 'client') {\n return [\n {\n libName: `@tanstack/${framework}-start`,\n rootExport: 'createMiddleware',\n kind: 'Root',\n },\n {\n libName: `@tanstack/${framework}-start`,\n rootExport: 'createStart',\n kind: 'Root',\n },\n ...commonConfigs,\n ]\n } else {\n // Server-only: add ClientOnly JSX component lookup\n return [\n ...commonConfigs,\n {\n libName: `@tanstack/${framework}-router`,\n rootExport: 'ClientOnly',\n kind: 'ClientOnlyJSX',\n },\n ]\n }\n}\nconst SERVER_FN_LOOKUP = 'server-fn-module-lookup'\n\nfunction buildDirectiveSplitParam(directive: string) {\n return `tsr-directive-${directive.replace(/[^a-zA-Z0-9]/g, '-')}`\n}\n\nexport function createServerFnPlugin(opts: {\n framework: CompileStartFrameworkOptions\n directive: string\n environments: Array<{ name: string; type: 'client' | 'server' }>\n}): PluginOption {\n const compilers: Record<string /* envName */, ServerFnCompiler> = {}\n const directiveSplitParam = buildDirectiveSplitParam(opts.directive)\n\n function perEnvServerFnPlugin(environment: {\n name: string\n type: 'client' | 'server'\n }): PluginOption {\n // Derive transform code filter from KindDetectionPatterns (single source of truth)\n const transformCodeFilter = getTransformCodeFilterForEnv(environment.type)\n\n return {\n name: `tanstack-start-core::server-fn:${environment.name}`,\n enforce: 'pre',\n applyToEnvironment(env) {\n return env.name === environment.name\n },\n transform: {\n filter: {\n id: {\n exclude: new RegExp(`${SERVER_FN_LOOKUP}$`),\n include: TRANSFORM_ID_REGEX,\n },\n code: {\n include: transformCodeFilter,\n },\n },\n async handler(code, id) {\n let compiler = compilers[this.environment.name]\n if (!compiler) {\n // Default to 'dev' mode for unknown environments (conservative: no caching)\n const mode =\n this.environment.mode === 'build' ? 'build' : ('dev' as const)\n compiler = new ServerFnCompiler({\n env: environment.type,\n directive: opts.directive,\n lookupKinds: LookupKindsPerEnv[environment.type],\n lookupConfigurations: getLookupConfigurationsForEnv(\n environment.type,\n opts.framework,\n ),\n mode,\n loadModule: async (id: string) => {\n if (this.environment.mode === 'build') {\n const loaded = await this.load({ id })\n if (!loaded.code) {\n throw new Error(`could not load module ${id}`)\n }\n compiler!.ingestModule({ code: loaded.code, id })\n } else if (this.environment.mode === 'dev') {\n /**\n * in dev, vite does not return code from `ctx.load()`\n * so instead, we need to take a different approach\n * we must force vite to load the module and run it through the vite plugin pipeline\n * we can do this by using the `fetchModule` method\n * the `captureServerFnModuleLookupPlugin` captures the module code via its transform hook and invokes analyzeModuleAST\n */\n await this.environment.fetchModule(\n id + '?' + SERVER_FN_LOOKUP,\n )\n } else {\n throw new Error(\n `could not load module ${id}: unknown environment mode ${this.environment.mode}`,\n )\n }\n },\n resolveId: async (source: string, importer?: string) => {\n const r = await this.resolve(source, importer)\n if (r) {\n if (!r.external) {\n return cleanId(r.id)\n }\n }\n return null\n },\n })\n compilers[this.environment.name] = compiler\n }\n\n const isProviderFile = id.includes(directiveSplitParam)\n\n // Detect which kinds are present in this file before parsing\n const detectedKinds = detectKindsInCode(code, environment.type)\n\n id = cleanId(id)\n const result = await compiler.compile({\n id,\n code,\n isProviderFile,\n detectedKinds,\n })\n return result\n },\n },\n\n hotUpdate(ctx) {\n const compiler = compilers[this.environment.name]\n\n ctx.modules.forEach((m) => {\n if (m.id) {\n const deleted = compiler?.invalidateModule(m.id)\n if (deleted) {\n m.importers.forEach((importer) => {\n if (importer.id) {\n compiler?.invalidateModule(importer.id)\n }\n })\n }\n }\n })\n },\n }\n }\n\n return [\n ...opts.environments.map(perEnvServerFnPlugin),\n {\n name: 'tanstack-start-core:capture-server-fn-module-lookup',\n // we only need this plugin in dev mode\n apply: 'serve',\n applyToEnvironment(env) {\n return !!opts.environments.find((e) => e.name === env.name)\n },\n transform: {\n filter: {\n id: new RegExp(`${SERVER_FN_LOOKUP}$`),\n },\n handler(code, id) {\n const compiler = compilers[this.environment.name]\n compiler?.ingestModule({ code, id: cleanId(id) })\n },\n },\n },\n ]\n}\n"],"names":["id"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/start-plugin-core",
3
- "version": "1.142.13",
3
+ "version": "1.143.0",
4
4
  "description": "Modern and scalable routing for React applications",
5
5
  "author": "Tanner Linsley",
6
6
  "license": "MIT",
@@ -59,11 +59,11 @@
59
59
  "vitefu": "^1.1.1",
60
60
  "xmlbuilder2": "^4.0.0",
61
61
  "zod": "^3.24.2",
62
- "@tanstack/router-core": "1.142.13",
63
62
  "@tanstack/router-generator": "1.142.13",
63
+ "@tanstack/router-utils": "1.141.0",
64
64
  "@tanstack/router-plugin": "1.142.13",
65
65
  "@tanstack/server-functions-plugin": "1.142.1",
66
- "@tanstack/router-utils": "1.141.0",
66
+ "@tanstack/router-core": "1.142.13",
67
67
  "@tanstack/start-client-core": "1.142.13",
68
68
  "@tanstack/start-server-core": "1.142.13"
69
69
  },
@@ -10,6 +10,7 @@ import { handleCreateServerFn } from './handleCreateServerFn'
10
10
  import { handleCreateMiddleware } from './handleCreateMiddleware'
11
11
  import { handleCreateIsomorphicFn } from './handleCreateIsomorphicFn'
12
12
  import { handleEnvOnlyFn } from './handleEnvOnly'
13
+ import { handleClientOnlyJSX } from './handleClientOnlyJSX'
13
14
  import type { MethodChainPaths, RewriteCandidate } from './types'
14
15
 
15
16
  type Binding =
@@ -38,6 +39,7 @@ export type LookupKind =
38
39
  | 'IsomorphicFn'
39
40
  | 'ServerOnlyFn'
40
41
  | 'ClientOnlyFn'
42
+ | 'ClientOnlyJSX'
41
43
 
42
44
  // Detection strategy for each kind
43
45
  type MethodChainSetup = {
@@ -49,8 +51,12 @@ type MethodChainSetup = {
49
51
  allowRootAsCandidate?: boolean
50
52
  }
51
53
  type DirectCallSetup = { type: 'directCall' }
54
+ type JSXSetup = { type: 'jsx'; componentName: string }
52
55
 
53
- const LookupSetup: Record<LookupKind, MethodChainSetup | DirectCallSetup> = {
56
+ const LookupSetup: Record<
57
+ LookupKind,
58
+ MethodChainSetup | DirectCallSetup | JSXSetup
59
+ > = {
54
60
  ServerFn: {
55
61
  type: 'methodChain',
56
62
  candidateCallIdentifier: new Set(['handler']),
@@ -66,6 +72,7 @@ const LookupSetup: Record<LookupKind, MethodChainSetup | DirectCallSetup> = {
66
72
  },
67
73
  ServerOnlyFn: { type: 'directCall' },
68
74
  ClientOnlyFn: { type: 'directCall' },
75
+ ClientOnlyJSX: { type: 'jsx', componentName: 'ClientOnly' },
69
76
  }
70
77
 
71
78
  // Single source of truth for detecting which kinds are present in code
@@ -78,6 +85,7 @@ export const KindDetectionPatterns: Record<LookupKind, RegExp> = {
78
85
  IsomorphicFn: /createIsomorphicFn/,
79
86
  ServerOnlyFn: /createServerOnlyFn/,
80
87
  ClientOnlyFn: /createClientOnlyFn/,
88
+ ClientOnlyJSX: /<ClientOnly|import\s*\{[^}]*\bClientOnly\b/,
81
89
  }
82
90
 
83
91
  // Which kinds are valid for each environment
@@ -94,6 +102,7 @@ export const LookupKindsPerEnv: Record<'client' | 'server', Set<LookupKind>> = {
94
102
  'IsomorphicFn',
95
103
  'ServerOnlyFn',
96
104
  'ClientOnlyFn',
105
+ 'ClientOnlyJSX', // Only transform on server to remove children
97
106
  ] as const),
98
107
  }
99
108
 
@@ -169,7 +178,10 @@ interface ModuleInfo {
169
178
  function needsDirectCallDetection(kinds: Set<LookupKind>): boolean {
170
179
  for (const kind of kinds) {
171
180
  const setup = LookupSetup[kind]
172
- if (setup.type === 'directCall' || setup.allowRootAsCandidate) {
181
+ if (
182
+ setup.type === 'directCall' ||
183
+ (setup.type === 'methodChain' && setup.allowRootAsCandidate)
184
+ ) {
173
185
  return true
174
186
  }
175
187
  }
@@ -186,6 +198,33 @@ function areAllKindsTopLevelOnly(kinds: Set<LookupKind>): boolean {
186
198
  return kinds.size === 1 && kinds.has('ServerFn')
187
199
  }
188
200
 
201
+ /**
202
+ * Checks if we need to detect JSX elements (e.g., <ClientOnly>).
203
+ */
204
+ function needsJSXDetection(kinds: Set<LookupKind>): boolean {
205
+ for (const kind of kinds) {
206
+ const setup = LookupSetup[kind]
207
+ if (setup.type === 'jsx') {
208
+ return true
209
+ }
210
+ }
211
+ return false
212
+ }
213
+
214
+ /**
215
+ * Gets the set of JSX component names to detect.
216
+ */
217
+ function getJSXComponentNames(kinds: Set<LookupKind>): Set<string> {
218
+ const names = new Set<string>()
219
+ for (const kind of kinds) {
220
+ const setup = LookupSetup[kind]
221
+ if (setup.type === 'jsx') {
222
+ names.add(setup.componentName)
223
+ }
224
+ }
225
+ return names
226
+ }
227
+
189
228
  /**
190
229
  * Checks if a CallExpression is a direct-call candidate for NESTED detection.
191
230
  * Returns true if the callee is a known factory function name.
@@ -322,6 +361,17 @@ export class ServerFnCompiler {
322
361
  }
323
362
  libExports.set(config.rootExport, config.kind)
324
363
 
364
+ // For JSX lookups (e.g., ClientOnlyJSX), we only need the knownRootImports
365
+ // fast path to verify imports. Skip module resolution which may fail if
366
+ // the package isn't a direct dependency (e.g., @tanstack/react-router from
367
+ // within start-plugin-core).
368
+ if (config.kind !== 'Root') {
369
+ const setup = LookupSetup[config.kind]
370
+ if (setup.type === 'jsx') {
371
+ return
372
+ }
373
+ }
374
+
325
375
  const libId = await this.resolveIdCached(config.libName)
326
376
  if (!libId) {
327
377
  throw new Error(`could not resolve "${config.libName}"`)
@@ -530,6 +580,16 @@ export class ServerFnCompiler {
530
580
  babel.NodePath<t.CallExpression>
531
581
  >()
532
582
 
583
+ // JSX candidates (e.g., <ClientOnly>)
584
+ const jsxCandidatePaths: Array<babel.NodePath<t.JSXElement>> = []
585
+ const checkJSX = needsJSXDetection(fileKinds)
586
+ // Get target component names from JSX setup (e.g., 'ClientOnly')
587
+ const jsxTargetComponentNames = checkJSX
588
+ ? getJSXComponentNames(fileKinds)
589
+ : null
590
+ // Get module info that was just cached by ingestModule
591
+ const moduleInfo = this.moduleCache.get(id)!
592
+
533
593
  if (canUseFastPath) {
534
594
  // Fast path: only visit top-level statements that have potential candidates
535
595
 
@@ -632,10 +692,33 @@ export class ServerFnCompiler {
632
692
  }
633
693
  }
634
694
  },
695
+ // Pattern 3: JSX element pattern (e.g., <ClientOnly>)
696
+ // Collect JSX elements where the component name matches a known import
697
+ // that resolves to a target component (e.g., ClientOnly from @tanstack/react-router)
698
+ JSXElement: (path) => {
699
+ if (!checkJSX || !jsxTargetComponentNames) return
700
+
701
+ const openingElement = path.node.openingElement
702
+ const nameNode = openingElement.name
703
+
704
+ // Only handle simple identifier names (not namespaced or member expressions)
705
+ if (!t.isJSXIdentifier(nameNode)) return
706
+
707
+ const componentName = nameNode.name
708
+ const binding = moduleInfo.bindings.get(componentName)
709
+
710
+ // Must be an import binding
711
+ if (!binding || binding.type !== 'import') return
712
+
713
+ // Check if the original import name matches a target component
714
+ if (jsxTargetComponentNames.has(binding.importedName)) {
715
+ jsxCandidatePaths.push(path)
716
+ }
717
+ },
635
718
  })
636
719
  }
637
720
 
638
- if (candidatePaths.length === 0) {
721
+ if (candidatePaths.length === 0 && jsxCandidatePaths.length === 0) {
639
722
  return null
640
723
  }
641
724
 
@@ -652,7 +735,7 @@ export class ServerFnCompiler {
652
735
  this.validLookupKinds.has(kind as LookupKind),
653
736
  ) as Array<{ path: babel.NodePath<t.CallExpression>; kind: LookupKind }>
654
737
 
655
- if (validCandidates.length === 0) {
738
+ if (validCandidates.length === 0 && jsxCandidatePaths.length === 0) {
656
739
  return null
657
740
  }
658
741
 
@@ -745,6 +828,29 @@ export class ServerFnCompiler {
745
828
  }
746
829
  }
747
830
 
831
+ // Handle JSX candidates (e.g., <ClientOnly>)
832
+ // Note: We only reach here on the server (ClientOnlyJSX is only in LookupKindsPerEnv.server)
833
+ // Verify import source using knownRootImports (same as function call resolution)
834
+ for (const jsxPath of jsxCandidatePaths) {
835
+ const openingElement = jsxPath.node.openingElement
836
+ const nameNode = openingElement.name
837
+ if (!t.isJSXIdentifier(nameNode)) continue
838
+
839
+ const componentName = nameNode.name
840
+ const binding = moduleInfo.bindings.get(componentName)
841
+ if (!binding || binding.type !== 'import') continue
842
+
843
+ // Verify the import source is a known TanStack router package
844
+ const knownExports = this.knownRootImports.get(binding.source)
845
+ if (!knownExports) continue
846
+
847
+ // Verify the imported name resolves to ClientOnlyJSX kind
848
+ const kind = knownExports.get(binding.importedName)
849
+ if (kind !== 'ClientOnlyJSX') continue
850
+
851
+ handleClientOnlyJSX(jsxPath, { env: 'server' })
852
+ }
853
+
748
854
  deadCodeElimination(ast, refIdents)
749
855
 
750
856
  return generateFromAst(ast, {
@@ -0,0 +1,32 @@
1
+ import type * as t from '@babel/types'
2
+ import type * as babel from '@babel/core'
3
+
4
+ /**
5
+ * Handles <ClientOnly> JSX elements on the server side.
6
+ *
7
+ * On the server, the children of <ClientOnly> should be removed since they
8
+ * are client-only code. Only the fallback prop (if present) will be rendered.
9
+ *
10
+ * Transform:
11
+ * <ClientOnly fallback={<Loading />}>{clientOnlyContent}</ClientOnly>
12
+ * Into:
13
+ * <ClientOnly fallback={<Loading />} />
14
+ *
15
+ * Or if no fallback:
16
+ * <ClientOnly>{clientOnlyContent}</ClientOnly>
17
+ * Into:
18
+ * <ClientOnly />
19
+ */
20
+ export function handleClientOnlyJSX(
21
+ path: babel.NodePath<t.JSXElement>,
22
+ _opts: { env: 'server' },
23
+ ): void {
24
+ const element = path.node
25
+
26
+ // Remove all children - they are client-only code
27
+ element.children = []
28
+
29
+ // Make it a self-closing element since there are no children
30
+ element.openingElement.selfClosing = true
31
+ element.closingElement = null
32
+ }
@@ -75,7 +75,15 @@ const getLookupConfigurationsForEnv = (
75
75
  ...commonConfigs,
76
76
  ]
77
77
  } else {
78
- return commonConfigs
78
+ // Server-only: add ClientOnly JSX component lookup
79
+ return [
80
+ ...commonConfigs,
81
+ {
82
+ libName: `@tanstack/${framework}-router`,
83
+ rootExport: 'ClientOnly',
84
+ kind: 'ClientOnlyJSX',
85
+ },
86
+ ]
79
87
  }
80
88
  }
81
89
  const SERVER_FN_LOOKUP = 'server-fn-module-lookup'