@tanstack/start-plugin-core 1.147.3 → 1.148.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.
@@ -5,7 +5,7 @@ import { VITE_ENVIRONMENT_NAMES } from "../constants.js";
5
5
  import { cleanId, codeFrameError, stripMethodCall } from "./utils.js";
6
6
  const TSS_SERVERFN_SPLIT_PARAM = "tss-serverfn-split";
7
7
  const serverRpcTemplate = babel.template.expression(
8
- `createServerRpc(%%functionId%%, %%fn%%)`
8
+ `createServerRpc(%%serverFnMeta%%, %%fn%%)`
9
9
  );
10
10
  const clientRpcTemplate = babel.template.expression(
11
11
  `createClientRpc(%%functionId%%)`
@@ -61,25 +61,33 @@ function getEnvConfig(context, isProviderFile) {
61
61
  runtimeCodeType: "ssr"
62
62
  };
63
63
  }
64
- function generateProviderRpcStub(functionId, fn) {
64
+ function buildServerFnMetaObject(functionId, variableName, filename) {
65
+ return t.objectExpression([
66
+ t.objectProperty(t.identifier("id"), t.stringLiteral(functionId)),
67
+ t.objectProperty(t.identifier("name"), t.stringLiteral(variableName)),
68
+ t.objectProperty(t.identifier("filename"), t.stringLiteral(filename))
69
+ ]);
70
+ }
71
+ function generateProviderRpcStub(serverFnMeta, fn) {
65
72
  return serverRpcTemplate({
66
- functionId: t.stringLiteral(functionId),
73
+ serverFnMeta,
67
74
  fn
68
75
  });
69
76
  }
70
77
  function generateCallerRpcStub(functionId, functionName, extractedFilename, isClientReferenced, envConfig) {
78
+ const functionIdLiteral = t.stringLiteral(functionId);
71
79
  if (envConfig.runtimeCodeType === "client") {
72
80
  return clientRpcTemplate({
73
- functionId: t.stringLiteral(functionId)
81
+ functionId: functionIdLiteral
74
82
  });
75
83
  }
76
84
  if (isClientReferenced || !envConfig.ssrIsProvider) {
77
85
  return ssrRpcManifestTemplate({
78
- functionId: t.stringLiteral(functionId)
86
+ functionId: functionIdLiteral
79
87
  });
80
88
  }
81
89
  return ssrRpcImporterTemplate({
82
- functionId: t.stringLiteral(functionId),
90
+ functionId: functionIdLiteral,
83
91
  extractedFilename: t.stringLiteral(extractedFilename),
84
92
  functionName: t.stringLiteral(functionName)
85
93
  });
@@ -173,8 +181,13 @@ function handleCreateServerFn(candidates, context) {
173
181
  [t.identifier("opts"), t.identifier("signal")]
174
182
  )
175
183
  );
176
- const extractedFnInit = generateProviderRpcStub(
184
+ const serverFnMeta = buildServerFnMetaObject(
177
185
  functionId,
186
+ existingVariableName,
187
+ relativeFilename
188
+ );
189
+ const extractedFnInit = generateProviderRpcStub(
190
+ serverFnMeta,
178
191
  executeServerArrowFn
179
192
  );
180
193
  const extractedFnStatement = t.variableDeclaration("const", [
@@ -1 +1 @@
1
- {"version":3,"file":"handleCreateServerFn.js","sources":["../../../src/start-compiler-plugin/handleCreateServerFn.ts"],"sourcesContent":["import * as t from '@babel/types'\nimport babel from '@babel/core'\nimport path from 'pathe'\nimport { VITE_ENVIRONMENT_NAMES } from '../constants'\nimport { cleanId, codeFrameError, stripMethodCall } from './utils'\nimport type { CompilationContext, RewriteCandidate, ServerFn } from './types'\nimport type { CompileStartFrameworkOptions } from '../types'\n\nconst TSS_SERVERFN_SPLIT_PARAM = 'tss-serverfn-split'\n\n// ============================================================================\n// Pre-compiled babel templates (compiled once at module load time)\n// ============================================================================\n\n// Template for provider files: createServerRpc('id', fn)\nconst serverRpcTemplate = babel.template.expression(\n `createServerRpc(%%functionId%%, %%fn%%)`,\n)\n\n// Template for client caller files: createClientRpc('id')\nconst clientRpcTemplate = babel.template.expression(\n `createClientRpc(%%functionId%%)`,\n)\n\n// Template for SSR caller files (manifest lookup): createSsrRpc('id')\nconst ssrRpcManifestTemplate = babel.template.expression(\n `createSsrRpc(%%functionId%%)`,\n)\n\n// Template for SSR caller files (with importer): createSsrRpc('id', () => import(...).then(m => m['name']))\nconst ssrRpcImporterTemplate = babel.template.expression(\n `createSsrRpc(%%functionId%%, () => import(%%extractedFilename%%).then(m => m[%%functionName%%]))`,\n)\n\n// ============================================================================\n// Runtime code cache (cached per framework to avoid repeated AST generation)\n// ============================================================================\n\ntype RuntimeCodeType = 'provider' | 'client' | 'ssr'\ntype FrameworkRuntimeCache = Record<RuntimeCodeType, t.Statement>\nconst RuntimeCodeCache = new Map<\n CompileStartFrameworkOptions,\n FrameworkRuntimeCache\n>()\n\nfunction getCachedRuntimeCode(\n framework: CompileStartFrameworkOptions,\n type: RuntimeCodeType,\n): t.Statement {\n let cache = RuntimeCodeCache.get(framework)\n if (!cache) {\n cache = {\n provider: babel.template.ast(\n `import { createServerRpc } from '@tanstack/${framework}-start/server-rpc'`,\n { placeholderPattern: false },\n ) as t.Statement,\n client: babel.template.ast(\n `import { createClientRpc } from '@tanstack/${framework}-start/client-rpc'`,\n { placeholderPattern: false },\n ) as t.Statement,\n ssr: babel.template.ast(\n `import { createSsrRpc } from '@tanstack/${framework}-start/ssr-rpc'`,\n { placeholderPattern: false },\n ) as t.Statement,\n }\n RuntimeCodeCache.set(framework, cache)\n }\n return cache[type]\n}\n\n/**\n * Environment-specific configuration for server function transformation.\n * This is computed internally based on the compilation context.\n */\ninterface EnvConfig {\n /** Whether this environment is a client environment */\n isClientEnvironment: boolean\n /** Whether SSR is the provider environment */\n ssrIsProvider: boolean\n /** The runtime code type to use for imports */\n runtimeCodeType: RuntimeCodeType\n}\n\n/**\n * Gets the environment configuration for the current compilation context.\n */\nfunction getEnvConfig(\n context: CompilationContext,\n isProviderFile: boolean,\n): EnvConfig {\n const { providerEnvName, env } = context\n\n // SSR is the provider when the provider environment is the default server environment\n const ssrIsProvider = providerEnvName === VITE_ENVIRONMENT_NAMES.server\n\n if (isProviderFile) {\n return {\n isClientEnvironment: false,\n ssrIsProvider,\n runtimeCodeType: 'provider',\n }\n }\n\n if (env === 'client') {\n return {\n isClientEnvironment: true,\n ssrIsProvider,\n runtimeCodeType: 'client',\n }\n }\n\n // Server caller (SSR)\n return {\n isClientEnvironment: false,\n ssrIsProvider,\n runtimeCodeType: 'ssr',\n }\n}\n\n/**\n * Generates the RPC stub expression for provider files.\n * Uses pre-compiled template for performance.\n */\nfunction generateProviderRpcStub(\n functionId: string,\n fn: t.Expression,\n): t.Expression {\n return serverRpcTemplate({\n functionId: t.stringLiteral(functionId),\n fn,\n })\n}\n\n/**\n * Generates the RPC stub expression for caller files.\n * Uses pre-compiled templates for performance.\n */\nfunction generateCallerRpcStub(\n functionId: string,\n functionName: string,\n extractedFilename: string,\n isClientReferenced: boolean,\n envConfig: EnvConfig,\n): t.Expression {\n if (envConfig.runtimeCodeType === 'client') {\n return clientRpcTemplate({\n functionId: t.stringLiteral(functionId),\n })\n }\n\n // SSR caller\n // When the function is client-referenced, it's in the manifest - use manifest lookup\n // When SSR is NOT the provider, always use manifest lookup (no import() for different env)\n // Otherwise, use the importer for functions only referenced on the server when SSR is the provider\n if (isClientReferenced || !envConfig.ssrIsProvider) {\n return ssrRpcManifestTemplate({\n functionId: t.stringLiteral(functionId),\n })\n }\n\n return ssrRpcImporterTemplate({\n functionId: t.stringLiteral(functionId),\n extractedFilename: t.stringLiteral(extractedFilename),\n functionName: t.stringLiteral(functionName),\n })\n}\n\n/**\n * Handles createServerFn transformations for a batch of candidates.\n *\n * This function performs extraction and replacement of server functions\n *\n * For caller files (non-provider):\n * - Replaces the server function with an RPC stub\n * - Does not include the handler function body\n *\n * For provider files:\n * - Creates an extractedFn that calls __executeServer\n * - Modifies .handler() to pass (extractedFn, serverFn) - two arguments\n *\n * @param candidates - All ServerFn candidates to process\n * @param context - The compilation context with helpers and mutable state\n * @returns Result containing runtime code to add, or null if no candidates processed\n */\nexport function handleCreateServerFn(\n candidates: Array<RewriteCandidate>,\n context: CompilationContext,\n) {\n if (candidates.length === 0) {\n return\n }\n\n const isProviderFile = context.id.includes(TSS_SERVERFN_SPLIT_PARAM)\n // Get environment-specific configuration\n const envConfig = getEnvConfig(context, isProviderFile)\n\n // Track function names to ensure uniqueness within this file\n const functionNameSet = new Set<string>()\n\n const exportNames = new Set<string>()\n const serverFnsById: Record<string, ServerFn> = {}\n\n const [baseFilename] = context.id.split('?') as [string]\n const extractedFilename = `${baseFilename}?${TSS_SERVERFN_SPLIT_PARAM}`\n const relativeFilename = path.relative(context.root, baseFilename)\n const knownFns = context.getKnownServerFns()\n const cleanedContextId = cleanId(context.id)\n\n for (const candidate of candidates) {\n const { path: candidatePath, methodChain } = candidate\n const { inputValidator, handler } = methodChain\n\n // Check if the call is assigned to a variable\n if (!candidatePath.parentPath.isVariableDeclarator()) {\n throw new Error('createServerFn must be assigned to a variable!')\n }\n\n // Get the identifier name of the variable\n const variableDeclarator = candidatePath.parentPath.node\n if (!t.isIdentifier(variableDeclarator.id)) {\n throw codeFrameError(\n context.code,\n variableDeclarator.id.loc!,\n 'createServerFn must be assigned to a simple identifier, not a destructuring pattern',\n )\n }\n const existingVariableName = variableDeclarator.id.name\n\n // Generate unique function name with _createServerFn_handler suffix\n // The function name is derived from the variable name\n let functionName = `${existingVariableName}_createServerFn_handler`\n while (functionNameSet.has(functionName)) {\n functionName = incrementFunctionNameVersion(functionName)\n }\n functionNameSet.add(functionName)\n\n // Generate function ID using pre-computed relative filename\n const functionId = context.generateFunctionId({\n filename: relativeFilename,\n functionName,\n extractedFilename,\n })\n\n // Check if this function was already discovered by the client build\n const knownFn = knownFns[functionId]\n const isClientReferenced = envConfig.isClientEnvironment || !!knownFn\n\n // Use canonical extracted filename from known functions if available\n const canonicalExtractedFilename =\n knownFn?.extractedFilename ?? extractedFilename\n\n // Handle input validator - remove on client\n if (inputValidator) {\n const innerInputExpression = inputValidator.callPath.node.arguments[0]\n\n if (!innerInputExpression) {\n throw new Error(\n 'createServerFn().inputValidator() must be called with a validator!',\n )\n }\n\n // If we're on the client, remove the validator call expression\n if (context.env === 'client') {\n stripMethodCall(inputValidator.callPath)\n }\n }\n\n const handlerFnPath = handler?.firstArgPath\n\n if (!handler || !handlerFnPath?.node) {\n throw codeFrameError(\n context.code,\n candidatePath.node.callee.loc!,\n `createServerFn must be called with a \"handler\" property!`,\n )\n }\n\n // Validate the handler argument is an expression (not a SpreadElement, etc.)\n if (!t.isExpression(handlerFnPath.node)) {\n throw codeFrameError(\n context.code,\n handlerFnPath.node.loc!,\n `handler() must be called with an expression, not a ${handlerFnPath.node.type}`,\n )\n }\n\n const handlerFn = handlerFnPath.node\n\n // Register function only from caller files (not provider files)\n // to avoid duplicates - provider files process the same functions\n\n if (!isProviderFile) {\n serverFnsById[functionId] = {\n functionName,\n functionId,\n filename: cleanedContextId,\n extractedFilename: canonicalExtractedFilename,\n isClientReferenced,\n }\n }\n\n if (isProviderFile) {\n // PROVIDER FILE: This is the extracted file that contains the actual implementation\n // We need to:\n // 1. Create an extractedFn that calls __executeServer\n // 2. Modify .handler() to pass (extractedFn, serverFn) - two arguments\n //\n // Expected output format:\n // const extractedFn = createServerRpc(\"id\", (opts) => varName.__executeServer(opts));\n // const varName = createServerFn().handler(extractedFn, originalHandler);\n\n // Build the arrow function: (opts, signal) => varName.__executeServer(opts, signal)\n // The signal parameter is passed through to allow abort signal propagation\n const executeServerArrowFn = t.arrowFunctionExpression(\n [t.identifier('opts'), t.identifier('signal')],\n t.callExpression(\n t.memberExpression(\n t.identifier(existingVariableName),\n t.identifier('__executeServer'),\n ),\n [t.identifier('opts'), t.identifier('signal')],\n ),\n )\n\n // Generate the replacement using pre-compiled template\n const extractedFnInit = generateProviderRpcStub(\n functionId,\n executeServerArrowFn,\n )\n\n // Build the extracted function statement\n const extractedFnStatement = t.variableDeclaration('const', [\n t.variableDeclarator(t.identifier(functionName), extractedFnInit),\n ])\n\n // Find the variable declaration statement containing our createServerFn\n const variableDeclaration = candidatePath.parentPath.parentPath\n if (!variableDeclaration.isVariableDeclaration()) {\n throw new Error(\n 'Expected createServerFn to be in a VariableDeclaration',\n )\n }\n\n // Insert the extracted function statement before the variable declaration\n variableDeclaration.insertBefore(extractedFnStatement)\n\n // Modify the .handler() call to pass two arguments: (extractedFn, serverFn)\n // The handlerFnPath.node contains the original serverFn\n const extractedFnIdentifier = t.identifier(functionName)\n const serverFnNode = t.cloneNode(handlerFn, true)\n\n // Replace handler's arguments with [extractedFn, serverFn]\n handler.callPath.node.arguments = [extractedFnIdentifier, serverFnNode]\n\n // Only export the extracted handler (e.g., myFn_createServerFn_handler)\n // The manifest and all import paths only look up this suffixed name.\n // The original variable (e.g., myFn) stays in the file but is not exported\n // since it's only used internally.\n exportNames.add(functionName)\n } else {\n // CALLER FILE: This file calls the server function but doesn't contain the implementation\n // We need to:\n // 1. Remove the handler function body (it will be in the provider file)\n // 2. Replace the handler argument with an RPC stub\n //\n // IMPORTANT: We must keep the createServerFn().handler(extractedFn) structure\n // so that the client middleware chain can unwrap the {result, error, context} response.\n //\n // Expected output format:\n // const myFn = createServerFn().handler(createClientRpc(\"id\"))\n // or\n // const myFn = createServerFn().handler(createSsrRpc(\"id\", () => import(...)))\n\n // If the handler function is an identifier, we need to remove the bound function\n // from the file since it won't be needed\n if (t.isIdentifier(handlerFn)) {\n const binding = handlerFnPath.scope.getBinding(handlerFn.name)\n if (binding) {\n binding.path.remove()\n }\n }\n\n // Generate the RPC stub using pre-compiled templates\n const rpcStub = generateCallerRpcStub(\n functionId,\n functionName,\n canonicalExtractedFilename,\n isClientReferenced,\n envConfig,\n )\n\n // Replace ONLY the handler argument with the RPC stub\n // Keep the createServerFn().handler() wrapper intact for client middleware\n handlerFnPath.replaceWith(rpcStub)\n }\n }\n\n // For provider files, add exports for all extracted functions\n if (isProviderFile) {\n // Remove all existing exports first\n safeRemoveExports(context.ast)\n\n // Export all server function related variables from exportNames\n // These were populated by handleCreateServerFn:\n // 1. Extracted handlers: const fn_createServerFn_handler = createServerRpc(...)\n // 2. Original variables: const fn = createServerFn().handler(...)\n if (exportNames.size > 0) {\n context.ast.program.body.push(\n t.exportNamedDeclaration(\n undefined,\n Array.from(exportNames).map((name) =>\n t.exportSpecifier(t.identifier(name), t.identifier(name)),\n ),\n ),\n )\n }\n }\n\n // Notify about discovered functions (only for non-provider files)\n if (\n !isProviderFile &&\n Object.keys(serverFnsById).length > 0 &&\n context.onServerFnsById\n ) {\n context.onServerFnsById(serverFnsById)\n }\n\n // Add runtime import using cached AST node\n const runtimeCode = getCachedRuntimeCode(\n context.framework,\n envConfig.runtimeCodeType,\n )\n context.ast.program.body.unshift(t.cloneNode(runtimeCode))\n}\n\n/**\n * Makes an identifier safe for use as a JavaScript identifier.\n */\nfunction makeIdentifierSafe(identifier: string): string {\n return identifier\n .replace(/[^a-zA-Z0-9_$]/g, '_') // Replace unsafe chars with underscore\n .replace(/^[0-9]/, '_$&') // Prefix leading number with underscore\n .replace(/^\\$/, '_$') // Prefix leading $ with underscore\n .replace(/_{2,}/g, '_') // Collapse multiple underscores\n .replace(/^_|_$/g, '') // Trim leading/trailing underscores\n}\n\n/**\n * Increments the version number suffix on a function name.\n */\nfunction incrementFunctionNameVersion(functionName: string): string {\n const [realReferenceName, count] = functionName.split(/_(\\d+)$/)\n const resolvedCount = Number(count || '0')\n const suffix = `_${resolvedCount + 1}`\n return makeIdentifierSafe(realReferenceName!) + suffix\n}\n\n/**\n * Removes all exports from the AST while preserving the declarations.\n * Used for provider files where we want to re-export only the server functions.\n */\nfunction safeRemoveExports(ast: t.File): void {\n ast.program.body = ast.program.body.flatMap((node) => {\n if (\n t.isExportNamedDeclaration(node) ||\n t.isExportDefaultDeclaration(node)\n ) {\n if (\n t.isFunctionDeclaration(node.declaration) ||\n t.isClassDeclaration(node.declaration) ||\n t.isVariableDeclaration(node.declaration)\n ) {\n // do not remove export if it is an anonymous function / class,\n // otherwise this would produce a syntax error\n if (\n t.isFunctionDeclaration(node.declaration) ||\n t.isClassDeclaration(node.declaration)\n ) {\n if (!node.declaration.id) {\n return node\n }\n }\n return node.declaration\n } else if (node.declaration === null) {\n // remove e.g. `export { RouteComponent as component }`\n return []\n }\n }\n return node\n })\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"handleCreateServerFn.js","sources":["../../../src/start-compiler-plugin/handleCreateServerFn.ts"],"sourcesContent":["import * as t from '@babel/types'\nimport babel from '@babel/core'\nimport path from 'pathe'\nimport { VITE_ENVIRONMENT_NAMES } from '../constants'\nimport { cleanId, codeFrameError, stripMethodCall } from './utils'\nimport type { CompilationContext, RewriteCandidate, ServerFn } from './types'\nimport type { CompileStartFrameworkOptions } from '../types'\n\nconst TSS_SERVERFN_SPLIT_PARAM = 'tss-serverfn-split'\n\n// ============================================================================\n// Pre-compiled babel templates (compiled once at module load time)\n// ============================================================================\n\n// Template for provider files: createServerRpc(serverFnMeta, fn)\nconst serverRpcTemplate = babel.template.expression(\n `createServerRpc(%%serverFnMeta%%, %%fn%%)`,\n)\n\n// Template for client caller files: createClientRpc(functionId)\nconst clientRpcTemplate = babel.template.expression(\n `createClientRpc(%%functionId%%)`,\n)\n\n// Template for SSR caller files (manifest lookup): createSsrRpc(functionId)\nconst ssrRpcManifestTemplate = babel.template.expression(\n `createSsrRpc(%%functionId%%)`,\n)\n\n// Template for SSR caller files (with importer): createSsrRpc(functionId, () => import(...).then(m => m['name']))\nconst ssrRpcImporterTemplate = babel.template.expression(\n `createSsrRpc(%%functionId%%, () => import(%%extractedFilename%%).then(m => m[%%functionName%%]))`,\n)\n\n// ============================================================================\n// Runtime code cache (cached per framework to avoid repeated AST generation)\n// ============================================================================\n\ntype RuntimeCodeType = 'provider' | 'client' | 'ssr'\ntype FrameworkRuntimeCache = Record<RuntimeCodeType, t.Statement>\nconst RuntimeCodeCache = new Map<\n CompileStartFrameworkOptions,\n FrameworkRuntimeCache\n>()\n\nfunction getCachedRuntimeCode(\n framework: CompileStartFrameworkOptions,\n type: RuntimeCodeType,\n): t.Statement {\n let cache = RuntimeCodeCache.get(framework)\n if (!cache) {\n cache = {\n provider: babel.template.ast(\n `import { createServerRpc } from '@tanstack/${framework}-start/server-rpc'`,\n { placeholderPattern: false },\n ) as t.Statement,\n client: babel.template.ast(\n `import { createClientRpc } from '@tanstack/${framework}-start/client-rpc'`,\n { placeholderPattern: false },\n ) as t.Statement,\n ssr: babel.template.ast(\n `import { createSsrRpc } from '@tanstack/${framework}-start/ssr-rpc'`,\n { placeholderPattern: false },\n ) as t.Statement,\n }\n RuntimeCodeCache.set(framework, cache)\n }\n return cache[type]\n}\n\n/**\n * Environment-specific configuration for server function transformation.\n * This is computed internally based on the compilation context.\n */\ninterface EnvConfig {\n /** Whether this environment is a client environment */\n isClientEnvironment: boolean\n /** Whether SSR is the provider environment */\n ssrIsProvider: boolean\n /** The runtime code type to use for imports */\n runtimeCodeType: RuntimeCodeType\n}\n\n/**\n * Gets the environment configuration for the current compilation context.\n */\nfunction getEnvConfig(\n context: CompilationContext,\n isProviderFile: boolean,\n): EnvConfig {\n const { providerEnvName, env } = context\n\n // SSR is the provider when the provider environment is the default server environment\n const ssrIsProvider = providerEnvName === VITE_ENVIRONMENT_NAMES.server\n\n if (isProviderFile) {\n return {\n isClientEnvironment: false,\n ssrIsProvider,\n runtimeCodeType: 'provider',\n }\n }\n\n if (env === 'client') {\n return {\n isClientEnvironment: true,\n ssrIsProvider,\n runtimeCodeType: 'client',\n }\n }\n\n // Server caller (SSR)\n return {\n isClientEnvironment: false,\n ssrIsProvider,\n runtimeCodeType: 'ssr',\n }\n}\n\n/**\n * Builds the serverFnMeta object literal AST node.\n * The object contains: { id, name, filename }\n */\nfunction buildServerFnMetaObject(\n functionId: string,\n variableName: string,\n filename: string,\n): t.ObjectExpression {\n return t.objectExpression([\n t.objectProperty(t.identifier('id'), t.stringLiteral(functionId)),\n t.objectProperty(t.identifier('name'), t.stringLiteral(variableName)),\n t.objectProperty(t.identifier('filename'), t.stringLiteral(filename)),\n ])\n}\n\n/**\n * Generates the RPC stub expression for provider files.\n * Uses pre-compiled template for performance.\n */\nfunction generateProviderRpcStub(\n serverFnMeta: t.ObjectExpression,\n fn: t.Expression,\n): t.Expression {\n return serverRpcTemplate({\n serverFnMeta,\n fn,\n })\n}\n\n/**\n * Generates the RPC stub expression for caller files.\n * Uses pre-compiled templates for performance.\n * Note: Client and SSR callers only receive the functionId string, not the full metadata.\n */\nfunction generateCallerRpcStub(\n functionId: string,\n functionName: string,\n extractedFilename: string,\n isClientReferenced: boolean,\n envConfig: EnvConfig,\n): t.Expression {\n const functionIdLiteral = t.stringLiteral(functionId)\n\n if (envConfig.runtimeCodeType === 'client') {\n return clientRpcTemplate({\n functionId: functionIdLiteral,\n })\n }\n\n // SSR caller\n // When the function is client-referenced, it's in the manifest - use manifest lookup\n // When SSR is NOT the provider, always use manifest lookup (no import() for different env)\n // Otherwise, use the importer for functions only referenced on the server when SSR is the provider\n if (isClientReferenced || !envConfig.ssrIsProvider) {\n return ssrRpcManifestTemplate({\n functionId: functionIdLiteral,\n })\n }\n\n return ssrRpcImporterTemplate({\n functionId: functionIdLiteral,\n extractedFilename: t.stringLiteral(extractedFilename),\n functionName: t.stringLiteral(functionName),\n })\n}\n\n/**\n * Handles createServerFn transformations for a batch of candidates.\n *\n * This function performs extraction and replacement of server functions\n *\n * For caller files (non-provider):\n * - Replaces the server function with an RPC stub\n * - Does not include the handler function body\n *\n * For provider files:\n * - Creates an extractedFn that calls __executeServer\n * - Modifies .handler() to pass (extractedFn, serverFn) - two arguments\n *\n * @param candidates - All ServerFn candidates to process\n * @param context - The compilation context with helpers and mutable state\n * @returns Result containing runtime code to add, or null if no candidates processed\n */\nexport function handleCreateServerFn(\n candidates: Array<RewriteCandidate>,\n context: CompilationContext,\n) {\n if (candidates.length === 0) {\n return\n }\n\n const isProviderFile = context.id.includes(TSS_SERVERFN_SPLIT_PARAM)\n // Get environment-specific configuration\n const envConfig = getEnvConfig(context, isProviderFile)\n\n // Track function names to ensure uniqueness within this file\n const functionNameSet = new Set<string>()\n\n const exportNames = new Set<string>()\n const serverFnsById: Record<string, ServerFn> = {}\n\n const [baseFilename] = context.id.split('?') as [string]\n const extractedFilename = `${baseFilename}?${TSS_SERVERFN_SPLIT_PARAM}`\n const relativeFilename = path.relative(context.root, baseFilename)\n const knownFns = context.getKnownServerFns()\n const cleanedContextId = cleanId(context.id)\n\n for (const candidate of candidates) {\n const { path: candidatePath, methodChain } = candidate\n const { inputValidator, handler } = methodChain\n\n // Check if the call is assigned to a variable\n if (!candidatePath.parentPath.isVariableDeclarator()) {\n throw new Error('createServerFn must be assigned to a variable!')\n }\n\n // Get the identifier name of the variable\n const variableDeclarator = candidatePath.parentPath.node\n if (!t.isIdentifier(variableDeclarator.id)) {\n throw codeFrameError(\n context.code,\n variableDeclarator.id.loc!,\n 'createServerFn must be assigned to a simple identifier, not a destructuring pattern',\n )\n }\n const existingVariableName = variableDeclarator.id.name\n\n // Generate unique function name with _createServerFn_handler suffix\n // The function name is derived from the variable name\n let functionName = `${existingVariableName}_createServerFn_handler`\n while (functionNameSet.has(functionName)) {\n functionName = incrementFunctionNameVersion(functionName)\n }\n functionNameSet.add(functionName)\n\n // Generate function ID using pre-computed relative filename\n const functionId = context.generateFunctionId({\n filename: relativeFilename,\n functionName,\n extractedFilename,\n })\n\n // Check if this function was already discovered by the client build\n const knownFn = knownFns[functionId]\n const isClientReferenced = envConfig.isClientEnvironment || !!knownFn\n\n // Use canonical extracted filename from known functions if available\n const canonicalExtractedFilename =\n knownFn?.extractedFilename ?? extractedFilename\n\n // Handle input validator - remove on client\n if (inputValidator) {\n const innerInputExpression = inputValidator.callPath.node.arguments[0]\n\n if (!innerInputExpression) {\n throw new Error(\n 'createServerFn().inputValidator() must be called with a validator!',\n )\n }\n\n // If we're on the client, remove the validator call expression\n if (context.env === 'client') {\n stripMethodCall(inputValidator.callPath)\n }\n }\n\n const handlerFnPath = handler?.firstArgPath\n\n if (!handler || !handlerFnPath?.node) {\n throw codeFrameError(\n context.code,\n candidatePath.node.callee.loc!,\n `createServerFn must be called with a \"handler\" property!`,\n )\n }\n\n // Validate the handler argument is an expression (not a SpreadElement, etc.)\n if (!t.isExpression(handlerFnPath.node)) {\n throw codeFrameError(\n context.code,\n handlerFnPath.node.loc!,\n `handler() must be called with an expression, not a ${handlerFnPath.node.type}`,\n )\n }\n\n const handlerFn = handlerFnPath.node\n\n // Register function only from caller files (not provider files)\n // to avoid duplicates - provider files process the same functions\n\n if (!isProviderFile) {\n serverFnsById[functionId] = {\n functionName,\n functionId,\n filename: cleanedContextId,\n extractedFilename: canonicalExtractedFilename,\n isClientReferenced,\n }\n }\n\n if (isProviderFile) {\n // PROVIDER FILE: This is the extracted file that contains the actual implementation\n // We need to:\n // 1. Create an extractedFn that calls __executeServer\n // 2. Modify .handler() to pass (extractedFn, serverFn) - two arguments\n //\n // Expected output format:\n // const extractedFn = createServerRpc({id, name, filename}, (opts) => varName.__executeServer(opts));\n // const varName = createServerFn().handler(extractedFn, originalHandler);\n\n // Build the arrow function: (opts, signal) => varName.__executeServer(opts, signal)\n // The signal parameter is passed through to allow abort signal propagation\n const executeServerArrowFn = t.arrowFunctionExpression(\n [t.identifier('opts'), t.identifier('signal')],\n t.callExpression(\n t.memberExpression(\n t.identifier(existingVariableName),\n t.identifier('__executeServer'),\n ),\n [t.identifier('opts'), t.identifier('signal')],\n ),\n )\n\n // Build the serverFnMeta object\n const serverFnMeta = buildServerFnMetaObject(\n functionId,\n existingVariableName,\n relativeFilename,\n )\n\n // Generate the replacement using pre-compiled template\n const extractedFnInit = generateProviderRpcStub(\n serverFnMeta,\n executeServerArrowFn,\n )\n\n // Build the extracted function statement\n const extractedFnStatement = t.variableDeclaration('const', [\n t.variableDeclarator(t.identifier(functionName), extractedFnInit),\n ])\n\n // Find the variable declaration statement containing our createServerFn\n const variableDeclaration = candidatePath.parentPath.parentPath\n if (!variableDeclaration.isVariableDeclaration()) {\n throw new Error(\n 'Expected createServerFn to be in a VariableDeclaration',\n )\n }\n\n // Insert the extracted function statement before the variable declaration\n variableDeclaration.insertBefore(extractedFnStatement)\n\n // Modify the .handler() call to pass two arguments: (extractedFn, serverFn)\n // The handlerFnPath.node contains the original serverFn\n const extractedFnIdentifier = t.identifier(functionName)\n const serverFnNode = t.cloneNode(handlerFn, true)\n\n // Replace handler's arguments with [extractedFn, serverFn]\n handler.callPath.node.arguments = [extractedFnIdentifier, serverFnNode]\n\n // Only export the extracted handler (e.g., myFn_createServerFn_handler)\n // The manifest and all import paths only look up this suffixed name.\n // The original variable (e.g., myFn) stays in the file but is not exported\n // since it's only used internally.\n exportNames.add(functionName)\n } else {\n // CALLER FILE: This file calls the server function but doesn't contain the implementation\n // We need to:\n // 1. Remove the handler function body (it will be in the provider file)\n // 2. Replace the handler argument with an RPC stub\n //\n // IMPORTANT: We must keep the createServerFn().handler(extractedFn) structure\n // so that the client middleware chain can unwrap the {result, error, context} response.\n //\n // Expected output format:\n // const myFn = createServerFn().handler(createClientRpc(\"id\"))\n // or\n // const myFn = createServerFn().handler(createSsrRpc(\"id\", () => import(...)))\n\n // If the handler function is an identifier, we need to remove the bound function\n // from the file since it won't be needed\n if (t.isIdentifier(handlerFn)) {\n const binding = handlerFnPath.scope.getBinding(handlerFn.name)\n if (binding) {\n binding.path.remove()\n }\n }\n\n // Generate the RPC stub using pre-compiled templates\n // Note: Caller files only pass functionId, not the full serverFnMeta\n const rpcStub = generateCallerRpcStub(\n functionId,\n functionName,\n canonicalExtractedFilename,\n isClientReferenced,\n envConfig,\n )\n\n // Replace ONLY the handler argument with the RPC stub\n // Keep the createServerFn().handler() wrapper intact for client middleware\n handlerFnPath.replaceWith(rpcStub)\n }\n }\n\n // For provider files, add exports for all extracted functions\n if (isProviderFile) {\n // Remove all existing exports first\n safeRemoveExports(context.ast)\n\n // Export all server function related variables from exportNames\n // These were populated by handleCreateServerFn:\n // 1. Extracted handlers: const fn_createServerFn_handler = createServerRpc(...)\n // 2. Original variables: const fn = createServerFn().handler(...)\n if (exportNames.size > 0) {\n context.ast.program.body.push(\n t.exportNamedDeclaration(\n undefined,\n Array.from(exportNames).map((name) =>\n t.exportSpecifier(t.identifier(name), t.identifier(name)),\n ),\n ),\n )\n }\n }\n\n // Notify about discovered functions (only for non-provider files)\n if (\n !isProviderFile &&\n Object.keys(serverFnsById).length > 0 &&\n context.onServerFnsById\n ) {\n context.onServerFnsById(serverFnsById)\n }\n\n // Add runtime import using cached AST node\n const runtimeCode = getCachedRuntimeCode(\n context.framework,\n envConfig.runtimeCodeType,\n )\n context.ast.program.body.unshift(t.cloneNode(runtimeCode))\n}\n\n/**\n * Makes an identifier safe for use as a JavaScript identifier.\n */\nfunction makeIdentifierSafe(identifier: string): string {\n return identifier\n .replace(/[^a-zA-Z0-9_$]/g, '_') // Replace unsafe chars with underscore\n .replace(/^[0-9]/, '_$&') // Prefix leading number with underscore\n .replace(/^\\$/, '_$') // Prefix leading $ with underscore\n .replace(/_{2,}/g, '_') // Collapse multiple underscores\n .replace(/^_|_$/g, '') // Trim leading/trailing underscores\n}\n\n/**\n * Increments the version number suffix on a function name.\n */\nfunction incrementFunctionNameVersion(functionName: string): string {\n const [realReferenceName, count] = functionName.split(/_(\\d+)$/)\n const resolvedCount = Number(count || '0')\n const suffix = `_${resolvedCount + 1}`\n return makeIdentifierSafe(realReferenceName!) + suffix\n}\n\n/**\n * Removes all exports from the AST while preserving the declarations.\n * Used for provider files where we want to re-export only the server functions.\n */\nfunction safeRemoveExports(ast: t.File): void {\n ast.program.body = ast.program.body.flatMap((node) => {\n if (\n t.isExportNamedDeclaration(node) ||\n t.isExportDefaultDeclaration(node)\n ) {\n if (\n t.isFunctionDeclaration(node.declaration) ||\n t.isClassDeclaration(node.declaration) ||\n t.isVariableDeclaration(node.declaration)\n ) {\n // do not remove export if it is an anonymous function / class,\n // otherwise this would produce a syntax error\n if (\n t.isFunctionDeclaration(node.declaration) ||\n t.isClassDeclaration(node.declaration)\n ) {\n if (!node.declaration.id) {\n return node\n }\n }\n return node.declaration\n } else if (node.declaration === null) {\n // remove e.g. `export { RouteComponent as component }`\n return []\n }\n }\n return node\n })\n}\n"],"names":[],"mappings":";;;;;AAQA,MAAM,2BAA2B;AAOjC,MAAM,oBAAoB,MAAM,SAAS;AAAA,EACvC;AACF;AAGA,MAAM,oBAAoB,MAAM,SAAS;AAAA,EACvC;AACF;AAGA,MAAM,yBAAyB,MAAM,SAAS;AAAA,EAC5C;AACF;AAGA,MAAM,yBAAyB,MAAM,SAAS;AAAA,EAC5C;AACF;AAQA,MAAM,uCAAuB,IAAA;AAK7B,SAAS,qBACP,WACA,MACa;AACb,MAAI,QAAQ,iBAAiB,IAAI,SAAS;AAC1C,MAAI,CAAC,OAAO;AACV,YAAQ;AAAA,MACN,UAAU,MAAM,SAAS;AAAA,QACvB,8CAA8C,SAAS;AAAA,QACvD,EAAE,oBAAoB,MAAA;AAAA,MAAM;AAAA,MAE9B,QAAQ,MAAM,SAAS;AAAA,QACrB,8CAA8C,SAAS;AAAA,QACvD,EAAE,oBAAoB,MAAA;AAAA,MAAM;AAAA,MAE9B,KAAK,MAAM,SAAS;AAAA,QAClB,2CAA2C,SAAS;AAAA,QACpD,EAAE,oBAAoB,MAAA;AAAA,MAAM;AAAA,IAC9B;AAEF,qBAAiB,IAAI,WAAW,KAAK;AAAA,EACvC;AACA,SAAO,MAAM,IAAI;AACnB;AAkBA,SAAS,aACP,SACA,gBACW;AACX,QAAM,EAAE,iBAAiB,IAAA,IAAQ;AAGjC,QAAM,gBAAgB,oBAAoB,uBAAuB;AAEjE,MAAI,gBAAgB;AAClB,WAAO;AAAA,MACL,qBAAqB;AAAA,MACrB;AAAA,MACA,iBAAiB;AAAA,IAAA;AAAA,EAErB;AAEA,MAAI,QAAQ,UAAU;AACpB,WAAO;AAAA,MACL,qBAAqB;AAAA,MACrB;AAAA,MACA,iBAAiB;AAAA,IAAA;AAAA,EAErB;AAGA,SAAO;AAAA,IACL,qBAAqB;AAAA,IACrB;AAAA,IACA,iBAAiB;AAAA,EAAA;AAErB;AAMA,SAAS,wBACP,YACA,cACA,UACoB;AACpB,SAAO,EAAE,iBAAiB;AAAA,IACxB,EAAE,eAAe,EAAE,WAAW,IAAI,GAAG,EAAE,cAAc,UAAU,CAAC;AAAA,IAChE,EAAE,eAAe,EAAE,WAAW,MAAM,GAAG,EAAE,cAAc,YAAY,CAAC;AAAA,IACpE,EAAE,eAAe,EAAE,WAAW,UAAU,GAAG,EAAE,cAAc,QAAQ,CAAC;AAAA,EAAA,CACrE;AACH;AAMA,SAAS,wBACP,cACA,IACc;AACd,SAAO,kBAAkB;AAAA,IACvB;AAAA,IACA;AAAA,EAAA,CACD;AACH;AAOA,SAAS,sBACP,YACA,cACA,mBACA,oBACA,WACc;AACd,QAAM,oBAAoB,EAAE,cAAc,UAAU;AAEpD,MAAI,UAAU,oBAAoB,UAAU;AAC1C,WAAO,kBAAkB;AAAA,MACvB,YAAY;AAAA,IAAA,CACb;AAAA,EACH;AAMA,MAAI,sBAAsB,CAAC,UAAU,eAAe;AAClD,WAAO,uBAAuB;AAAA,MAC5B,YAAY;AAAA,IAAA,CACb;AAAA,EACH;AAEA,SAAO,uBAAuB;AAAA,IAC5B,YAAY;AAAA,IACZ,mBAAmB,EAAE,cAAc,iBAAiB;AAAA,IACpD,cAAc,EAAE,cAAc,YAAY;AAAA,EAAA,CAC3C;AACH;AAmBO,SAAS,qBACd,YACA,SACA;AACA,MAAI,WAAW,WAAW,GAAG;AAC3B;AAAA,EACF;AAEA,QAAM,iBAAiB,QAAQ,GAAG,SAAS,wBAAwB;AAEnE,QAAM,YAAY,aAAa,SAAS,cAAc;AAGtD,QAAM,sCAAsB,IAAA;AAE5B,QAAM,kCAAkB,IAAA;AACxB,QAAM,gBAA0C,CAAA;AAEhD,QAAM,CAAC,YAAY,IAAI,QAAQ,GAAG,MAAM,GAAG;AAC3C,QAAM,oBAAoB,GAAG,YAAY,IAAI,wBAAwB;AACrE,QAAM,mBAAmB,KAAK,SAAS,QAAQ,MAAM,YAAY;AACjE,QAAM,WAAW,QAAQ,kBAAA;AACzB,QAAM,mBAAmB,QAAQ,QAAQ,EAAE;AAE3C,aAAW,aAAa,YAAY;AAClC,UAAM,EAAE,MAAM,eAAe,YAAA,IAAgB;AAC7C,UAAM,EAAE,gBAAgB,QAAA,IAAY;AAGpC,QAAI,CAAC,cAAc,WAAW,wBAAwB;AACpD,YAAM,IAAI,MAAM,gDAAgD;AAAA,IAClE;AAGA,UAAM,qBAAqB,cAAc,WAAW;AACpD,QAAI,CAAC,EAAE,aAAa,mBAAmB,EAAE,GAAG;AAC1C,YAAM;AAAA,QACJ,QAAQ;AAAA,QACR,mBAAmB,GAAG;AAAA,QACtB;AAAA,MAAA;AAAA,IAEJ;AACA,UAAM,uBAAuB,mBAAmB,GAAG;AAInD,QAAI,eAAe,GAAG,oBAAoB;AAC1C,WAAO,gBAAgB,IAAI,YAAY,GAAG;AACxC,qBAAe,6BAA6B,YAAY;AAAA,IAC1D;AACA,oBAAgB,IAAI,YAAY;AAGhC,UAAM,aAAa,QAAQ,mBAAmB;AAAA,MAC5C,UAAU;AAAA,MACV;AAAA,MACA;AAAA,IAAA,CACD;AAGD,UAAM,UAAU,SAAS,UAAU;AACnC,UAAM,qBAAqB,UAAU,uBAAuB,CAAC,CAAC;AAG9D,UAAM,6BACJ,SAAS,qBAAqB;AAGhC,QAAI,gBAAgB;AAClB,YAAM,uBAAuB,eAAe,SAAS,KAAK,UAAU,CAAC;AAErE,UAAI,CAAC,sBAAsB;AACzB,cAAM,IAAI;AAAA,UACR;AAAA,QAAA;AAAA,MAEJ;AAGA,UAAI,QAAQ,QAAQ,UAAU;AAC5B,wBAAgB,eAAe,QAAQ;AAAA,MACzC;AAAA,IACF;AAEA,UAAM,gBAAgB,SAAS;AAE/B,QAAI,CAAC,WAAW,CAAC,eAAe,MAAM;AACpC,YAAM;AAAA,QACJ,QAAQ;AAAA,QACR,cAAc,KAAK,OAAO;AAAA,QAC1B;AAAA,MAAA;AAAA,IAEJ;AAGA,QAAI,CAAC,EAAE,aAAa,cAAc,IAAI,GAAG;AACvC,YAAM;AAAA,QACJ,QAAQ;AAAA,QACR,cAAc,KAAK;AAAA,QACnB,sDAAsD,cAAc,KAAK,IAAI;AAAA,MAAA;AAAA,IAEjF;AAEA,UAAM,YAAY,cAAc;AAKhC,QAAI,CAAC,gBAAgB;AACnB,oBAAc,UAAU,IAAI;AAAA,QAC1B;AAAA,QACA;AAAA,QACA,UAAU;AAAA,QACV,mBAAmB;AAAA,QACnB;AAAA,MAAA;AAAA,IAEJ;AAEA,QAAI,gBAAgB;AAYlB,YAAM,uBAAuB,EAAE;AAAA,QAC7B,CAAC,EAAE,WAAW,MAAM,GAAG,EAAE,WAAW,QAAQ,CAAC;AAAA,QAC7C,EAAE;AAAA,UACA,EAAE;AAAA,YACA,EAAE,WAAW,oBAAoB;AAAA,YACjC,EAAE,WAAW,iBAAiB;AAAA,UAAA;AAAA,UAEhC,CAAC,EAAE,WAAW,MAAM,GAAG,EAAE,WAAW,QAAQ,CAAC;AAAA,QAAA;AAAA,MAC/C;AAIF,YAAM,eAAe;AAAA,QACnB;AAAA,QACA;AAAA,QACA;AAAA,MAAA;AAIF,YAAM,kBAAkB;AAAA,QACtB;AAAA,QACA;AAAA,MAAA;AAIF,YAAM,uBAAuB,EAAE,oBAAoB,SAAS;AAAA,QAC1D,EAAE,mBAAmB,EAAE,WAAW,YAAY,GAAG,eAAe;AAAA,MAAA,CACjE;AAGD,YAAM,sBAAsB,cAAc,WAAW;AACrD,UAAI,CAAC,oBAAoB,yBAAyB;AAChD,cAAM,IAAI;AAAA,UACR;AAAA,QAAA;AAAA,MAEJ;AAGA,0BAAoB,aAAa,oBAAoB;AAIrD,YAAM,wBAAwB,EAAE,WAAW,YAAY;AACvD,YAAM,eAAe,EAAE,UAAU,WAAW,IAAI;AAGhD,cAAQ,SAAS,KAAK,YAAY,CAAC,uBAAuB,YAAY;AAMtE,kBAAY,IAAI,YAAY;AAAA,IAC9B,OAAO;AAgBL,UAAI,EAAE,aAAa,SAAS,GAAG;AAC7B,cAAM,UAAU,cAAc,MAAM,WAAW,UAAU,IAAI;AAC7D,YAAI,SAAS;AACX,kBAAQ,KAAK,OAAA;AAAA,QACf;AAAA,MACF;AAIA,YAAM,UAAU;AAAA,QACd;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MAAA;AAKF,oBAAc,YAAY,OAAO;AAAA,IACnC;AAAA,EACF;AAGA,MAAI,gBAAgB;AAElB,sBAAkB,QAAQ,GAAG;AAM7B,QAAI,YAAY,OAAO,GAAG;AACxB,cAAQ,IAAI,QAAQ,KAAK;AAAA,QACvB,EAAE;AAAA,UACA;AAAA,UACA,MAAM,KAAK,WAAW,EAAE;AAAA,YAAI,CAAC,SAC3B,EAAE,gBAAgB,EAAE,WAAW,IAAI,GAAG,EAAE,WAAW,IAAI,CAAC;AAAA,UAAA;AAAA,QAC1D;AAAA,MACF;AAAA,IAEJ;AAAA,EACF;AAGA,MACE,CAAC,kBACD,OAAO,KAAK,aAAa,EAAE,SAAS,KACpC,QAAQ,iBACR;AACA,YAAQ,gBAAgB,aAAa;AAAA,EACvC;AAGA,QAAM,cAAc;AAAA,IAClB,QAAQ;AAAA,IACR,UAAU;AAAA,EAAA;AAEZ,UAAQ,IAAI,QAAQ,KAAK,QAAQ,EAAE,UAAU,WAAW,CAAC;AAC3D;AAKA,SAAS,mBAAmB,YAA4B;AACtD,SAAO,WACJ,QAAQ,mBAAmB,GAAG,EAC9B,QAAQ,UAAU,KAAK,EACvB,QAAQ,OAAO,IAAI,EACnB,QAAQ,UAAU,GAAG,EACrB,QAAQ,UAAU,EAAE;AACzB;AAKA,SAAS,6BAA6B,cAA8B;AAClE,QAAM,CAAC,mBAAmB,KAAK,IAAI,aAAa,MAAM,SAAS;AAC/D,QAAM,gBAAgB,OAAO,SAAS,GAAG;AACzC,QAAM,SAAS,IAAI,gBAAgB,CAAC;AACpC,SAAO,mBAAmB,iBAAkB,IAAI;AAClD;AAMA,SAAS,kBAAkB,KAAmB;AAC5C,MAAI,QAAQ,OAAO,IAAI,QAAQ,KAAK,QAAQ,CAAC,SAAS;AACpD,QACE,EAAE,yBAAyB,IAAI,KAC/B,EAAE,2BAA2B,IAAI,GACjC;AACA,UACE,EAAE,sBAAsB,KAAK,WAAW,KACxC,EAAE,mBAAmB,KAAK,WAAW,KACrC,EAAE,sBAAsB,KAAK,WAAW,GACxC;AAGA,YACE,EAAE,sBAAsB,KAAK,WAAW,KACxC,EAAE,mBAAmB,KAAK,WAAW,GACrC;AACA,cAAI,CAAC,KAAK,YAAY,IAAI;AACxB,mBAAO;AAAA,UACT;AAAA,QACF;AACA,eAAO,KAAK;AAAA,MACd,WAAW,KAAK,gBAAgB,MAAM;AAEpC,eAAO,CAAA;AAAA,MACT;AAAA,IACF;AACA,WAAO;AAAA,EACT,CAAC;AACH;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/start-plugin-core",
3
- "version": "1.147.3",
3
+ "version": "1.148.0",
4
4
  "description": "Modern and scalable routing for React applications",
5
5
  "author": "Tanner Linsley",
6
6
  "license": "MIT",
@@ -60,11 +60,11 @@
60
60
  "xmlbuilder2": "^4.0.3",
61
61
  "zod": "^3.24.2",
62
62
  "@tanstack/router-generator": "1.147.1",
63
+ "@tanstack/router-core": "1.147.1",
63
64
  "@tanstack/router-utils": "1.143.11",
64
65
  "@tanstack/router-plugin": "1.147.3",
65
- "@tanstack/start-server-core": "1.147.3",
66
- "@tanstack/router-core": "1.147.1",
67
- "@tanstack/start-client-core": "1.147.1"
66
+ "@tanstack/start-client-core": "1.148.0",
67
+ "@tanstack/start-server-core": "1.148.0"
68
68
  },
69
69
  "devDependencies": {
70
70
  "@types/babel__code-frame": "^7.0.6",
@@ -12,22 +12,22 @@ const TSS_SERVERFN_SPLIT_PARAM = 'tss-serverfn-split'
12
12
  // Pre-compiled babel templates (compiled once at module load time)
13
13
  // ============================================================================
14
14
 
15
- // Template for provider files: createServerRpc('id', fn)
15
+ // Template for provider files: createServerRpc(serverFnMeta, fn)
16
16
  const serverRpcTemplate = babel.template.expression(
17
- `createServerRpc(%%functionId%%, %%fn%%)`,
17
+ `createServerRpc(%%serverFnMeta%%, %%fn%%)`,
18
18
  )
19
19
 
20
- // Template for client caller files: createClientRpc('id')
20
+ // Template for client caller files: createClientRpc(functionId)
21
21
  const clientRpcTemplate = babel.template.expression(
22
22
  `createClientRpc(%%functionId%%)`,
23
23
  )
24
24
 
25
- // Template for SSR caller files (manifest lookup): createSsrRpc('id')
25
+ // Template for SSR caller files (manifest lookup): createSsrRpc(functionId)
26
26
  const ssrRpcManifestTemplate = babel.template.expression(
27
27
  `createSsrRpc(%%functionId%%)`,
28
28
  )
29
29
 
30
- // Template for SSR caller files (with importer): createSsrRpc('id', () => import(...).then(m => m['name']))
30
+ // Template for SSR caller files (with importer): createSsrRpc(functionId, () => import(...).then(m => m['name']))
31
31
  const ssrRpcImporterTemplate = babel.template.expression(
32
32
  `createSsrRpc(%%functionId%%, () => import(%%extractedFilename%%).then(m => m[%%functionName%%]))`,
33
33
  )
@@ -117,16 +117,32 @@ function getEnvConfig(
117
117
  }
118
118
  }
119
119
 
120
+ /**
121
+ * Builds the serverFnMeta object literal AST node.
122
+ * The object contains: { id, name, filename }
123
+ */
124
+ function buildServerFnMetaObject(
125
+ functionId: string,
126
+ variableName: string,
127
+ filename: string,
128
+ ): t.ObjectExpression {
129
+ return t.objectExpression([
130
+ t.objectProperty(t.identifier('id'), t.stringLiteral(functionId)),
131
+ t.objectProperty(t.identifier('name'), t.stringLiteral(variableName)),
132
+ t.objectProperty(t.identifier('filename'), t.stringLiteral(filename)),
133
+ ])
134
+ }
135
+
120
136
  /**
121
137
  * Generates the RPC stub expression for provider files.
122
138
  * Uses pre-compiled template for performance.
123
139
  */
124
140
  function generateProviderRpcStub(
125
- functionId: string,
141
+ serverFnMeta: t.ObjectExpression,
126
142
  fn: t.Expression,
127
143
  ): t.Expression {
128
144
  return serverRpcTemplate({
129
- functionId: t.stringLiteral(functionId),
145
+ serverFnMeta,
130
146
  fn,
131
147
  })
132
148
  }
@@ -134,6 +150,7 @@ function generateProviderRpcStub(
134
150
  /**
135
151
  * Generates the RPC stub expression for caller files.
136
152
  * Uses pre-compiled templates for performance.
153
+ * Note: Client and SSR callers only receive the functionId string, not the full metadata.
137
154
  */
138
155
  function generateCallerRpcStub(
139
156
  functionId: string,
@@ -142,9 +159,11 @@ function generateCallerRpcStub(
142
159
  isClientReferenced: boolean,
143
160
  envConfig: EnvConfig,
144
161
  ): t.Expression {
162
+ const functionIdLiteral = t.stringLiteral(functionId)
163
+
145
164
  if (envConfig.runtimeCodeType === 'client') {
146
165
  return clientRpcTemplate({
147
- functionId: t.stringLiteral(functionId),
166
+ functionId: functionIdLiteral,
148
167
  })
149
168
  }
150
169
 
@@ -154,12 +173,12 @@ function generateCallerRpcStub(
154
173
  // Otherwise, use the importer for functions only referenced on the server when SSR is the provider
155
174
  if (isClientReferenced || !envConfig.ssrIsProvider) {
156
175
  return ssrRpcManifestTemplate({
157
- functionId: t.stringLiteral(functionId),
176
+ functionId: functionIdLiteral,
158
177
  })
159
178
  }
160
179
 
161
180
  return ssrRpcImporterTemplate({
162
- functionId: t.stringLiteral(functionId),
181
+ functionId: functionIdLiteral,
163
182
  extractedFilename: t.stringLiteral(extractedFilename),
164
183
  functionName: t.stringLiteral(functionName),
165
184
  })
@@ -306,7 +325,7 @@ export function handleCreateServerFn(
306
325
  // 2. Modify .handler() to pass (extractedFn, serverFn) - two arguments
307
326
  //
308
327
  // Expected output format:
309
- // const extractedFn = createServerRpc("id", (opts) => varName.__executeServer(opts));
328
+ // const extractedFn = createServerRpc({id, name, filename}, (opts) => varName.__executeServer(opts));
310
329
  // const varName = createServerFn().handler(extractedFn, originalHandler);
311
330
 
312
331
  // Build the arrow function: (opts, signal) => varName.__executeServer(opts, signal)
@@ -322,9 +341,16 @@ export function handleCreateServerFn(
322
341
  ),
323
342
  )
324
343
 
344
+ // Build the serverFnMeta object
345
+ const serverFnMeta = buildServerFnMetaObject(
346
+ functionId,
347
+ existingVariableName,
348
+ relativeFilename,
349
+ )
350
+
325
351
  // Generate the replacement using pre-compiled template
326
352
  const extractedFnInit = generateProviderRpcStub(
327
- functionId,
353
+ serverFnMeta,
328
354
  executeServerArrowFn,
329
355
  )
330
356
 
@@ -381,6 +407,7 @@ export function handleCreateServerFn(
381
407
  }
382
408
 
383
409
  // Generate the RPC stub using pre-compiled templates
410
+ // Note: Caller files only pass functionId, not the full serverFnMeta
384
411
  const rpcStub = generateCallerRpcStub(
385
412
  functionId,
386
413
  functionName,