8
8
import * as ts from 'typescript' ;
9
9
import { collectDeepNodes } from '../helpers/ast-utils' ;
10
10
11
- const tslibDecorateRe = / \b t s l i b (?: _ \d + ) ? \. _ _ d e c o r a t e \b / ;
12
- const tslibRe = / \b t s l i b (?: _ \d + ) ? \b / ;
13
-
14
11
15
12
export function testScrubFile ( content : string ) {
16
13
const markers = [
@@ -20,8 +17,7 @@ export function testScrubFile(content: string) {
20
17
'ctorParameters' ,
21
18
] ;
22
19
23
- return markers . some ( ( marker ) => content . indexOf ( marker ) !== - 1 )
24
- || tslibDecorateRe . test ( content ) ;
20
+ return markers . some ( ( marker ) => content . indexOf ( marker ) !== - 1 ) ;
25
21
}
26
22
27
23
// Don't remove `ctorParameters` from these.
@@ -59,6 +55,7 @@ export function getScrubFileTransformer(program: ts.Program): ts.TransformerFact
59
55
const transformer : ts . Transformer < ts . SourceFile > = ( sf : ts . SourceFile ) => {
60
56
61
57
const ngMetadata = findAngularMetadata ( sf ) ;
58
+ const tslibImports = findTslibImports ( sf ) ;
62
59
63
60
const nodes : ts . Node [ ] = [ ] ;
64
61
ts . forEachChild ( sf , checkNodeForDecorators ) ;
@@ -73,7 +70,7 @@ export function getScrubFileTransformer(program: ts.Program): ts.TransformerFact
73
70
if ( isDecoratorAssignmentExpression ( exprStmt ) ) {
74
71
nodes . push ( ...pickDecorationNodesToRemove ( exprStmt , ngMetadata , checker ) ) ;
75
72
}
76
- if ( isDecorateAssignmentExpression ( exprStmt ) ) {
73
+ if ( isDecorateAssignmentExpression ( exprStmt , tslibImports , checker ) ) {
77
74
nodes . push ( ...pickDecorateNodesToRemove ( exprStmt , ngMetadata , checker ) ) ;
78
75
}
79
76
if ( isPropDecoratorAssignmentExpression ( exprStmt ) ) {
@@ -193,7 +190,12 @@ function isDecoratorAssignmentExpression(exprStmt: ts.ExpressionStatement): bool
193
190
}
194
191
195
192
// Check if assignment is `Clazz = __decorate([...], Clazz)`.
196
- function isDecorateAssignmentExpression ( exprStmt : ts . ExpressionStatement ) : boolean {
193
+ function isDecorateAssignmentExpression (
194
+ exprStmt : ts . ExpressionStatement ,
195
+ tslibIdentifiers : ts . NamespaceImport [ ] ,
196
+ checker : ts . TypeChecker ,
197
+ ) : boolean {
198
+
197
199
if ( exprStmt . expression . kind !== ts . SyntaxKind . BinaryExpression ) {
198
200
return false ;
199
201
}
@@ -206,27 +208,11 @@ function isDecorateAssignmentExpression(exprStmt: ts.ExpressionStatement): boole
206
208
}
207
209
const classIdent = expr . left as ts . Identifier ;
208
210
const callExpr = expr . right as ts . CallExpression ;
209
- let callExprIdent = callExpr . expression as ts . Identifier ;
210
-
211
- if ( callExpr . expression . kind !== ts . SyntaxKind . Identifier ) {
212
- if ( callExpr . expression . kind === ts . SyntaxKind . PropertyAccessExpression ) {
213
- const propAccess = callExpr . expression as ts . PropertyAccessExpression ;
214
- const left = propAccess . expression ;
215
- callExprIdent = propAccess . name ;
216
-
217
- if ( ! ( left . kind === ts . SyntaxKind . Identifier && tslibRe . test ( ( left as ts . Identifier ) . text ) ) ) {
218
- return false ;
219
- }
220
- } else {
221
- return false ;
222
- }
223
- }
224
211
225
- // node.text on a name that starts with two underscores will return three instead.
226
- // Unless it's an expression like tslib.__decorate, in which case it's only 2.
227
- if ( callExprIdent . text !== '___decorate' && callExprIdent . text !== '__decorate' ) {
212
+ if ( ! isTslibHelper ( callExpr , '__decorate' , tslibIdentifiers , checker ) ) {
228
213
return false ;
229
214
}
215
+
230
216
if ( callExpr . arguments . length !== 2 ) {
231
217
return false ;
232
218
}
@@ -454,3 +440,86 @@ function identifierIsMetadata(
454
440
. declarations
455
441
. some ( ( spec ) => metadata . indexOf ( spec ) !== - 1 ) ;
456
442
}
443
+
444
+ // Check if an import is a tslib helper import (`import * as tslib from "tslib";`)
445
+ function isTslibImport ( node : ts . ImportDeclaration ) : boolean {
446
+ return ! ! ( node . moduleSpecifier &&
447
+ node . moduleSpecifier . kind === ts . SyntaxKind . StringLiteral &&
448
+ ( node . moduleSpecifier as ts . StringLiteral ) . text === 'tslib' &&
449
+ node . importClause &&
450
+ node . importClause . namedBindings &&
451
+ node . importClause . namedBindings . kind === ts . SyntaxKind . NamespaceImport ) ;
452
+ }
453
+
454
+ // Find all namespace imports for `tslib`.
455
+ function findTslibImports ( node : ts . Node ) : ts . NamespaceImport [ ] {
456
+ const imports : ts . NamespaceImport [ ] = [ ] ;
457
+ ts . forEachChild ( node , ( child ) => {
458
+ if ( child . kind === ts . SyntaxKind . ImportDeclaration ) {
459
+ const importDecl = child as ts . ImportDeclaration ;
460
+ if ( isTslibImport ( importDecl ) ) {
461
+ const importClause = importDecl . importClause as ts . ImportClause ;
462
+ const namespaceImport = importClause . namedBindings as ts . NamespaceImport ;
463
+ imports . push ( namespaceImport ) ;
464
+ }
465
+ }
466
+ } ) ;
467
+
468
+ return imports ;
469
+ }
470
+
471
+ // Check if an identifier is part of the known tslib identifiers.
472
+ function identifierIsTslib (
473
+ id : ts . Identifier ,
474
+ tslibImports : ts . NamespaceImport [ ] ,
475
+ checker : ts . TypeChecker ,
476
+ ) : boolean {
477
+ const symbol = checker . getSymbolAtLocation ( id ) ;
478
+ if ( ! symbol || ! symbol . declarations || ! symbol . declarations . length ) {
479
+ return false ;
480
+ }
481
+
482
+ return symbol
483
+ . declarations
484
+ . some ( ( spec ) => tslibImports . indexOf ( spec as ts . NamespaceImport ) !== - 1 ) ;
485
+ }
486
+
487
+ // Check if a function call is a tslib helper.
488
+ function isTslibHelper (
489
+ callExpr : ts . CallExpression ,
490
+ helper : string ,
491
+ tslibImports : ts . NamespaceImport [ ] ,
492
+ checker : ts . TypeChecker ,
493
+ ) {
494
+
495
+ let callExprIdent = callExpr . expression as ts . Identifier ;
496
+
497
+ if ( callExpr . expression . kind !== ts . SyntaxKind . Identifier ) {
498
+ if ( callExpr . expression . kind === ts . SyntaxKind . PropertyAccessExpression ) {
499
+ const propAccess = callExpr . expression as ts . PropertyAccessExpression ;
500
+ const left = propAccess . expression ;
501
+ callExprIdent = propAccess . name ;
502
+
503
+ if ( left . kind !== ts . SyntaxKind . Identifier ) {
504
+ return false ;
505
+ }
506
+
507
+ const id = left as ts . Identifier ;
508
+
509
+ if ( ! identifierIsTslib ( id , tslibImports , checker ) ) {
510
+ return false ;
511
+ }
512
+
513
+ } else {
514
+ return false ;
515
+ }
516
+ }
517
+
518
+ // node.text on a name that starts with two underscores will return three instead.
519
+ // Unless it's an expression like tslib.__decorate, in which case it's only 2.
520
+ if ( callExprIdent . text !== `_${ helper } ` && callExprIdent . text !== helper ) {
521
+ return false ;
522
+ }
523
+
524
+ return true ;
525
+ }
0 commit comments