@@ -18,11 +18,137 @@ function _getContentOfKeyLiteral(source: ts.SourceFile, node: ts.Node): string {
18
18
}
19
19
}
20
20
21
+
22
+ function _angularImportsFromNode ( node : ts . ImportDeclaration , sourceFile : ts . SourceFile ) : string [ ] {
23
+ const ms = node . moduleSpecifier ;
24
+ let modulePath : string | null = null ;
25
+ switch ( ms . kind ) {
26
+ case ts . SyntaxKind . StringLiteral :
27
+ modulePath = ( ms as ts . StringLiteral ) . text ;
28
+ break ;
29
+ default :
30
+ return [ ] ;
31
+ }
32
+
33
+ if ( ! modulePath . startsWith ( '@angular/' ) ) {
34
+ return [ ] ;
35
+ }
36
+
37
+ if ( node . importClause ) {
38
+ if ( node . importClause . name ) {
39
+ // This is of the form `import Name from 'path'`. Ignore.
40
+ return [ ] ;
41
+ } else if ( node . importClause . namedBindings ) {
42
+ const nb = node . importClause . namedBindings ;
43
+ if ( nb . kind == ts . SyntaxKind . NamespaceImport ) {
44
+ // This is of the form `import * as name from 'path'`. Return `name.`.
45
+ return [ ( nb as ts . NamespaceImport ) . name . text + '.' ] ;
46
+ } else {
47
+ // This is of the form `import {a,b,c} from 'path'`
48
+ const namedImports = nb as ts . NamedImports ;
49
+
50
+ return namedImports . elements
51
+ . map ( ( is : ts . ImportSpecifier ) => is . propertyName ? is . propertyName . text : is . name . text ) ;
52
+ }
53
+ }
54
+ } else {
55
+ // This is of the form `import 'path';`. Nothing to do.
56
+ return [ ] ;
57
+ }
58
+ }
59
+
60
+
61
+ function _ctorParameterFromTypeReference ( paramNode : ts . ParameterDeclaration ,
62
+ angularImports : string [ ] ,
63
+ refactor : TypeScriptFileRefactor ) {
64
+ if ( paramNode . type . kind == ts . SyntaxKind . TypeReference ) {
65
+ const type = paramNode . type as ts . TypeReferenceNode ;
66
+ const decorators = refactor . findAstNodes ( paramNode , ts . SyntaxKind . Decorator ) as ts . Decorator [ ] ;
67
+ const decoratorStr = decorators
68
+ . map ( decorator => {
69
+ const fnName =
70
+ ( refactor . findFirstAstNode ( decorator , ts . SyntaxKind . CallExpression ) as ts . CallExpression )
71
+ . expression . getText ( refactor . sourceFile ) ;
72
+
73
+ if ( angularImports . indexOf ( fnName ) === - 1 ) {
74
+ return null ;
75
+ } else {
76
+ return fnName ;
77
+ }
78
+ } )
79
+ . filter ( x => ! ! x )
80
+ . map ( name => `{ type: ${ name } }` )
81
+ . join ( ', ' ) ;
82
+
83
+ if ( type . typeName . kind == ts . SyntaxKind . Identifier ) {
84
+ const typeName = type . typeName as ts . Identifier ;
85
+ if ( decorators . length > 0 ) {
86
+ return `{ type: ${ typeName . text } , decorators: [${ decoratorStr } ] }` ;
87
+ }
88
+ return `{ type: ${ typeName . text } }` ;
89
+ }
90
+ }
91
+
92
+ return 'null' ;
93
+ }
94
+
95
+
96
+ function _addCtorParameters ( classNode : ts . ClassDeclaration ,
97
+ angularImports : string [ ] ,
98
+ refactor : TypeScriptFileRefactor ) {
99
+ // For every classes with constructors, output the ctorParameters function which contains a list
100
+ // of injectable types.
101
+ const ctor = (
102
+ refactor . findFirstAstNode ( classNode , ts . SyntaxKind . Constructor ) as ts . ConstructorDeclaration ) ;
103
+ if ( ! ctor ) {
104
+ // A class can be missing a constructor, and that's _okay_.
105
+ return ;
106
+ }
107
+
108
+ const params = Array . from ( ctor . parameters ) . map ( paramNode => {
109
+ switch ( paramNode . type . kind ) {
110
+ case ts . SyntaxKind . TypeReference :
111
+ return _ctorParameterFromTypeReference ( paramNode , angularImports , refactor ) ;
112
+ default :
113
+ return 'null' ;
114
+ }
115
+ } ) ;
116
+
117
+ const ctorParametersDecl = `static ctorParameters() { return [ ${ params . join ( ', ' ) } ]; }` ;
118
+ refactor . prependBefore ( classNode . getLastToken ( refactor . sourceFile ) , ctorParametersDecl ) ;
119
+ }
120
+
121
+
21
122
function _removeDecorators ( refactor : TypeScriptFileRefactor ) {
22
- // TODO: replace this by tsickle.
123
+ const angularImports : string [ ]
124
+ = refactor . findAstNodes ( refactor . sourceFile , ts . SyntaxKind . ImportDeclaration )
125
+ . map ( ( node : ts . ImportDeclaration ) => _angularImportsFromNode ( node , refactor . sourceFile ) )
126
+ . reduce ( ( acc : string [ ] , current : string [ ] ) => acc . concat ( current ) , [ ] ) ;
127
+
23
128
// Find all decorators.
24
- // refactor.findAstNodes(refactor.sourceFile, ts.SyntaxKind.Decorator)
25
- // .forEach(d => refactor.removeNode(d));
129
+ refactor . findAstNodes ( refactor . sourceFile , ts . SyntaxKind . Decorator )
130
+ . forEach ( node => {
131
+ // First, add decorators to classes to the classes array.
132
+ if ( node . parent ) {
133
+ const declarations = refactor . findAstNodes ( node . parent ,
134
+ ts . SyntaxKind . ClassDeclaration , false , 1 ) ;
135
+ if ( declarations . length > 0 ) {
136
+ _addCtorParameters ( declarations [ 0 ] as ts . ClassDeclaration , angularImports , refactor ) ;
137
+ }
138
+ }
139
+
140
+ refactor . findAstNodes ( node , ts . SyntaxKind . CallExpression )
141
+ . filter ( ( node : ts . CallExpression ) => {
142
+ const fnName = node . expression . getText ( refactor . sourceFile ) ;
143
+ if ( fnName . indexOf ( '.' ) != - 1 ) {
144
+ // Since this is `a.b`, see if it's the same namespace as a namespace import.
145
+ return angularImports . indexOf ( fnName . replace ( / \. .* $ / , '' ) + '.' ) != - 1 ;
146
+ } else {
147
+ return angularImports . indexOf ( fnName ) != - 1 ;
148
+ }
149
+ } )
150
+ . forEach ( ( ) => refactor . removeNode ( node ) ) ;
151
+ } ) ;
26
152
}
27
153
28
154
0 commit comments