@tanstack/router-plugin 1.39.11 → 1.39.13
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/dist/cjs/compilers.cjs +176 -105
- package/dist/cjs/compilers.cjs.map +1 -1
- package/dist/esm/compilers.js +176 -105
- package/dist/esm/compilers.js.map +1 -1
- package/package.json +1 -1
- package/src/compilers.ts +266 -145
package/src/compilers.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import * as t from '@babel/types'
|
|
2
|
+
import babel from '@babel/core'
|
|
3
|
+
import generate from '@babel/generator'
|
|
2
4
|
import * as template from '@babel/template'
|
|
3
5
|
import { splitPrefix } from './constants'
|
|
4
6
|
import { eliminateUnreferencedIdentifiers } from './eliminateUnreferencedIdentifiers'
|
|
5
|
-
import type * as babel from '@babel/core'
|
|
6
7
|
import type { CompileAstFn } from './ast'
|
|
7
8
|
|
|
8
9
|
type SplitModulesById = Record<
|
|
@@ -40,134 +41,160 @@ export async function compileFile(opts: {
|
|
|
40
41
|
enter(programPath: babel.NodePath<t.Program>, state: State) {
|
|
41
42
|
const splitUrl = `${splitPrefix}:${opts.filename}?${splitPrefix}`
|
|
42
43
|
|
|
44
|
+
/**
|
|
45
|
+
* If the component for the route is being imported from
|
|
46
|
+
* another file, this is to track the path to that file
|
|
47
|
+
* the path itself doesn't matter, we just need to keep
|
|
48
|
+
* track of it so that we can remove it from the imports
|
|
49
|
+
* list if it's not being used like:
|
|
50
|
+
*
|
|
51
|
+
* `import '../shared/imported'`
|
|
52
|
+
*/
|
|
53
|
+
let existingCompImportPath: string | null = null
|
|
54
|
+
let existingLoaderImportPath: string | null = null
|
|
55
|
+
|
|
43
56
|
programPath.traverse(
|
|
44
57
|
{
|
|
45
58
|
CallExpression: (path) => {
|
|
46
|
-
if (path.node.callee
|
|
47
|
-
|
|
59
|
+
if (!t.isIdentifier(path.node.callee)) {
|
|
60
|
+
return
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (
|
|
64
|
+
!(
|
|
48
65
|
path.node.callee.name === 'createRoute' ||
|
|
49
66
|
path.node.callee.name === 'createFileRoute'
|
|
50
|
-
)
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
const hasImportedOrDefinedIdentifier = (
|
|
62
|
-
name: string,
|
|
63
|
-
) => {
|
|
64
|
-
return programPath.scope.hasBinding(name)
|
|
65
|
-
}
|
|
67
|
+
)
|
|
68
|
+
) {
|
|
69
|
+
return
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (t.isCallExpression(path.parentPath.node)) {
|
|
73
|
+
const options = resolveIdentifier(
|
|
74
|
+
path,
|
|
75
|
+
path.parentPath.node.arguments[0],
|
|
76
|
+
)
|
|
66
77
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
)() as t.Statement,
|
|
91
|
-
])
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
if (
|
|
95
|
-
!hasImportedOrDefinedIdentifier(
|
|
96
|
-
'$$splitComponentImporter',
|
|
97
|
-
)
|
|
98
|
-
) {
|
|
99
|
-
programPath.unshiftContainer('body', [
|
|
100
|
-
template.smart(
|
|
101
|
-
`const $$splitComponentImporter = () => import('${splitUrl}')`,
|
|
102
|
-
)() as t.Statement,
|
|
103
|
-
])
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
prop.value = template.expression(
|
|
107
|
-
`lazyRouteComponent($$splitComponentImporter, 'component')`,
|
|
108
|
-
)() as any
|
|
109
|
-
|
|
110
|
-
programPath.pushContainer('body', [
|
|
111
|
-
template.smart(
|
|
112
|
-
`function DummyComponent() { return null }`,
|
|
113
|
-
)() as t.Statement,
|
|
114
|
-
])
|
|
115
|
-
|
|
116
|
-
found = true
|
|
117
|
-
} else if (prop.key.name === 'loader') {
|
|
118
|
-
const value = prop.value
|
|
119
|
-
|
|
120
|
-
if (t.isIdentifier(value)) {
|
|
121
|
-
removeIdentifierLiteral(path, value)
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
// Prepend the import statement to the program along with the importer function
|
|
125
|
-
|
|
126
|
-
if (
|
|
127
|
-
!hasImportedOrDefinedIdentifier(
|
|
128
|
-
'lazyFn',
|
|
129
|
-
)
|
|
130
|
-
) {
|
|
131
|
-
programPath.unshiftContainer('body', [
|
|
132
|
-
template.smart(
|
|
133
|
-
`import { lazyFn } from '@tanstack/react-router'`,
|
|
134
|
-
)() as t.Statement,
|
|
135
|
-
])
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
if (
|
|
139
|
-
!hasImportedOrDefinedIdentifier(
|
|
140
|
-
'$$splitLoaderImporter',
|
|
141
|
-
)
|
|
142
|
-
) {
|
|
143
|
-
programPath.unshiftContainer('body', [
|
|
144
|
-
template.smart(
|
|
145
|
-
`const $$splitLoaderImporter = () => import('${splitUrl}')`,
|
|
146
|
-
)() as t.Statement,
|
|
147
|
-
])
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
prop.value = template.expression(
|
|
151
|
-
`lazyFn($$splitLoaderImporter, 'loader')`,
|
|
152
|
-
)() as any
|
|
153
|
-
|
|
154
|
-
found = true
|
|
155
|
-
}
|
|
78
|
+
let found = false
|
|
79
|
+
|
|
80
|
+
const hasImportedOrDefinedIdentifier = (
|
|
81
|
+
name: string,
|
|
82
|
+
) => {
|
|
83
|
+
return programPath.scope.hasBinding(name)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (t.isObjectExpression(options)) {
|
|
87
|
+
options.properties.forEach((prop) => {
|
|
88
|
+
if (t.isObjectProperty(prop)) {
|
|
89
|
+
if (t.isIdentifier(prop.key)) {
|
|
90
|
+
if (prop.key.name === 'component') {
|
|
91
|
+
const value = prop.value
|
|
92
|
+
|
|
93
|
+
if (t.isIdentifier(value)) {
|
|
94
|
+
existingCompImportPath =
|
|
95
|
+
getImportSpecifierAndPathFromLocalName(
|
|
96
|
+
programPath,
|
|
97
|
+
value.name,
|
|
98
|
+
).path
|
|
99
|
+
|
|
100
|
+
removeIdentifierLiteral(path, value)
|
|
156
101
|
}
|
|
157
|
-
}
|
|
158
102
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
103
|
+
// Prepend the import statement to the program along with the importer function
|
|
104
|
+
// Check to see if lazyRouteComponent is already imported before attempting
|
|
105
|
+
// to import it again
|
|
106
|
+
|
|
107
|
+
if (
|
|
108
|
+
!hasImportedOrDefinedIdentifier(
|
|
109
|
+
'lazyRouteComponent',
|
|
110
|
+
)
|
|
111
|
+
) {
|
|
112
|
+
programPath.unshiftContainer('body', [
|
|
113
|
+
template.statement(
|
|
114
|
+
`import { lazyRouteComponent } from '@tanstack/react-router'`,
|
|
115
|
+
)(),
|
|
116
|
+
])
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (
|
|
120
|
+
!hasImportedOrDefinedIdentifier(
|
|
121
|
+
'$$splitComponentImporter',
|
|
122
|
+
)
|
|
123
|
+
) {
|
|
124
|
+
programPath.unshiftContainer('body', [
|
|
125
|
+
template.statement(
|
|
126
|
+
`const $$splitComponentImporter = () => import('${splitUrl}')`,
|
|
127
|
+
)(),
|
|
128
|
+
])
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
prop.value = template.expression(
|
|
132
|
+
`lazyRouteComponent($$splitComponentImporter, 'component')`,
|
|
133
|
+
)()
|
|
134
|
+
|
|
135
|
+
programPath.pushContainer('body', [
|
|
136
|
+
template.statement(
|
|
137
|
+
`function DummyComponent() { return null }`,
|
|
138
|
+
)(),
|
|
139
|
+
])
|
|
140
|
+
|
|
141
|
+
found = true
|
|
142
|
+
} else if (prop.key.name === 'loader') {
|
|
143
|
+
const value = prop.value
|
|
144
|
+
|
|
145
|
+
if (t.isIdentifier(value)) {
|
|
146
|
+
existingLoaderImportPath =
|
|
147
|
+
getImportSpecifierAndPathFromLocalName(
|
|
148
|
+
programPath,
|
|
149
|
+
value.name,
|
|
150
|
+
).path
|
|
151
|
+
|
|
152
|
+
removeIdentifierLiteral(path, value)
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Prepend the import statement to the program along with the importer function
|
|
162
156
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
157
|
+
if (
|
|
158
|
+
!hasImportedOrDefinedIdentifier('lazyFn')
|
|
159
|
+
) {
|
|
160
|
+
programPath.unshiftContainer('body', [
|
|
161
|
+
template.smart(
|
|
162
|
+
`import { lazyFn } from '@tanstack/react-router'`,
|
|
163
|
+
)() as t.Statement,
|
|
164
|
+
])
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
if (
|
|
168
|
+
!hasImportedOrDefinedIdentifier(
|
|
169
|
+
'$$splitLoaderImporter',
|
|
170
|
+
)
|
|
171
|
+
) {
|
|
172
|
+
programPath.unshiftContainer('body', [
|
|
173
|
+
template.statement(
|
|
174
|
+
`const $$splitLoaderImporter = () => import('${splitUrl}')`,
|
|
175
|
+
)(),
|
|
176
|
+
])
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
prop.value = template.expression(
|
|
180
|
+
`lazyFn($$splitLoaderImporter, 'loader')`,
|
|
181
|
+
)()
|
|
182
|
+
|
|
183
|
+
found = true
|
|
184
|
+
}
|
|
185
|
+
}
|
|
169
186
|
}
|
|
170
|
-
|
|
187
|
+
|
|
188
|
+
programPath.scope.crawl()
|
|
189
|
+
})
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
if (found as boolean) {
|
|
193
|
+
programPath.pushContainer('body', [
|
|
194
|
+
template.statement(
|
|
195
|
+
`function TSR_Dummy_Component() {}`,
|
|
196
|
+
)(),
|
|
197
|
+
])
|
|
171
198
|
}
|
|
172
199
|
}
|
|
173
200
|
},
|
|
@@ -176,6 +203,29 @@ export async function compileFile(opts: {
|
|
|
176
203
|
)
|
|
177
204
|
|
|
178
205
|
eliminateUnreferencedIdentifiers(programPath)
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* If the component for the route is being imported,
|
|
209
|
+
* and it's not being used, remove the import statement
|
|
210
|
+
* from the program, by checking that the import has no
|
|
211
|
+
* specifiers
|
|
212
|
+
*/
|
|
213
|
+
if (
|
|
214
|
+
(existingCompImportPath as string | null) ||
|
|
215
|
+
(existingLoaderImportPath as string | null)
|
|
216
|
+
) {
|
|
217
|
+
programPath.traverse({
|
|
218
|
+
ImportDeclaration(path) {
|
|
219
|
+
if (path.node.specifiers.length > 0) return
|
|
220
|
+
if (
|
|
221
|
+
path.node.source.value === existingCompImportPath ||
|
|
222
|
+
path.node.source.value === existingLoaderImportPath
|
|
223
|
+
) {
|
|
224
|
+
path.remove()
|
|
225
|
+
}
|
|
226
|
+
},
|
|
227
|
+
})
|
|
228
|
+
}
|
|
179
229
|
},
|
|
180
230
|
},
|
|
181
231
|
},
|
|
@@ -190,6 +240,39 @@ export async function compileFile(opts: {
|
|
|
190
240
|
})
|
|
191
241
|
}
|
|
192
242
|
|
|
243
|
+
function getImportSpecifierAndPathFromLocalName(
|
|
244
|
+
programPath: babel.NodePath<t.Program>,
|
|
245
|
+
name: string,
|
|
246
|
+
): {
|
|
247
|
+
specifier:
|
|
248
|
+
| t.ImportSpecifier
|
|
249
|
+
| t.ImportDefaultSpecifier
|
|
250
|
+
| t.ImportNamespaceSpecifier
|
|
251
|
+
| null
|
|
252
|
+
path: string | null
|
|
253
|
+
} {
|
|
254
|
+
let specifier:
|
|
255
|
+
| t.ImportSpecifier
|
|
256
|
+
| t.ImportDefaultSpecifier
|
|
257
|
+
| t.ImportNamespaceSpecifier
|
|
258
|
+
| null = null
|
|
259
|
+
let path: string | null = null
|
|
260
|
+
|
|
261
|
+
programPath.traverse({
|
|
262
|
+
ImportDeclaration(importPath) {
|
|
263
|
+
const found = importPath.node.specifiers.find(
|
|
264
|
+
(targetSpecifier) => targetSpecifier.local.name === name,
|
|
265
|
+
)
|
|
266
|
+
if (found) {
|
|
267
|
+
specifier = found
|
|
268
|
+
path = importPath.node.source.value
|
|
269
|
+
}
|
|
270
|
+
},
|
|
271
|
+
})
|
|
272
|
+
|
|
273
|
+
return { specifier, path }
|
|
274
|
+
}
|
|
275
|
+
|
|
193
276
|
// Reusable function to get literal value or resolve variable to literal
|
|
194
277
|
function resolveIdentifier(path: any, node: any) {
|
|
195
278
|
if (t.isIdentifier(node)) {
|
|
@@ -250,36 +333,40 @@ export async function splitFile(opts: {
|
|
|
250
333
|
programPath.traverse(
|
|
251
334
|
{
|
|
252
335
|
CallExpression: (path) => {
|
|
253
|
-
if (path.node.callee
|
|
254
|
-
|
|
336
|
+
if (!t.isIdentifier(path.node.callee)) {
|
|
337
|
+
return
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
if (
|
|
341
|
+
!(
|
|
255
342
|
path.node.callee.name === 'createRoute' ||
|
|
256
343
|
path.node.callee.name === 'createFileRoute'
|
|
257
|
-
)
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
}
|
|
344
|
+
)
|
|
345
|
+
) {
|
|
346
|
+
return
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
if (t.isCallExpression(path.parentPath.node)) {
|
|
350
|
+
const options = resolveIdentifier(
|
|
351
|
+
path,
|
|
352
|
+
path.parentPath.node.arguments[0],
|
|
353
|
+
)
|
|
354
|
+
|
|
355
|
+
if (t.isObjectExpression(options)) {
|
|
356
|
+
options.properties.forEach((prop) => {
|
|
357
|
+
if (t.isObjectProperty(prop)) {
|
|
358
|
+
splitNodeTypes.forEach((type) => {
|
|
359
|
+
if (t.isIdentifier(prop.key)) {
|
|
360
|
+
if (prop.key.name === type) {
|
|
361
|
+
splitNodesByType[type] = prop.value
|
|
362
|
+
}
|
|
276
363
|
}
|
|
277
364
|
})
|
|
278
|
-
|
|
279
|
-
// Remove all of the options
|
|
280
|
-
options.properties = []
|
|
281
365
|
}
|
|
282
|
-
}
|
|
366
|
+
})
|
|
367
|
+
|
|
368
|
+
// Remove all of the options
|
|
369
|
+
options.properties = []
|
|
283
370
|
}
|
|
284
371
|
}
|
|
285
372
|
},
|
|
@@ -345,8 +432,42 @@ export async function splitFile(opts: {
|
|
|
345
432
|
),
|
|
346
433
|
]),
|
|
347
434
|
)
|
|
435
|
+
} else if (t.isCallExpression(splitNode)) {
|
|
436
|
+
const outputSplitNodeCode = generate(splitNode).code
|
|
437
|
+
const splitNodeAst = babel.parse(outputSplitNodeCode)
|
|
438
|
+
|
|
439
|
+
if (!splitNodeAst) {
|
|
440
|
+
throw new Error(
|
|
441
|
+
`Failed to parse the generated code for "${splitType}" in the node type "${splitNode.type}"`,
|
|
442
|
+
)
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
const statement = splitNodeAst.program.body[0]
|
|
446
|
+
|
|
447
|
+
if (!statement) {
|
|
448
|
+
throw new Error(
|
|
449
|
+
`Failed to parse the generated code for "${splitType}" in the node type "${splitNode.type}" as no statement was found in the program body`,
|
|
450
|
+
)
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
if (t.isExpressionStatement(statement)) {
|
|
454
|
+
const expression = statement.expression
|
|
455
|
+
programPath.pushContainer(
|
|
456
|
+
'body',
|
|
457
|
+
t.variableDeclaration('const', [
|
|
458
|
+
t.variableDeclarator(
|
|
459
|
+
t.identifier(splitType),
|
|
460
|
+
expression,
|
|
461
|
+
),
|
|
462
|
+
]),
|
|
463
|
+
)
|
|
464
|
+
} else {
|
|
465
|
+
throw new Error(
|
|
466
|
+
`Unexpected expression type encounter for "${splitType}" in the node type "${splitNode.type}"`,
|
|
467
|
+
)
|
|
468
|
+
}
|
|
348
469
|
} else {
|
|
349
|
-
console.info(splitNode)
|
|
470
|
+
console.info('Unexpected splitNode type:', splitNode)
|
|
350
471
|
throw new Error(
|
|
351
472
|
`Unexpected splitNode type ☝️: ${splitNode.type}`,
|
|
352
473
|
)
|