@tanstack/router-plugin 1.41.0 → 1.43.1

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/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 generate from '@babel/generator'
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 { eliminateUnreferencedIdentifiers } from './eliminateUnreferencedIdentifiers'
7
- import type { CompileAstFn } from './ast'
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 async function compileFile(opts: {
28
- code: string
29
- compileAst: CompileAstFn
30
- filename: string
31
- }) {
32
- return await opts.compileAst({
33
- code: opts.code,
34
- filename: opts.filename,
35
- getBabelConfig: () => ({
36
- plugins: [
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
- visitor: {
40
- Program: {
41
- enter(programPath: babel.NodePath<t.Program>, state: State) {
42
- const splitUrl = `${splitPrefix}:${opts.filename}?${splitPrefix}`
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
-
56
- programPath.traverse(
57
- {
58
- CallExpression: (path) => {
59
- if (!t.isIdentifier(path.node.callee)) {
60
- return
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
- if (
64
- !(
65
- path.node.callee.name === 'createRoute' ||
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
- if (t.isCallExpression(path.parentPath.node)) {
73
- const options = resolveIdentifier(
74
- path,
75
- path.parentPath.node.arguments[0],
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
- let found = false
149
+ found = true
150
+ } else if (prop.key.name === 'loader') {
151
+ const value = prop.value
79
152
 
80
- const hasImportedOrDefinedIdentifier = (
81
- name: string,
82
- ) => {
83
- return programPath.scope.hasBinding(name)
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
- 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)
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 (found as boolean) {
193
- programPath.pushContainer('body', [
173
+ if (
174
+ !hasImportedOrDefinedIdentifier(
175
+ '$$splitLoaderImporter',
176
+ )
177
+ ) {
178
+ programPath.unshiftContainer('body', [
194
179
  template.statement(
195
- `function TSR_Dummy_Component() {}`,
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
- state,
203
- )
204
-
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
- }
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
- root: process.cwd(),
235
- minify: process.env.NODE_ENV === 'production',
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
- ].filter(Boolean),
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
- }