@tanstack/start-plugin-core 1.132.0-alpha.2 → 1.132.0-alpha.21

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 (63) hide show
  1. package/dist/esm/create-server-fn-plugin/compiler.d.ts +61 -0
  2. package/dist/esm/create-server-fn-plugin/compiler.js +336 -0
  3. package/dist/esm/create-server-fn-plugin/compiler.js.map +1 -0
  4. package/dist/esm/create-server-fn-plugin/handleCreateServerFn.d.ts +6 -0
  5. package/dist/esm/create-server-fn-plugin/handleCreateServerFn.js +85 -0
  6. package/dist/esm/create-server-fn-plugin/handleCreateServerFn.js.map +1 -0
  7. package/dist/esm/create-server-fn-plugin/plugin.d.ts +3 -0
  8. package/dist/esm/create-server-fn-plugin/plugin.js +113 -0
  9. package/dist/esm/create-server-fn-plugin/plugin.js.map +1 -0
  10. package/dist/esm/output-directory.js +5 -2
  11. package/dist/esm/output-directory.js.map +1 -1
  12. package/dist/esm/plugin.d.ts +1 -6
  13. package/dist/esm/plugin.js +14 -61
  14. package/dist/esm/plugin.js.map +1 -1
  15. package/dist/esm/schema.d.ts +20 -28
  16. package/dist/esm/schema.js +10 -14
  17. package/dist/esm/schema.js.map +1 -1
  18. package/dist/esm/start-compiler-plugin/compilers.d.ts +15 -0
  19. package/dist/esm/start-compiler-plugin/compilers.js +131 -0
  20. package/dist/esm/start-compiler-plugin/compilers.js.map +1 -0
  21. package/dist/esm/start-compiler-plugin/constants.d.ts +1 -0
  22. package/dist/esm/start-compiler-plugin/constants.js +13 -0
  23. package/dist/esm/start-compiler-plugin/constants.js.map +1 -0
  24. package/dist/esm/start-compiler-plugin/envOnly.d.ts +5 -0
  25. package/dist/esm/start-compiler-plugin/envOnly.js +41 -0
  26. package/dist/esm/start-compiler-plugin/envOnly.js.map +1 -0
  27. package/dist/esm/start-compiler-plugin/isomorphicFn.d.ts +4 -0
  28. package/dist/esm/start-compiler-plugin/isomorphicFn.js +49 -0
  29. package/dist/esm/start-compiler-plugin/isomorphicFn.js.map +1 -0
  30. package/dist/esm/start-compiler-plugin/middleware.d.ts +4 -0
  31. package/dist/esm/start-compiler-plugin/middleware.js +51 -0
  32. package/dist/esm/start-compiler-plugin/middleware.js.map +1 -0
  33. package/dist/esm/{start-compiler-plugin.d.ts → start-compiler-plugin/plugin.d.ts} +1 -8
  34. package/dist/esm/start-compiler-plugin/plugin.js +96 -0
  35. package/dist/esm/start-compiler-plugin/plugin.js.map +1 -0
  36. package/dist/esm/start-compiler-plugin/serverFileRoute.d.ts +4 -0
  37. package/dist/esm/start-compiler-plugin/serverFileRoute.js +38 -0
  38. package/dist/esm/start-compiler-plugin/serverFileRoute.js.map +1 -0
  39. package/dist/esm/start-compiler-plugin/utils.d.ts +13 -0
  40. package/dist/esm/start-compiler-plugin/utils.js +30 -0
  41. package/dist/esm/start-compiler-plugin/utils.js.map +1 -0
  42. package/package.json +8 -8
  43. package/src/create-server-fn-plugin/compiler.ts +456 -0
  44. package/src/create-server-fn-plugin/handleCreateServerFn.ts +153 -0
  45. package/src/create-server-fn-plugin/plugin.ts +138 -0
  46. package/src/output-directory.ts +13 -6
  47. package/src/plugin.ts +18 -79
  48. package/src/schema.ts +10 -16
  49. package/src/start-compiler-plugin/compilers.ts +195 -0
  50. package/src/start-compiler-plugin/constants.ts +9 -0
  51. package/src/start-compiler-plugin/envOnly.ts +58 -0
  52. package/src/start-compiler-plugin/isomorphicFn.ts +78 -0
  53. package/src/start-compiler-plugin/middleware.ts +79 -0
  54. package/src/start-compiler-plugin/plugin.ts +122 -0
  55. package/src/start-compiler-plugin/serverFileRoute.ts +59 -0
  56. package/src/start-compiler-plugin/utils.ts +41 -0
  57. package/dist/esm/compilers.d.ts +0 -21
  58. package/dist/esm/compilers.js +0 -395
  59. package/dist/esm/compilers.js.map +0 -1
  60. package/dist/esm/start-compiler-plugin.js +0 -78
  61. package/dist/esm/start-compiler-plugin.js.map +0 -1
  62. package/src/compilers.ts +0 -659
  63. package/src/start-compiler-plugin.ts +0 -115
@@ -0,0 +1,138 @@
1
+ import { VITE_ENVIRONMENT_NAMES } from '../constants'
2
+ import { ServerFnCompiler } from './compiler'
3
+ import type { ViteEnvironmentNames } from '../constants'
4
+ import type { PluginOption } from 'vite'
5
+ import type { CompileStartFrameworkOptions } from '../start-compiler-plugin/compilers'
6
+
7
+ function cleanId(id: string): string {
8
+ return id.split('?')[0]!
9
+ }
10
+
11
+ export function createServerFnPlugin(
12
+ framework: CompileStartFrameworkOptions,
13
+ ): PluginOption {
14
+ const libName = `@tanstack/${framework}-start`
15
+ const rootExport = 'createServerFn'
16
+
17
+ const SERVER_FN_LOOKUP = 'server-fn-module-lookup'
18
+
19
+ const compilers: Partial<Record<ViteEnvironmentNames, ServerFnCompiler>> = {}
20
+ return [
21
+ {
22
+ name: 'tanstack-start-core:capture-server-fn-module-lookup',
23
+ // we only need this plugin in dev mode
24
+ apply: 'serve',
25
+ applyToEnvironment(env) {
26
+ return [
27
+ VITE_ENVIRONMENT_NAMES.client,
28
+ VITE_ENVIRONMENT_NAMES.server,
29
+ ].includes(env.name as ViteEnvironmentNames)
30
+ },
31
+ transform: {
32
+ filter: {
33
+ id: new RegExp(`${SERVER_FN_LOOKUP}$`),
34
+ },
35
+ handler(code, id) {
36
+ const compiler =
37
+ compilers[this.environment.name as ViteEnvironmentNames]
38
+ compiler?.ingestModule({ code, id: cleanId(id) })
39
+ },
40
+ },
41
+ },
42
+ {
43
+ name: 'tanstack-start-core::server-fn',
44
+ enforce: 'pre',
45
+
46
+ applyToEnvironment(env) {
47
+ return [
48
+ VITE_ENVIRONMENT_NAMES.client,
49
+ VITE_ENVIRONMENT_NAMES.server,
50
+ ].includes(env.name as ViteEnvironmentNames)
51
+ },
52
+ transform: {
53
+ filter: {
54
+ id: {
55
+ exclude: new RegExp(`${SERVER_FN_LOOKUP}$`),
56
+ },
57
+ code: {
58
+ // only scan files that mention `.handler(`
59
+ include: [/\.handler\(/],
60
+ },
61
+ },
62
+ async handler(code, id) {
63
+ let compiler =
64
+ compilers[this.environment.name as ViteEnvironmentNames]
65
+ if (!compiler) {
66
+ const env =
67
+ this.environment.name === VITE_ENVIRONMENT_NAMES.client
68
+ ? 'client'
69
+ : this.environment.name === VITE_ENVIRONMENT_NAMES.server
70
+ ? 'server'
71
+ : (() => {
72
+ throw new Error(
73
+ `Environment ${this.environment.name} not configured`,
74
+ )
75
+ })()
76
+
77
+ compiler = new ServerFnCompiler({
78
+ env,
79
+ libName,
80
+ rootExport,
81
+ loadModule: async (id: string) => {
82
+ if (this.environment.mode === 'build') {
83
+ const loaded = await this.load({ id })
84
+ if (!loaded.code) {
85
+ throw new Error(`could not load module ${id}`)
86
+ }
87
+ compiler!.ingestModule({ code: loaded.code, id })
88
+ } else if (this.environment.mode === 'dev') {
89
+ /**
90
+ * in dev, vite does not return code from `ctx.load()`
91
+ * so instead, we need to take a different approach
92
+ * we must force vite to load the module and run it through the vite plugin pipeline
93
+ * we can do this by using the `fetchModule` method
94
+ * the `captureServerFnModuleLookupPlugin` captures the module code via its transform hook and invokes analyzeModuleAST
95
+ */
96
+ await this.environment.fetchModule(
97
+ id + '?' + SERVER_FN_LOOKUP,
98
+ )
99
+ } else {
100
+ throw new Error(
101
+ `could not load module ${id}: unknown environment mode ${this.environment.mode}`,
102
+ )
103
+ }
104
+ },
105
+ resolveId: async (source: string, importer?: string) => {
106
+ const r = await this.resolve(source, importer)
107
+ return r ? cleanId(r.id) : null
108
+ },
109
+ })
110
+ compilers[this.environment.name as ViteEnvironmentNames] = compiler
111
+ }
112
+
113
+ id = cleanId(id)
114
+ const result = await compiler.compile({ id, code })
115
+ return result
116
+ },
117
+ },
118
+
119
+ hotUpdate(ctx) {
120
+ const compiler =
121
+ compilers[this.environment.name as ViteEnvironmentNames]
122
+
123
+ ctx.modules.forEach((m) => {
124
+ if (m.id) {
125
+ const deleted = compiler?.invalidateModule(m.id)
126
+ if (deleted) {
127
+ m.importers.forEach((importer) => {
128
+ if (importer.id) {
129
+ compiler?.invalidateModule(importer.id)
130
+ }
131
+ })
132
+ }
133
+ }
134
+ })
135
+ },
136
+ },
137
+ ]
138
+ }
@@ -1,18 +1,25 @@
1
1
  import { join } from 'pathe'
2
2
  import { VITE_ENVIRONMENT_NAMES } from './constants'
3
+ import type { ViteEnvironmentNames } from './constants'
3
4
  import type * as vite from 'vite'
4
5
 
5
6
  export function getClientOutputDirectory(userConfig: vite.UserConfig) {
6
- return (
7
- userConfig.environments?.[VITE_ENVIRONMENT_NAMES.client]?.build?.outDir ??
8
- join(getServerOutputDirectory(userConfig), 'public')
9
- )
7
+ return getOutputDirectory(userConfig, VITE_ENVIRONMENT_NAMES.client, 'client')
10
8
  }
11
9
 
12
10
  export function getServerOutputDirectory(userConfig: vite.UserConfig) {
11
+ return getOutputDirectory(userConfig, VITE_ENVIRONMENT_NAMES.server, 'server')
12
+ }
13
+
14
+ function getOutputDirectory(
15
+ userConfig: vite.UserConfig,
16
+ environmentName: ViteEnvironmentNames,
17
+ directoryName: string,
18
+ ) {
13
19
  const rootOutputDirectory = userConfig.build?.outDir ?? 'dist'
20
+
14
21
  return (
15
- userConfig.environments?.[VITE_ENVIRONMENT_NAMES.server]?.build?.outDir ??
16
- rootOutputDirectory
22
+ userConfig.environments?.[environmentName]?.build?.outDir ??
23
+ join(rootOutputDirectory, directoryName)
17
24
  )
18
25
  }
package/src/plugin.ts CHANGED
@@ -6,7 +6,7 @@ import * as vite from 'vite'
6
6
  import { crawlFrameworkPkgs } from 'vitefu'
7
7
  import { join } from 'pathe'
8
8
  import { startManifestPlugin } from './start-manifest-plugin/plugin'
9
- import { startCompilerPlugin } from './start-compiler-plugin'
9
+ import { startCompilerPlugin } from './start-compiler-plugin/plugin'
10
10
  import { ENTRY_POINTS, VITE_ENVIRONMENT_NAMES } from './constants'
11
11
  import { tanStackStartRouter } from './start-router-plugin/plugin'
12
12
  import { loadEnvPlugin } from './load-env-plugin/plugin'
@@ -18,10 +18,11 @@ import {
18
18
  getServerOutputDirectory,
19
19
  } from './output-directory'
20
20
  import { postServerBuild } from './post-server-build'
21
+ import { createServerFnPlugin } from './create-server-fn-plugin/plugin'
21
22
  import type { ViteEnvironmentNames } from './constants'
22
23
  import type { TanStackStartInputConfig } from './schema'
23
24
  import type { PluginOption } from 'vite'
24
- import type { CompileStartFrameworkOptions } from './compilers'
25
+ import type { CompileStartFrameworkOptions } from './start-compiler-plugin/compilers'
25
26
 
26
27
  export interface TanStackStartVitePluginCoreOptions {
27
28
  framework: CompileStartFrameworkOptions
@@ -29,11 +30,6 @@ export interface TanStackStartVitePluginCoreOptions {
29
30
  client: string
30
31
  server: string
31
32
  }
32
- crawlPackages?: (opts: {
33
- name: string
34
- peerDependencies: Record<string, any>
35
- exports?: Record<string, any> | string
36
- }) => 'include' | 'exclude' | undefined
37
33
  }
38
34
 
39
35
  export function TanStackStartVitePluginCore(
@@ -56,7 +52,7 @@ export function TanStackStartVitePluginCore(
56
52
 
57
53
  return [
58
54
  tanStackStartRouter({
59
- ...startConfig.tsr,
55
+ ...startConfig.router,
60
56
  target: corePluginOpts.framework,
61
57
  autoCodeSplitting: true,
62
58
  }),
@@ -67,7 +63,7 @@ export function TanStackStartVitePluginCore(
67
63
  globalThis.TSS_APP_BASE = viteAppBase
68
64
 
69
65
  const root = viteConfig.root || process.cwd()
70
- const resolvedSrcDirectory = join(root, startConfig.tsr.srcDirectory)
66
+ const resolvedSrcDirectory = join(root, startConfig.srcDirectory)
71
67
 
72
68
  const routerFilePath = resolveEntry({
73
69
  type: 'router entry',
@@ -132,13 +128,8 @@ export function TanStackStartVitePluginCore(
132
128
  return nitroOutputPublicDir
133
129
  })()*/
134
130
 
135
- const startPackageName = `@tanstack/${corePluginOpts.framework}-start`
136
- const routerPackageName = `@tanstack/${corePluginOpts.framework}-router`
137
-
138
- const additionalOptimizeDeps = {
139
- include: new Set<string>(),
140
- exclude: new Set<string>(),
141
- }
131
+ const startPackageName =
132
+ `@tanstack/${corePluginOpts.framework}-start` as const
142
133
 
143
134
  // crawl packages that have start in "peerDependencies"
144
135
  // see https://github.com/svitejs/vitefu/blob/d8d82fa121e3b2215ba437107093c77bde51b63b/src/index.js#L95-L101
@@ -146,34 +137,16 @@ export function TanStackStartVitePluginCore(
146
137
  // this is currently uncached; could be implemented similarly as vite handles lock file changes
147
138
  // see https://github.com/vitejs/vite/blob/557f797d29422027e8c451ca50dd84bf8c41b5f0/packages/vite/src/node/optimizer/index.ts#L1282
148
139
 
149
- const result = await crawlFrameworkPkgs({
140
+ const crawlFrameworkPkgsResult = await crawlFrameworkPkgs({
150
141
  root: process.cwd(),
151
142
  isBuild: command === 'build',
152
143
  isFrameworkPkgByJson(pkgJson) {
153
- if ([routerPackageName, startPackageName].includes(pkgJson.name)) {
154
- return false
155
- }
156
-
157
144
  const peerDependencies = pkgJson['peerDependencies']
158
145
 
159
146
  if (peerDependencies) {
160
- const internalResult = corePluginOpts.crawlPackages?.({
161
- name: pkgJson.name,
162
- peerDependencies,
163
- exports: pkgJson.exports,
164
- })
165
- if (internalResult) {
166
- if (internalResult === 'exclude') {
167
- additionalOptimizeDeps.exclude.add(pkgJson.name)
168
- } else {
169
- additionalOptimizeDeps.include.add(pkgJson.name)
170
- }
171
- }
172
- return (
173
- startPackageName in peerDependencies ||
174
- routerPackageName in peerDependencies
175
- )
147
+ return startPackageName in peerDependencies
176
148
  }
149
+
177
150
  return false
178
151
  },
179
152
  })
@@ -187,9 +160,6 @@ export function TanStackStartVitePluginCore(
187
160
  [VITE_ENVIRONMENT_NAMES.client]: {
188
161
  consumer: 'client',
189
162
  build: {
190
- emptyOutDir:
191
- viteConfig.environments?.[VITE_ENVIRONMENT_NAMES.client]
192
- ?.build?.emptyOutDir ?? true,
193
163
  rollupOptions: {
194
164
  input: {
195
165
  main: ENTRY_POINTS.client,
@@ -201,9 +171,6 @@ export function TanStackStartVitePluginCore(
201
171
  [VITE_ENVIRONMENT_NAMES.server]: {
202
172
  consumer: 'server',
203
173
  build: {
204
- emptyOutDir:
205
- viteConfig.environments?.[VITE_ENVIRONMENT_NAMES.server]
206
- ?.build?.emptyOutDir ?? false,
207
174
  ssr: true,
208
175
  rollupOptions: {
209
176
  input:
@@ -218,42 +185,18 @@ export function TanStackStartVitePluginCore(
218
185
  viteConfig.environments?.[VITE_ENVIRONMENT_NAMES.server]
219
186
  ?.build?.copyPublicDir ?? false,
220
187
  },
221
- optimizeDeps: {
222
- exclude: [
223
- ...Object.values(VIRTUAL_MODULES),
224
- ...result.optimizeDeps.exclude.sort(),
225
- ...additionalOptimizeDeps.exclude,
226
- `@tanstack/${corePluginOpts.framework}-start/server-functions-server`,
227
- ],
228
- include: [
229
- ...additionalOptimizeDeps.include,
230
- ...result.optimizeDeps.include.sort(),
231
- ],
232
- },
233
188
  },
234
189
  },
235
190
  resolve: {
236
191
  noExternal: [
237
192
  '@tanstack/start**',
238
193
  `@tanstack/${corePluginOpts.framework}-start**`,
239
- ...Object.values(VIRTUAL_MODULES),
240
- startPackageName,
241
- ...result.ssr.noExternal.sort(),
194
+ ...crawlFrameworkPkgsResult.ssr.noExternal.sort(),
242
195
  ],
243
- dedupe: [startPackageName],
244
196
  alias: {
245
197
  ...entryAliasConfiguration,
246
198
  },
247
199
  },
248
- optimizeDeps: {
249
- exclude: [
250
- ...Object.values(VIRTUAL_MODULES),
251
- startPackageName,
252
- ...result.optimizeDeps.exclude.sort(),
253
- ...additionalOptimizeDeps.exclude,
254
- ],
255
- include: [...additionalOptimizeDeps.include],
256
- },
257
200
  /* prettier-ignore */
258
201
  define: {
259
202
  // define is an esbuild function that replaces the any instances of given keys with the given values
@@ -261,7 +204,7 @@ export function TanStackStartVitePluginCore(
261
204
  // This is not the same as injecting environment variables.
262
205
 
263
206
  ...defineReplaceEnv('TSS_SERVER_FN_BASE', startConfig.serverFns.base),
264
- ...defineReplaceEnv('TSS_OUTPUT_PUBLIC_DIR', getClientOutputDirectory(viteConfig)),
207
+ ...defineReplaceEnv('TSS_CLIENT_OUTPUT_DIR', getClientOutputDirectory(viteConfig)),
265
208
  ...defineReplaceEnv('TSS_APP_BASE', viteAppBase),
266
209
  ...(command === 'serve' ? defineReplaceEnv('TSS_SHELL', startConfig.spa?.enabled ? 'true' : 'false') : {}),
267
210
  ...defineReplaceEnv('TSS_DEV_SERVER', command === 'serve' ? 'true' : 'false'),
@@ -295,27 +238,23 @@ export function TanStackStartVitePluginCore(
295
238
  }
296
239
  },
297
240
  },
241
+ createServerFnPlugin(corePluginOpts.framework),
298
242
  // N.B. TanStackStartCompilerPlugin must be before the TanStackServerFnPluginEnv
299
- startCompilerPlugin(corePluginOpts.framework, {
300
- client: { envName: VITE_ENVIRONMENT_NAMES.client },
301
- server: { envName: VITE_ENVIRONMENT_NAMES.server },
302
- }),
243
+ startCompilerPlugin(corePluginOpts.framework),
303
244
  TanStackServerFnPluginEnv({
304
245
  // This is the ID that will be available to look up and import
305
246
  // our server function manifest and resolve its module
306
247
  manifestVirtualImportId: VIRTUAL_MODULES.serverFnManifest,
307
248
  client: {
308
249
  getRuntimeCode: () =>
309
- `import { createClientRpc } from '@tanstack/${corePluginOpts.framework}-start/server-functions-client'`,
310
- replacer: (d) =>
311
- `createClientRpc('${d.functionId}', '${startConfig.serverFns.base}')`,
250
+ `import { createClientRpc } from '@tanstack/${corePluginOpts.framework}-start/client'`,
251
+ replacer: (d) => `createClientRpc('${d.functionId}')`,
312
252
  envName: VITE_ENVIRONMENT_NAMES.client,
313
253
  },
314
254
  server: {
315
255
  getRuntimeCode: () =>
316
- `import { createServerRpc } from '@tanstack/${corePluginOpts.framework}-start/server-functions-server'`,
317
- replacer: (d) =>
318
- `createServerRpc('${d.functionId}', '${startConfig.serverFns.base}', ${d.fn})`,
256
+ `import { createServerRpc } from '@tanstack/${corePluginOpts.framework}-start/server'`,
257
+ replacer: (d) => `createServerRpc('${d.functionId}', ${d.fn})`,
319
258
  envName: VITE_ENVIRONMENT_NAMES.server,
320
259
  },
321
260
  }),
package/src/schema.ts CHANGED
@@ -2,35 +2,28 @@ import path from 'node:path'
2
2
  import { z } from 'zod'
3
3
  import { configSchema, getConfig } from '@tanstack/router-generator'
4
4
 
5
- const tsrConfig = configSchema
6
- .omit({ autoCodeSplitting: true })
7
- .partial()
8
- .extend({
9
- // this is relative to vite root
10
- // TODO why is this nested under tsr?
11
- srcDirectory: z.string().optional().default('src'),
12
- })
5
+ const tsrConfig = configSchema.omit({ autoCodeSplitting: true }).partial()
13
6
 
14
7
  export function parseStartConfig(
15
8
  opts?: z.input<typeof tanstackStartOptionsSchema>,
16
9
  ) {
17
10
  const options = tanstackStartOptionsSchema.parse(opts)
18
11
 
19
- const srcDirectory = options.tsr.srcDirectory
12
+ const srcDirectory = options.srcDirectory
20
13
 
21
14
  const routesDirectory =
22
- options.tsr.routesDirectory ?? path.join(srcDirectory, 'routes')
15
+ options.router.routesDirectory ?? path.join(srcDirectory, 'routes')
23
16
 
24
17
  const generatedRouteTree =
25
- options.tsr.generatedRouteTree ??
18
+ options.router.generatedRouteTree ??
26
19
  path.join(srcDirectory, 'routeTree.gen.ts')
27
20
 
28
21
  return {
29
22
  ...options,
30
- tsr: {
31
- ...options.tsr,
23
+ router: {
24
+ ...options.router,
32
25
  ...getConfig({
33
- ...options.tsr,
26
+ ...options.router,
34
27
  routesDirectory,
35
28
  generatedRouteTree,
36
29
  }),
@@ -121,12 +114,13 @@ const pageSchema = pageBaseSchema.extend({
121
114
 
122
115
  const tanstackStartOptionsSchema = z
123
116
  .object({
124
- tsr: tsrConfig.optional().default({}),
117
+ srcDirectory: z.string().optional().default('src'),
125
118
  router: z
126
119
  .object({
127
- // TODO naming?
120
+ // TODO this will move to 'start' once we have `createStart`
128
121
  entry: z.string().optional(),
129
122
  })
123
+ .and(tsrConfig.optional().default({}))
130
124
  .optional()
131
125
  .default({}),
132
126
  client: z
@@ -0,0 +1,195 @@
1
+ import * as babel from '@babel/core'
2
+ import * as t from '@babel/types'
3
+
4
+ import {
5
+ deadCodeElimination,
6
+ findReferencedIdentifiers,
7
+ } from 'babel-dead-code-elimination'
8
+ import { generateFromAst, parseAst } from '@tanstack/router-utils'
9
+ import { transformFuncs } from './constants'
10
+ import { handleCreateServerFileRouteCallExpressionFactory } from './serverFileRoute'
11
+ import { handleCreateIsomorphicFnCallExpression } from './isomorphicFn'
12
+ import { handleCreateMiddlewareCallExpression } from './middleware'
13
+ import {
14
+ handleCreateClientOnlyFnCallExpression,
15
+ handleCreateServerOnlyFnCallExpression,
16
+ } from './envOnly'
17
+ import type { GeneratorResult, ParseAstOptions } from '@tanstack/router-utils'
18
+
19
+ export type CompileStartFrameworkOptions = 'react' | 'solid'
20
+
21
+ type Identifiers = { [K in (typeof transformFuncs)[number]]: IdentifierConfig }
22
+ const getIdentifiers = (
23
+ framework: CompileStartFrameworkOptions,
24
+ ): Identifiers => ({
25
+ createServerRootRoute: {
26
+ name: 'createServerRootRoute',
27
+ handleCallExpression: handleCreateServerFileRouteCallExpressionFactory(
28
+ framework,
29
+ 'createServerRootRoute',
30
+ ),
31
+ paths: [],
32
+ },
33
+ createServerRoute: {
34
+ name: 'createServerRoute',
35
+ handleCallExpression: handleCreateServerFileRouteCallExpressionFactory(
36
+ framework,
37
+ 'createServerRoute',
38
+ ),
39
+ paths: [],
40
+ },
41
+ createServerFileRoute: {
42
+ name: 'createServerFileRoute',
43
+ handleCallExpression: handleCreateServerFileRouteCallExpressionFactory(
44
+ framework,
45
+ 'createServerFileRoute',
46
+ ),
47
+ paths: [],
48
+ },
49
+ createMiddleware: {
50
+ name: 'createMiddleware',
51
+ handleCallExpression: handleCreateMiddlewareCallExpression,
52
+ paths: [],
53
+ },
54
+ createServerOnlyFn: {
55
+ name: 'createServerOnlyFn',
56
+ handleCallExpression: handleCreateServerOnlyFnCallExpression,
57
+ paths: [],
58
+ },
59
+ createClientOnlyFn: {
60
+ name: 'createClientOnlyFn',
61
+ handleCallExpression: handleCreateClientOnlyFnCallExpression,
62
+ paths: [],
63
+ },
64
+ createIsomorphicFn: {
65
+ name: 'createIsomorphicFn',
66
+ handleCallExpression: handleCreateIsomorphicFnCallExpression,
67
+ paths: [],
68
+ },
69
+ })
70
+
71
+ export function compileStartOutputFactory(
72
+ framework: CompileStartFrameworkOptions,
73
+ ) {
74
+ return function compileStartOutput(opts: CompileOptions): GeneratorResult {
75
+ const ast = parseAst(opts)
76
+
77
+ const doDce = opts.dce ?? true
78
+ // find referenced identifiers *before* we transform anything
79
+ const refIdents = doDce ? findReferencedIdentifiers(ast) : undefined
80
+
81
+ babel.traverse(ast, {
82
+ Program: {
83
+ enter(programPath) {
84
+ const identifiers = getIdentifiers(framework)
85
+ programPath.traverse({
86
+ ImportDeclaration: (path) => {
87
+ if (path.node.source.value !== `@tanstack/${framework}-start`) {
88
+ return
89
+ }
90
+
91
+ // handle a destructured imports being renamed like "import { createServerFn as myCreateServerFn } from '@tanstack/react-start';"
92
+ path.node.specifiers.forEach((specifier) => {
93
+ transformFuncs.forEach((identifierKey) => {
94
+ const identifier = identifiers[identifierKey]
95
+
96
+ if (
97
+ specifier.type === 'ImportSpecifier' &&
98
+ specifier.imported.type === 'Identifier'
99
+ ) {
100
+ if (specifier.imported.name === identifierKey) {
101
+ identifier.name = specifier.local.name
102
+ }
103
+ }
104
+
105
+ // handle namespace imports like "import * as TanStackStart from '@tanstack/react-start';"
106
+ if (specifier.type === 'ImportNamespaceSpecifier') {
107
+ identifier.name = `${specifier.local.name}.${identifierKey}`
108
+ }
109
+ })
110
+ })
111
+ },
112
+ CallExpression: (path) => {
113
+ transformFuncs.forEach((identifierKey) => {
114
+ // Check to see if the call expression is a call to the
115
+ // identifiers[identifierKey].name
116
+ if (
117
+ t.isIdentifier(path.node.callee) &&
118
+ path.node.callee.name === identifiers[identifierKey].name
119
+ ) {
120
+ // The identifier could be a call to the original function
121
+ // in the source code. If this is case, we need to ignore it.
122
+ // Check the scope to see if the identifier is a function declaration.
123
+ // if it is, then we can ignore it.
124
+
125
+ if (
126
+ path.scope.getBinding(identifiers[identifierKey].name)?.path
127
+ .node.type === 'FunctionDeclaration'
128
+ ) {
129
+ return
130
+ }
131
+
132
+ return identifiers[identifierKey].paths.push(path)
133
+ }
134
+
135
+ // handle namespace imports like "import * as TanStackStart from '@tanstack/react-start';"
136
+ // which are then called like "TanStackStart.createServerFn()"
137
+ if (t.isMemberExpression(path.node.callee)) {
138
+ if (
139
+ t.isIdentifier(path.node.callee.object) &&
140
+ t.isIdentifier(path.node.callee.property)
141
+ ) {
142
+ const callname = [
143
+ path.node.callee.object.name,
144
+ path.node.callee.property.name,
145
+ ].join('.')
146
+
147
+ if (callname === identifiers[identifierKey].name) {
148
+ identifiers[identifierKey].paths.push(path)
149
+ }
150
+ }
151
+ }
152
+
153
+ return
154
+ })
155
+ },
156
+ })
157
+
158
+ transformFuncs.forEach((identifierKey) => {
159
+ identifiers[identifierKey].paths.forEach((path) => {
160
+ identifiers[identifierKey].handleCallExpression(
161
+ path as babel.NodePath<t.CallExpression>,
162
+ opts,
163
+ )
164
+ })
165
+ })
166
+ },
167
+ },
168
+ })
169
+
170
+ if (doDce) {
171
+ deadCodeElimination(ast, refIdents)
172
+ }
173
+
174
+ return generateFromAst(ast, {
175
+ sourceMaps: true,
176
+ sourceFileName: opts.filename,
177
+ filename: opts.filename,
178
+ })
179
+ }
180
+ }
181
+
182
+ export type CompileOptions = ParseAstOptions & {
183
+ env: 'server' | 'client'
184
+ dce?: boolean
185
+ filename: string
186
+ }
187
+
188
+ export type IdentifierConfig = {
189
+ name: string
190
+ handleCallExpression: (
191
+ path: babel.NodePath<t.CallExpression>,
192
+ opts: CompileOptions,
193
+ ) => void
194
+ paths: Array<babel.NodePath>
195
+ }
@@ -0,0 +1,9 @@
1
+ export const transformFuncs = [
2
+ 'createMiddleware',
3
+ 'createServerOnlyFn',
4
+ 'createClientOnlyFn',
5
+ 'createIsomorphicFn',
6
+ 'createServerRoute',
7
+ 'createServerFileRoute',
8
+ 'createServerRootRoute',
9
+ ] as const