rsbuild-plugin-react-router 0.2.0 → 0.3.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.
Files changed (105) hide show
  1. package/README.md +213 -201
  2. package/dist/451.js +1103 -0
  3. package/dist/bounded-cache.d.ts +1 -0
  4. package/dist/build-manifest.d.ts +7 -4
  5. package/dist/build-output-transforms.d.ts +30 -0
  6. package/dist/concurrency.d.ts +3 -0
  7. package/dist/config-imports.d.ts +10 -0
  8. package/dist/constants.d.ts +3 -0
  9. package/dist/dev-background-resources.d.ts +38 -0
  10. package/dist/dev-generation.d.ts +25 -0
  11. package/dist/dev-runtime-artifacts.d.ts +47 -0
  12. package/dist/dev-runtime-compilation.d.ts +47 -0
  13. package/dist/dev-runtime-controller.d.ts +14 -0
  14. package/dist/dev-runtime-session.d.ts +34 -0
  15. package/dist/dev-server.d.ts +16 -2
  16. package/dist/effect-runtime.d.ts +18 -0
  17. package/dist/export-utils.d.ts +15 -7
  18. package/dist/index.cjs +13775 -1412
  19. package/dist/index.d.ts +8 -1
  20. package/dist/index.js +9429 -1501
  21. package/dist/lazy-compilation-prewarm.d.ts +25 -0
  22. package/dist/lazy-compilation.d.ts +6 -0
  23. package/dist/manifest.d.ts +48 -10
  24. package/dist/modify-browser-manifest.d.ts +30 -13
  25. package/dist/parallel-route-transform-protocol.d.ts +25 -0
  26. package/dist/parallel-route-transform-worker.d.ts +1 -0
  27. package/dist/parallel-route-transform-worker.js +38 -0
  28. package/dist/parallel-route-transforms.d.ts +29 -0
  29. package/dist/performance.d.ts +26 -0
  30. package/dist/plugin-utils.d.ts +2 -12
  31. package/dist/prerender-build.d.ts +36 -0
  32. package/dist/prerender.d.ts +27 -2
  33. package/dist/react-router-config.d.ts +15 -5
  34. package/dist/route-artifacts.d.ts +33 -0
  35. package/dist/route-ast.d.ts +21 -0
  36. package/dist/route-chunks.d.ts +9 -1
  37. package/dist/route-component-transform.d.ts +5 -0
  38. package/dist/route-export-pruning.d.ts +6 -0
  39. package/dist/route-export-resolution.d.ts +5 -0
  40. package/dist/route-transform-tasks.d.ts +47 -0
  41. package/dist/route-watch.d.ts +27 -0
  42. package/dist/server-build-plan.d.ts +22 -0
  43. package/dist/server-build-resolution.d.ts +3 -0
  44. package/dist/server-utils.d.ts +3 -2
  45. package/dist/ssr-externals.d.ts +1 -0
  46. package/dist/templates/entry.server.cjs +3 -3
  47. package/dist/templates/entry.server.js +3 -3
  48. package/dist/typegen.d.ts +15 -0
  49. package/dist/types.d.ts +41 -14
  50. package/dist/virtual-modules.d.ts +2 -0
  51. package/dist/warnings/warn-on-client-source-maps.d.ts +1 -0
  52. package/dist/yuku.d.ts +15 -0
  53. package/package.json +24 -19
  54. package/src/bounded-cache.ts +18 -0
  55. package/src/build-manifest.ts +205 -0
  56. package/src/build-output-transforms.ts +273 -0
  57. package/src/concurrency.ts +15 -0
  58. package/src/config-imports.ts +83 -0
  59. package/src/constants.ts +91 -0
  60. package/src/dev-background-resources.ts +255 -0
  61. package/src/dev-generation.ts +690 -0
  62. package/src/dev-runtime-artifacts.ts +239 -0
  63. package/src/dev-runtime-compilation.ts +171 -0
  64. package/src/dev-runtime-controller.ts +542 -0
  65. package/src/dev-runtime-session.ts +191 -0
  66. package/src/dev-server.ts +88 -0
  67. package/src/effect-runtime.ts +130 -0
  68. package/src/export-utils.ts +278 -0
  69. package/src/index.ts +990 -0
  70. package/src/lazy-compilation-prewarm.ts +279 -0
  71. package/src/lazy-compilation.ts +101 -0
  72. package/src/manifest.ts +591 -0
  73. package/src/modify-browser-manifest.ts +246 -0
  74. package/src/parallel-route-transform-protocol.ts +35 -0
  75. package/src/parallel-route-transform-worker.ts +82 -0
  76. package/src/parallel-route-transforms.ts +458 -0
  77. package/src/performance.ts +254 -0
  78. package/src/plugin-utils.ts +82 -0
  79. package/src/prerender-build.ts +687 -0
  80. package/src/prerender.ts +349 -0
  81. package/src/react-router-config.ts +245 -0
  82. package/src/route-artifacts.ts +155 -0
  83. package/src/route-ast.ts +163 -0
  84. package/src/route-chunks.ts +857 -0
  85. package/src/route-component-transform.ts +314 -0
  86. package/src/route-config.ts +106 -0
  87. package/src/route-export-pruning.ts +668 -0
  88. package/src/route-export-resolution.ts +329 -0
  89. package/src/route-transform-tasks.ts +249 -0
  90. package/src/route-watch.ts +357 -0
  91. package/src/server-build-plan.ts +91 -0
  92. package/src/server-build-resolution.ts +131 -0
  93. package/src/server-utils.ts +111 -0
  94. package/src/ssr-externals.ts +59 -0
  95. package/src/templates/context.ts +12 -0
  96. package/src/templates/entry.client.tsx +12 -0
  97. package/src/templates/entry.server.tsx +76 -0
  98. package/src/typegen.ts +178 -0
  99. package/src/types.ts +92 -0
  100. package/src/validation/validate-plugin-order.ts +76 -0
  101. package/src/virtual-modules.ts +30 -0
  102. package/src/warnings/warn-on-client-source-maps.ts +96 -0
  103. package/src/yuku.ts +67 -0
  104. package/dist/0~rslib-runtime.js +0 -16
  105. package/dist/babel.d.ts +0 -8
@@ -0,0 +1,668 @@
1
+ import { walk, type ParseResult } from 'yuku-parser';
2
+ import {
3
+ getExportedName,
4
+ getPatternIdentifierNames,
5
+ getProgram,
6
+ removeFromArray,
7
+ type AnyNode,
8
+ type ProgramNode,
9
+ } from './route-ast.js';
10
+
11
+ export function validateDestructuredExports(
12
+ id: AnyNode,
13
+ exportsToRemove: readonly string[]
14
+ ): void {
15
+ validateBindingTarget(id, new Set(exportsToRemove));
16
+ }
17
+
18
+ export function invalidDestructureError(name: string): Error {
19
+ return new Error(`Cannot remove destructured export "${name}"`);
20
+ }
21
+
22
+ const assertAllowedBindingName = (
23
+ name: string,
24
+ exportsToRemove: ReadonlySet<string>
25
+ ): void => {
26
+ if (exportsToRemove.has(name)) {
27
+ throw invalidDestructureError(name);
28
+ }
29
+ };
30
+
31
+ const validateRestElement = (
32
+ element: AnyNode,
33
+ exportsToRemove: ReadonlySet<string>
34
+ ): void => {
35
+ if (element.argument?.type === 'Identifier' && element.argument.name) {
36
+ assertAllowedBindingName(element.argument.name, exportsToRemove);
37
+ }
38
+ };
39
+
40
+ const validateObjectProperty = (
41
+ property: AnyNode,
42
+ exportsToRemove: ReadonlySet<string>
43
+ ): void => {
44
+ if (property.type === 'RestElement') {
45
+ validateRestElement(property, exportsToRemove);
46
+ return;
47
+ }
48
+ if (property.type === 'Property') {
49
+ validateBindingTarget(
50
+ property.value as AnyNode | null | undefined,
51
+ exportsToRemove
52
+ );
53
+ }
54
+ };
55
+
56
+ const validateBindingTarget = (
57
+ node: AnyNode | null | undefined,
58
+ exportsToRemove: ReadonlySet<string>
59
+ ): void => {
60
+ if (!node) {
61
+ return;
62
+ }
63
+
64
+ switch (node.type) {
65
+ case 'Identifier':
66
+ if (node.name) {
67
+ assertAllowedBindingName(node.name, exportsToRemove);
68
+ }
69
+ return;
70
+ case 'AssignmentPattern':
71
+ validateBindingTarget(node.left, exportsToRemove);
72
+ return;
73
+ case 'ArrayPattern':
74
+ for (const element of node.elements ?? []) {
75
+ if (element?.type === 'RestElement') {
76
+ validateRestElement(element, exportsToRemove);
77
+ } else {
78
+ validateBindingTarget(element, exportsToRemove);
79
+ }
80
+ }
81
+ return;
82
+ case 'ObjectPattern':
83
+ for (const property of node.properties ?? []) {
84
+ validateObjectProperty(property, exportsToRemove);
85
+ }
86
+ }
87
+ };
88
+
89
+ const getDeclaredNames = (node: AnyNode): Set<string> => {
90
+ const names = new Set<string>();
91
+ if (node.type === 'VariableDeclaration') {
92
+ for (const declarator of node.declarations ?? []) {
93
+ getPatternIdentifierNames(declarator.id, names);
94
+ }
95
+ } else if (
96
+ (node.type === 'FunctionDeclaration' || node.type === 'ClassDeclaration') &&
97
+ node.id?.name
98
+ ) {
99
+ names.add(node.id.name);
100
+ } else if (node.type === 'ImportDeclaration') {
101
+ for (const specifier of node.specifiers ?? []) {
102
+ if (specifier.local?.name) {
103
+ names.add(specifier.local.name);
104
+ }
105
+ }
106
+ }
107
+ return names;
108
+ };
109
+
110
+ const isIdentifierDeclaration = (node: AnyNode, parent: AnyNode | null) => {
111
+ if (!parent || node.type !== 'Identifier') {
112
+ return false;
113
+ }
114
+ if (
115
+ (parent.type === 'FunctionDeclaration' ||
116
+ parent.type === 'FunctionExpression' ||
117
+ parent.type === 'ClassDeclaration' ||
118
+ parent.type === 'ClassExpression') &&
119
+ parent.id === node
120
+ ) {
121
+ return true;
122
+ }
123
+ if (parent.type === 'VariableDeclarator') {
124
+ return Boolean(
125
+ node.name && getPatternIdentifierNames(parent.id).has(node.name)
126
+ );
127
+ }
128
+ if (
129
+ (parent.type === 'ImportSpecifier' ||
130
+ parent.type === 'ImportDefaultSpecifier' ||
131
+ parent.type === 'ImportNamespaceSpecifier') &&
132
+ parent.local === node
133
+ ) {
134
+ return true;
135
+ }
136
+ if (
137
+ (parent.type === 'FunctionDeclaration' ||
138
+ parent.type === 'FunctionExpression' ||
139
+ parent.type === 'ArrowFunctionExpression') &&
140
+ node.name
141
+ ) {
142
+ const name = node.name;
143
+ return (parent.params ?? []).some((param: AnyNode) =>
144
+ getPatternIdentifierNames(param).has(name)
145
+ );
146
+ }
147
+ return false;
148
+ };
149
+
150
+ const isNonReferenceIdentifier = (node: AnyNode, parent: AnyNode | null) => {
151
+ if (!parent || node.type !== 'Identifier') {
152
+ return false;
153
+ }
154
+ if (isIdentifierDeclaration(node, parent)) {
155
+ return true;
156
+ }
157
+ if (
158
+ parent.type === 'MemberExpression' &&
159
+ parent.property === node &&
160
+ !parent.computed
161
+ ) {
162
+ return true;
163
+ }
164
+ if (
165
+ parent.type === 'Property' &&
166
+ parent.key === node &&
167
+ !parent.computed &&
168
+ !parent.shorthand
169
+ ) {
170
+ return true;
171
+ }
172
+ if (
173
+ parent.type === 'MethodDefinition' &&
174
+ parent.key === node &&
175
+ !parent.computed
176
+ ) {
177
+ return true;
178
+ }
179
+ if (
180
+ parent.type === 'ExportSpecifier' ||
181
+ parent.type === 'ExportDefaultSpecifier' ||
182
+ parent.type === 'ExportNamespaceSpecifier'
183
+ ) {
184
+ return true;
185
+ }
186
+ if (parent.type === 'ImportSpecifier' && parent.imported === node) {
187
+ return true;
188
+ }
189
+ if (
190
+ parent.type === 'LabeledStatement' ||
191
+ parent.type === 'BreakStatement' ||
192
+ parent.type === 'ContinueStatement'
193
+ ) {
194
+ return true;
195
+ }
196
+ return false;
197
+ };
198
+
199
+ const isUppercaseName = (name: string): boolean => /^[A-Z]/.test(name);
200
+
201
+ const collectReferencedNames = (node: AnyNode): Set<string> => {
202
+ const referenced = new Set<string>();
203
+ walk(node as never, {
204
+ Identifier(node, ctx) {
205
+ const current = node as unknown as AnyNode;
206
+ const parent = (ctx as { parent?: unknown }).parent as AnyNode | null;
207
+ if (!isNonReferenceIdentifier(current, parent) && current.name) {
208
+ referenced.add(current.name);
209
+ }
210
+ },
211
+ JSXIdentifier(node, ctx) {
212
+ const current = node as unknown as AnyNode;
213
+ const parent = (ctx as { parent?: unknown }).parent as AnyNode | null;
214
+ if (!parent) {
215
+ return;
216
+ }
217
+ if (parent.type === 'JSXMemberExpression' && parent.object === current) {
218
+ if (current.name) {
219
+ referenced.add(current.name);
220
+ }
221
+ return;
222
+ }
223
+ if (!current.name || !isUppercaseName(current.name)) {
224
+ return;
225
+ }
226
+ if (
227
+ (parent.type === 'JSXOpeningElement' ||
228
+ parent.type === 'JSXClosingElement') &&
229
+ parent.name === current
230
+ ) {
231
+ referenced.add(current.name);
232
+ return;
233
+ }
234
+ },
235
+ ExportSpecifier(node, ctx) {
236
+ const current = node as unknown as AnyNode;
237
+ const declaration = (ctx as { parent?: unknown })
238
+ .parent as AnyNode | null;
239
+ if (
240
+ !declaration?.source &&
241
+ declaration?.exportKind !== 'type' &&
242
+ current.local?.name &&
243
+ current.exportKind !== 'type'
244
+ ) {
245
+ referenced.add(current.local.name);
246
+ }
247
+ },
248
+ });
249
+ return referenced;
250
+ };
251
+
252
+ type TopLevelDeclaration = {
253
+ referencedNames: Set<string>;
254
+ };
255
+
256
+ type TopLevelDeclarationGraph = {
257
+ declarationsByNode: Map<AnyNode, TopLevelDeclaration>;
258
+ declarationsByName: Map<string, Set<TopLevelDeclaration>>;
259
+ };
260
+
261
+ const createTopLevelDeclarationGraph = (
262
+ program: ProgramNode
263
+ ): TopLevelDeclarationGraph => {
264
+ const declarationsByNode = new Map<AnyNode, TopLevelDeclaration>();
265
+ const declarationsByName = new Map<string, Set<TopLevelDeclaration>>();
266
+
267
+ const registerDeclaration = (
268
+ node: AnyNode,
269
+ declarationNode: AnyNode,
270
+ declaredNames: Set<string>
271
+ ) => {
272
+ const declaration: TopLevelDeclaration = {
273
+ referencedNames: collectReferencedNames(declarationNode),
274
+ };
275
+ declarationsByNode.set(node, declaration);
276
+ for (const name of declaredNames) {
277
+ const namedDeclarations = declarationsByName.get(name) ?? new Set();
278
+ namedDeclarations.add(declaration);
279
+ declarationsByName.set(name, namedDeclarations);
280
+ }
281
+ };
282
+
283
+ for (const statement of [...(program.body ?? [])]) {
284
+ if (statement.type === 'VariableDeclaration') {
285
+ for (const declarator of statement.declarations ?? []) {
286
+ registerDeclaration(
287
+ declarator,
288
+ declarator,
289
+ getPatternIdentifierNames(declarator.id)
290
+ );
291
+ }
292
+ continue;
293
+ }
294
+ if (
295
+ statement.type === 'FunctionDeclaration' ||
296
+ statement.type === 'ClassDeclaration'
297
+ ) {
298
+ registerDeclaration(statement, statement, getDeclaredNames(statement));
299
+ }
300
+ }
301
+
302
+ return { declarationsByNode, declarationsByName };
303
+ };
304
+
305
+ const collectLiveTopLevelDeclarations = (
306
+ program: ProgramNode,
307
+ graph: TopLevelDeclarationGraph
308
+ ): Set<TopLevelDeclaration> => {
309
+ const pendingNames: string[] = [];
310
+
311
+ for (const statement of program.body ?? []) {
312
+ if (statement.type === 'VariableDeclaration') {
313
+ continue;
314
+ }
315
+ if (graph.declarationsByNode.has(statement)) {
316
+ continue;
317
+ }
318
+ for (const name of collectReferencedNames(statement)) {
319
+ pendingNames.push(name);
320
+ }
321
+ }
322
+
323
+ // This is intentionally name-based and conservative: shadowing may retain a
324
+ // declaration, but it must never make a live declaration removable.
325
+ const visitedNames = new Set<string>();
326
+ const liveDeclarations = new Set<TopLevelDeclaration>();
327
+ while (pendingNames.length > 0) {
328
+ const name = pendingNames.pop();
329
+ if (!name || visitedNames.has(name)) {
330
+ continue;
331
+ }
332
+ visitedNames.add(name);
333
+ for (const declaration of graph.declarationsByName.get(name) ?? []) {
334
+ if (!liveDeclarations.has(declaration)) {
335
+ liveDeclarations.add(declaration);
336
+ for (const referencedName of declaration.referencedNames) {
337
+ pendingNames.push(referencedName);
338
+ }
339
+ }
340
+ }
341
+ }
342
+
343
+ return liveDeclarations;
344
+ };
345
+
346
+ const declarationReferencesName = (
347
+ declaration: TopLevelDeclaration,
348
+ names: ReadonlySet<string>,
349
+ graph: TopLevelDeclarationGraph,
350
+ cache: Map<TopLevelDeclaration, boolean>,
351
+ visitedNames = new Set<string>()
352
+ ): boolean => {
353
+ const cached = cache.get(declaration);
354
+ if (cached !== undefined) {
355
+ return cached;
356
+ }
357
+
358
+ for (const referencedName of declaration.referencedNames) {
359
+ if (names.has(referencedName)) {
360
+ cache.set(declaration, true);
361
+ return true;
362
+ }
363
+ if (visitedNames.has(referencedName)) {
364
+ continue;
365
+ }
366
+ visitedNames.add(referencedName);
367
+ for (const referencedDeclaration of graph.declarationsByName.get(
368
+ referencedName
369
+ ) ?? []) {
370
+ if (
371
+ declarationReferencesName(
372
+ referencedDeclaration,
373
+ names,
374
+ graph,
375
+ cache,
376
+ visitedNames
377
+ )
378
+ ) {
379
+ cache.set(declaration, true);
380
+ return true;
381
+ }
382
+ }
383
+ }
384
+ cache.set(declaration, false);
385
+ return false;
386
+ };
387
+
388
+ const removeNewlyDeadTopLevelDeclarations = (
389
+ program: ProgramNode,
390
+ graph: TopLevelDeclarationGraph,
391
+ previouslyLive: ReadonlySet<TopLevelDeclaration>,
392
+ removedExportReferencedNames: ReadonlySet<string>
393
+ ): void => {
394
+ const currentlyLive = collectLiveTopLevelDeclarations(program, graph);
395
+ const removedReferenceCache = new Map<TopLevelDeclaration, boolean>();
396
+ const isRemovableDeadDeclaration = (node: AnyNode) => {
397
+ const declaration = graph.declarationsByNode.get(node);
398
+ if (!declaration || currentlyLive.has(declaration)) {
399
+ return false;
400
+ }
401
+ return (
402
+ previouslyLive.has(declaration) ||
403
+ declarationReferencesName(
404
+ declaration,
405
+ removedExportReferencedNames,
406
+ graph,
407
+ removedReferenceCache
408
+ )
409
+ );
410
+ };
411
+
412
+ program.body = program.body.filter((statement: AnyNode) => {
413
+ if (statement.type === 'VariableDeclaration') {
414
+ statement.declarations = (statement.declarations ?? []).filter(
415
+ (declarator: AnyNode) => !isRemovableDeadDeclaration(declarator)
416
+ );
417
+ return statement.declarations.length > 0;
418
+ }
419
+ return !isRemovableDeadDeclaration(statement);
420
+ });
421
+ };
422
+
423
+ const hasRemovableExport = (
424
+ program: ProgramNode,
425
+ exportsToRemove: ReadonlySet<string>
426
+ ): boolean => {
427
+ const removesNamedExports = [...exportsToRemove].some(
428
+ name => name !== 'default'
429
+ );
430
+ for (const statement of program.body ?? []) {
431
+ if (statement.type === 'ExportAllDeclaration') {
432
+ const exportedName = statement.exported
433
+ ? getExportedName({ exported: statement.exported })
434
+ : null;
435
+ if (exportedName && exportsToRemove.has(exportedName)) {
436
+ return true;
437
+ }
438
+ if (!exportedName && removesNamedExports) {
439
+ return true;
440
+ }
441
+ continue;
442
+ }
443
+
444
+ if (statement.type === 'ExportDefaultDeclaration') {
445
+ if (exportsToRemove.has('default')) {
446
+ return true;
447
+ }
448
+ continue;
449
+ }
450
+
451
+ if (statement.type !== 'ExportNamedDeclaration') {
452
+ continue;
453
+ }
454
+
455
+ for (const specifier of statement.specifiers ?? []) {
456
+ if (specifier.type !== 'ExportSpecifier') {
457
+ continue;
458
+ }
459
+ const exportedName = getExportedName(specifier);
460
+ if (exportedName && exportsToRemove.has(exportedName)) {
461
+ return true;
462
+ }
463
+ }
464
+
465
+ const declaration = statement.declaration;
466
+ if (declaration?.type === 'VariableDeclaration') {
467
+ for (const declarator of declaration.declarations ?? []) {
468
+ for (const name of getPatternIdentifierNames(declarator.id)) {
469
+ if (exportsToRemove.has(name)) {
470
+ return true;
471
+ }
472
+ }
473
+ }
474
+ continue;
475
+ }
476
+
477
+ if (
478
+ (declaration?.type === 'FunctionDeclaration' ||
479
+ declaration?.type === 'ClassDeclaration') &&
480
+ declaration.id?.name &&
481
+ exportsToRemove.has(declaration.id.name)
482
+ ) {
483
+ return true;
484
+ }
485
+ }
486
+ return false;
487
+ };
488
+
489
+ export const removeExports = (
490
+ ast: ParseResult | AnyNode,
491
+ exportsToRemove: readonly string[],
492
+ exportsToRemoveSet: ReadonlySet<string> = new Set(exportsToRemove)
493
+ ): boolean => {
494
+ const program = getProgram(ast);
495
+ if (!hasRemovableExport(program, exportsToRemoveSet)) {
496
+ return false;
497
+ }
498
+
499
+ const declarationGraph = createTopLevelDeclarationGraph(program);
500
+ const previouslyLive = collectLiveTopLevelDeclarations(
501
+ program,
502
+ declarationGraph
503
+ );
504
+ let exportsChanged = false;
505
+ const removedExportLocalNames = new Set<string>();
506
+ const removedExportReferencedNames = new Set<string>();
507
+ const removesNamedExports = exportsToRemove.some(name => name !== 'default');
508
+ const trackRemovedExportReferences = (node: AnyNode | null | undefined) => {
509
+ if (!node) {
510
+ return;
511
+ }
512
+ const declaration = declarationGraph.declarationsByNode.get(node);
513
+ for (const name of declaration?.referencedNames ??
514
+ collectReferencedNames(node)) {
515
+ removedExportReferencedNames.add(name);
516
+ }
517
+ };
518
+
519
+ for (const statement of [...program.body]) {
520
+ if (statement.type === 'ExportAllDeclaration') {
521
+ const exportedName = statement.exported
522
+ ? getExportedName({ exported: statement.exported })
523
+ : null;
524
+ if (exportedName && exportsToRemoveSet.has(exportedName)) {
525
+ exportsChanged = true;
526
+ removeFromArray(program.body, statement);
527
+ }
528
+ if (!exportedName && removesNamedExports) {
529
+ throw new Error(
530
+ 'Cannot remove named exports from `export *`; use explicit named re-exports.'
531
+ );
532
+ }
533
+ continue;
534
+ }
535
+
536
+ if (statement.type === 'ExportNamedDeclaration') {
537
+ if (statement.specifiers?.length) {
538
+ statement.specifiers = statement.specifiers.filter(
539
+ (specifier: AnyNode) => {
540
+ if (specifier.type !== 'ExportSpecifier') {
541
+ return true;
542
+ }
543
+ const exportedName = getExportedName(specifier);
544
+ if (exportedName && exportsToRemoveSet.has(exportedName)) {
545
+ exportsChanged = true;
546
+ if (specifier.local?.name) {
547
+ removedExportLocalNames.add(specifier.local.name);
548
+ removedExportReferencedNames.add(specifier.local.name);
549
+ }
550
+ return false;
551
+ }
552
+ return true;
553
+ }
554
+ );
555
+ if (statement.specifiers.length === 0 && !statement.declaration) {
556
+ removeFromArray(program.body, statement);
557
+ }
558
+ }
559
+
560
+ const declaration = statement.declaration;
561
+ if (declaration?.type === 'VariableDeclaration') {
562
+ declaration.declarations = (declaration.declarations ?? []).filter(
563
+ (declarator: AnyNode) => {
564
+ const id = declarator.id;
565
+ if (id?.type === 'Identifier') {
566
+ if (id.name && exportsToRemoveSet.has(id.name)) {
567
+ exportsChanged = true;
568
+ removedExportLocalNames.add(id.name);
569
+ removedExportReferencedNames.add(id.name);
570
+ trackRemovedExportReferences(declarator);
571
+ return false;
572
+ }
573
+ return true;
574
+ }
575
+
576
+ if (id) {
577
+ validateDestructuredExports(id, exportsToRemove);
578
+ }
579
+ return true;
580
+ }
581
+ );
582
+ if (declaration.declarations.length === 0) {
583
+ removeFromArray(program.body, statement);
584
+ }
585
+ }
586
+
587
+ if (
588
+ (declaration?.type === 'FunctionDeclaration' ||
589
+ declaration?.type === 'ClassDeclaration') &&
590
+ declaration.id?.name &&
591
+ exportsToRemoveSet.has(declaration.id.name)
592
+ ) {
593
+ exportsChanged = true;
594
+ removedExportLocalNames.add(declaration.id.name);
595
+ removedExportReferencedNames.add(declaration.id.name);
596
+ trackRemovedExportReferences(statement);
597
+ removeFromArray(program.body, statement);
598
+ }
599
+ }
600
+
601
+ if (
602
+ statement.type === 'ExportDefaultDeclaration' &&
603
+ exportsToRemoveSet.has('default')
604
+ ) {
605
+ exportsChanged = true;
606
+ const declaration = statement.declaration;
607
+ if (declaration?.type === 'Identifier' && declaration.name) {
608
+ removedExportLocalNames.add(declaration.name);
609
+ removedExportReferencedNames.add(declaration.name);
610
+ } else if (declaration?.id?.name) {
611
+ removedExportLocalNames.add(declaration.id.name);
612
+ removedExportReferencedNames.add(declaration.id.name);
613
+ }
614
+ trackRemovedExportReferences(statement);
615
+ removeFromArray(program.body, statement);
616
+ }
617
+ }
618
+
619
+ for (const statement of [...program.body]) {
620
+ const expression =
621
+ statement.type === 'ExpressionStatement' ? statement.expression : null;
622
+ const left =
623
+ expression?.type === 'AssignmentExpression' ? expression.left : null;
624
+ if (
625
+ left?.type === 'MemberExpression' &&
626
+ left.object?.type === 'Identifier' &&
627
+ left.object.name &&
628
+ removedExportLocalNames.has(left.object.name)
629
+ ) {
630
+ removeFromArray(program.body, statement);
631
+ }
632
+ }
633
+
634
+ if (exportsChanged) {
635
+ removeNewlyDeadTopLevelDeclarations(
636
+ program,
637
+ declarationGraph,
638
+ previouslyLive,
639
+ removedExportReferencedNames
640
+ );
641
+ }
642
+
643
+ return exportsChanged;
644
+ };
645
+
646
+ export const removeUnusedImports = (ast: ParseResult | AnyNode): void => {
647
+ const program = getProgram(ast);
648
+ const referenced = collectReferencedNames(program);
649
+ for (const statement of [...program.body]) {
650
+ if (statement.type !== 'ImportDeclaration') {
651
+ continue;
652
+ }
653
+ if ((statement.specifiers ?? []).length === 0) {
654
+ continue;
655
+ }
656
+ statement.specifiers = (statement.specifiers ?? []).filter(
657
+ (specifier: AnyNode) => {
658
+ if (specifier.importKind === 'type') {
659
+ return false;
660
+ }
661
+ return !specifier.local?.name || referenced.has(specifier.local.name);
662
+ }
663
+ );
664
+ if (statement.specifiers.length === 0) {
665
+ removeFromArray(program.body, statement);
666
+ }
667
+ }
668
+ };