@@ -5,7 +5,26 @@ type ImportSpecifier =
5
5
| types . ImportNamespaceSpecifier
6
6
| types . ImportSpecifier ;
7
7
8
- const decorators = new Set ( ) ;
8
+ class ImportTracker {
9
+ private decorators = new Set < string > ( ) ;
10
+ private typeParameters = new Set < string > ( ) ;
11
+
12
+ addDecorator ( name : string ) {
13
+ this . decorators . add ( name ) ;
14
+ }
15
+
16
+ addTypeParameter ( name : string ) {
17
+ this . typeParameters . add ( name ) ;
18
+ }
19
+
20
+ shouldPreserve ( name : string , userPreserved ?: string [ ] ) {
21
+ return (
22
+ this . decorators . has ( name ) ||
23
+ this . typeParameters . has ( name ) ||
24
+ userPreserved ?. includes ( name )
25
+ ) ;
26
+ }
27
+ }
9
28
10
29
function isUnusedImportSpecifier ( path : NodePath < ImportSpecifier > ) {
11
30
return ! path . scope . bindings [ path . node . local . name ] . referenced ;
@@ -15,36 +34,61 @@ function hasAllSpecifiersRemoved(path: NodePath<types.ImportDeclaration>) {
15
34
return path . get ( 'specifiers' ) . length === 0 ;
16
35
}
17
36
18
- function collectParameterDecorators ( path : NodePath < types . TSParameterProperty > ) {
37
+ function collectParameterDecorators (
38
+ path : NodePath < types . TSParameterProperty > ,
39
+ tracker : ImportTracker ,
40
+ ) {
19
41
path . node . decorators ?. forEach ( ( { expression } ) => {
20
42
if ( expression . type !== 'CallExpression' ) return ;
21
43
22
44
if ( expression . callee . type === 'Identifier' ) {
23
- decorators . add ( expression . callee . name ) ;
45
+ tracker . addDecorator ( expression . callee . name ) ;
24
46
}
25
47
26
48
expression . arguments . forEach ( ( arg ) => {
27
49
if ( arg . type === 'Identifier' ) {
28
- decorators . add ( arg . name ) ;
50
+ tracker . addDecorator ( arg . name ) ;
29
51
}
30
52
} ) ;
31
53
} ) ;
32
54
}
33
55
56
+ function collectJSXTypeParameters (
57
+ path : NodePath < types . JSXElement > ,
58
+ tracker : ImportTracker ,
59
+ ) {
60
+ const openingElement = path . node . openingElement ;
61
+ if ( openingElement . typeParameters ) {
62
+ openingElement . typeParameters . params . forEach ( ( param ) => {
63
+ if (
64
+ param . type === 'TSTypeReference' &&
65
+ param . typeName . type === 'Identifier'
66
+ ) {
67
+ tracker . addTypeParameter ( param . typeName . name ) ;
68
+ }
69
+ } ) ;
70
+ }
71
+ }
72
+
34
73
export function removeUnusedImports ( preserve ?: string [ ] ) : PluginObj {
74
+ const tracker = new ImportTracker ( ) ;
75
+
35
76
return {
36
77
visitor : {
37
78
Program ( path : NodePath < types . Program > ) {
38
79
path . traverse ( {
39
80
TSParameterProperty ( path : NodePath < types . TSParameterProperty > ) {
40
- collectParameterDecorators ( path ) ;
81
+ collectParameterDecorators ( path , tracker ) ;
82
+ } ,
83
+ JSXElement ( path : NodePath < types . JSXElement > ) {
84
+ collectJSXTypeParameters ( path , tracker ) ;
41
85
} ,
42
86
} ) ;
43
87
} ,
44
88
ImportDeclaration ( path : NodePath < types . ImportDeclaration > ) {
45
89
path . get ( 'specifiers' ) . forEach ( ( specifier ) => {
46
- if ( decorators . has ( specifier . node . local . name ) ) return ;
47
- if ( preserve ?. includes ( specifier . node . local . name ) ) return ;
90
+ const localName = specifier . node . local . name ;
91
+ if ( tracker . shouldPreserve ( localName , preserve ) ) return ;
48
92
49
93
isUnusedImportSpecifier ( specifier ) && specifier . remove ( ) ;
50
94
hasAllSpecifiersRemoved ( path ) && path . remove ( ) ;
0 commit comments