1
- import * as ts from 'typescript ' ;
1
+ import * as fs from 'fs ' ;
2
2
import * as path from 'path' ;
3
+ import * as ts from 'typescript' ;
3
4
4
5
import { NgModule } from '@angular/core' ;
5
6
import * as ngCompiler from '@angular/compiler-cli' ;
@@ -8,81 +9,129 @@ import {tsc} from '@angular/tsc-wrapped/src/tsc';
8
9
import { patchReflectorHost } from './reflector_host' ;
9
10
import { WebpackResourceLoader } from './resource_loader' ;
10
11
import { createResolveDependenciesFromContextMap } from './utils' ;
11
- import { AngularCompilerOptions } from '@angular/tsc-wrapped' ;
12
12
import { WebpackCompilerHost } from './compiler_host' ;
13
13
import { resolveEntryModuleFromMain } from './entry_resolver' ;
14
14
15
15
16
16
/**
17
17
* Option Constants
18
18
*/
19
- export interface AngularWebpackPluginOptions {
20
- tsconfigPath ?: string ;
21
- providers ?: any [ ] ;
22
- entryModule ?: string ;
23
- project : string ;
24
- baseDir : string ;
19
+ export interface AotPluginOptions {
20
+ tsConfigPath : string ;
25
21
basePath ?: string ;
22
+ entryModule ?: string ;
26
23
genDir ?: string ;
27
- main ?: string ;
24
+ mainPath ?: string ;
25
+ typeChecking ?: boolean ;
28
26
}
29
27
30
28
31
- export class NgcWebpackPlugin {
32
- projectPath : string ;
33
- rootModule : string ;
34
- rootModuleName : string ;
35
- reflector : ngCompiler . StaticReflector ;
36
- reflectorHost : ngCompiler . ReflectorHost ;
37
- program : ts . Program ;
38
- compilerHost : WebpackCompilerHost ;
39
- compilerOptions : ts . CompilerOptions ;
40
- angularCompilerOptions : AngularCompilerOptions ;
41
- files : any [ ] ;
42
- lazyRoutes : any ;
43
- loader : any ;
44
- genDir : string ;
45
- entryModule : string ;
46
-
47
- done : Promise < void > ;
48
-
49
- nmf : any = null ;
50
- cmf : any = null ;
51
- compiler : any = null ;
52
- compilation : any = null ;
53
-
54
- constructor ( public options : AngularWebpackPluginOptions ) {
55
- const tsConfig = tsc . readConfiguration ( options . project , options . baseDir ) ;
56
- this . compilerOptions = tsConfig . parsed . options ;
57
- this . files = tsConfig . parsed . fileNames ;
58
- this . angularCompilerOptions = Object . assign ( { } , tsConfig . ngOptions , options ) ;
59
-
60
- this . angularCompilerOptions . basePath = options . baseDir || process . cwd ( ) ;
61
- this . genDir = this . options . genDir
62
- || path . resolve ( process . cwd ( ) , this . angularCompilerOptions . genDir + '/app' ) ;
63
- this . entryModule = options . entryModule || ( this . angularCompilerOptions as any ) . entryModule ;
64
- if ( ! options . entryModule && options . main ) {
65
- this . entryModule = resolveEntryModuleFromMain ( options . main ) ;
29
+ export class ModuleRoute {
30
+ constructor ( public readonly path : string , public readonly className : string | null ) { }
31
+
32
+ toString ( ) {
33
+ return `${ this . path } #${ this . className } ` ;
34
+ }
35
+
36
+ static fromString ( entry : string ) : ModuleRoute {
37
+ const split = entry . split ( '#' ) ;
38
+ return new ModuleRoute ( split [ 0 ] , split [ 1 ] ) ;
39
+ }
40
+ }
41
+
42
+
43
+ export class AotPlugin {
44
+ private _entryModule : ModuleRoute ;
45
+ private _compilerOptions : ts . CompilerOptions ;
46
+ private _angularCompilerOptions : ngCompiler . AngularCompilerOptions ;
47
+ private _program : ts . Program ;
48
+ private _reflector : ngCompiler . StaticReflector ;
49
+ private _reflectorHost : ngCompiler . ReflectorHost ;
50
+ private _compilerHost : WebpackCompilerHost ;
51
+ private _resourceLoader : WebpackResourceLoader ;
52
+ private _lazyRoutes : { [ route : string ] : string } ;
53
+
54
+ private _donePromise : Promise < void > ;
55
+ private _compiler : any = null ;
56
+ private _compilation : any = null ;
57
+
58
+ private _typeCheck : boolean = true ;
59
+
60
+
61
+ constructor ( options : AotPluginOptions ) {
62
+ this . _setupOptions ( options ) ;
63
+ }
64
+
65
+ get basePath ( ) { return this . _angularCompilerOptions . basePath ; }
66
+ get compilation ( ) { return this . _compilation ; }
67
+ get compilerOptions ( ) { return this . _compilerOptions ; }
68
+ get done ( ) { return this . _donePromise ; }
69
+ get entryModule ( ) { return this . _entryModule ; }
70
+ get genDir ( ) { return this . _angularCompilerOptions . genDir ; }
71
+ get program ( ) { return this . _program ; }
72
+ get typeCheck ( ) { return this . _typeCheck ; }
73
+
74
+ private _setupOptions ( options : AotPluginOptions ) {
75
+ // Fill in the missing options.
76
+ if ( ! options . hasOwnProperty ( 'tsConfigPath' ) ) {
77
+ throw new Error ( 'Must specify "tsConfigPath" in the configuration of @ngtools/webpack.' ) ;
78
+ }
79
+
80
+ // Check the base path.
81
+ let basePath = path . resolve ( process . cwd ( ) , path . dirname ( options . tsConfigPath ) ) ;
82
+ if ( fs . statSync ( options . tsConfigPath ) . isDirectory ( ) ) {
83
+ basePath = options . tsConfigPath ;
84
+ }
85
+ if ( options . hasOwnProperty ( 'basePath' ) ) {
86
+ basePath = options . basePath ;
87
+ }
88
+
89
+ const tsConfig = tsc . readConfiguration ( options . tsConfigPath , basePath ) ;
90
+
91
+ // Check the genDir.
92
+ let genDir = basePath ;
93
+ if ( options . hasOwnProperty ( 'genDir' ) ) {
94
+ genDir = options . genDir ;
95
+ } else if ( tsConfig . ngOptions . hasOwnProperty ( 'genDir' ) ) {
96
+ genDir = tsConfig . ngOptions . genDir ;
97
+ }
98
+
99
+ this . _compilerOptions = tsConfig . parsed . options ;
100
+
101
+ if ( options . entryModule ) {
102
+ this . _entryModule = ModuleRoute . fromString ( options . entryModule ) ;
103
+ } else {
104
+ if ( options . mainPath ) {
105
+ this . _entryModule = ModuleRoute . fromString ( resolveEntryModuleFromMain ( options . mainPath ) ) ;
106
+ } else {
107
+ this . _entryModule = ModuleRoute . fromString ( ( tsConfig . ngOptions as any ) . entryModule ) ;
108
+ }
109
+ }
110
+ this . _angularCompilerOptions = Object . assign ( { } , tsConfig . ngOptions , {
111
+ basePath,
112
+ entryModule : this . _entryModule . toString ( ) ,
113
+ genDir
114
+ } ) ;
115
+
116
+ if ( options . hasOwnProperty ( 'typeChecking' ) ) {
117
+ this . _typeCheck = options . typeChecking ;
66
118
}
67
119
68
- const entryModule = this . entryModule ;
69
- const [ rootModule , rootNgModule ] = entryModule . split ( '#' ) ;
70
- this . projectPath = options . project ;
71
- this . rootModule = rootModule ;
72
- this . rootModuleName = rootNgModule ;
73
- this . compilerHost = new WebpackCompilerHost ( this . compilerOptions ) ;
74
- this . program = ts . createProgram ( this . files , this . compilerOptions , this . compilerHost ) ;
75
- this . reflectorHost = new ngCompiler . ReflectorHost (
76
- this . program , this . compilerHost , this . angularCompilerOptions ) ;
77
- this . reflector = new ngCompiler . StaticReflector ( this . reflectorHost ) ;
120
+ this . _compilerHost = new WebpackCompilerHost ( this . _compilerOptions ) ;
121
+ this . _program = ts . createProgram (
122
+ tsConfig . parsed . fileNames , this . _compilerOptions , this . _compilerHost ) ;
123
+ this . _reflectorHost = new ngCompiler . ReflectorHost (
124
+ this . _program , this . _compilerHost , this . _angularCompilerOptions ) ;
125
+ this . _reflector = new ngCompiler . StaticReflector ( this . _reflectorHost ) ;
126
+
127
+ // this._writeMain();
78
128
}
79
129
80
130
// registration hook for webpack plugin
81
131
apply ( compiler : any ) {
82
- this . compiler = compiler ;
83
- compiler . plugin ( 'normal-module-factory' , ( nmf : any ) => this . nmf = nmf ) ;
132
+ this . _compiler = compiler ;
133
+
84
134
compiler . plugin ( 'context-module-factory' , ( cmf : any ) => {
85
- this . cmf = cmf ;
86
135
cmf . plugin ( 'before-resolve' , ( request : any , callback : ( err ?: any , request ?: any ) => void ) => {
87
136
if ( ! request ) {
88
137
return callback ( ) ;
@@ -112,8 +161,8 @@ export class NgcWebpackPlugin {
112
161
113
162
compiler . plugin ( 'make' , ( compilation : any , cb : any ) => this . _make ( compilation , cb ) ) ;
114
163
compiler . plugin ( 'after-emit' , ( compilation : any , cb : any ) => {
115
- this . done = null ;
116
- this . compilation = null ;
164
+ this . _donePromise = null ;
165
+ this . _compilation = null ;
117
166
compilation . _ngToolsWebpackPluginInstance = null ;
118
167
cb ( ) ;
119
168
} ) ;
@@ -129,68 +178,82 @@ export class NgcWebpackPlugin {
129
178
}
130
179
131
180
private _make ( compilation : any , cb : ( err ?: any , request ?: any ) => void ) {
132
- const rootModulePath = path . normalize ( this . rootModule + '.ts' ) ;
133
- const rootModuleName = this . rootModuleName ;
134
- this . compilation = compilation ;
181
+ const rootModulePath = path . normalize ( this . _entryModule . path + '.ts' ) ;
182
+ const rootModuleName = this . _entryModule . className ;
183
+ this . _compilation = compilation ;
135
184
136
- if ( this . compilation . _ngToolsWebpackPluginInstance ) {
137
- cb ( new Error ( 'A ngtools/webpack plugin already exist for this compilation.' ) ) ;
185
+ if ( this . _compilation . _ngToolsWebpackPluginInstance ) {
186
+ cb ( new Error ( 'An @ ngtools/webpack plugin already exist for this compilation.' ) ) ;
138
187
}
139
- this . compilation . _ngToolsWebpackPluginInstance = this ;
188
+ this . _compilation . _ngToolsWebpackPluginInstance = this ;
140
189
141
- this . loader = new WebpackResourceLoader ( compilation ) ;
190
+ this . _resourceLoader = new WebpackResourceLoader ( compilation ) ;
142
191
143
- const i18nOptions : any = {
192
+ const i18nOptions : ngCompiler . NgcCliOptions = {
144
193
i18nFile : undefined ,
145
194
i18nFormat : undefined ,
146
195
locale : undefined ,
147
- basePath : this . options . baseDir
196
+ basePath : this . basePath
148
197
} ;
149
198
150
199
// Create the Code Generator.
151
200
const codeGenerator = ngCompiler . CodeGenerator . create (
152
- this . angularCompilerOptions ,
201
+ this . _angularCompilerOptions ,
153
202
i18nOptions ,
154
- this . program ,
155
- this . compilerHost ,
156
- new ngCompiler . NodeReflectorHostContext ( this . compilerHost ) ,
157
- this . loader
203
+ this . _program ,
204
+ this . _compilerHost ,
205
+ new ngCompiler . NodeReflectorHostContext ( this . _compilerHost ) ,
206
+ this . _resourceLoader
158
207
) ;
159
208
160
209
// We need to temporarily patch the CodeGenerator until either it's patched or allows us
161
210
// to pass in our own ReflectorHost.
162
211
patchReflectorHost ( codeGenerator ) ;
163
- this . done = codeGenerator . codegen ( )
212
+ this . _donePromise = codeGenerator . codegen ( )
164
213
. then ( ( ) => {
165
- // process the lazy routes
166
- const lazyModules = this . _processNgModule ( rootModulePath , rootModuleName , rootModulePath )
167
- . map ( moduleKey => moduleKey . split ( '#' ) [ 0 ] ) ;
168
- this . lazyRoutes = lazyModules . reduce ( ( lazyRoutes : any , lazyModule : any ) => {
169
- const genDir = this . genDir ;
170
- lazyRoutes [ `${ lazyModule } .ngfactory` ] = path . join ( genDir , lazyModule + '.ngfactory.ts' ) ;
214
+ const diagnostics = this . _program . getGlobalDiagnostics ( ) ;
215
+ if ( diagnostics . length > 0 ) {
216
+ const message = diagnostics
217
+ . map ( diagnostic => {
218
+ const { line, character} = diagnostic . file . getLineAndCharacterOfPosition ( diagnostic . start ) ;
219
+ const message = ts . flattenDiagnosticMessageText ( diagnostic . messageText , '\n' ) ;
220
+ return `${ diagnostic . file . fileName } (${ line + 1 } ,${ character + 1 } ): ${ message } )` ;
221
+ } )
222
+ . join ( '\n' ) ;
223
+
224
+ throw new Error ( message ) ;
225
+ }
226
+ } )
227
+ . then ( ( ) => {
228
+ // Process the lazy routes
229
+ this . _lazyRoutes =
230
+ this . _processNgModule ( rootModulePath , rootModuleName , rootModulePath )
231
+ . map ( module => ModuleRoute . fromString ( module ) )
232
+ . reduce ( ( lazyRoutes : any , module : ModuleRoute ) => {
233
+ lazyRoutes [ `${ module . path } .ngfactory` ] = path . join (
234
+ this . genDir , module . path + '.ngfactory.ts' ) ;
171
235
return lazyRoutes ;
172
236
} , { } ) ;
173
237
} )
174
238
. then ( ( ) => cb ( ) , ( err ) => cb ( err ) ) ;
175
239
}
176
240
177
241
private _processNgModule ( mod : string , ngModuleName : string , containingFile : string ) : string [ ] {
178
- const staticSymbol = this . reflectorHost . findDeclaration ( mod , ngModuleName , containingFile ) ;
242
+ const staticSymbol = this . _reflectorHost . findDeclaration ( mod , ngModuleName , containingFile ) ;
179
243
const entryNgModuleMetadata = this . getNgModuleMetadata ( staticSymbol ) ;
180
244
const loadChildren = this . extractLoadChildren ( entryNgModuleMetadata ) ;
181
245
182
- return loadChildren . reduce ( ( res , lc ) => {
183
- const [ childModule , childNgModule ] = lc . split ( '#' ) ;
246
+ return loadChildren . reduce ( ( res : string [ ] , loadChildren : string ) => {
247
+ const childModule = ModuleRoute . fromString ( loadChildren ) ;
184
248
185
249
// TODO calculate a different containingFile for relative paths
186
-
187
- const children = this . _processNgModule ( childModule , childNgModule , containingFile ) ;
188
- return res . concat ( children ) ;
250
+ return res . concat (
251
+ this . _processNgModule ( childModule . path , childModule . className , containingFile ) ) ;
189
252
} , loadChildren ) ;
190
253
}
191
254
192
255
private getNgModuleMetadata ( staticSymbol : ngCompiler . StaticSymbol ) {
193
- const ngModules = this . reflector . annotations ( staticSymbol ) . filter ( s => s instanceof NgModule ) ;
256
+ const ngModules = this . _reflector . annotations ( staticSymbol ) . filter ( s => s instanceof NgModule ) ;
194
257
if ( ngModules . length === 0 ) {
195
258
throw new Error ( `${ staticSymbol . name } is not an NgModule` ) ;
196
259
}
@@ -208,7 +271,7 @@ export class NgcWebpackPlugin {
208
271
if ( ! providers ) {
209
272
return [ ] ;
210
273
}
211
- const ROUTES = this . reflectorHost . findDeclaration (
274
+ const ROUTES = this . _reflectorHost . findDeclaration (
212
275
'@angular/router/src/router_config_loader' , 'ROUTES' , undefined ) ;
213
276
214
277
return providers . reduce ( ( m , p ) => {
0 commit comments