@tanstack/start-plugin-core 1.143.3 → 1.143.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/dist/esm/plugin.js +11 -46
  2. package/dist/esm/plugin.js.map +1 -1
  3. package/dist/esm/{create-server-fn-plugin → start-compiler-plugin}/compiler.d.ts +39 -4
  4. package/dist/esm/{create-server-fn-plugin → start-compiler-plugin}/compiler.js +82 -24
  5. package/dist/esm/start-compiler-plugin/compiler.js.map +1 -0
  6. package/dist/esm/start-compiler-plugin/handleClientOnlyJSX.js.map +1 -0
  7. package/dist/esm/start-compiler-plugin/handleCreateIsomorphicFn.d.ts +8 -0
  8. package/dist/esm/start-compiler-plugin/handleCreateIsomorphicFn.js +33 -0
  9. package/dist/esm/start-compiler-plugin/handleCreateIsomorphicFn.js.map +1 -0
  10. package/dist/esm/start-compiler-plugin/handleCreateMiddleware.d.ts +8 -0
  11. package/dist/esm/start-compiler-plugin/handleCreateMiddleware.js +25 -0
  12. package/dist/esm/start-compiler-plugin/handleCreateMiddleware.js.map +1 -0
  13. package/dist/esm/start-compiler-plugin/handleCreateServerFn.d.ts +19 -0
  14. package/dist/esm/start-compiler-plugin/handleCreateServerFn.js +262 -0
  15. package/dist/esm/start-compiler-plugin/handleCreateServerFn.js.map +1 -0
  16. package/dist/esm/start-compiler-plugin/handleEnvOnly.d.ts +10 -0
  17. package/dist/esm/start-compiler-plugin/handleEnvOnly.js +38 -0
  18. package/dist/esm/start-compiler-plugin/handleEnvOnly.js.map +1 -0
  19. package/dist/esm/start-compiler-plugin/plugin.d.ts +19 -0
  20. package/dist/esm/start-compiler-plugin/plugin.js +314 -0
  21. package/dist/esm/start-compiler-plugin/plugin.js.map +1 -0
  22. package/dist/esm/start-compiler-plugin/types.d.ts +116 -0
  23. package/dist/esm/start-compiler-plugin/utils.d.ts +23 -0
  24. package/dist/esm/start-compiler-plugin/utils.js +34 -0
  25. package/dist/esm/start-compiler-plugin/utils.js.map +1 -0
  26. package/dist/esm/types.d.ts +0 -1
  27. package/package.json +6 -7
  28. package/src/plugin.ts +10 -50
  29. package/src/{create-server-fn-plugin → start-compiler-plugin}/compiler.ts +162 -30
  30. package/src/start-compiler-plugin/handleCreateIsomorphicFn.ts +54 -0
  31. package/src/start-compiler-plugin/handleCreateMiddleware.ts +39 -0
  32. package/src/start-compiler-plugin/handleCreateServerFn.ts +491 -0
  33. package/src/start-compiler-plugin/handleEnvOnly.ts +56 -0
  34. package/src/start-compiler-plugin/plugin.ts +423 -0
  35. package/src/start-compiler-plugin/types.ts +133 -0
  36. package/src/start-compiler-plugin/utils.ts +52 -0
  37. package/src/types.ts +0 -1
  38. package/dist/esm/create-server-fn-plugin/compiler.js.map +0 -1
  39. package/dist/esm/create-server-fn-plugin/handleClientOnlyJSX.js.map +0 -1
  40. package/dist/esm/create-server-fn-plugin/handleCreateIsomorphicFn.d.ts +0 -4
  41. package/dist/esm/create-server-fn-plugin/handleCreateIsomorphicFn.js +0 -31
  42. package/dist/esm/create-server-fn-plugin/handleCreateIsomorphicFn.js.map +0 -1
  43. package/dist/esm/create-server-fn-plugin/handleCreateMiddleware.d.ts +0 -10
  44. package/dist/esm/create-server-fn-plugin/handleCreateMiddleware.js +0 -29
  45. package/dist/esm/create-server-fn-plugin/handleCreateMiddleware.js.map +0 -1
  46. package/dist/esm/create-server-fn-plugin/handleCreateServerFn.d.ts +0 -17
  47. package/dist/esm/create-server-fn-plugin/handleCreateServerFn.js +0 -82
  48. package/dist/esm/create-server-fn-plugin/handleCreateServerFn.js.map +0 -1
  49. package/dist/esm/create-server-fn-plugin/handleEnvOnly.d.ts +0 -6
  50. package/dist/esm/create-server-fn-plugin/handleEnvOnly.js +0 -36
  51. package/dist/esm/create-server-fn-plugin/handleEnvOnly.js.map +0 -1
  52. package/dist/esm/create-server-fn-plugin/plugin.d.ts +0 -10
  53. package/dist/esm/create-server-fn-plugin/plugin.js +0 -186
  54. package/dist/esm/create-server-fn-plugin/plugin.js.map +0 -1
  55. package/dist/esm/create-server-fn-plugin/types.d.ts +0 -30
  56. package/dist/esm/create-server-fn-plugin/utils.d.ts +0 -10
  57. package/dist/esm/create-server-fn-plugin/utils.js +0 -19
  58. package/dist/esm/create-server-fn-plugin/utils.js.map +0 -1
  59. package/src/create-server-fn-plugin/handleCreateIsomorphicFn.ts +0 -46
  60. package/src/create-server-fn-plugin/handleCreateMiddleware.ts +0 -45
  61. package/src/create-server-fn-plugin/handleCreateServerFn.ts +0 -145
  62. package/src/create-server-fn-plugin/handleEnvOnly.ts +0 -45
  63. package/src/create-server-fn-plugin/plugin.ts +0 -234
  64. package/src/create-server-fn-plugin/types.ts +0 -34
  65. package/src/create-server-fn-plugin/utils.ts +0 -24
  66. /package/dist/esm/{create-server-fn-plugin → start-compiler-plugin}/handleClientOnlyJSX.d.ts +0 -0
  67. /package/dist/esm/{create-server-fn-plugin → start-compiler-plugin}/handleClientOnlyJSX.js +0 -0
  68. /package/src/{create-server-fn-plugin → start-compiler-plugin}/handleClientOnlyJSX.ts +0 -0
@@ -0,0 +1,314 @@
1
+ import { VIRTUAL_MODULES } from "@tanstack/start-server-core";
2
+ import { VITE_ENVIRONMENT_NAMES, TRANSFORM_ID_REGEX } from "../constants.js";
3
+ import { StartCompiler, LookupKindsPerEnv, detectKindsInCode, KindDetectionPatterns } from "./compiler.js";
4
+ import { cleanId } from "./utils.js";
5
+ function getTransformCodeFilterForEnv(env) {
6
+ const validKinds = LookupKindsPerEnv[env];
7
+ const patterns = [];
8
+ for (const [kind, pattern] of Object.entries(KindDetectionPatterns)) {
9
+ if (validKinds.has(kind)) {
10
+ patterns.push(pattern);
11
+ }
12
+ }
13
+ return patterns;
14
+ }
15
+ const getLookupConfigurationsForEnv = (env, framework) => {
16
+ const commonConfigs = [
17
+ {
18
+ libName: `@tanstack/${framework}-start`,
19
+ rootExport: "createServerFn",
20
+ kind: "Root"
21
+ },
22
+ {
23
+ libName: `@tanstack/${framework}-start`,
24
+ rootExport: "createIsomorphicFn",
25
+ kind: "IsomorphicFn"
26
+ },
27
+ {
28
+ libName: `@tanstack/${framework}-start`,
29
+ rootExport: "createServerOnlyFn",
30
+ kind: "ServerOnlyFn"
31
+ },
32
+ {
33
+ libName: `@tanstack/${framework}-start`,
34
+ rootExport: "createClientOnlyFn",
35
+ kind: "ClientOnlyFn"
36
+ }
37
+ ];
38
+ if (env === "client") {
39
+ return [
40
+ {
41
+ libName: `@tanstack/${framework}-start`,
42
+ rootExport: "createMiddleware",
43
+ kind: "Root"
44
+ },
45
+ {
46
+ libName: `@tanstack/${framework}-start`,
47
+ rootExport: "createStart",
48
+ kind: "Root"
49
+ },
50
+ ...commonConfigs
51
+ ];
52
+ } else {
53
+ return [
54
+ ...commonConfigs,
55
+ {
56
+ libName: `@tanstack/${framework}-router`,
57
+ rootExport: "ClientOnly",
58
+ kind: "ClientOnlyJSX"
59
+ }
60
+ ];
61
+ }
62
+ };
63
+ const SERVER_FN_LOOKUP = "server-fn-module-lookup";
64
+ function resolveViteId(id) {
65
+ return `\0${id}`;
66
+ }
67
+ const validateServerFnIdVirtualModule = `virtual:tanstack-start-validate-server-fn-id`;
68
+ function parseIdQuery(id) {
69
+ if (!id.includes("?")) return { filename: id, query: {} };
70
+ const [filename, rawQuery] = id.split(`?`, 2);
71
+ const query = Object.fromEntries(new URLSearchParams(rawQuery));
72
+ return { filename, query };
73
+ }
74
+ function generateManifestModule(serverFnsById, includeClientReferencedCheck) {
75
+ const manifestEntries = Object.entries(serverFnsById).map(([id, fn]) => {
76
+ const baseEntry = `'${id}': {
77
+ functionName: '${fn.functionName}',
78
+ importer: () => import(${JSON.stringify(fn.extractedFilename)})${includeClientReferencedCheck ? `,
79
+ isClientReferenced: ${fn.isClientReferenced ?? true}` : ""}
80
+ }`;
81
+ return baseEntry;
82
+ }).join(",");
83
+ const getServerFnByIdParams = includeClientReferencedCheck ? "id, opts" : "id";
84
+ const clientReferencedCheck = includeClientReferencedCheck ? `
85
+ // If called from client, only allow client-referenced functions
86
+ if (opts?.fromClient && !serverFnInfo.isClientReferenced) {
87
+ throw new Error('Server function not accessible from client: ' + id)
88
+ }
89
+ ` : "";
90
+ return `
91
+ const manifest = {${manifestEntries}}
92
+
93
+ export async function getServerFnById(${getServerFnByIdParams}) {
94
+ const serverFnInfo = manifest[id]
95
+ if (!serverFnInfo) {
96
+ throw new Error('Server function info not found for ' + id)
97
+ }
98
+ ${clientReferencedCheck}
99
+ const fnModule = await serverFnInfo.importer()
100
+
101
+ if (!fnModule) {
102
+ console.info('serverFnInfo', serverFnInfo)
103
+ throw new Error('Server function module not resolved for ' + id)
104
+ }
105
+
106
+ const action = fnModule[serverFnInfo.functionName]
107
+
108
+ if (!action) {
109
+ console.info('serverFnInfo', serverFnInfo)
110
+ console.info('fnModule', fnModule)
111
+
112
+ throw new Error(
113
+ \`Server function module export not resolved for serverFn ID: \${id}\`,
114
+ )
115
+ }
116
+ return action
117
+ }
118
+ `;
119
+ }
120
+ function startCompilerPlugin(opts) {
121
+ const compilers = {};
122
+ const serverFnsById = {};
123
+ const onServerFnsById = (d) => {
124
+ Object.assign(serverFnsById, d);
125
+ };
126
+ let root = process.cwd();
127
+ const resolvedResolverVirtualImportId = resolveViteId(
128
+ VIRTUAL_MODULES.serverFnResolver
129
+ );
130
+ const ssrEnvName = VITE_ENVIRONMENT_NAMES.server;
131
+ const ssrIsProvider = opts.providerEnvName === ssrEnvName;
132
+ const appliedResolverEnvironments = new Set(
133
+ ssrIsProvider ? [opts.providerEnvName] : [ssrEnvName, opts.providerEnvName]
134
+ );
135
+ function perEnvServerFnPlugin(environment) {
136
+ const transformCodeFilter = getTransformCodeFilterForEnv(environment.type);
137
+ return {
138
+ name: `tanstack-start-core::server-fn:${environment.name}`,
139
+ enforce: "pre",
140
+ applyToEnvironment(env) {
141
+ return env.name === environment.name;
142
+ },
143
+ configResolved(config) {
144
+ root = config.root;
145
+ config.command;
146
+ },
147
+ transform: {
148
+ filter: {
149
+ id: {
150
+ exclude: new RegExp(`${SERVER_FN_LOOKUP}$`),
151
+ include: TRANSFORM_ID_REGEX
152
+ },
153
+ code: {
154
+ include: transformCodeFilter
155
+ }
156
+ },
157
+ async handler(code, id) {
158
+ let compiler = compilers[this.environment.name];
159
+ if (!compiler) {
160
+ const mode = this.environment.mode === "build" ? "build" : "dev";
161
+ compiler = new StartCompiler({
162
+ env: environment.type,
163
+ envName: environment.name,
164
+ root,
165
+ lookupKinds: LookupKindsPerEnv[environment.type],
166
+ lookupConfigurations: getLookupConfigurationsForEnv(
167
+ environment.type,
168
+ opts.framework
169
+ ),
170
+ mode,
171
+ framework: opts.framework,
172
+ providerEnvName: opts.providerEnvName,
173
+ generateFunctionId: opts.generateFunctionId,
174
+ onServerFnsById,
175
+ getKnownServerFns: () => serverFnsById,
176
+ loadModule: async (id2) => {
177
+ if (this.environment.mode === "build") {
178
+ const loaded = await this.load({ id: id2 });
179
+ const code2 = loaded.code ?? "";
180
+ compiler.ingestModule({ code: code2, id: id2 });
181
+ } else if (this.environment.mode === "dev") {
182
+ await this.environment.fetchModule(
183
+ id2 + "?" + SERVER_FN_LOOKUP
184
+ );
185
+ } else {
186
+ throw new Error(
187
+ `could not load module ${id2}: unknown environment mode ${this.environment.mode}`
188
+ );
189
+ }
190
+ },
191
+ resolveId: async (source, importer) => {
192
+ const r = await this.resolve(source, importer);
193
+ if (r) {
194
+ if (!r.external) {
195
+ return cleanId(r.id);
196
+ }
197
+ }
198
+ return null;
199
+ }
200
+ });
201
+ compilers[this.environment.name] = compiler;
202
+ }
203
+ const detectedKinds = detectKindsInCode(code, environment.type);
204
+ const result = await compiler.compile({
205
+ id,
206
+ code,
207
+ detectedKinds
208
+ });
209
+ return result;
210
+ }
211
+ },
212
+ hotUpdate(ctx) {
213
+ const compiler = compilers[this.environment.name];
214
+ ctx.modules.forEach((m) => {
215
+ if (m.id) {
216
+ const deleted = compiler?.invalidateModule(m.id);
217
+ if (deleted) {
218
+ m.importers.forEach((importer) => {
219
+ if (importer.id) {
220
+ compiler?.invalidateModule(importer.id);
221
+ }
222
+ });
223
+ }
224
+ }
225
+ });
226
+ }
227
+ };
228
+ }
229
+ return [
230
+ ...opts.environments.map(perEnvServerFnPlugin),
231
+ {
232
+ name: "tanstack-start-core:capture-server-fn-module-lookup",
233
+ // we only need this plugin in dev mode
234
+ apply: "serve",
235
+ applyToEnvironment(env) {
236
+ return !!opts.environments.find((e) => e.name === env.name);
237
+ },
238
+ transform: {
239
+ filter: {
240
+ id: new RegExp(`${SERVER_FN_LOOKUP}$`)
241
+ },
242
+ handler(code, id) {
243
+ const compiler = compilers[this.environment.name];
244
+ compiler?.ingestModule({ code, id: cleanId(id) });
245
+ }
246
+ }
247
+ },
248
+ // Validate server function ID in dev mode
249
+ {
250
+ name: "tanstack-start-core:validate-server-fn-id",
251
+ apply: "serve",
252
+ load: {
253
+ filter: {
254
+ id: new RegExp(resolveViteId(validateServerFnIdVirtualModule))
255
+ },
256
+ handler(id) {
257
+ const parsed = parseIdQuery(id);
258
+ if (parsed.query.id && serverFnsById[parsed.query.id]) {
259
+ return `export {}`;
260
+ }
261
+ this.error(`Invalid server function ID: ${parsed.query.id}`);
262
+ }
263
+ }
264
+ },
265
+ // Manifest plugin for server environments
266
+ {
267
+ name: "tanstack-start-core:server-fn-resolver",
268
+ enforce: "pre",
269
+ applyToEnvironment: (env) => {
270
+ return appliedResolverEnvironments.has(env.name);
271
+ },
272
+ configResolved(config) {
273
+ root = config.root;
274
+ config.command;
275
+ },
276
+ resolveId: {
277
+ filter: { id: new RegExp(VIRTUAL_MODULES.serverFnResolver) },
278
+ handler() {
279
+ return resolvedResolverVirtualImportId;
280
+ }
281
+ },
282
+ load: {
283
+ filter: { id: new RegExp(resolvedResolverVirtualImportId) },
284
+ handler() {
285
+ if (this.environment.name !== opts.providerEnvName) {
286
+ return `export { getServerFnById } from '@tanstack/start-server-core/server-fn-ssr-caller'`;
287
+ }
288
+ if (this.environment.mode !== "build") {
289
+ const mod = `
290
+ export async function getServerFnById(id) {
291
+ const validateIdImport = ${JSON.stringify(validateServerFnIdVirtualModule)} + '?id=' + id
292
+ await import(/* @vite-ignore */ '/@id/__x00__' + validateIdImport)
293
+ const decoded = Buffer.from(id, 'base64url').toString('utf8')
294
+ const devServerFn = JSON.parse(decoded)
295
+ const mod = await import(/* @vite-ignore */ devServerFn.file)
296
+ return mod[devServerFn.export]
297
+ }
298
+ `;
299
+ return mod;
300
+ }
301
+ const includeClientReferencedCheck = !ssrIsProvider;
302
+ return generateManifestModule(
303
+ serverFnsById,
304
+ includeClientReferencedCheck
305
+ );
306
+ }
307
+ }
308
+ }
309
+ ];
310
+ }
311
+ export {
312
+ startCompilerPlugin
313
+ };
314
+ //# sourceMappingURL=plugin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.js","sources":["../../../src/start-compiler-plugin/plugin.ts"],"sourcesContent":["import { VIRTUAL_MODULES } from '@tanstack/start-server-core'\nimport { TRANSFORM_ID_REGEX, VITE_ENVIRONMENT_NAMES } from '../constants'\nimport {\n KindDetectionPatterns,\n LookupKindsPerEnv,\n StartCompiler,\n detectKindsInCode,\n} from './compiler'\nimport { cleanId } from './utils'\nimport type { CompileStartFrameworkOptions } from '../types'\nimport type { LookupConfig, LookupKind } from './compiler'\nimport type { GenerateFunctionIdFnOptional, ServerFn } from './types'\nimport type { PluginOption } from 'vite'\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 resolveViteId(id: string) {\n return `\\0${id}`\n}\n\nconst validateServerFnIdVirtualModule = `virtual:tanstack-start-validate-server-fn-id`\n\nfunction parseIdQuery(id: string): {\n filename: string\n query: {\n [k: string]: string\n }\n} {\n if (!id.includes('?')) return { filename: id, query: {} }\n const [filename, rawQuery] = id.split(`?`, 2) as [string, string]\n const query = Object.fromEntries(new URLSearchParams(rawQuery))\n return { filename, query }\n}\n\n/**\n * Generates the manifest module code for server functions.\n * @param serverFnsById - Map of function IDs to their server function info\n * @param includeClientReferencedCheck - Whether to include isClientReferenced flag and runtime check.\n * This is needed when SSR is NOT the provider, so server-only-referenced functions in the manifest\n * can be blocked from client HTTP requests.\n */\nfunction generateManifestModule(\n serverFnsById: Record<string, ServerFn>,\n includeClientReferencedCheck: boolean,\n): string {\n const manifestEntries = Object.entries(serverFnsById)\n .map(([id, fn]) => {\n const baseEntry = `'${id}': {\n functionName: '${fn.functionName}',\n importer: () => import(${JSON.stringify(fn.extractedFilename)})${\n includeClientReferencedCheck\n ? `,\n isClientReferenced: ${fn.isClientReferenced ?? true}`\n : ''\n }\n }`\n return baseEntry\n })\n .join(',')\n\n const getServerFnByIdParams = includeClientReferencedCheck ? 'id, opts' : 'id'\n const clientReferencedCheck = includeClientReferencedCheck\n ? `\n // If called from client, only allow client-referenced functions\n if (opts?.fromClient && !serverFnInfo.isClientReferenced) {\n throw new Error('Server function not accessible from client: ' + id)\n }\n`\n : ''\n\n return `\n const manifest = {${manifestEntries}}\n\n export async function getServerFnById(${getServerFnByIdParams}) {\n const serverFnInfo = manifest[id]\n if (!serverFnInfo) {\n throw new Error('Server function info not found for ' + id)\n }\n${clientReferencedCheck}\n const fnModule = await serverFnInfo.importer()\n\n if (!fnModule) {\n console.info('serverFnInfo', serverFnInfo)\n throw new Error('Server function module not resolved for ' + id)\n }\n\n const action = fnModule[serverFnInfo.functionName]\n\n if (!action) {\n console.info('serverFnInfo', serverFnInfo)\n console.info('fnModule', fnModule)\n\n throw new Error(\n \\`Server function module export not resolved for serverFn ID: \\${id}\\`,\n )\n }\n return action\n }\n `\n}\n\nexport interface StartCompilerPluginOptions {\n framework: CompileStartFrameworkOptions\n environments: Array<{ name: string; type: 'client' | 'server' }>\n /**\n * Custom function ID generator (optional).\n */\n generateFunctionId?: GenerateFunctionIdFnOptional\n /**\n * The Vite environment name for the server function provider.\n */\n providerEnvName: string\n}\n\nexport function startCompilerPlugin(\n opts: StartCompilerPluginOptions,\n): PluginOption {\n const compilers: Record<string /* envName */, StartCompiler> = {}\n\n // Shared registry of server functions across all environments\n const serverFnsById: Record<string, ServerFn> = {}\n\n const onServerFnsById = (d: Record<string, ServerFn>) => {\n Object.assign(serverFnsById, d)\n }\n\n let root = process.cwd()\n let command: 'build' | 'serve' = 'build'\n\n const resolvedResolverVirtualImportId = resolveViteId(\n VIRTUAL_MODULES.serverFnResolver,\n )\n\n // Determine which environments need the resolver (getServerFnById)\n // SSR environment always needs the resolver for server-side calls\n // Provider environment needs it for the actual implementation\n const ssrEnvName = VITE_ENVIRONMENT_NAMES.server\n\n // SSR is the provider when the provider environment is the default server environment\n const ssrIsProvider = opts.providerEnvName === ssrEnvName\n\n // Environments that need the resolver: SSR (for server calls) and provider (for implementation)\n const appliedResolverEnvironments = new Set(\n ssrIsProvider ? [opts.providerEnvName] : [ssrEnvName, opts.providerEnvName],\n )\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 configResolved(config) {\n root = config.root\n command = config.command\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 = this.environment.mode === 'build' ? 'build' : 'dev'\n compiler = new StartCompiler({\n env: environment.type,\n envName: environment.name,\n root,\n lookupKinds: LookupKindsPerEnv[environment.type],\n lookupConfigurations: getLookupConfigurationsForEnv(\n environment.type,\n opts.framework,\n ),\n mode,\n framework: opts.framework,\n providerEnvName: opts.providerEnvName,\n generateFunctionId: opts.generateFunctionId,\n onServerFnsById,\n getKnownServerFns: () => serverFnsById,\n loadModule: async (id: string) => {\n if (this.environment.mode === 'build') {\n const loaded = await this.load({ id })\n // Handle modules with no runtime code (e.g., type-only exports).\n // After TypeScript compilation, these become empty modules.\n // Create an empty module info instead of throwing.\n const code = loaded.code ?? ''\n compiler!.ingestModule({ 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 // Detect which kinds are present in this file before parsing\n const detectedKinds = detectKindsInCode(code, environment.type)\n\n const result = await compiler.compile({\n id,\n code,\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 // Validate server function ID in dev mode\n {\n name: 'tanstack-start-core:validate-server-fn-id',\n apply: 'serve',\n load: {\n filter: {\n id: new RegExp(resolveViteId(validateServerFnIdVirtualModule)),\n },\n handler(id) {\n const parsed = parseIdQuery(id)\n if (parsed.query.id && serverFnsById[parsed.query.id]) {\n return `export {}`\n }\n this.error(`Invalid server function ID: ${parsed.query.id}`)\n },\n },\n },\n // Manifest plugin for server environments\n {\n name: 'tanstack-start-core:server-fn-resolver',\n enforce: 'pre',\n applyToEnvironment: (env) => {\n return appliedResolverEnvironments.has(env.name)\n },\n configResolved(config) {\n root = config.root\n command = config.command\n },\n resolveId: {\n filter: { id: new RegExp(VIRTUAL_MODULES.serverFnResolver) },\n handler() {\n return resolvedResolverVirtualImportId\n },\n },\n load: {\n filter: { id: new RegExp(resolvedResolverVirtualImportId) },\n handler() {\n // When SSR is not the provider, SSR callers need to use HTTP to call server functions\n // since they can't directly import from the provider environment\n if (this.environment.name !== opts.providerEnvName) {\n // SSR caller: use HTTP-based getServerFnById\n // This re-exports from the start-server-core package which handles HTTP calls\n return `export { getServerFnById } from '@tanstack/start-server-core/server-fn-ssr-caller'`\n }\n\n if (this.environment.mode !== 'build') {\n const mod = `\n export async function getServerFnById(id) {\n const validateIdImport = ${JSON.stringify(validateServerFnIdVirtualModule)} + '?id=' + id\n await import(/* @vite-ignore */ '/@id/__x00__' + validateIdImport)\n const decoded = Buffer.from(id, 'base64url').toString('utf8')\n const devServerFn = JSON.parse(decoded)\n const mod = await import(/* @vite-ignore */ devServerFn.file)\n return mod[devServerFn.export]\n }\n `\n return mod\n }\n\n // When SSR is the provider, server-only-referenced functions aren't in the manifest,\n // so no isClientReferenced check is needed.\n // When SSR is NOT the provider (custom provider env), server-only-referenced\n // functions ARE in the manifest and need the isClientReferenced check to\n // block direct client HTTP requests to server-only-referenced functions.\n const includeClientReferencedCheck = !ssrIsProvider\n return generateManifestModule(\n serverFnsById,\n includeClientReferencedCheck,\n )\n },\n },\n },\n ]\n}\n"],"names":["id","code"],"mappings":"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"}
@@ -0,0 +1,116 @@
1
+ import { CompileStartFrameworkOptions } from '../types.js';
2
+ import type * as babel from '@babel/core';
3
+ import type * as t from '@babel/types';
4
+ /**
5
+ * Context passed to all plugin handlers during compilation.
6
+ * Contains both read-only input data and mutable state that handlers update.
7
+ */
8
+ export interface CompilationContext {
9
+ readonly ast: t.File;
10
+ readonly code: string;
11
+ readonly id: string;
12
+ readonly env: 'client' | 'server';
13
+ readonly envName: string;
14
+ readonly root: string;
15
+ /** The framework being used (e.g., 'react', 'solid') */
16
+ readonly framework: CompileStartFrameworkOptions;
17
+ /** The Vite environment name for the server function provider */
18
+ readonly providerEnvName: string;
19
+ /** Generate a unique function ID */
20
+ generateFunctionId: GenerateFunctionIdFn;
21
+ /** Get known server functions from previous builds (e.g., client build) */
22
+ getKnownServerFns: () => Record<string, ServerFn>;
23
+ /**
24
+ * Callback when server functions are discovered.
25
+ * Called after each file is compiled with its new functions.
26
+ */
27
+ onServerFnsById: ((d: Record<string, ServerFn>) => void) | undefined;
28
+ }
29
+ /**
30
+ * Batched plugin handler signature.
31
+ * Receives ALL candidates of a specific kind in one call.
32
+ * Mutates the CompilationContext directly.
33
+ */
34
+ export type BatchedPluginHandler<TOpts = unknown> = (candidates: Array<RewriteCandidate>, context: CompilationContext, opts: TOpts) => void;
35
+ /**
36
+ * Info about a method call in the chain, including the call expression path
37
+ * and the path to its first argument (if any).
38
+ */
39
+ export interface MethodCallInfo {
40
+ callPath: babel.NodePath<t.CallExpression>;
41
+ /** Path to the first argument, or null if no arguments */
42
+ firstArgPath: babel.NodePath | null;
43
+ }
44
+ /**
45
+ * Pre-collected method chain paths for a root call expression.
46
+ * This avoids needing to traverse the AST again in handlers.
47
+ */
48
+ export interface MethodChainPaths {
49
+ middleware: MethodCallInfo | null;
50
+ inputValidator: MethodCallInfo | null;
51
+ handler: MethodCallInfo | null;
52
+ server: MethodCallInfo | null;
53
+ client: MethodCallInfo | null;
54
+ }
55
+ export type MethodChainKey = keyof MethodChainPaths;
56
+ /**
57
+ * Information about a candidate that needs to be rewritten.
58
+ */
59
+ export interface RewriteCandidate {
60
+ path: babel.NodePath<t.CallExpression>;
61
+ methodChain: MethodChainPaths;
62
+ }
63
+ /**
64
+ * Represents an extracted server function that has been registered.
65
+ * Used for manifest generation and tracking function metadata.
66
+ */
67
+ export interface ServerFn {
68
+ /** The unique name used to export this function */
69
+ functionName: string;
70
+ /** The unique ID for this function (used in RPC calls) */
71
+ functionId: string;
72
+ /** The filename with query param where the extracted implementation lives */
73
+ extractedFilename: string;
74
+ /** The original source filename */
75
+ filename: string;
76
+ /**
77
+ * True when this function was discovered by the client build.
78
+ * Used to restrict HTTP access to only client-referenced functions.
79
+ */
80
+ isClientReferenced?: boolean;
81
+ }
82
+ /**
83
+ * Function type for generating unique function IDs.
84
+ */
85
+ export type GenerateFunctionIdFn = (opts: {
86
+ filename: string;
87
+ functionName: string;
88
+ extractedFilename: string;
89
+ }) => string;
90
+ /**
91
+ * Optional version that allows returning undefined to use default ID generation.
92
+ */
93
+ export type GenerateFunctionIdFnOptional = (opts: Omit<Parameters<GenerateFunctionIdFn>[0], 'extractedFilename'>) => string | undefined;
94
+ /**
95
+ * Function type for generating replacement code for server functions.
96
+ * Used internally by handleCreateServerFn.
97
+ */
98
+ export type ReplacerFn = (opts: {
99
+ /** Placeholder for the original function expression */
100
+ fn: string;
101
+ /** The filename where the extracted implementation lives */
102
+ extractedFilename: string;
103
+ /** The original source filename */
104
+ filename: string;
105
+ /** The unique function ID */
106
+ functionId: string;
107
+ /** The export name for this function */
108
+ functionName: string;
109
+ /** True if this is the source/provider file (has the implementation) */
110
+ isSourceFn: boolean;
111
+ /**
112
+ * True when this function was already discovered by a previous build (e.g., client).
113
+ * For SSR callers, this means the function is in the manifest.
114
+ */
115
+ isClientReferenced: boolean;
116
+ }) => string;
@@ -0,0 +1,23 @@
1
+ import { default as babel } from '@babel/core';
2
+ import * as t from '@babel/types';
3
+ export declare function codeFrameError(code: string, loc: {
4
+ start: {
5
+ line: number;
6
+ column: number;
7
+ };
8
+ end: {
9
+ line: number;
10
+ column: number;
11
+ };
12
+ }, message: string): Error;
13
+ export declare function cleanId(id: string): string;
14
+ /**
15
+ * Strips a method call by replacing it with its callee object.
16
+ * E.g., `foo().bar()` -> `foo()`
17
+ *
18
+ * This is a common pattern used when removing method calls from chains
19
+ * (e.g., removing .server() from middleware on client, or .inputValidator() on client).
20
+ *
21
+ * @param callPath - The path to the CallExpression to strip
22
+ */
23
+ export declare function stripMethodCall(callPath: babel.NodePath<t.CallExpression>): void;
@@ -0,0 +1,34 @@
1
+ import { codeFrameColumns } from "@babel/code-frame";
2
+ import * as t from "@babel/types";
3
+ function codeFrameError(code, loc, message) {
4
+ const frame = codeFrameColumns(
5
+ code,
6
+ {
7
+ start: loc.start,
8
+ end: loc.end
9
+ },
10
+ {
11
+ highlightCode: true,
12
+ message
13
+ }
14
+ );
15
+ return new Error(frame);
16
+ }
17
+ function cleanId(id) {
18
+ if (id.startsWith("\0")) {
19
+ id = id.slice(1);
20
+ }
21
+ const queryIndex = id.indexOf("?");
22
+ return queryIndex === -1 ? id : id.substring(0, queryIndex);
23
+ }
24
+ function stripMethodCall(callPath) {
25
+ if (t.isMemberExpression(callPath.node.callee)) {
26
+ callPath.replaceWith(callPath.node.callee.object);
27
+ }
28
+ }
29
+ export {
30
+ cleanId,
31
+ codeFrameError,
32
+ stripMethodCall
33
+ };
34
+ //# sourceMappingURL=utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.js","sources":["../../../src/start-compiler-plugin/utils.ts"],"sourcesContent":["import { codeFrameColumns } from '@babel/code-frame'\nimport * as t from '@babel/types'\nimport type babel from '@babel/core'\n\nexport function codeFrameError(\n code: string,\n loc: {\n start: { line: number; column: number }\n end: { line: number; column: number }\n },\n message: string,\n) {\n const frame = codeFrameColumns(\n code,\n {\n start: loc.start,\n end: loc.end,\n },\n {\n highlightCode: true,\n message,\n },\n )\n\n return new Error(frame)\n}\n\nexport function 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/**\n * Strips a method call by replacing it with its callee object.\n * E.g., `foo().bar()` -> `foo()`\n *\n * This is a common pattern used when removing method calls from chains\n * (e.g., removing .server() from middleware on client, or .inputValidator() on client).\n *\n * @param callPath - The path to the CallExpression to strip\n */\nexport function stripMethodCall(\n callPath: babel.NodePath<t.CallExpression>,\n): void {\n if (t.isMemberExpression(callPath.node.callee)) {\n callPath.replaceWith(callPath.node.callee.object)\n }\n}\n"],"names":[],"mappings":";;AAIO,SAAS,eACd,MACA,KAIA,SACA;AACA,QAAM,QAAQ;AAAA,IACZ;AAAA,IACA;AAAA,MACE,OAAO,IAAI;AAAA,MACX,KAAK,IAAI;AAAA,IAAA;AAAA,IAEX;AAAA,MACE,eAAe;AAAA,MACf;AAAA,IAAA;AAAA,EACF;AAGF,SAAO,IAAI,MAAM,KAAK;AACxB;AAEO,SAAS,QAAQ,IAAoB;AAE1C,MAAI,GAAG,WAAW,IAAI,GAAG;AACvB,SAAK,GAAG,MAAM,CAAC;AAAA,EACjB;AACA,QAAM,aAAa,GAAG,QAAQ,GAAG;AACjC,SAAO,eAAe,KAAK,KAAK,GAAG,UAAU,GAAG,UAAU;AAC5D;AAWO,SAAS,gBACd,UACM;AACN,MAAI,EAAE,mBAAmB,SAAS,KAAK,MAAM,GAAG;AAC9C,aAAS,YAAY,SAAS,KAAK,OAAO,MAAM;AAAA,EAClD;AACF;"}
@@ -8,7 +8,6 @@ export interface TanStackStartVitePluginCoreOptions {
8
8
  start: string;
9
9
  };
10
10
  serverFn?: {
11
- directive?: string;
12
11
  ssr?: {
13
12
  getServerFnById?: string;
14
13
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/start-plugin-core",
3
- "version": "1.143.3",
3
+ "version": "1.143.5",
4
4
  "description": "Modern and scalable routing for React applications",
5
5
  "author": "Tanner Linsley",
6
6
  "license": "MIT",
@@ -59,13 +59,12 @@
59
59
  "vitefu": "^1.1.1",
60
60
  "xmlbuilder2": "^4.0.0",
61
61
  "zod": "^3.24.2",
62
- "@tanstack/router-generator": "1.143.3",
63
- "@tanstack/router-core": "1.143.3",
64
- "@tanstack/router-plugin": "1.143.3",
62
+ "@tanstack/router-core": "1.143.4",
63
+ "@tanstack/router-generator": "1.143.4",
64
+ "@tanstack/router-plugin": "1.143.4",
65
65
  "@tanstack/router-utils": "1.141.0",
66
- "@tanstack/server-functions-plugin": "1.142.1",
67
- "@tanstack/start-client-core": "1.143.3",
68
- "@tanstack/start-server-core": "1.143.3"
66
+ "@tanstack/start-client-core": "1.143.4",
67
+ "@tanstack/start-server-core": "1.143.5"
69
68
  },
70
69
  "devDependencies": {
71
70
  "@types/babel__code-frame": "^7.0.6",
package/src/plugin.ts CHANGED
@@ -1,6 +1,4 @@
1
1
  import { joinPaths } from '@tanstack/router-core'
2
- import { VIRTUAL_MODULES } from '@tanstack/start-server-core'
3
- import { TanStackServerFnPlugin } from '@tanstack/server-functions-plugin'
4
2
  import * as vite from 'vite'
5
3
  import { crawlFrameworkPkgs } from 'vitefu'
6
4
  import { join } from 'pathe'
@@ -18,7 +16,7 @@ import {
18
16
  getServerOutputDirectory,
19
17
  } from './output-directory'
20
18
  import { postServerBuild } from './post-server-build'
21
- import { createServerFnPlugin } from './create-server-fn-plugin/plugin'
19
+ import { startCompilerPlugin } from './start-compiler-plugin/plugin'
22
20
  import type {
23
21
  GetConfigFn,
24
22
  ResolvedStartConfig,
@@ -59,8 +57,6 @@ export function TanStackStartVitePluginCore(
59
57
  serverFnProviderEnv,
60
58
  }
61
59
 
62
- const directive = corePluginOpts.serverFn?.directive ?? 'use server'
63
-
64
60
  let startConfig: TanStackStartOutputConfig | null
65
61
  const getConfig: GetConfigFn = () => {
66
62
  if (!resolvedStartConfig.root) {
@@ -356,55 +352,19 @@ export function TanStackStartVitePluginCore(
356
352
  },
357
353
  },
358
354
  },
359
- tanStackStartRouter(startPluginOpts, getConfig, corePluginOpts),
360
- // N.B. Server function plugins must run BEFORE startCompilerPlugin because:
361
- // 1. createServerFnPlugin transforms createServerFn().handler() to inject 'use server' directive
362
- // 2. TanStackServerFnPlugin extracts 'use server' functions and registers them in the manifest
363
- // 3. startCompilerPlugin handles createClientOnlyFn/createServerOnlyFn and runs DCE
364
- // If startCompilerPlugin runs first, DCE may remove server function code before it can be registered
365
- // (e.g., when a server function is only referenced inside a createClientOnlyFn callback)
366
- createServerFnPlugin({
355
+ // Server function plugin handles:
356
+ // 1. Identifying createServerFn().handler() calls
357
+ // 2. Extracting server functions to separate modules
358
+ // 3. Replacing call sites with RPC stubs
359
+ // 4. Generating the server function manifest
360
+ // Also handles createIsomorphicFn, createServerOnlyFn, createClientOnlyFn, createMiddleware
361
+ startCompilerPlugin({
367
362
  framework: corePluginOpts.framework,
368
- directive,
369
363
  environments,
370
- }),
371
- TanStackServerFnPlugin({
372
- // This is the ID that will be available to look up and import
373
- // our server function manifest and resolve its module
374
- manifestVirtualImportId: VIRTUAL_MODULES.serverFnManifest,
375
- directive,
376
364
  generateFunctionId: startPluginOpts?.serverFns?.generateFunctionId,
377
- callers: [
378
- {
379
- envConsumer: 'client',
380
- getRuntimeCode: () =>
381
- `import { createClientRpc } from '@tanstack/${corePluginOpts.framework}-start/client-rpc'`,
382
- replacer: (d) => `createClientRpc('${d.functionId}')`,
383
- envName: VITE_ENVIRONMENT_NAMES.client,
384
- },
385
- {
386
- envConsumer: 'server' as const,
387
- getRuntimeCode: () =>
388
- `import { createSsrRpc } from '@tanstack/${corePluginOpts.framework}-start/ssr-rpc'`,
389
- envName: VITE_ENVIRONMENT_NAMES.server,
390
- replacer: (d: any) =>
391
- // When the function is client-referenced, it's in the manifest - use manifest lookup
392
- // When SSR is NOT the provider, always use manifest lookup (no import() for different env)
393
- // Otherwise, use the importer for functions only referenced on the server when SSR is the provider
394
- d.isClientReferenced || !ssrIsProvider
395
- ? `createSsrRpc('${d.functionId}')`
396
- : `createSsrRpc('${d.functionId}', () => import(${JSON.stringify(d.extractedFilename)}).then(m => m['${d.functionName}']))`,
397
- },
398
- ],
399
- provider: {
400
- getRuntimeCode: () =>
401
- `import { createServerRpc } from '@tanstack/${corePluginOpts.framework}-start/server-rpc'`,
402
- replacer: (d) => `createServerRpc('${d.functionId}', ${d.fn})`,
403
- envName: serverFnProviderEnv,
404
- },
365
+ providerEnvName: serverFnProviderEnv,
405
366
  }),
406
- // Note: startCompilerPlugin functionality (createIsomorphicFn, createServerOnlyFn, createClientOnlyFn)
407
- // is now merged into createServerFnPlugin above
367
+ tanStackStartRouter(startPluginOpts, getConfig, corePluginOpts),
408
368
  loadEnvPlugin(),
409
369
  startManifestPlugin({
410
370
  getClientBundle: () => getBundle(VITE_ENVIRONMENT_NAMES.client),