11import * as ts from 'typescript' ;
2+ import { normalize , resolve } from '@angular-devkit/core' ;
23import { Tree , DirEntry } from '@angular-devkit/schematics' ;
34
45export function visitTSSourceFiles < Result = void > (
@@ -17,6 +18,190 @@ export function visitTSSourceFiles<Result = void>(
1718 return result ;
1819}
1920
21+ export function visitTemplates (
22+ tree : Tree ,
23+ visitor : (
24+ template : {
25+ fileName : string ;
26+ content : string ;
27+ inline : boolean ;
28+ start : number ;
29+ } ,
30+ tree : Tree
31+ ) => void
32+ ) : void {
33+ visitTSSourceFiles ( tree , source => {
34+ visitComponents ( source , ( _ , decoratorExpressionNode ) => {
35+ ts . forEachChild ( decoratorExpressionNode , function findTemplates ( n ) {
36+ if ( ts . isPropertyAssignment ( n ) && ts . isIdentifier ( n . name ) ) {
37+ if (
38+ n . name . text === 'template' &&
39+ ts . isStringLiteralLike ( n . initializer )
40+ ) {
41+ // Need to add an offset of one to the start because the template quotes are
42+ // not part of the template content.
43+ const templateStartIdx = n . initializer . getStart ( ) + 1 ;
44+ visitor (
45+ {
46+ fileName : source . fileName ,
47+ content : n . initializer . text ,
48+ inline : true ,
49+ start : templateStartIdx ,
50+ } ,
51+ tree
52+ ) ;
53+ return ;
54+ } else if (
55+ n . name . text === 'templateUrl' &&
56+ ts . isStringLiteralLike ( n . initializer )
57+ ) {
58+ const parts = normalize ( source . fileName )
59+ . split ( '/' )
60+ . slice ( 0 , - 1 ) ;
61+ const templatePath = resolve (
62+ normalize ( parts . join ( '/' ) ) ,
63+ normalize ( n . initializer . text )
64+ ) ;
65+ if ( ! tree . exists ( templatePath ) ) {
66+ return ;
67+ }
68+
69+ const fileContent = tree . read ( templatePath ) ;
70+ if ( ! fileContent ) {
71+ return ;
72+ }
73+
74+ visitor (
75+ {
76+ fileName : templatePath ,
77+ content : fileContent . toString ( ) ,
78+ inline : false ,
79+ start : 0 ,
80+ } ,
81+ tree
82+ ) ;
83+ return ;
84+ }
85+ }
86+
87+ ts . forEachChild ( n , findTemplates ) ;
88+ } ) ;
89+ } ) ;
90+ } ) ;
91+ }
92+
93+ export function visitNgModuleImports (
94+ sourceFile : ts . SourceFile ,
95+ callback : (
96+ importNode : ts . PropertyAssignment ,
97+ elementExpressions : ts . NodeArray < ts . Expression >
98+ ) => void
99+ ) {
100+ visitNgModuleProperty ( sourceFile , callback , 'imports' ) ;
101+ }
102+
103+ export function visitNgModuleExports (
104+ sourceFile : ts . SourceFile ,
105+ callback : (
106+ exportNode : ts . PropertyAssignment ,
107+ elementExpressions : ts . NodeArray < ts . Expression >
108+ ) => void
109+ ) {
110+ visitNgModuleProperty ( sourceFile , callback , 'exports' ) ;
111+ }
112+
113+ function visitNgModuleProperty (
114+ sourceFile : ts . SourceFile ,
115+ callback : (
116+ nodes : ts . PropertyAssignment ,
117+ elementExpressions : ts . NodeArray < ts . Expression >
118+ ) => void ,
119+ property : string
120+ ) {
121+ visitNgModules ( sourceFile , ( _ , decoratorExpressionNode ) => {
122+ ts . forEachChild ( decoratorExpressionNode , function findTemplates ( n ) {
123+ if (
124+ ts . isPropertyAssignment ( n ) &&
125+ ts . isIdentifier ( n . name ) &&
126+ n . name . text === property &&
127+ ts . isArrayLiteralExpression ( n . initializer )
128+ ) {
129+ callback ( n , n . initializer . elements ) ;
130+ return ;
131+ }
132+
133+ ts . forEachChild ( n , findTemplates ) ;
134+ } ) ;
135+ } ) ;
136+ }
137+ export function visitComponents (
138+ sourceFile : ts . SourceFile ,
139+ callback : (
140+ classDeclarationNode : ts . ClassDeclaration ,
141+ decoratorExpressionNode : ts . ObjectLiteralExpression
142+ ) => void
143+ ) {
144+ visitDecorator ( sourceFile , 'Component' , callback ) ;
145+ }
146+
147+ export function visitNgModules (
148+ sourceFile : ts . SourceFile ,
149+ callback : (
150+ classDeclarationNode : ts . ClassDeclaration ,
151+ decoratorExpressionNode : ts . ObjectLiteralExpression
152+ ) => void
153+ ) {
154+ visitDecorator ( sourceFile , 'NgModule' , callback ) ;
155+ }
156+
157+ export function visitDecorator (
158+ sourceFile : ts . SourceFile ,
159+ decoratorName : string ,
160+ callback : (
161+ classDeclarationNode : ts . ClassDeclaration ,
162+ decoratorExpressionNode : ts . ObjectLiteralExpression
163+ ) => void
164+ ) {
165+ ts . forEachChild ( sourceFile , function findClassDeclaration ( node ) {
166+ if ( ! ts . isClassDeclaration ( node ) ) {
167+ ts . forEachChild ( node , findClassDeclaration ) ;
168+ }
169+
170+ const classDeclarationNode = node as ts . ClassDeclaration ;
171+
172+ if (
173+ ! classDeclarationNode . decorators ||
174+ ! classDeclarationNode . decorators . length
175+ ) {
176+ return ;
177+ }
178+
179+ const componentDecorator = classDeclarationNode . decorators . find ( d => {
180+ return (
181+ ts . isCallExpression ( d . expression ) &&
182+ ts . isIdentifier ( d . expression . expression ) &&
183+ d . expression . expression . text === decoratorName
184+ ) ;
185+ } ) ;
186+
187+ if ( ! componentDecorator ) {
188+ return ;
189+ }
190+
191+ const { expression } = componentDecorator ;
192+ if ( ! ts . isCallExpression ( expression ) ) {
193+ return ;
194+ }
195+
196+ const [ arg ] = expression . arguments ;
197+ if ( ! ts . isObjectLiteralExpression ( arg ) ) {
198+ return ;
199+ }
200+
201+ callback ( classDeclarationNode , arg ) ;
202+ } ) ;
203+ }
204+
20205function * visit ( directory : DirEntry ) : IterableIterator < ts . SourceFile > {
21206 for ( const path of directory . subfiles ) {
22207 if ( path . endsWith ( '.ts' ) && ! path . endsWith ( '.d.ts' ) ) {
@@ -42,31 +227,3 @@ function* visit(directory: DirEntry): IterableIterator<ts.SourceFile> {
42227 yield * visit ( directory . dir ( path ) ) ;
43228 }
44229}
45-
46- export function visitNgModuleImports (
47- sourceFile : ts . SourceFile ,
48- callback : (
49- importNode : ts . PropertyAssignment ,
50- elementExpressions : ts . NodeArray < ts . Expression >
51- ) => void
52- ) {
53- ts . forEachChild ( sourceFile , function findDecorator ( node ) {
54- if ( ! ts . isDecorator ( node ) ) {
55- ts . forEachChild ( node , findDecorator ) ;
56- return ;
57- }
58-
59- ts . forEachChild ( node , function findImportsNode ( n ) {
60- if (
61- ts . isPropertyAssignment ( n ) &&
62- ts . isArrayLiteralExpression ( n . initializer ) &&
63- ts . isIdentifier ( n . name ) &&
64- n . name . text === 'imports'
65- ) {
66- callback ( n , n . initializer . elements ) ;
67- }
68-
69- ts . forEachChild ( n , findImportsNode ) ;
70- } ) ;
71- } ) ;
72- }
0 commit comments