@tanstack/start-plugin-core 1.142.4 → 1.142.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.
@@ -1,9 +1,14 @@
1
1
  import * as t from '@babel/types'
2
- import { getRootCallExpression } from '../start-compiler-plugin/utils'
3
- import type * as babel from '@babel/core'
4
-
2
+ import type { RewriteCandidate } from './types'
3
+
4
+ /**
5
+ * Handles createMiddleware transformations.
6
+ *
7
+ * @param candidate - The rewrite candidate containing path and method chain
8
+ * @param opts - Options including the environment
9
+ */
5
10
  export function handleCreateMiddleware(
6
- path: babel.NodePath<t.CallExpression>,
11
+ candidate: RewriteCandidate,
7
12
  opts: {
8
13
  env: 'client' | 'server'
9
14
  },
@@ -11,36 +16,11 @@ export function handleCreateMiddleware(
11
16
  if (opts.env === 'server') {
12
17
  throw new Error('handleCreateMiddleware should not be called on the server')
13
18
  }
14
- const rootCallExpression = getRootCallExpression(path)
15
-
16
- const callExpressionPaths = {
17
- middleware: null as babel.NodePath<t.CallExpression> | null,
18
- inputValidator: null as babel.NodePath<t.CallExpression> | null,
19
- client: null as babel.NodePath<t.CallExpression> | null,
20
- server: null as babel.NodePath<t.CallExpression> | null,
21
- }
22
-
23
- const validMethods = Object.keys(callExpressionPaths)
24
19
 
25
- rootCallExpression.traverse({
26
- MemberExpression(memberExpressionPath) {
27
- if (t.isIdentifier(memberExpressionPath.node.property)) {
28
- const name = memberExpressionPath.node.property
29
- .name as keyof typeof callExpressionPaths
20
+ const { inputValidator, server } = candidate.methodChain
30
21
 
31
- if (
32
- validMethods.includes(name) &&
33
- memberExpressionPath.parentPath.isCallExpression()
34
- ) {
35
- callExpressionPaths[name] = memberExpressionPath.parentPath
36
- }
37
- }
38
- },
39
- })
40
-
41
- if (callExpressionPaths.inputValidator) {
42
- const innerInputExpression =
43
- callExpressionPaths.inputValidator.node.arguments[0]
22
+ if (inputValidator) {
23
+ const innerInputExpression = inputValidator.callPath.node.arguments[0]
44
24
 
45
25
  if (!innerInputExpression) {
46
26
  throw new Error(
@@ -49,23 +29,17 @@ export function handleCreateMiddleware(
49
29
  }
50
30
 
51
31
  // remove the validator call expression
52
- if (t.isMemberExpression(callExpressionPaths.inputValidator.node.callee)) {
53
- callExpressionPaths.inputValidator.replaceWith(
54
- callExpressionPaths.inputValidator.node.callee.object,
32
+ if (t.isMemberExpression(inputValidator.callPath.node.callee)) {
33
+ inputValidator.callPath.replaceWith(
34
+ inputValidator.callPath.node.callee.object,
55
35
  )
56
36
  }
57
37
  }
58
38
 
59
- const serverFnPath = callExpressionPaths.server?.get(
60
- 'arguments.0',
61
- ) as babel.NodePath<any>
62
-
63
- if (callExpressionPaths.server && serverFnPath.node) {
39
+ if (server) {
64
40
  // remove the server call expression
65
- if (t.isMemberExpression(callExpressionPaths.server.node.callee)) {
66
- callExpressionPaths.server.replaceWith(
67
- callExpressionPaths.server.node.callee.object,
68
- )
41
+ if (t.isMemberExpression(server.callPath.node.callee)) {
42
+ server.callPath.replaceWith(server.callPath.node.callee.object)
69
43
  }
70
44
  }
71
45
  }
@@ -1,12 +1,15 @@
1
1
  import * as t from '@babel/types'
2
- import {
3
- codeFrameError,
4
- getRootCallExpression,
5
- } from '../start-compiler-plugin/utils'
6
- import type * as babel from '@babel/core'
7
-
2
+ import { codeFrameError } from '../start-compiler-plugin/utils'
3
+ import type { RewriteCandidate } from './types'
4
+
5
+ /**
6
+ * Handles createServerFn transformations.
7
+ *
8
+ * @param candidate - The rewrite candidate containing path and method chain
9
+ * @param opts - Options including the environment, code, directive, and provider file flag
10
+ */
8
11
  export function handleCreateServerFn(
9
- path: babel.NodePath<t.CallExpression>,
12
+ candidate: RewriteCandidate,
10
13
  opts: {
11
14
  env: 'client' | 'server'
12
15
  code: string
@@ -18,56 +21,27 @@ export function handleCreateServerFn(
18
21
  isProviderFile: boolean
19
22
  },
20
23
  ) {
21
- // Traverse the member expression and find the call expressions for
22
- // the validator, handler, and middleware methods. Check to make sure they
23
- // are children of the createServerFn call expression.
24
-
25
- const validMethods = ['middleware', 'inputValidator', 'handler'] as const
26
- type ValidMethods = (typeof validMethods)[number]
27
- const callExpressionPaths: Record<
28
- ValidMethods,
29
- babel.NodePath<t.CallExpression> | null
30
- > = {
31
- middleware: null,
32
- inputValidator: null,
33
- handler: null,
34
- }
35
-
36
- const rootCallExpression = getRootCallExpression(path)
37
-
38
- // if (debug)
39
- // console.info(
40
- // 'Handling createServerFn call expression:',
41
- // rootCallExpression.toString(),
42
- // )
24
+ const { path, methodChain } = candidate
25
+ const { inputValidator, handler } = methodChain
43
26
 
44
27
  // Check if the call is assigned to a variable
45
- if (!rootCallExpression.parentPath.isVariableDeclarator()) {
28
+ if (!path.parentPath.isVariableDeclarator()) {
46
29
  throw new Error('createServerFn must be assigned to a variable!')
47
30
  }
48
31
 
49
32
  // Get the identifier name of the variable
50
- const variableDeclarator = rootCallExpression.parentPath.node
51
- const existingVariableName = (variableDeclarator.id as t.Identifier).name
52
-
53
- rootCallExpression.traverse({
54
- MemberExpression(memberExpressionPath) {
55
- if (t.isIdentifier(memberExpressionPath.node.property)) {
56
- const name = memberExpressionPath.node.property.name as ValidMethods
57
-
58
- if (
59
- validMethods.includes(name) &&
60
- memberExpressionPath.parentPath.isCallExpression()
61
- ) {
62
- callExpressionPaths[name] = memberExpressionPath.parentPath
63
- }
64
- }
65
- },
66
- })
33
+ const variableDeclarator = path.parentPath.node
34
+ if (!t.isIdentifier(variableDeclarator.id)) {
35
+ throw codeFrameError(
36
+ opts.code,
37
+ variableDeclarator.id.loc!,
38
+ 'createServerFn must be assigned to a simple identifier, not a destructuring pattern',
39
+ )
40
+ }
41
+ const existingVariableName = variableDeclarator.id.name
67
42
 
68
- if (callExpressionPaths.inputValidator) {
69
- const innerInputExpression =
70
- callExpressionPaths.inputValidator.node.arguments[0]
43
+ if (inputValidator) {
44
+ const innerInputExpression = inputValidator.callPath.node.arguments[0]
71
45
 
72
46
  if (!innerInputExpression) {
73
47
  throw new Error(
@@ -77,11 +51,9 @@ export function handleCreateServerFn(
77
51
 
78
52
  // If we're on the client, remove the validator call expression
79
53
  if (opts.env === 'client') {
80
- if (
81
- t.isMemberExpression(callExpressionPaths.inputValidator.node.callee)
82
- ) {
83
- callExpressionPaths.inputValidator.replaceWith(
84
- callExpressionPaths.inputValidator.node.callee.object,
54
+ if (t.isMemberExpression(inputValidator.callPath.node.callee)) {
55
+ inputValidator.callPath.replaceWith(
56
+ inputValidator.callPath.node.callee.object,
85
57
  )
86
58
  }
87
59
  }
@@ -90,11 +62,9 @@ export function handleCreateServerFn(
90
62
  // First, we need to move the handler function to a nested function call
91
63
  // that is applied to the arguments passed to the server function.
92
64
 
93
- const handlerFnPath = callExpressionPaths.handler?.get(
94
- 'arguments.0',
95
- ) as babel.NodePath<any>
65
+ const handlerFnPath = handler?.firstArgPath
96
66
 
97
- if (!callExpressionPaths.handler || !handlerFnPath.node) {
67
+ if (!handler || !handlerFnPath?.node) {
98
68
  throw codeFrameError(
99
69
  opts.code,
100
70
  path.node.callee.loc!,
@@ -102,6 +72,15 @@ export function handleCreateServerFn(
102
72
  )
103
73
  }
104
74
 
75
+ // Validate the handler argument is an expression (not a SpreadElement, etc.)
76
+ if (!t.isExpression(handlerFnPath.node)) {
77
+ throw codeFrameError(
78
+ opts.code,
79
+ handlerFnPath.node.loc!,
80
+ `handler() must be called with an expression, not a ${handlerFnPath.node.type}`,
81
+ )
82
+ }
83
+
105
84
  const handlerFn = handlerFnPath.node
106
85
 
107
86
  // So, the way we do this is we give the handler function a way
@@ -161,6 +140,6 @@ export function handleCreateServerFn(
161
140
  // Caller files must NOT have the second argument because the implementation is already available in the extracted chunk
162
141
  // and including it would duplicate code
163
142
  if (opts.env === 'server' && opts.isProviderFile) {
164
- callExpressionPaths.handler.node.arguments.push(handlerFn)
143
+ handler.callPath.node.arguments.push(handlerFn)
165
144
  }
166
145
  }
@@ -0,0 +1,42 @@
1
+ import type * as babel from '@babel/core'
2
+ import type * as t from '@babel/types'
3
+
4
+ /**
5
+ * Info about a method call in the chain, including the call expression path
6
+ * and the path to its first argument (if any).
7
+ */
8
+ export interface MethodCallInfo {
9
+ callPath: babel.NodePath<t.CallExpression>
10
+ /** Path to the first argument, or null if no arguments */
11
+ firstArgPath: babel.NodePath | null
12
+ }
13
+
14
+ /**
15
+ * Pre-collected method chain paths for a root call expression.
16
+ * This avoids needing to traverse the AST again in handlers.
17
+ */
18
+ export interface MethodChainPaths {
19
+ middleware: MethodCallInfo | null
20
+ inputValidator: MethodCallInfo | null
21
+ handler: MethodCallInfo | null
22
+ server: MethodCallInfo | null
23
+ client: MethodCallInfo | null
24
+ }
25
+
26
+ export type MethodChainKey = keyof MethodChainPaths
27
+
28
+ export const METHOD_CHAIN_KEYS: ReadonlyArray<MethodChainKey> = [
29
+ 'middleware',
30
+ 'inputValidator',
31
+ 'handler',
32
+ 'server',
33
+ 'client',
34
+ ] as const
35
+
36
+ /**
37
+ * Information about a candidate that needs to be rewritten.
38
+ */
39
+ export interface RewriteCandidate {
40
+ path: babel.NodePath<t.CallExpression>
41
+ methodChain: MethodChainPaths
42
+ }