@@ -80,8 +80,112 @@ const importTransformer: ts.TransformerFactory<ts.SourceFile> = context => {
80
80
return node => ts . visitNode ( node , visit ) ;
81
81
} ;
82
82
83
+ function findImportUsages (
84
+ node : ts . Node ,
85
+ context : ts . TransformationContext ,
86
+ ) : { [ name : string ] : number } {
87
+ const usages : { [ name : string ] : number } = { } ;
88
+ let locals = new Set < string > ( ) ;
89
+
90
+ const enterScope = < T > ( action : ( ) => T ) => {
91
+ const parentLocals = locals ;
92
+ locals = new Set ( [ ...locals ] ) ;
93
+ const result = action ( ) ;
94
+ locals = parentLocals ;
95
+ return result ;
96
+ } ;
97
+
98
+ const findUsages : ts . Visitor = node => {
99
+ if ( ts . isImportClause ( node ) ) {
100
+ const bindings = node . namedBindings ;
101
+
102
+ if ( bindings && 'elements' in bindings ) {
103
+ bindings . elements . forEach (
104
+ binding => ( usages [ binding . name . getText ( ) ] = 0 ) ,
105
+ ) ;
106
+ }
107
+
108
+ return node ;
109
+ }
110
+
111
+ if ( ts . isFunctionDeclaration ( node ) ) {
112
+ return enterScope ( ( ) => {
113
+ node . parameters
114
+ . map ( p => p . name )
115
+ . filter ( ts . isIdentifier )
116
+ . forEach ( p => locals . add ( p . getText ( ) ) ) ;
117
+ return ts . visitEachChild ( node , child => findUsages ( child ) , context ) ;
118
+ } ) ;
119
+ }
120
+
121
+ if ( ts . isBlock ( node ) ) {
122
+ return enterScope ( ( ) =>
123
+ ts . visitEachChild ( node , child => findUsages ( child ) , context ) ,
124
+ ) ;
125
+ }
126
+
127
+ if ( ts . isVariableDeclaration ( node ) ) {
128
+ if ( ts . isIdentifier ( node . name ) ) {
129
+ locals . add ( node . name . getText ( ) ) ;
130
+ }
131
+ } else if ( ts . isIdentifier ( node ) ) {
132
+ const identifier = node . getText ( ) ;
133
+ if ( ! locals . has ( identifier ) && usages [ identifier ] != undefined ) {
134
+ usages [ identifier ] ++ ;
135
+ }
136
+ }
137
+
138
+ return ts . visitEachChild ( node , child => findUsages ( child ) , context ) ;
139
+ } ;
140
+
141
+ ts . visitNode ( node , findUsages ) ;
142
+
143
+ return usages ;
144
+ }
145
+
146
+ const removeNonEmittingImports : ts . TransformerFactory < ts . SourceFile > = context => {
147
+ function createVisitor ( usages : { [ name : string ] : number } ) {
148
+ const visit : ts . Visitor = node => {
149
+ if ( ts . isImportDeclaration ( node ) ) {
150
+ let importClause = node . importClause ;
151
+ const bindings = importClause . namedBindings ;
152
+
153
+ if ( bindings && ts . isNamedImports ( bindings ) ) {
154
+ const namedImports = bindings . elements . filter (
155
+ element => usages [ element . name . getText ( ) ] > 0 ,
156
+ ) ;
157
+
158
+ if ( namedImports . length !== bindings . elements . length ) {
159
+ return ts . createImportDeclaration (
160
+ node . decorators ,
161
+ node . modifiers ,
162
+ namedImports . length == 0
163
+ ? undefined
164
+ : ts . createImportClause (
165
+ importClause . name ,
166
+ ts . createNamedImports ( namedImports )
167
+ ) ,
168
+ node . moduleSpecifier ,
169
+ ) ;
170
+ }
171
+ }
172
+
173
+ return node ;
174
+ }
175
+
176
+ return ts . visitEachChild ( node , child => visit ( child ) , context ) ;
177
+ } ;
178
+
179
+ return visit ;
180
+ }
181
+
182
+ return node =>
183
+ ts . visitNode ( node , createVisitor ( findImportUsages ( node , context ) ) ) ;
184
+ } ;
185
+
83
186
const TS_TRANSFORMERS = {
84
187
before : [ importTransformer ] ,
188
+ after : [ removeNonEmittingImports ] ,
85
189
} ;
86
190
87
191
const TS2552_REGEX = / C a n n o t f i n d n a m e ' \$ ( [ a - z A - Z 0 - 9 _ ] + ) ' . D i d y o u m e a n ' ( [ a - z A - Z 0 - 9 _ ] + ) ' \? / i;
0 commit comments