@tanstack/start-plugin-core 1.143.4 → 1.143.6

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,423 @@
1
+ import { VIRTUAL_MODULES } from '@tanstack/start-server-core'
2
+ import { TRANSFORM_ID_REGEX, VITE_ENVIRONMENT_NAMES } from '../constants'
3
+ import {
4
+ KindDetectionPatterns,
5
+ LookupKindsPerEnv,
6
+ StartCompiler,
7
+ detectKindsInCode,
8
+ } from './compiler'
9
+ import { cleanId } from './utils'
10
+ import type { CompileStartFrameworkOptions } from '../types'
11
+ import type { LookupConfig, LookupKind } from './compiler'
12
+ import type { GenerateFunctionIdFnOptional, ServerFn } from './types'
13
+ import type { PluginOption } from 'vite'
14
+
15
+ // Derive transform code filter from KindDetectionPatterns (single source of truth)
16
+ function getTransformCodeFilterForEnv(env: 'client' | 'server'): Array<RegExp> {
17
+ const validKinds = LookupKindsPerEnv[env]
18
+ const patterns: Array<RegExp> = []
19
+ for (const [kind, pattern] of Object.entries(KindDetectionPatterns) as Array<
20
+ [LookupKind, RegExp]
21
+ >) {
22
+ if (validKinds.has(kind)) {
23
+ patterns.push(pattern)
24
+ }
25
+ }
26
+ return patterns
27
+ }
28
+
29
+ const getLookupConfigurationsForEnv = (
30
+ env: 'client' | 'server',
31
+ framework: CompileStartFrameworkOptions,
32
+ ): Array<LookupConfig> => {
33
+ // Common configs for all environments
34
+ const commonConfigs: Array<LookupConfig> = [
35
+ {
36
+ libName: `@tanstack/${framework}-start`,
37
+ rootExport: 'createServerFn',
38
+ kind: 'Root',
39
+ },
40
+ {
41
+ libName: `@tanstack/${framework}-start`,
42
+ rootExport: 'createIsomorphicFn',
43
+ kind: 'IsomorphicFn',
44
+ },
45
+ {
46
+ libName: `@tanstack/${framework}-start`,
47
+ rootExport: 'createServerOnlyFn',
48
+ kind: 'ServerOnlyFn',
49
+ },
50
+ {
51
+ libName: `@tanstack/${framework}-start`,
52
+ rootExport: 'createClientOnlyFn',
53
+ kind: 'ClientOnlyFn',
54
+ },
55
+ ]
56
+
57
+ if (env === 'client') {
58
+ return [
59
+ {
60
+ libName: `@tanstack/${framework}-start`,
61
+ rootExport: 'createMiddleware',
62
+ kind: 'Root',
63
+ },
64
+ {
65
+ libName: `@tanstack/${framework}-start`,
66
+ rootExport: 'createStart',
67
+ kind: 'Root',
68
+ },
69
+ ...commonConfigs,
70
+ ]
71
+ } else {
72
+ // Server-only: add ClientOnly JSX component lookup
73
+ return [
74
+ ...commonConfigs,
75
+ {
76
+ libName: `@tanstack/${framework}-router`,
77
+ rootExport: 'ClientOnly',
78
+ kind: 'ClientOnlyJSX',
79
+ },
80
+ ]
81
+ }
82
+ }
83
+ const SERVER_FN_LOOKUP = 'server-fn-module-lookup'
84
+
85
+ function resolveViteId(id: string) {
86
+ return `\0${id}`
87
+ }
88
+
89
+ const validateServerFnIdVirtualModule = `virtual:tanstack-start-validate-server-fn-id`
90
+
91
+ function parseIdQuery(id: string): {
92
+ filename: string
93
+ query: {
94
+ [k: string]: string
95
+ }
96
+ } {
97
+ if (!id.includes('?')) return { filename: id, query: {} }
98
+ const [filename, rawQuery] = id.split(`?`, 2) as [string, string]
99
+ const query = Object.fromEntries(new URLSearchParams(rawQuery))
100
+ return { filename, query }
101
+ }
102
+
103
+ /**
104
+ * Generates the manifest module code for server functions.
105
+ * @param serverFnsById - Map of function IDs to their server function info
106
+ * @param includeClientReferencedCheck - Whether to include isClientReferenced flag and runtime check.
107
+ * This is needed when SSR is NOT the provider, so server-only-referenced functions in the manifest
108
+ * can be blocked from client HTTP requests.
109
+ */
110
+ function generateManifestModule(
111
+ serverFnsById: Record<string, ServerFn>,
112
+ includeClientReferencedCheck: boolean,
113
+ ): string {
114
+ const manifestEntries = Object.entries(serverFnsById)
115
+ .map(([id, fn]) => {
116
+ const baseEntry = `'${id}': {
117
+ functionName: '${fn.functionName}',
118
+ importer: () => import(${JSON.stringify(fn.extractedFilename)})${
119
+ includeClientReferencedCheck
120
+ ? `,
121
+ isClientReferenced: ${fn.isClientReferenced ?? true}`
122
+ : ''
123
+ }
124
+ }`
125
+ return baseEntry
126
+ })
127
+ .join(',')
128
+
129
+ const getServerFnByIdParams = includeClientReferencedCheck ? 'id, opts' : 'id'
130
+ const clientReferencedCheck = includeClientReferencedCheck
131
+ ? `
132
+ // If called from client, only allow client-referenced functions
133
+ if (opts?.fromClient && !serverFnInfo.isClientReferenced) {
134
+ throw new Error('Server function not accessible from client: ' + id)
135
+ }
136
+ `
137
+ : ''
138
+
139
+ return `
140
+ const manifest = {${manifestEntries}}
141
+
142
+ export async function getServerFnById(${getServerFnByIdParams}) {
143
+ const serverFnInfo = manifest[id]
144
+ if (!serverFnInfo) {
145
+ throw new Error('Server function info not found for ' + id)
146
+ }
147
+ ${clientReferencedCheck}
148
+ const fnModule = await serverFnInfo.importer()
149
+
150
+ if (!fnModule) {
151
+ console.info('serverFnInfo', serverFnInfo)
152
+ throw new Error('Server function module not resolved for ' + id)
153
+ }
154
+
155
+ const action = fnModule[serverFnInfo.functionName]
156
+
157
+ if (!action) {
158
+ console.info('serverFnInfo', serverFnInfo)
159
+ console.info('fnModule', fnModule)
160
+
161
+ throw new Error(
162
+ \`Server function module export not resolved for serverFn ID: \${id}\`,
163
+ )
164
+ }
165
+ return action
166
+ }
167
+ `
168
+ }
169
+
170
+ export interface StartCompilerPluginOptions {
171
+ framework: CompileStartFrameworkOptions
172
+ environments: Array<{ name: string; type: 'client' | 'server' }>
173
+ /**
174
+ * Custom function ID generator (optional).
175
+ */
176
+ generateFunctionId?: GenerateFunctionIdFnOptional
177
+ /**
178
+ * The Vite environment name for the server function provider.
179
+ */
180
+ providerEnvName: string
181
+ }
182
+
183
+ export function startCompilerPlugin(
184
+ opts: StartCompilerPluginOptions,
185
+ ): PluginOption {
186
+ const compilers: Record<string /* envName */, StartCompiler> = {}
187
+
188
+ // Shared registry of server functions across all environments
189
+ const serverFnsById: Record<string, ServerFn> = {}
190
+
191
+ const onServerFnsById = (d: Record<string, ServerFn>) => {
192
+ Object.assign(serverFnsById, d)
193
+ }
194
+
195
+ let root = process.cwd()
196
+ let command: 'build' | 'serve' = 'build'
197
+
198
+ const resolvedResolverVirtualImportId = resolveViteId(
199
+ VIRTUAL_MODULES.serverFnResolver,
200
+ )
201
+
202
+ // Determine which environments need the resolver (getServerFnById)
203
+ // SSR environment always needs the resolver for server-side calls
204
+ // Provider environment needs it for the actual implementation
205
+ const ssrEnvName = VITE_ENVIRONMENT_NAMES.server
206
+
207
+ // SSR is the provider when the provider environment is the default server environment
208
+ const ssrIsProvider = opts.providerEnvName === ssrEnvName
209
+
210
+ // Environments that need the resolver: SSR (for server calls) and provider (for implementation)
211
+ const appliedResolverEnvironments = new Set(
212
+ ssrIsProvider ? [opts.providerEnvName] : [ssrEnvName, opts.providerEnvName],
213
+ )
214
+
215
+ function perEnvServerFnPlugin(environment: {
216
+ name: string
217
+ type: 'client' | 'server'
218
+ }): PluginOption {
219
+ // Derive transform code filter from KindDetectionPatterns (single source of truth)
220
+ const transformCodeFilter = getTransformCodeFilterForEnv(environment.type)
221
+
222
+ return {
223
+ name: `tanstack-start-core::server-fn:${environment.name}`,
224
+ enforce: 'pre',
225
+ applyToEnvironment(env) {
226
+ return env.name === environment.name
227
+ },
228
+ configResolved(config) {
229
+ root = config.root
230
+ command = config.command
231
+ },
232
+ transform: {
233
+ filter: {
234
+ id: {
235
+ exclude: new RegExp(`${SERVER_FN_LOOKUP}$`),
236
+ include: TRANSFORM_ID_REGEX,
237
+ },
238
+ code: {
239
+ include: transformCodeFilter,
240
+ },
241
+ },
242
+ async handler(code, id) {
243
+ let compiler = compilers[this.environment.name]
244
+ if (!compiler) {
245
+ // Default to 'dev' mode for unknown environments (conservative: no caching)
246
+ const mode = this.environment.mode === 'build' ? 'build' : 'dev'
247
+ compiler = new StartCompiler({
248
+ env: environment.type,
249
+ envName: environment.name,
250
+ root,
251
+ lookupKinds: LookupKindsPerEnv[environment.type],
252
+ lookupConfigurations: getLookupConfigurationsForEnv(
253
+ environment.type,
254
+ opts.framework,
255
+ ),
256
+ mode,
257
+ framework: opts.framework,
258
+ providerEnvName: opts.providerEnvName,
259
+ generateFunctionId: opts.generateFunctionId,
260
+ onServerFnsById,
261
+ getKnownServerFns: () => serverFnsById,
262
+ loadModule: async (id: string) => {
263
+ if (this.environment.mode === 'build') {
264
+ const loaded = await this.load({ id })
265
+ // Handle modules with no runtime code (e.g., type-only exports).
266
+ // After TypeScript compilation, these become empty modules.
267
+ // Create an empty module info instead of throwing.
268
+ const code = loaded.code ?? ''
269
+ compiler!.ingestModule({ code, id })
270
+ } else if (this.environment.mode === 'dev') {
271
+ /**
272
+ * in dev, vite does not return code from `ctx.load()`
273
+ * so instead, we need to take a different approach
274
+ * we must force vite to load the module and run it through the vite plugin pipeline
275
+ * we can do this by using the `fetchModule` method
276
+ * the `captureServerFnModuleLookupPlugin` captures the module code via its transform hook and invokes analyzeModuleAST
277
+ */
278
+ await this.environment.fetchModule(
279
+ id + '?' + SERVER_FN_LOOKUP,
280
+ )
281
+ } else {
282
+ throw new Error(
283
+ `could not load module ${id}: unknown environment mode ${this.environment.mode}`,
284
+ )
285
+ }
286
+ },
287
+ resolveId: async (source: string, importer?: string) => {
288
+ const r = await this.resolve(source, importer)
289
+ if (r) {
290
+ if (!r.external) {
291
+ return cleanId(r.id)
292
+ }
293
+ }
294
+ return null
295
+ },
296
+ })
297
+ compilers[this.environment.name] = compiler
298
+ }
299
+
300
+ // Detect which kinds are present in this file before parsing
301
+ const detectedKinds = detectKindsInCode(code, environment.type)
302
+
303
+ const result = await compiler.compile({
304
+ id,
305
+ code,
306
+ detectedKinds,
307
+ })
308
+ return result
309
+ },
310
+ },
311
+
312
+ hotUpdate(ctx) {
313
+ const compiler = compilers[this.environment.name]
314
+
315
+ ctx.modules.forEach((m) => {
316
+ if (m.id) {
317
+ const deleted = compiler?.invalidateModule(m.id)
318
+ if (deleted) {
319
+ m.importers.forEach((importer) => {
320
+ if (importer.id) {
321
+ compiler?.invalidateModule(importer.id)
322
+ }
323
+ })
324
+ }
325
+ }
326
+ })
327
+ },
328
+ }
329
+ }
330
+
331
+ return [
332
+ ...opts.environments.map(perEnvServerFnPlugin),
333
+ {
334
+ name: 'tanstack-start-core:capture-server-fn-module-lookup',
335
+ // we only need this plugin in dev mode
336
+ apply: 'serve',
337
+ applyToEnvironment(env) {
338
+ return !!opts.environments.find((e) => e.name === env.name)
339
+ },
340
+ transform: {
341
+ filter: {
342
+ id: new RegExp(`${SERVER_FN_LOOKUP}$`),
343
+ },
344
+ handler(code, id) {
345
+ const compiler = compilers[this.environment.name]
346
+ compiler?.ingestModule({ code, id: cleanId(id) })
347
+ },
348
+ },
349
+ },
350
+ // Validate server function ID in dev mode
351
+ {
352
+ name: 'tanstack-start-core:validate-server-fn-id',
353
+ apply: 'serve',
354
+ load: {
355
+ filter: {
356
+ id: new RegExp(resolveViteId(validateServerFnIdVirtualModule)),
357
+ },
358
+ handler(id) {
359
+ const parsed = parseIdQuery(id)
360
+ if (parsed.query.id && serverFnsById[parsed.query.id]) {
361
+ return `export {}`
362
+ }
363
+ this.error(`Invalid server function ID: ${parsed.query.id}`)
364
+ },
365
+ },
366
+ },
367
+ // Manifest plugin for server environments
368
+ {
369
+ name: 'tanstack-start-core:server-fn-resolver',
370
+ enforce: 'pre',
371
+ applyToEnvironment: (env) => {
372
+ return appliedResolverEnvironments.has(env.name)
373
+ },
374
+ configResolved(config) {
375
+ root = config.root
376
+ command = config.command
377
+ },
378
+ resolveId: {
379
+ filter: { id: new RegExp(VIRTUAL_MODULES.serverFnResolver) },
380
+ handler() {
381
+ return resolvedResolverVirtualImportId
382
+ },
383
+ },
384
+ load: {
385
+ filter: { id: new RegExp(resolvedResolverVirtualImportId) },
386
+ handler() {
387
+ // When SSR is not the provider, SSR callers need to use HTTP to call server functions
388
+ // since they can't directly import from the provider environment
389
+ if (this.environment.name !== opts.providerEnvName) {
390
+ // SSR caller: use HTTP-based getServerFnById
391
+ // This re-exports from the start-server-core package which handles HTTP calls
392
+ return `export { getServerFnById } from '@tanstack/start-server-core/server-fn-ssr-caller'`
393
+ }
394
+
395
+ if (this.environment.mode !== 'build') {
396
+ const mod = `
397
+ export async function getServerFnById(id) {
398
+ const validateIdImport = ${JSON.stringify(validateServerFnIdVirtualModule)} + '?id=' + id
399
+ await import(/* @vite-ignore */ '/@id/__x00__' + validateIdImport)
400
+ const decoded = Buffer.from(id, 'base64url').toString('utf8')
401
+ const devServerFn = JSON.parse(decoded)
402
+ const mod = await import(/* @vite-ignore */ devServerFn.file)
403
+ return mod[devServerFn.export]
404
+ }
405
+ `
406
+ return mod
407
+ }
408
+
409
+ // When SSR is the provider, server-only-referenced functions aren't in the manifest,
410
+ // so no isClientReferenced check is needed.
411
+ // When SSR is NOT the provider (custom provider env), server-only-referenced
412
+ // functions ARE in the manifest and need the isClientReferenced check to
413
+ // block direct client HTTP requests to server-only-referenced functions.
414
+ const includeClientReferencedCheck = !ssrIsProvider
415
+ return generateManifestModule(
416
+ serverFnsById,
417
+ includeClientReferencedCheck,
418
+ )
419
+ },
420
+ },
421
+ },
422
+ ]
423
+ }
@@ -0,0 +1,133 @@
1
+ import type * as babel from '@babel/core'
2
+ import type * as t from '@babel/types'
3
+ import type { CompileStartFrameworkOptions } from '../types'
4
+
5
+ /**
6
+ * Context passed to all plugin handlers during compilation.
7
+ * Contains both read-only input data and mutable state that handlers update.
8
+ */
9
+ export interface CompilationContext {
10
+ readonly ast: t.File
11
+ readonly code: string
12
+ readonly id: string
13
+ readonly env: 'client' | 'server'
14
+ readonly envName: string
15
+ readonly root: string
16
+ /** The framework being used (e.g., 'react', 'solid') */
17
+ readonly framework: CompileStartFrameworkOptions
18
+ /** The Vite environment name for the server function provider */
19
+ readonly providerEnvName: string
20
+
21
+ /** Generate a unique function ID */
22
+ generateFunctionId: GenerateFunctionIdFn
23
+ /** Get known server functions from previous builds (e.g., client build) */
24
+ getKnownServerFns: () => Record<string, ServerFn>
25
+
26
+ /**
27
+ * Callback when server functions are discovered.
28
+ * Called after each file is compiled with its new functions.
29
+ */
30
+ onServerFnsById: ((d: Record<string, ServerFn>) => void) | undefined
31
+ }
32
+ /**
33
+ * Batched plugin handler signature.
34
+ * Receives ALL candidates of a specific kind in one call.
35
+ * Mutates the CompilationContext directly.
36
+ */
37
+ export type BatchedPluginHandler<TOpts = unknown> = (
38
+ candidates: Array<RewriteCandidate>,
39
+ context: CompilationContext,
40
+ opts: TOpts,
41
+ ) => void
42
+
43
+ /**
44
+ * Info about a method call in the chain, including the call expression path
45
+ * and the path to its first argument (if any).
46
+ */
47
+ export interface MethodCallInfo {
48
+ callPath: babel.NodePath<t.CallExpression>
49
+ /** Path to the first argument, or null if no arguments */
50
+ firstArgPath: babel.NodePath | null
51
+ }
52
+
53
+ /**
54
+ * Pre-collected method chain paths for a root call expression.
55
+ * This avoids needing to traverse the AST again in handlers.
56
+ */
57
+ export interface MethodChainPaths {
58
+ middleware: MethodCallInfo | null
59
+ inputValidator: MethodCallInfo | null
60
+ handler: MethodCallInfo | null
61
+ server: MethodCallInfo | null
62
+ client: MethodCallInfo | null
63
+ }
64
+
65
+ export type MethodChainKey = keyof MethodChainPaths
66
+
67
+ /**
68
+ * Information about a candidate that needs to be rewritten.
69
+ */
70
+ export interface RewriteCandidate {
71
+ path: babel.NodePath<t.CallExpression>
72
+ methodChain: MethodChainPaths
73
+ }
74
+
75
+ /**
76
+ * Represents an extracted server function that has been registered.
77
+ * Used for manifest generation and tracking function metadata.
78
+ */
79
+ export interface ServerFn {
80
+ /** The unique name used to export this function */
81
+ functionName: string
82
+ /** The unique ID for this function (used in RPC calls) */
83
+ functionId: string
84
+ /** The filename with query param where the extracted implementation lives */
85
+ extractedFilename: string
86
+ /** The original source filename */
87
+ filename: string
88
+ /**
89
+ * True when this function was discovered by the client build.
90
+ * Used to restrict HTTP access to only client-referenced functions.
91
+ */
92
+ isClientReferenced?: boolean
93
+ }
94
+
95
+ /**
96
+ * Function type for generating unique function IDs.
97
+ */
98
+ export type GenerateFunctionIdFn = (opts: {
99
+ filename: string
100
+ functionName: string
101
+ extractedFilename: string
102
+ }) => string
103
+
104
+ /**
105
+ * Optional version that allows returning undefined to use default ID generation.
106
+ */
107
+ export type GenerateFunctionIdFnOptional = (
108
+ opts: Omit<Parameters<GenerateFunctionIdFn>[0], 'extractedFilename'>,
109
+ ) => string | undefined
110
+
111
+ /**
112
+ * Function type for generating replacement code for server functions.
113
+ * Used internally by handleCreateServerFn.
114
+ */
115
+ export type ReplacerFn = (opts: {
116
+ /** Placeholder for the original function expression */
117
+ fn: string
118
+ /** The filename where the extracted implementation lives */
119
+ extractedFilename: string
120
+ /** The original source filename */
121
+ filename: string
122
+ /** The unique function ID */
123
+ functionId: string
124
+ /** The export name for this function */
125
+ functionName: string
126
+ /** True if this is the source/provider file (has the implementation) */
127
+ isSourceFn: boolean
128
+ /**
129
+ * True when this function was already discovered by a previous build (e.g., client).
130
+ * For SSR callers, this means the function is in the manifest.
131
+ */
132
+ isClientReferenced: boolean
133
+ }) => string
@@ -0,0 +1,52 @@
1
+ import { codeFrameColumns } from '@babel/code-frame'
2
+ import * as t from '@babel/types'
3
+ import type babel from '@babel/core'
4
+
5
+ export function codeFrameError(
6
+ code: string,
7
+ loc: {
8
+ start: { line: number; column: number }
9
+ end: { line: number; column: number }
10
+ },
11
+ message: string,
12
+ ) {
13
+ const frame = codeFrameColumns(
14
+ code,
15
+ {
16
+ start: loc.start,
17
+ end: loc.end,
18
+ },
19
+ {
20
+ highlightCode: true,
21
+ message,
22
+ },
23
+ )
24
+
25
+ return new Error(frame)
26
+ }
27
+
28
+ export function cleanId(id: string): string {
29
+ // Remove null byte prefix used by Vite/Rollup for virtual modules
30
+ if (id.startsWith('\0')) {
31
+ id = id.slice(1)
32
+ }
33
+ const queryIndex = id.indexOf('?')
34
+ return queryIndex === -1 ? id : id.substring(0, queryIndex)
35
+ }
36
+
37
+ /**
38
+ * Strips a method call by replacing it with its callee object.
39
+ * E.g., `foo().bar()` -> `foo()`
40
+ *
41
+ * This is a common pattern used when removing method calls from chains
42
+ * (e.g., removing .server() from middleware on client, or .inputValidator() on client).
43
+ *
44
+ * @param callPath - The path to the CallExpression to strip
45
+ */
46
+ export function stripMethodCall(
47
+ callPath: babel.NodePath<t.CallExpression>,
48
+ ): void {
49
+ if (t.isMemberExpression(callPath.node.callee)) {
50
+ callPath.replaceWith(callPath.node.callee.object)
51
+ }
52
+ }
package/src/types.ts CHANGED
@@ -10,7 +10,6 @@ export interface TanStackStartVitePluginCoreOptions {
10
10
  start: string
11
11
  }
12
12
  serverFn?: {
13
- directive?: string
14
13
  ssr?: {
15
14
  getServerFnById?: string
16
15
  }