@tanstack/router-plugin 1.41.0 → 1.43.5
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/ast.cjs +19 -29
- package/dist/cjs/ast.cjs.map +1 -1
- package/dist/cjs/ast.d.cts +4 -19
- package/dist/cjs/code-splitter.cjs +8 -15
- package/dist/cjs/code-splitter.cjs.map +1 -1
- package/dist/cjs/compilers.cjs +299 -317
- package/dist/cjs/compilers.cjs.map +1 -1
- package/dist/cjs/compilers.d.cts +3 -17
- package/dist/esm/ast.d.ts +4 -19
- package/dist/esm/ast.js +19 -29
- package/dist/esm/ast.js.map +1 -1
- package/dist/esm/code-splitter.js +9 -16
- package/dist/esm/code-splitter.js.map +1 -1
- package/dist/esm/compilers.d.ts +3 -17
- package/dist/esm/compilers.js +299 -317
- package/dist/esm/compilers.js.map +1 -1
- package/package.json +5 -3
- package/src/ast.ts +21 -42
- package/src/code-splitter.ts +12 -18
- package/src/compilers.ts +413 -432
- package/dist/cjs/eliminateUnreferencedIdentifiers.cjs +0 -148
- package/dist/cjs/eliminateUnreferencedIdentifiers.cjs.map +0 -1
- package/dist/cjs/eliminateUnreferencedIdentifiers.d.cts +0 -9
- package/dist/esm/eliminateUnreferencedIdentifiers.d.ts +0 -9
- package/dist/esm/eliminateUnreferencedIdentifiers.js +0 -131
- package/dist/esm/eliminateUnreferencedIdentifiers.js.map +0 -1
- package/src/eliminateUnreferencedIdentifiers.ts +0 -211
package/src/compilers.ts
CHANGED
|
@@ -1,10 +1,21 @@
|
|
|
1
1
|
import * as t from '@babel/types'
|
|
2
2
|
import babel from '@babel/core'
|
|
3
|
-
import
|
|
3
|
+
import _generate from '@babel/generator'
|
|
4
4
|
import * as template from '@babel/template'
|
|
5
|
+
import { deadCodeElimination } from 'babel-dead-code-elimination'
|
|
6
|
+
|
|
5
7
|
import { splitPrefix } from './constants'
|
|
6
|
-
import {
|
|
7
|
-
import type {
|
|
8
|
+
import { parseAst } from './ast'
|
|
9
|
+
import type { ParseAstOptions } from './ast'
|
|
10
|
+
|
|
11
|
+
// Babel is a CJS package and uses `default` as named binding (`exports.default =`).
|
|
12
|
+
// https://github.com/babel/babel/issues/15269.
|
|
13
|
+
let generate = (_generate as any)['default'] as typeof _generate
|
|
14
|
+
|
|
15
|
+
// eslint-disable-next-line ts/no-unnecessary-condition
|
|
16
|
+
if (!generate) {
|
|
17
|
+
generate = _generate
|
|
18
|
+
}
|
|
8
19
|
|
|
9
20
|
type SplitModulesById = Record<
|
|
10
21
|
string,
|
|
@@ -24,219 +35,423 @@ interface State {
|
|
|
24
35
|
splitModulesById: SplitModulesById
|
|
25
36
|
}
|
|
26
37
|
|
|
27
|
-
export
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
+
export function compileCodeSplitReferenceRoute(opts: ParseAstOptions) {
|
|
39
|
+
const ast = parseAst(opts)
|
|
40
|
+
|
|
41
|
+
if (!ast) {
|
|
42
|
+
throw new Error(
|
|
43
|
+
`Failed to compile ast for compileCodeSplitReferenceRoute() for the file: ${opts.filename}`,
|
|
44
|
+
)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
babel.traverse(ast, {
|
|
48
|
+
Program: {
|
|
49
|
+
enter(programPath, programState) {
|
|
50
|
+
const state = programState as unknown as State
|
|
51
|
+
|
|
52
|
+
const splitUrl = `${splitPrefix}:${opts.filename}?${splitPrefix}`
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* If the component for the route is being imported from
|
|
56
|
+
* another file, this is to track the path to that file
|
|
57
|
+
* the path itself doesn't matter, we just need to keep
|
|
58
|
+
* track of it so that we can remove it from the imports
|
|
59
|
+
* list if it's not being used like:
|
|
60
|
+
*
|
|
61
|
+
* `import '../shared/imported'`
|
|
62
|
+
*/
|
|
63
|
+
let existingCompImportPath: string | null = null
|
|
64
|
+
let existingLoaderImportPath: string | null = null
|
|
65
|
+
|
|
66
|
+
programPath.traverse(
|
|
38
67
|
{
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
68
|
+
CallExpression: (path) => {
|
|
69
|
+
if (!t.isIdentifier(path.node.callee)) {
|
|
70
|
+
return
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (
|
|
74
|
+
!(
|
|
75
|
+
path.node.callee.name === 'createRoute' ||
|
|
76
|
+
path.node.callee.name === 'createFileRoute'
|
|
77
|
+
)
|
|
78
|
+
) {
|
|
79
|
+
return
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (t.isCallExpression(path.parentPath.node)) {
|
|
83
|
+
const options = resolveIdentifier(
|
|
84
|
+
path,
|
|
85
|
+
path.parentPath.node.arguments[0],
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
let found = false
|
|
89
|
+
|
|
90
|
+
const hasImportedOrDefinedIdentifier = (name: string) => {
|
|
91
|
+
return programPath.scope.hasBinding(name)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (t.isObjectExpression(options)) {
|
|
95
|
+
options.properties.forEach((prop) => {
|
|
96
|
+
if (t.isObjectProperty(prop)) {
|
|
97
|
+
if (t.isIdentifier(prop.key)) {
|
|
98
|
+
if (prop.key.name === 'component') {
|
|
99
|
+
const value = prop.value
|
|
100
|
+
|
|
101
|
+
if (t.isIdentifier(value)) {
|
|
102
|
+
existingCompImportPath =
|
|
103
|
+
getImportSpecifierAndPathFromLocalName(
|
|
104
|
+
programPath,
|
|
105
|
+
value.name,
|
|
106
|
+
).path
|
|
107
|
+
|
|
108
|
+
removeIdentifierLiteral(path, value)
|
|
109
|
+
}
|
|
62
110
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
path.node.callee.name === 'createFileRoute'
|
|
67
|
-
)
|
|
68
|
-
) {
|
|
69
|
-
return
|
|
70
|
-
}
|
|
111
|
+
// Prepend the import statement to the program along with the importer function
|
|
112
|
+
// Check to see if lazyRouteComponent is already imported before attempting
|
|
113
|
+
// to import it again
|
|
71
114
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
)
|
|
115
|
+
if (
|
|
116
|
+
!hasImportedOrDefinedIdentifier(
|
|
117
|
+
'lazyRouteComponent',
|
|
118
|
+
)
|
|
119
|
+
) {
|
|
120
|
+
programPath.unshiftContainer('body', [
|
|
121
|
+
template.statement(
|
|
122
|
+
`import { lazyRouteComponent } from '@tanstack/react-router'`,
|
|
123
|
+
)(),
|
|
124
|
+
])
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (
|
|
128
|
+
!hasImportedOrDefinedIdentifier(
|
|
129
|
+
'$$splitComponentImporter',
|
|
130
|
+
)
|
|
131
|
+
) {
|
|
132
|
+
programPath.unshiftContainer('body', [
|
|
133
|
+
template.statement(
|
|
134
|
+
`const $$splitComponentImporter = () => import('${splitUrl}')`,
|
|
135
|
+
)(),
|
|
136
|
+
])
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
prop.value = template.expression(
|
|
140
|
+
`lazyRouteComponent($$splitComponentImporter, 'component')`,
|
|
141
|
+
)()
|
|
142
|
+
|
|
143
|
+
programPath.pushContainer('body', [
|
|
144
|
+
template.statement(
|
|
145
|
+
`function DummyComponent() { return null }`,
|
|
146
|
+
)(),
|
|
147
|
+
])
|
|
77
148
|
|
|
78
|
-
|
|
149
|
+
found = true
|
|
150
|
+
} else if (prop.key.name === 'loader') {
|
|
151
|
+
const value = prop.value
|
|
79
152
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
153
|
+
if (t.isIdentifier(value)) {
|
|
154
|
+
existingLoaderImportPath =
|
|
155
|
+
getImportSpecifierAndPathFromLocalName(
|
|
156
|
+
programPath,
|
|
157
|
+
value.name,
|
|
158
|
+
).path
|
|
159
|
+
|
|
160
|
+
removeIdentifierLiteral(path, value)
|
|
84
161
|
}
|
|
85
162
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
existingCompImportPath =
|
|
95
|
-
getImportSpecifierAndPathFromLocalName(
|
|
96
|
-
programPath,
|
|
97
|
-
value.name,
|
|
98
|
-
).path
|
|
99
|
-
|
|
100
|
-
removeIdentifierLiteral(path, value)
|
|
101
|
-
}
|
|
102
|
-
|
|
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
|
|
156
|
-
|
|
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
|
-
}
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
programPath.scope.crawl()
|
|
189
|
-
})
|
|
163
|
+
// Prepend the import statement to the program along with the importer function
|
|
164
|
+
|
|
165
|
+
if (!hasImportedOrDefinedIdentifier('lazyFn')) {
|
|
166
|
+
programPath.unshiftContainer('body', [
|
|
167
|
+
template.smart(
|
|
168
|
+
`import { lazyFn } from '@tanstack/react-router'`,
|
|
169
|
+
)() as t.Statement,
|
|
170
|
+
])
|
|
190
171
|
}
|
|
191
172
|
|
|
192
|
-
if (
|
|
193
|
-
|
|
173
|
+
if (
|
|
174
|
+
!hasImportedOrDefinedIdentifier(
|
|
175
|
+
'$$splitLoaderImporter',
|
|
176
|
+
)
|
|
177
|
+
) {
|
|
178
|
+
programPath.unshiftContainer('body', [
|
|
194
179
|
template.statement(
|
|
195
|
-
`
|
|
180
|
+
`const $$splitLoaderImporter = () => import('${splitUrl}')`,
|
|
196
181
|
)(),
|
|
197
182
|
])
|
|
198
183
|
}
|
|
184
|
+
|
|
185
|
+
prop.value = template.expression(
|
|
186
|
+
`lazyFn($$splitLoaderImporter, 'loader')`,
|
|
187
|
+
)()
|
|
188
|
+
|
|
189
|
+
found = true
|
|
199
190
|
}
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
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
|
-
}
|
|
229
|
-
},
|
|
230
|
-
},
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
programPath.scope.crawl()
|
|
195
|
+
})
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
if (found as boolean) {
|
|
199
|
+
programPath.pushContainer('body', [
|
|
200
|
+
template.statement(`function TSR_Dummy_Component() {}`)(),
|
|
201
|
+
])
|
|
202
|
+
}
|
|
203
|
+
}
|
|
231
204
|
},
|
|
232
205
|
},
|
|
206
|
+
state,
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* If the component for the route is being imported,
|
|
211
|
+
* and it's not being used, remove the import statement
|
|
212
|
+
* from the program, by checking that the import has no
|
|
213
|
+
* specifiers
|
|
214
|
+
*/
|
|
215
|
+
if (
|
|
216
|
+
(existingCompImportPath as string | null) ||
|
|
217
|
+
(existingLoaderImportPath as string | null)
|
|
218
|
+
) {
|
|
219
|
+
programPath.traverse({
|
|
220
|
+
ImportDeclaration(path) {
|
|
221
|
+
if (path.node.specifiers.length > 0) return
|
|
222
|
+
if (
|
|
223
|
+
path.node.source.value === existingCompImportPath ||
|
|
224
|
+
path.node.source.value === existingLoaderImportPath
|
|
225
|
+
) {
|
|
226
|
+
path.remove()
|
|
227
|
+
}
|
|
228
|
+
},
|
|
229
|
+
})
|
|
230
|
+
}
|
|
231
|
+
},
|
|
232
|
+
},
|
|
233
|
+
})
|
|
234
|
+
|
|
235
|
+
deadCodeElimination(ast)
|
|
236
|
+
|
|
237
|
+
return generate(ast, {
|
|
238
|
+
sourceMaps: true,
|
|
239
|
+
minified: process.env.NODE_ENV === 'production',
|
|
240
|
+
})
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
const splitNodeTypes = ['component', 'loader'] as const
|
|
244
|
+
type SplitNodeType = (typeof splitNodeTypes)[number]
|
|
245
|
+
|
|
246
|
+
export function compileCodeSplitVirtualRoute(opts: ParseAstOptions) {
|
|
247
|
+
const ast = parseAst(opts)
|
|
248
|
+
|
|
249
|
+
if (!ast) {
|
|
250
|
+
throw new Error(
|
|
251
|
+
`Failed to compile ast for compileCodeSplitVirtualRoute() for the file: ${opts.filename}`,
|
|
252
|
+
)
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
babel.traverse(ast, {
|
|
256
|
+
Program: {
|
|
257
|
+
enter(programPath, programState) {
|
|
258
|
+
const state = programState as unknown as State
|
|
259
|
+
|
|
260
|
+
const splitNodesByType: Record<SplitNodeType, t.Node | undefined> = {
|
|
261
|
+
component: undefined,
|
|
262
|
+
loader: undefined,
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// Find the node
|
|
266
|
+
programPath.traverse(
|
|
233
267
|
{
|
|
234
|
-
|
|
235
|
-
|
|
268
|
+
CallExpression: (path) => {
|
|
269
|
+
if (!t.isIdentifier(path.node.callee)) {
|
|
270
|
+
return
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
if (
|
|
274
|
+
!(
|
|
275
|
+
path.node.callee.name === 'createRoute' ||
|
|
276
|
+
path.node.callee.name === 'createFileRoute'
|
|
277
|
+
)
|
|
278
|
+
) {
|
|
279
|
+
return
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
if (t.isCallExpression(path.parentPath.node)) {
|
|
283
|
+
const options = resolveIdentifier(
|
|
284
|
+
path,
|
|
285
|
+
path.parentPath.node.arguments[0],
|
|
286
|
+
)
|
|
287
|
+
|
|
288
|
+
if (t.isObjectExpression(options)) {
|
|
289
|
+
options.properties.forEach((prop) => {
|
|
290
|
+
if (t.isObjectProperty(prop)) {
|
|
291
|
+
splitNodeTypes.forEach((type) => {
|
|
292
|
+
if (t.isIdentifier(prop.key)) {
|
|
293
|
+
if (prop.key.name === type) {
|
|
294
|
+
splitNodesByType[type] = prop.value
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
})
|
|
298
|
+
}
|
|
299
|
+
})
|
|
300
|
+
|
|
301
|
+
// Remove all of the options
|
|
302
|
+
options.properties = []
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
},
|
|
306
|
+
},
|
|
307
|
+
state,
|
|
308
|
+
)
|
|
309
|
+
|
|
310
|
+
splitNodeTypes.forEach((splitType) => {
|
|
311
|
+
let splitNode = splitNodesByType[splitType]
|
|
312
|
+
|
|
313
|
+
if (!splitNode) {
|
|
314
|
+
return
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
while (t.isIdentifier(splitNode)) {
|
|
318
|
+
const binding = programPath.scope.getBinding(splitNode.name)
|
|
319
|
+
splitNode = binding?.path.node
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// Add the node to the program
|
|
323
|
+
if (splitNode) {
|
|
324
|
+
if (t.isFunctionDeclaration(splitNode)) {
|
|
325
|
+
programPath.pushContainer(
|
|
326
|
+
'body',
|
|
327
|
+
t.variableDeclaration('const', [
|
|
328
|
+
t.variableDeclarator(
|
|
329
|
+
t.identifier(splitType),
|
|
330
|
+
t.functionExpression(
|
|
331
|
+
splitNode.id || null, // Anonymize the function expression
|
|
332
|
+
splitNode.params,
|
|
333
|
+
splitNode.body,
|
|
334
|
+
splitNode.generator,
|
|
335
|
+
splitNode.async,
|
|
336
|
+
),
|
|
337
|
+
),
|
|
338
|
+
]),
|
|
339
|
+
)
|
|
340
|
+
} else if (
|
|
341
|
+
t.isFunctionExpression(splitNode) ||
|
|
342
|
+
t.isArrowFunctionExpression(splitNode)
|
|
343
|
+
) {
|
|
344
|
+
programPath.pushContainer(
|
|
345
|
+
'body',
|
|
346
|
+
t.variableDeclaration('const', [
|
|
347
|
+
t.variableDeclarator(
|
|
348
|
+
t.identifier(splitType),
|
|
349
|
+
splitNode as any,
|
|
350
|
+
),
|
|
351
|
+
]),
|
|
352
|
+
)
|
|
353
|
+
} else if (
|
|
354
|
+
t.isImportSpecifier(splitNode) ||
|
|
355
|
+
t.isImportDefaultSpecifier(splitNode)
|
|
356
|
+
) {
|
|
357
|
+
programPath.pushContainer(
|
|
358
|
+
'body',
|
|
359
|
+
t.variableDeclaration('const', [
|
|
360
|
+
t.variableDeclarator(
|
|
361
|
+
t.identifier(splitType),
|
|
362
|
+
splitNode.local,
|
|
363
|
+
),
|
|
364
|
+
]),
|
|
365
|
+
)
|
|
366
|
+
} else if (t.isCallExpression(splitNode)) {
|
|
367
|
+
const outputSplitNodeCode = generate(splitNode).code
|
|
368
|
+
const splitNodeAst = babel.parse(outputSplitNodeCode)
|
|
369
|
+
|
|
370
|
+
if (!splitNodeAst) {
|
|
371
|
+
throw new Error(
|
|
372
|
+
`Failed to parse the generated code for "${splitType}" in the node type "${splitNode.type}"`,
|
|
373
|
+
)
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
const statement = splitNodeAst.program.body[0]
|
|
377
|
+
|
|
378
|
+
if (!statement) {
|
|
379
|
+
throw new Error(
|
|
380
|
+
`Failed to parse the generated code for "${splitType}" in the node type "${splitNode.type}" as no statement was found in the program body`,
|
|
381
|
+
)
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
if (t.isExpressionStatement(statement)) {
|
|
385
|
+
const expression = statement.expression
|
|
386
|
+
programPath.pushContainer(
|
|
387
|
+
'body',
|
|
388
|
+
t.variableDeclaration('const', [
|
|
389
|
+
t.variableDeclarator(t.identifier(splitType), expression),
|
|
390
|
+
]),
|
|
391
|
+
)
|
|
392
|
+
} else {
|
|
393
|
+
throw new Error(
|
|
394
|
+
`Unexpected expression type encounter for "${splitType}" in the node type "${splitNode.type}"`,
|
|
395
|
+
)
|
|
396
|
+
}
|
|
397
|
+
} else {
|
|
398
|
+
console.info('Unexpected splitNode type:', splitNode)
|
|
399
|
+
throw new Error(`Unexpected splitNode type ☝️: ${splitNode.type}`)
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
// If the splitNode exists at the top of the program
|
|
404
|
+
// then we need to remove that copy
|
|
405
|
+
programPath.node.body = programPath.node.body.filter((node) => {
|
|
406
|
+
return node !== splitNode
|
|
407
|
+
})
|
|
408
|
+
|
|
409
|
+
// Export the node
|
|
410
|
+
programPath.pushContainer('body', [
|
|
411
|
+
t.exportNamedDeclaration(null, [
|
|
412
|
+
t.exportSpecifier(
|
|
413
|
+
t.identifier(splitType),
|
|
414
|
+
t.identifier(splitType),
|
|
415
|
+
),
|
|
416
|
+
]),
|
|
417
|
+
])
|
|
418
|
+
})
|
|
419
|
+
|
|
420
|
+
// convert exports to imports from the original file
|
|
421
|
+
programPath.traverse({
|
|
422
|
+
ExportNamedDeclaration(path) {
|
|
423
|
+
// e.g. export const x = 1 or export { x }
|
|
424
|
+
// becomes
|
|
425
|
+
// import { x } from '${opts.id}'
|
|
426
|
+
|
|
427
|
+
if (path.node.declaration) {
|
|
428
|
+
if (t.isVariableDeclaration(path.node.declaration)) {
|
|
429
|
+
path.replaceWith(
|
|
430
|
+
t.importDeclaration(
|
|
431
|
+
path.node.declaration.declarations.map((decl) =>
|
|
432
|
+
t.importSpecifier(
|
|
433
|
+
t.identifier((decl.id as any).name),
|
|
434
|
+
t.identifier((decl.id as any).name),
|
|
435
|
+
),
|
|
436
|
+
),
|
|
437
|
+
t.stringLiteral(
|
|
438
|
+
opts.filename.split(`?${splitPrefix}`)[0] as string,
|
|
439
|
+
),
|
|
440
|
+
),
|
|
441
|
+
)
|
|
442
|
+
}
|
|
443
|
+
}
|
|
236
444
|
},
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
}
|
|
445
|
+
})
|
|
446
|
+
},
|
|
447
|
+
},
|
|
448
|
+
})
|
|
449
|
+
|
|
450
|
+
deadCodeElimination(ast)
|
|
451
|
+
|
|
452
|
+
return generate(ast, {
|
|
453
|
+
sourceMaps: true,
|
|
454
|
+
minified: process.env.NODE_ENV === 'production',
|
|
240
455
|
})
|
|
241
456
|
}
|
|
242
457
|
|
|
@@ -302,237 +517,3 @@ function removeIdentifierLiteral(path: any, node: any) {
|
|
|
302
517
|
}
|
|
303
518
|
}
|
|
304
519
|
}
|
|
305
|
-
|
|
306
|
-
const splitNodeTypes = ['component', 'loader'] as const
|
|
307
|
-
type SplitNodeType = (typeof splitNodeTypes)[number]
|
|
308
|
-
|
|
309
|
-
export async function splitFile(opts: {
|
|
310
|
-
code: string
|
|
311
|
-
compileAst: CompileAstFn
|
|
312
|
-
filename: string
|
|
313
|
-
}) {
|
|
314
|
-
return await opts.compileAst({
|
|
315
|
-
code: opts.code,
|
|
316
|
-
filename: opts.filename,
|
|
317
|
-
getBabelConfig: () => ({
|
|
318
|
-
plugins: [
|
|
319
|
-
[
|
|
320
|
-
{
|
|
321
|
-
visitor: {
|
|
322
|
-
Program: {
|
|
323
|
-
enter(programPath: babel.NodePath<t.Program>, state: State) {
|
|
324
|
-
const splitNodesByType: Record<
|
|
325
|
-
SplitNodeType,
|
|
326
|
-
t.Node | undefined
|
|
327
|
-
> = {
|
|
328
|
-
component: undefined,
|
|
329
|
-
loader: undefined,
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
// Find the node
|
|
333
|
-
programPath.traverse(
|
|
334
|
-
{
|
|
335
|
-
CallExpression: (path) => {
|
|
336
|
-
if (!t.isIdentifier(path.node.callee)) {
|
|
337
|
-
return
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
if (
|
|
341
|
-
!(
|
|
342
|
-
path.node.callee.name === 'createRoute' ||
|
|
343
|
-
path.node.callee.name === 'createFileRoute'
|
|
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
|
-
}
|
|
363
|
-
}
|
|
364
|
-
})
|
|
365
|
-
}
|
|
366
|
-
})
|
|
367
|
-
|
|
368
|
-
// Remove all of the options
|
|
369
|
-
options.properties = []
|
|
370
|
-
}
|
|
371
|
-
}
|
|
372
|
-
},
|
|
373
|
-
},
|
|
374
|
-
state,
|
|
375
|
-
)
|
|
376
|
-
|
|
377
|
-
splitNodeTypes.forEach((splitType) => {
|
|
378
|
-
let splitNode = splitNodesByType[splitType]
|
|
379
|
-
|
|
380
|
-
if (!splitNode) {
|
|
381
|
-
return
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
while (t.isIdentifier(splitNode)) {
|
|
385
|
-
const binding = programPath.scope.getBinding(
|
|
386
|
-
splitNode.name,
|
|
387
|
-
)
|
|
388
|
-
splitNode = binding?.path.node
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
// Add the node to the program
|
|
392
|
-
if (splitNode) {
|
|
393
|
-
if (t.isFunctionDeclaration(splitNode)) {
|
|
394
|
-
programPath.pushContainer(
|
|
395
|
-
'body',
|
|
396
|
-
t.variableDeclaration('const', [
|
|
397
|
-
t.variableDeclarator(
|
|
398
|
-
t.identifier(splitType),
|
|
399
|
-
t.functionExpression(
|
|
400
|
-
splitNode.id || null, // Anonymize the function expression
|
|
401
|
-
splitNode.params,
|
|
402
|
-
splitNode.body,
|
|
403
|
-
splitNode.generator,
|
|
404
|
-
splitNode.async,
|
|
405
|
-
),
|
|
406
|
-
),
|
|
407
|
-
]),
|
|
408
|
-
)
|
|
409
|
-
} else if (
|
|
410
|
-
t.isFunctionExpression(splitNode) ||
|
|
411
|
-
t.isArrowFunctionExpression(splitNode)
|
|
412
|
-
) {
|
|
413
|
-
programPath.pushContainer(
|
|
414
|
-
'body',
|
|
415
|
-
t.variableDeclaration('const', [
|
|
416
|
-
t.variableDeclarator(
|
|
417
|
-
t.identifier(splitType),
|
|
418
|
-
splitNode as any,
|
|
419
|
-
),
|
|
420
|
-
]),
|
|
421
|
-
)
|
|
422
|
-
} else if (
|
|
423
|
-
t.isImportSpecifier(splitNode) ||
|
|
424
|
-
t.isImportDefaultSpecifier(splitNode)
|
|
425
|
-
) {
|
|
426
|
-
programPath.pushContainer(
|
|
427
|
-
'body',
|
|
428
|
-
t.variableDeclaration('const', [
|
|
429
|
-
t.variableDeclarator(
|
|
430
|
-
t.identifier(splitType),
|
|
431
|
-
splitNode.local,
|
|
432
|
-
),
|
|
433
|
-
]),
|
|
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
|
-
}
|
|
469
|
-
} else {
|
|
470
|
-
console.info('Unexpected splitNode type:', splitNode)
|
|
471
|
-
throw new Error(
|
|
472
|
-
`Unexpected splitNode type ☝️: ${splitNode.type}`,
|
|
473
|
-
)
|
|
474
|
-
}
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
// If the splitNode exists at the top of the program
|
|
478
|
-
// then we need to remove that copy
|
|
479
|
-
programPath.node.body = programPath.node.body.filter(
|
|
480
|
-
(node) => {
|
|
481
|
-
return node !== splitNode
|
|
482
|
-
},
|
|
483
|
-
)
|
|
484
|
-
|
|
485
|
-
// Export the node
|
|
486
|
-
programPath.pushContainer('body', [
|
|
487
|
-
t.exportNamedDeclaration(null, [
|
|
488
|
-
t.exportSpecifier(
|
|
489
|
-
t.identifier(splitType),
|
|
490
|
-
t.identifier(splitType),
|
|
491
|
-
),
|
|
492
|
-
]),
|
|
493
|
-
])
|
|
494
|
-
})
|
|
495
|
-
|
|
496
|
-
// convert exports to imports from the original file
|
|
497
|
-
programPath.traverse({
|
|
498
|
-
ExportNamedDeclaration(path) {
|
|
499
|
-
// e.g. export const x = 1 or export { x }
|
|
500
|
-
// becomes
|
|
501
|
-
// import { x } from '${opts.id}'
|
|
502
|
-
|
|
503
|
-
if (path.node.declaration) {
|
|
504
|
-
if (t.isVariableDeclaration(path.node.declaration)) {
|
|
505
|
-
path.replaceWith(
|
|
506
|
-
t.importDeclaration(
|
|
507
|
-
path.node.declaration.declarations.map((decl) =>
|
|
508
|
-
t.importSpecifier(
|
|
509
|
-
t.identifier((decl.id as any).name),
|
|
510
|
-
t.identifier((decl.id as any).name),
|
|
511
|
-
),
|
|
512
|
-
),
|
|
513
|
-
t.stringLiteral(
|
|
514
|
-
opts.filename.split(
|
|
515
|
-
`?${splitPrefix}`,
|
|
516
|
-
)[0] as string,
|
|
517
|
-
),
|
|
518
|
-
),
|
|
519
|
-
)
|
|
520
|
-
}
|
|
521
|
-
}
|
|
522
|
-
},
|
|
523
|
-
})
|
|
524
|
-
|
|
525
|
-
eliminateUnreferencedIdentifiers(programPath)
|
|
526
|
-
},
|
|
527
|
-
},
|
|
528
|
-
},
|
|
529
|
-
},
|
|
530
|
-
{
|
|
531
|
-
root: process.cwd(),
|
|
532
|
-
minify: process.env.NODE_ENV === 'production',
|
|
533
|
-
},
|
|
534
|
-
],
|
|
535
|
-
].filter(Boolean),
|
|
536
|
-
}),
|
|
537
|
-
})
|
|
538
|
-
}
|