1
1
// @ignoreDep @angular /compiler-cli
2
- // @ignoreDep @angular /compiler-cli/ngtools2
3
2
import * as fs from 'fs' ;
3
+ import { fork , ChildProcess } from 'child_process' ;
4
4
import * as path from 'path' ;
5
5
import * as ts from 'typescript' ;
6
6
7
7
const { __NGTOOLS_PRIVATE_API_2 , VERSION } = require ( '@angular/compiler-cli' ) ;
8
8
const ContextElementDependency = require ( 'webpack/lib/dependencies/ContextElementDependency' ) ;
9
9
const NodeWatchFileSystem = require ( 'webpack/lib/node/NodeWatchFileSystem' ) ;
10
+ const treeKill = require ( 'tree-kill' ) ;
10
11
11
12
import { WebpackResourceLoader } from './resource_loader' ;
12
13
import { WebpackCompilerHost } from './compiler_host' ;
@@ -24,10 +25,10 @@ import {
24
25
replaceResources
25
26
} from './transformers' ;
26
27
import { time , timeEnd } from './benchmark' ;
28
+ import { InitMessage , UpdateMessage } from './type_checker' ;
27
29
28
30
// These imports do not exist on Angular versions lower than 5, so we cannot use a static ES6
29
31
// import. Instead we copy their types into './ngtools_api2.d.ts'.
30
- // @ignoreDep @angular /compiler-cli/src/transformers/api
31
32
let compilerCliNgtools : any = { } ;
32
33
try {
33
34
compilerCliNgtools = require ( '@angular/compiler-cli/ngtools2' ) ;
@@ -41,6 +42,7 @@ import {
41
42
UNKNOWN_ERROR_CODE ,
42
43
SOURCE ,
43
44
Program ,
45
+ CompilerOptions ,
44
46
CompilerHost ,
45
47
CreateProgramInterface ,
46
48
CreateCompilerHostInterface ,
@@ -88,9 +90,9 @@ export class AngularCompilerPlugin implements Tapable {
88
90
89
91
// TS compilation.
90
92
private _compilerOptions : ts . CompilerOptions ;
91
- private _angularCompilerOptions : any ;
93
+ private _angularCompilerOptions : CompilerOptions ;
92
94
private _tsFilenames : string [ ] ;
93
- private _program : ts . Program & Program ;
95
+ private _program : ts . Program | Program ;
94
96
private _compilerHost : WebpackCompilerHost ;
95
97
private _angularCompilerHost : WebpackCompilerHost & CompilerHost ;
96
98
// Contains `moduleImportPath#exportName` => `fullModulePath`.
@@ -109,6 +111,10 @@ export class AngularCompilerPlugin implements Tapable {
109
111
private _compilation : any = null ;
110
112
private _failedCompilation = false ;
111
113
114
+ // TypeChecker process.
115
+ private _forkTypeChecker = true ;
116
+ private _typeCheckerProcess : ChildProcess ;
117
+
112
118
constructor ( options : AngularCompilerPluginOptions ) {
113
119
this . _options = Object . assign ( { } , options ) ;
114
120
this . _setupOptions ( this . _options ) ;
@@ -244,7 +250,8 @@ export class AngularCompilerPlugin implements Tapable {
244
250
this . _angularCompilerOptions . i18nInLocale = options . locale ;
245
251
}
246
252
if ( options . hasOwnProperty ( 'missingTranslation' ) ) {
247
- this . _angularCompilerOptions . i18nInMissingTranslations = options . missingTranslation ;
253
+ this . _angularCompilerOptions . i18nInMissingTranslations =
254
+ options . missingTranslation as 'error' | 'warning' | 'ignore' ;
248
255
}
249
256
250
257
// Use entryModule if available in options, otherwise resolve it from mainPath after program
@@ -278,10 +285,15 @@ export class AngularCompilerPlugin implements Tapable {
278
285
279
286
// TODO: consider really using platform names in the plugin options.
280
287
this . _platform = options . replaceExport ? PLATFORM . Server : PLATFORM . Browser ;
288
+
289
+ // Create a new process for the type checker.
290
+ if ( this . _forkTypeChecker ) {
291
+ this . _createForkedTypeChecker ( ) ;
292
+ }
281
293
}
282
294
283
295
private _getTsProgram ( ) {
284
- return this . _JitMode ? this . _program : this . _program . getTsProgram ( ) ;
296
+ return this . _JitMode ? this . _program as ts . Program : ( this . _program as Program ) . getTsProgram ( ) ;
285
297
}
286
298
287
299
private _getChangedTsFiles ( ) {
@@ -293,19 +305,26 @@ export class AngularCompilerPlugin implements Tapable {
293
305
private _createOrUpdateProgram ( ) {
294
306
this . _getChangedTsFiles ( ) . forEach ( ( file ) => {
295
307
if ( ! this . _tsFilenames . includes ( file ) ) {
308
+ // TODO: figure out if action is needed for files that were removed from the compilation.
296
309
this . _tsFilenames . push ( file ) ;
297
310
}
298
311
} ) ;
299
312
313
+ // Update the forked type checker.
314
+ if ( ! this . _firstRun && this . _forkTypeChecker ) {
315
+ this . _updateForkedTypeChecker ( this . _tsFilenames ) ;
316
+ }
317
+
300
318
if ( this . _JitMode ) {
319
+
301
320
// Create the TypeScript program.
302
321
time ( '_createOrUpdateProgram.ts.createProgram' ) ;
303
322
this . _program = ts . createProgram (
304
323
this . _tsFilenames ,
305
324
this . _angularCompilerOptions ,
306
325
this . _angularCompilerHost ,
307
- this . _program
308
- ) as ts . Program & Program ;
326
+ this . _program as ts . Program
327
+ ) ;
309
328
timeEnd ( '_createOrUpdateProgram.ts.createProgram' ) ;
310
329
311
330
return Promise . resolve ( ) ;
@@ -316,8 +335,8 @@ export class AngularCompilerPlugin implements Tapable {
316
335
rootNames : this . _tsFilenames ,
317
336
options : this . _angularCompilerOptions ,
318
337
host : this . _angularCompilerHost ,
319
- oldProgram : this . _program
320
- } ) as ts . Program & Program ;
338
+ oldProgram : this . _program as Program
339
+ } ) ;
321
340
timeEnd ( '_createOrUpdateProgram.ng.createProgram' ) ;
322
341
323
342
time ( '_createOrUpdateProgram.ng.loadNgStructureAsync' ) ;
@@ -408,6 +427,26 @@ export class AngularCompilerPlugin implements Tapable {
408
427
} ) ;
409
428
}
410
429
430
+ private _createForkedTypeChecker ( ) {
431
+ // Bootstrap type checker is using local CLI.
432
+ const g : any = global ;
433
+ const typeCheckerFile : string = g [ 'angularCliIsLocal' ]
434
+ ? './type_checker_bootstrap.js'
435
+ : './type_checker.js' ;
436
+
437
+ this . _typeCheckerProcess = fork ( path . resolve ( __dirname , typeCheckerFile ) ) ;
438
+ this . _typeCheckerProcess . send ( new InitMessage (
439
+ this . _compilerOptions , this . _basePath , this . _JitMode ) ) ;
440
+
441
+ process . on ( 'exit' , ( ) => {
442
+ treeKill ( process . pid , 'SIGTERM' ) ;
443
+ } ) ;
444
+ }
445
+
446
+ private _updateForkedTypeChecker ( changedTsFiles : string [ ] ) {
447
+ this . _typeCheckerProcess . send ( new UpdateMessage ( changedTsFiles ) ) ;
448
+ }
449
+
411
450
412
451
// Registration hook for webpack plugin.
413
452
apply ( compiler : any ) {
@@ -596,17 +635,20 @@ export class AngularCompilerPlugin implements Tapable {
596
635
// Build transforms, emit and report errors if there are changes or it's the first run.
597
636
if ( changedFiles . length > 0 || this . _firstRun ) {
598
637
638
+ // We now have the final list of changed TS files.
599
639
// Go through each changed file and add transforms as needed.
600
- time ( '_update.transformOps' ) ;
601
640
const sourceFiles = this . _getChangedTsFiles ( ) . map ( ( fileName ) => {
641
+ time ( '_update.getSourceFile' ) ;
602
642
const sourceFile = this . _getTsProgram ( ) . getSourceFile ( fileName ) ;
603
643
if ( ! sourceFile ) {
604
644
throw new Error ( `${ fileName } is not part of the TypeScript compilation. `
605
- + `Please include it in your tsconfig via the 'files' or 'include' property.` ) ;
645
+ + `Please include it in your tsconfig via the 'files' or 'include' property.` ) ;
606
646
}
647
+ timeEnd ( '_update.getSourceFile' ) ;
607
648
return sourceFile ;
608
649
} ) ;
609
650
651
+ time ( '_update.transformOps' ) ;
610
652
sourceFiles . forEach ( ( sf ) => {
611
653
const fileName = this . _compilerHost . resolve ( sf . fileName ) ;
612
654
let transformOps = [ ] ;
@@ -710,23 +752,26 @@ export class AngularCompilerPlugin implements Tapable {
710
752
let shouldEmit = true ;
711
753
712
754
if ( this . _JitMode ) {
713
- const tsProgram : ts . Program = program ;
755
+ const tsProgram = program as ts . Program ;
714
756
715
- // Check parameter diagnostics.
716
- // TODO: check this only on initial program creation.
717
- time ( '_emit.ts.getOptionsDiagnostics' ) ;
718
- shouldEmit = shouldEmit && checkDiagnostics ( tsProgram . getOptionsDiagnostics ( ) ) ;
719
- timeEnd ( '_emit.ts.getOptionsDiagnostics' ) ;
757
+ if ( this . _firstRun ) {
758
+ // Check parameter diagnostics.
759
+ time ( '_emit.ts.getOptionsDiagnostics' ) ;
760
+ shouldEmit = shouldEmit && checkDiagnostics ( tsProgram . getOptionsDiagnostics ( ) ) ;
761
+ timeEnd ( '_emit.ts.getOptionsDiagnostics' ) ;
762
+ }
720
763
721
- // Check syntactic diagnostics.
722
- time ( '_emit.ts.getSyntacticDiagnostics' ) ;
723
- shouldEmit = shouldEmit && checkDiagnostics ( tsProgram . getSyntacticDiagnostics ( ) ) ;
724
- timeEnd ( '_emit.ts.getSyntacticDiagnostics' ) ;
764
+ if ( this . _firstRun || ! this . _forkTypeChecker ) {
765
+ // Check syntactic diagnostics.
766
+ time ( '_emit.ts.getSyntacticDiagnostics' ) ;
767
+ shouldEmit = shouldEmit && checkDiagnostics ( tsProgram . getSyntacticDiagnostics ( ) ) ;
768
+ timeEnd ( '_emit.ts.getSyntacticDiagnostics' ) ;
725
769
726
- // Check semantic diagnostics.
727
- time ( '_emit.ts.getSemanticDiagnostics' ) ;
728
- shouldEmit = shouldEmit && checkDiagnostics ( tsProgram . getSemanticDiagnostics ( ) ) ;
729
- timeEnd ( '_emit.ts.getSemanticDiagnostics' ) ;
770
+ // Check semantic diagnostics.
771
+ time ( '_emit.ts.getSemanticDiagnostics' ) ;
772
+ shouldEmit = shouldEmit && checkDiagnostics ( tsProgram . getSemanticDiagnostics ( ) ) ;
773
+ timeEnd ( '_emit.ts.getSemanticDiagnostics' ) ;
774
+ }
730
775
731
776
if ( shouldEmit ) {
732
777
sourceFiles . forEach ( ( sf ) => {
@@ -740,31 +785,41 @@ export class AngularCompilerPlugin implements Tapable {
740
785
} ) ;
741
786
}
742
787
} else {
743
- const angularProgram : Program = program ;
744
-
745
- // Check parameter diagnostics.
746
- // TODO: check this only on initial program creation.
747
- time ( '_emit.ng.getTsOptionDiagnostics+getNgOptionDiagnostics' ) ;
748
- shouldEmit = shouldEmit && checkDiagnostics ( [
749
- ...angularProgram . getTsOptionDiagnostics ( ) , ...angularProgram . getNgOptionDiagnostics ( )
750
- ] ) ;
751
- timeEnd ( '_emit.ng.getTsOptionDiagnostics+getNgOptionDiagnostics' ) ;
752
-
753
- // Check syntactic diagnostics.
754
- time ( '_emit.ng.getTsSyntacticDiagnostics' ) ;
755
- shouldEmit = shouldEmit && checkDiagnostics ( angularProgram . getTsSyntacticDiagnostics ( ) ) ;
756
- timeEnd ( '_emit.ng.getTsSyntacticDiagnostics' ) ;
757
-
758
- // Check TypeScript semantic and Angular structure diagnostics.
759
- time ( '_emit.ng.getTsSemanticDiagnostics+getNgStructuralDiagnostics' ) ;
760
- shouldEmit = shouldEmit && checkDiagnostics (
761
- [ ...angularProgram . getTsSemanticDiagnostics ( ) ,
762
- ...angularProgram . getNgStructuralDiagnostics ( )
763
- ] ) ;
764
- timeEnd ( '_emit.ng.getTsSemanticDiagnostics+getNgStructuralDiagnostics' ) ;
765
-
766
- // Check Angular semantic diagnostics
767
- shouldEmit = shouldEmit && checkDiagnostics ( angularProgram . getNgSemanticDiagnostics ( ) ) ;
788
+ const angularProgram = program as Program ;
789
+
790
+ if ( this . _firstRun ) {
791
+ // Check TypeScript parameter diagnostics.
792
+ time ( '_emit.ng.getTsOptionDiagnostics' ) ;
793
+ shouldEmit = shouldEmit && checkDiagnostics ( angularProgram . getTsOptionDiagnostics ( ) ) ;
794
+ timeEnd ( '_emit.ng.getTsOptionDiagnostics' ) ;
795
+
796
+ // Check Angular parameter diagnostics.
797
+ time ( '_emit.ng.getNgOptionDiagnostics' ) ;
798
+ shouldEmit = shouldEmit && checkDiagnostics ( angularProgram . getNgOptionDiagnostics ( ) ) ;
799
+ timeEnd ( '_emit.ng.getNgOptionDiagnostics' ) ;
800
+ }
801
+
802
+ if ( this . _firstRun || ! this . _forkTypeChecker ) {
803
+ // Check TypeScript syntactic diagnostics.
804
+ time ( '_emit.ng.getTsSyntacticDiagnostics' ) ;
805
+ shouldEmit = shouldEmit && checkDiagnostics ( angularProgram . getTsSyntacticDiagnostics ( ) ) ;
806
+ timeEnd ( '_emit.ng.getTsSyntacticDiagnostics' ) ;
807
+
808
+ // Check TypeScript semantic.
809
+ time ( '_emit.ng.getTsSemanticDiagnostics' ) ;
810
+ shouldEmit = shouldEmit && checkDiagnostics ( angularProgram . getTsSemanticDiagnostics ( ) ) ;
811
+ timeEnd ( '_emit.ng.getTsSemanticDiagnostics' ) ;
812
+
813
+ // Check Angular structural diagnostics.
814
+ time ( '_emit.ng.getNgStructuralDiagnostics' ) ;
815
+ shouldEmit = shouldEmit && checkDiagnostics ( angularProgram . getNgStructuralDiagnostics ( ) ) ;
816
+ timeEnd ( '_emit.ng.getNgStructuralDiagnostics' ) ;
817
+
818
+ // Check Angular semantic diagnostics
819
+ time ( '_emit.ng.getNgSemanticDiagnostics' ) ;
820
+ shouldEmit = shouldEmit && checkDiagnostics ( angularProgram . getNgSemanticDiagnostics ( ) ) ;
821
+ timeEnd ( '_emit.ng.getNgSemanticDiagnostics' ) ;
822
+ }
768
823
769
824
if ( shouldEmit ) {
770
825
time ( '_emit.ng.emit' ) ;
0 commit comments