@tanstack/router-vite-plugin 1.32.2 → 1.32.17
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.
- package/README.md +5 -0
- package/dist/cjs/compilers.cjs +74 -18
- package/dist/cjs/compilers.cjs.map +1 -1
- package/dist/cjs/eliminateUnreferencedIdentifiers.cjs +23 -20
- package/dist/cjs/eliminateUnreferencedIdentifiers.cjs.map +1 -1
- package/dist/cjs/index.cjs +58 -1
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/index.d.cts +8 -7
- package/dist/esm/compilers.js +74 -18
- package/dist/esm/compilers.js.map +1 -1
- package/dist/esm/eliminateUnreferencedIdentifiers.js +23 -20
- package/dist/esm/eliminateUnreferencedIdentifiers.js.map +1 -1
- package/dist/esm/index.d.ts +8 -7
- package/dist/esm/index.js +58 -1
- package/dist/esm/index.js.map +1 -1
- package/package.json +2 -2
- package/src/compilers.ts +117 -18
- package/src/eliminateUnreferencedIdentifiers.ts +27 -115
- package/src/index.ts +9 -4
package/src/compilers.ts
CHANGED
|
@@ -91,7 +91,35 @@ export async function compileFile(opts: {
|
|
|
91
91
|
{
|
|
92
92
|
CallExpression: (path) => {
|
|
93
93
|
if (path.node.callee.type === 'Identifier') {
|
|
94
|
-
if (
|
|
94
|
+
if (path.node.callee.name === 'createServerFn') {
|
|
95
|
+
// If the function at createServerFn(_, MyFunc) doesn't have a
|
|
96
|
+
// 'use server' directive at the top of the function scope,
|
|
97
|
+
// then add it.
|
|
98
|
+
|
|
99
|
+
const fn = path.node.arguments[1]
|
|
100
|
+
|
|
101
|
+
if (
|
|
102
|
+
t.isFunctionExpression(fn) ||
|
|
103
|
+
t.isArrowFunctionExpression(fn)
|
|
104
|
+
) {
|
|
105
|
+
if (t.isBlockStatement(fn.body)) {
|
|
106
|
+
const hasUseServerDirective =
|
|
107
|
+
fn.body.directives.some((directive) => {
|
|
108
|
+
return (
|
|
109
|
+
directive.value.value === 'use server'
|
|
110
|
+
)
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
if (!hasUseServerDirective) {
|
|
114
|
+
fn.body.directives.unshift(
|
|
115
|
+
t.directive(
|
|
116
|
+
t.directiveLiteral('use server'),
|
|
117
|
+
),
|
|
118
|
+
)
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
} else if (
|
|
95
123
|
path.node.callee.name === 'createRoute' ||
|
|
96
124
|
path.node.callee.name === 'createFileRoute'
|
|
97
125
|
) {
|
|
@@ -105,6 +133,12 @@ export async function compileFile(opts: {
|
|
|
105
133
|
|
|
106
134
|
let found = false
|
|
107
135
|
|
|
136
|
+
const hasImportedOrDefinedIdentifier = (
|
|
137
|
+
name: string,
|
|
138
|
+
) => {
|
|
139
|
+
return programPath.scope.hasBinding(name)
|
|
140
|
+
}
|
|
141
|
+
|
|
108
142
|
if (t.isObjectExpression(options)) {
|
|
109
143
|
options.properties.forEach((prop) => {
|
|
110
144
|
if (t.isObjectProperty(prop)) {
|
|
@@ -117,15 +151,32 @@ export async function compileFile(opts: {
|
|
|
117
151
|
}
|
|
118
152
|
|
|
119
153
|
// Prepend the import statement to the program along with the importer function
|
|
154
|
+
// Check to see if lazyRouteComponent is already imported before attempting
|
|
155
|
+
// to import it again
|
|
156
|
+
|
|
157
|
+
if (
|
|
158
|
+
!hasImportedOrDefinedIdentifier(
|
|
159
|
+
'lazyRouteComponent',
|
|
160
|
+
)
|
|
161
|
+
) {
|
|
162
|
+
programPath.unshiftContainer('body', [
|
|
163
|
+
template.smart(
|
|
164
|
+
`import { lazyRouteComponent } from '@tanstack/react-router'`,
|
|
165
|
+
)() as t.Statement,
|
|
166
|
+
])
|
|
167
|
+
}
|
|
120
168
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
)
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
169
|
+
if (
|
|
170
|
+
!hasImportedOrDefinedIdentifier(
|
|
171
|
+
'$$splitComponentImporter',
|
|
172
|
+
)
|
|
173
|
+
) {
|
|
174
|
+
programPath.unshiftContainer('body', [
|
|
175
|
+
template.smart(
|
|
176
|
+
`const $$splitComponentImporter = () => import('${splitUrl}')`,
|
|
177
|
+
)() as t.Statement,
|
|
178
|
+
])
|
|
179
|
+
}
|
|
129
180
|
|
|
130
181
|
prop.value = template.expression(
|
|
131
182
|
`lazyRouteComponent($$splitComponentImporter, 'component')`,
|
|
@@ -147,14 +198,29 @@ export async function compileFile(opts: {
|
|
|
147
198
|
|
|
148
199
|
// Prepend the import statement to the program along with the importer function
|
|
149
200
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
)
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
201
|
+
if (
|
|
202
|
+
!hasImportedOrDefinedIdentifier(
|
|
203
|
+
'lazyFn',
|
|
204
|
+
)
|
|
205
|
+
) {
|
|
206
|
+
programPath.unshiftContainer('body', [
|
|
207
|
+
template.smart(
|
|
208
|
+
`import { lazyFn } from '@tanstack/react-router'`,
|
|
209
|
+
)() as t.Statement,
|
|
210
|
+
])
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
if (
|
|
214
|
+
!hasImportedOrDefinedIdentifier(
|
|
215
|
+
'$$splitLoaderImporter',
|
|
216
|
+
)
|
|
217
|
+
) {
|
|
218
|
+
programPath.unshiftContainer('body', [
|
|
219
|
+
template.smart(
|
|
220
|
+
`const $$splitLoaderImporter = () => import('${splitUrl}')`,
|
|
221
|
+
)() as t.Statement,
|
|
222
|
+
])
|
|
223
|
+
}
|
|
158
224
|
|
|
159
225
|
prop.value = template.expression(
|
|
160
226
|
`lazyFn($$splitLoaderImporter, 'loader')`,
|
|
@@ -164,6 +230,8 @@ export async function compileFile(opts: {
|
|
|
164
230
|
}
|
|
165
231
|
}
|
|
166
232
|
}
|
|
233
|
+
|
|
234
|
+
programPath.scope.crawl()
|
|
167
235
|
})
|
|
168
236
|
}
|
|
169
237
|
|
|
@@ -259,7 +327,38 @@ export async function splitFile(opts: {
|
|
|
259
327
|
{
|
|
260
328
|
CallExpression: (path) => {
|
|
261
329
|
if (path.node.callee.type === 'Identifier') {
|
|
262
|
-
if (path.node.callee.name === '
|
|
330
|
+
if (path.node.callee.name === 'createServerFn') {
|
|
331
|
+
// If the function at createServerFn(_, MyFunc) doesn't have a
|
|
332
|
+
// 'use server' directive at the top of the function scope,
|
|
333
|
+
// then add it.
|
|
334
|
+
|
|
335
|
+
const fn = path.node.arguments[1]
|
|
336
|
+
|
|
337
|
+
if (
|
|
338
|
+
t.isFunctionExpression(fn) ||
|
|
339
|
+
t.isArrowFunctionExpression(fn)
|
|
340
|
+
) {
|
|
341
|
+
if (t.isBlockStatement(fn.body)) {
|
|
342
|
+
const hasUseServerDirective =
|
|
343
|
+
fn.body.directives.some((directive) => {
|
|
344
|
+
return (
|
|
345
|
+
directive.value.value === 'use server'
|
|
346
|
+
)
|
|
347
|
+
})
|
|
348
|
+
|
|
349
|
+
if (!hasUseServerDirective) {
|
|
350
|
+
fn.body.directives.unshift(
|
|
351
|
+
t.directive(
|
|
352
|
+
t.directiveLiteral('use server'),
|
|
353
|
+
),
|
|
354
|
+
)
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
} else if (
|
|
359
|
+
path.node.callee.name === 'createRoute' ||
|
|
360
|
+
path.node.callee.name === 'createFileRoute'
|
|
361
|
+
) {
|
|
263
362
|
if (
|
|
264
363
|
path.parentPath.node.type === 'CallExpression'
|
|
265
364
|
) {
|
|
@@ -7,93 +7,6 @@ import type { NodePath } from '@babel/traverse'
|
|
|
7
7
|
|
|
8
8
|
type IdentifierPath = NodePath<BabelTypes.Identifier>
|
|
9
9
|
|
|
10
|
-
// export function findReferencedIdentifiers(
|
|
11
|
-
// programPath: NodePath<BabelTypes.Program>,
|
|
12
|
-
// ): Set<IdentifierPath> {
|
|
13
|
-
// const refs = new Set<IdentifierPath>()
|
|
14
|
-
|
|
15
|
-
// function markFunction(
|
|
16
|
-
// path: NodePath<
|
|
17
|
-
// | BabelTypes.FunctionDeclaration
|
|
18
|
-
// | BabelTypes.FunctionExpression
|
|
19
|
-
// | BabelTypes.ArrowFunctionExpression
|
|
20
|
-
// >,
|
|
21
|
-
// ) {
|
|
22
|
-
// const ident = getIdentifier(path)
|
|
23
|
-
// if (ident?.node && isIdentifierReferenced(ident)) {
|
|
24
|
-
// refs.add(ident)
|
|
25
|
-
// }
|
|
26
|
-
// }
|
|
27
|
-
|
|
28
|
-
// function markImport(
|
|
29
|
-
// path: NodePath<
|
|
30
|
-
// | BabelTypes.ImportSpecifier
|
|
31
|
-
// | BabelTypes.ImportDefaultSpecifier
|
|
32
|
-
// | BabelTypes.ImportNamespaceSpecifier
|
|
33
|
-
// >,
|
|
34
|
-
// ) {
|
|
35
|
-
// const local = path.get('local')
|
|
36
|
-
// if (isIdentifierReferenced(local)) {
|
|
37
|
-
// refs.add(local)
|
|
38
|
-
// }
|
|
39
|
-
// }
|
|
40
|
-
|
|
41
|
-
// programPath.traverse({
|
|
42
|
-
// VariableDeclarator(path) {
|
|
43
|
-
// if (path.node.id.type === 'Identifier') {
|
|
44
|
-
// const local = path.get('id') as NodePath<BabelTypes.Identifier>
|
|
45
|
-
// if (isIdentifierReferenced(local)) {
|
|
46
|
-
// refs.add(local)
|
|
47
|
-
// }
|
|
48
|
-
// } else if (path.node.id.type === 'ObjectPattern') {
|
|
49
|
-
// const pattern = path.get('id') as NodePath<BabelTypes.ObjectPattern>
|
|
50
|
-
|
|
51
|
-
// const properties = pattern.get('properties')
|
|
52
|
-
// properties.forEach((p) => {
|
|
53
|
-
// const local = p.get(
|
|
54
|
-
// p.node.type === 'ObjectProperty'
|
|
55
|
-
// ? 'value'
|
|
56
|
-
// : p.node.type === 'RestElement'
|
|
57
|
-
// ? 'argument'
|
|
58
|
-
// : (function () {
|
|
59
|
-
// throw new Error('invariant')
|
|
60
|
-
// })(),
|
|
61
|
-
// ) as NodePath<BabelTypes.Identifier>
|
|
62
|
-
// if (isIdentifierReferenced(local)) {
|
|
63
|
-
// refs.add(local)
|
|
64
|
-
// }
|
|
65
|
-
// })
|
|
66
|
-
// } else if (path.node.id.type === 'ArrayPattern') {
|
|
67
|
-
// const pattern = path.get('id') as NodePath<BabelTypes.ArrayPattern>
|
|
68
|
-
|
|
69
|
-
// const elements = pattern.get('elements')
|
|
70
|
-
// elements.forEach((e) => {
|
|
71
|
-
// let local: NodePath<BabelTypes.Identifier>
|
|
72
|
-
// if (e.node?.type === 'Identifier') {
|
|
73
|
-
// local = e as NodePath<BabelTypes.Identifier>
|
|
74
|
-
// } else if (e.node?.type === 'RestElement') {
|
|
75
|
-
// local = e.get('argument') as NodePath<BabelTypes.Identifier>
|
|
76
|
-
// } else {
|
|
77
|
-
// return
|
|
78
|
-
// }
|
|
79
|
-
|
|
80
|
-
// if (isIdentifierReferenced(local)) {
|
|
81
|
-
// refs.add(local)
|
|
82
|
-
// }
|
|
83
|
-
// })
|
|
84
|
-
// }
|
|
85
|
-
// },
|
|
86
|
-
|
|
87
|
-
// FunctionDeclaration: markFunction,
|
|
88
|
-
// FunctionExpression: markFunction,
|
|
89
|
-
// ArrowFunctionExpression: markFunction,
|
|
90
|
-
// ImportSpecifier: markImport,
|
|
91
|
-
// ImportDefaultSpecifier: markImport,
|
|
92
|
-
// ImportNamespaceSpecifier: markImport,
|
|
93
|
-
// })
|
|
94
|
-
// return refs
|
|
95
|
-
// }
|
|
96
|
-
|
|
97
10
|
/**
|
|
98
11
|
* @param refs - If provided, only these identifiers will be considered for removal.
|
|
99
12
|
*/
|
|
@@ -150,6 +63,30 @@ export const eliminateUnreferencedIdentifiers = (
|
|
|
150
63
|
}
|
|
151
64
|
}
|
|
152
65
|
|
|
66
|
+
const handleObjectPattern = (pattern: NodePath<BabelTypes.ObjectPattern>) => {
|
|
67
|
+
const properties = pattern.get('properties')
|
|
68
|
+
properties.forEach((property) => {
|
|
69
|
+
if (property.node.type === 'ObjectProperty') {
|
|
70
|
+
const value = property.get('value') as any
|
|
71
|
+
if (t.isIdentifier(value)) {
|
|
72
|
+
if (shouldBeRemoved(value as any)) {
|
|
73
|
+
property.remove()
|
|
74
|
+
}
|
|
75
|
+
} else if (t.isObjectPattern(value)) {
|
|
76
|
+
handleObjectPattern(value as any)
|
|
77
|
+
}
|
|
78
|
+
} else if (t.isRestElement(property.node)) {
|
|
79
|
+
const argument = property.get('argument')
|
|
80
|
+
if (
|
|
81
|
+
t.isIdentifier(argument as any) &&
|
|
82
|
+
shouldBeRemoved(argument as NodePath<BabelTypes.Identifier>)
|
|
83
|
+
) {
|
|
84
|
+
property.remove()
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
})
|
|
88
|
+
}
|
|
89
|
+
|
|
153
90
|
// Traverse again to remove unused references. This happens at least once,
|
|
154
91
|
// then repeats until no more references are removed.
|
|
155
92
|
do {
|
|
@@ -166,34 +103,9 @@ export const eliminateUnreferencedIdentifiers = (
|
|
|
166
103
|
path.remove()
|
|
167
104
|
}
|
|
168
105
|
} else if (path.node.id.type === 'ObjectPattern') {
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
const properties = pattern.get('properties')
|
|
173
|
-
properties.forEach((property) => {
|
|
174
|
-
const local = property.get(
|
|
175
|
-
property.node.type === 'ObjectProperty'
|
|
176
|
-
? 'value'
|
|
177
|
-
: // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
178
|
-
property.node.type === 'RestElement'
|
|
179
|
-
? 'argument'
|
|
180
|
-
: (function () {
|
|
181
|
-
throw new Error('invariant')
|
|
182
|
-
})(),
|
|
183
|
-
) as NodePath<BabelTypes.Identifier>
|
|
184
|
-
|
|
185
|
-
if (shouldBeRemoved(local)) {
|
|
186
|
-
++referencesRemovedInThisPass
|
|
187
|
-
property.remove()
|
|
188
|
-
}
|
|
189
|
-
})
|
|
190
|
-
|
|
191
|
-
if (
|
|
192
|
-
beforeCount !== referencesRemovedInThisPass &&
|
|
193
|
-
pattern.get('properties').length < 1
|
|
194
|
-
) {
|
|
195
|
-
path.remove()
|
|
196
|
-
}
|
|
106
|
+
handleObjectPattern(
|
|
107
|
+
path.get('id') as NodePath<BabelTypes.ObjectPattern>,
|
|
108
|
+
)
|
|
197
109
|
} else if (path.node.id.type === 'ArrayPattern') {
|
|
198
110
|
const pattern = path.get('id') as NodePath<BabelTypes.ArrayPattern>
|
|
199
111
|
|
package/src/index.ts
CHANGED
|
@@ -22,7 +22,7 @@ export const configSchema = generatorConfigSchema.extend({
|
|
|
22
22
|
export type Config = z.infer<typeof configSchema>
|
|
23
23
|
|
|
24
24
|
const CONFIG_FILE_NAME = 'tsr.config.json'
|
|
25
|
-
const debug =
|
|
25
|
+
const debug = Boolean(process.env.TSR_VITE_DEBUG)
|
|
26
26
|
|
|
27
27
|
const getConfig = async (inlineConfig: Partial<Config>, root: string) => {
|
|
28
28
|
const config = await getGeneratorConfig(inlineConfig, root)
|
|
@@ -66,10 +66,12 @@ export function TanStackRouterViteGenerator(
|
|
|
66
66
|
event: 'create' | 'update' | 'delete',
|
|
67
67
|
) => {
|
|
68
68
|
const filePath = normalize(file)
|
|
69
|
+
|
|
69
70
|
if (filePath === join(ROOT, CONFIG_FILE_NAME)) {
|
|
70
71
|
userConfig = await getConfig(inlineConfig, ROOT)
|
|
71
72
|
return
|
|
72
73
|
}
|
|
74
|
+
|
|
73
75
|
if (
|
|
74
76
|
event === 'update' &&
|
|
75
77
|
filePath === resolve(userConfig.generatedRouteTree)
|
|
@@ -77,9 +79,11 @@ export function TanStackRouterViteGenerator(
|
|
|
77
79
|
// skip generating routes if the generated route tree is updated
|
|
78
80
|
return
|
|
79
81
|
}
|
|
82
|
+
|
|
80
83
|
const routesDirectoryPath = isAbsolute(userConfig.routesDirectory)
|
|
81
84
|
? userConfig.routesDirectory
|
|
82
85
|
: join(ROOT, userConfig.routesDirectory)
|
|
86
|
+
|
|
83
87
|
if (filePath.startsWith(routesDirectoryPath)) {
|
|
84
88
|
await generate()
|
|
85
89
|
}
|
|
@@ -90,7 +94,6 @@ export function TanStackRouterViteGenerator(
|
|
|
90
94
|
configResolved: async (config) => {
|
|
91
95
|
ROOT = process.cwd()
|
|
92
96
|
userConfig = await getConfig(inlineConfig, ROOT)
|
|
93
|
-
|
|
94
97
|
if (userConfig.enableRouteGeneration ?? true) {
|
|
95
98
|
await generate()
|
|
96
99
|
}
|
|
@@ -175,8 +178,10 @@ export function TanStackRouterViteCodeSplitter(
|
|
|
175
178
|
|
|
176
179
|
return compiled
|
|
177
180
|
} else if (
|
|
178
|
-
fileIsInRoutesDirectory(id, userConfig.routesDirectory) &&
|
|
179
|
-
|
|
181
|
+
(fileIsInRoutesDirectory(id, userConfig.routesDirectory) &&
|
|
182
|
+
(code.includes('createRoute(') ||
|
|
183
|
+
code.includes('createFileRoute('))) ||
|
|
184
|
+
code.includes('createServerFn')
|
|
180
185
|
) {
|
|
181
186
|
if (code.includes('@react-refresh')) {
|
|
182
187
|
throw new Error(
|