@@ -110,7 +110,9 @@ export class AngularCompilerPlugin {
110
110
// even whe only a single file gets updated.
111
111
private _hadFullJitEmit : boolean | undefined ;
112
112
private _unusedFiles = new Set < string > ( ) ;
113
+ private _typeDeps = new Set < string > ( ) ;
113
114
private _changedFileExtensions = new Set ( [ 'ts' , 'tsx' , 'html' , 'css' , 'js' , 'json' ] ) ;
115
+ private _nodeModulesRegExp = / [ \\ \/ ] n o d e _ m o d u l e s [ \\ \/ ] / ;
114
116
115
117
// Webpack plugin.
116
118
private _firstRun = true ;
@@ -592,7 +594,7 @@ export class AngularCompilerPlugin {
592
594
}
593
595
}
594
596
595
- private _warnOnUnusedFiles ( compilation : compilation . Compilation ) {
597
+ private _checkUnusedFiles ( compilation : compilation . Compilation ) {
596
598
// Only do the unused TS files checks when under Ivy
597
599
// since previously we did include unused files in the compilation
598
600
// See: https://github.com/angular/angular-cli/pull/15030
@@ -601,6 +603,7 @@ export class AngularCompilerPlugin {
601
603
return ;
602
604
}
603
605
606
+ // Bail if there's no TS program. Nothing to do in that case.
604
607
const program = this . _getTsProgram ( ) ;
605
608
if ( ! program ) {
606
609
return ;
@@ -609,26 +612,36 @@ export class AngularCompilerPlugin {
609
612
// Exclude the following files from unused checks
610
613
// - ngfactories & ngstyle might not have a correspondent
611
614
// JS file example `@angular/core/core.ngfactory.ts`.
612
- // - .d.ts files might not have a correspondent JS file due to bundling.
613
615
// - __ng_typecheck__.ts will never be requested.
614
- const fileExcludeRegExp = / ( \. ( d | n g f a c t o r y | n g s t y l e | n g s u m m a r y ) \. t s | n g _ t y p e c h e c k _ _ \. t s ) $ / ;
616
+ const fileExcludeRegExp = / ( \. ( n g f a c t o r y | n g s t y l e | n g s u m m a r y ) \. t s | n g _ t y p e c h e c k _ _ \. t s ) $ / ;
617
+
618
+ // Start all the source file names we care about.
619
+ // Ignore matches to the regexp above, files we've already reported once before, and
620
+ // node_modules.
621
+ const sourceFiles = program . getSourceFiles ( )
622
+ . map ( x => this . _compilerHost . denormalizePath ( x . fileName ) )
623
+ . filter ( f => ! ( fileExcludeRegExp . test ( f ) || this . _unusedFiles . has ( f )
624
+ || this . _nodeModulesRegExp . test ( f ) ) ) ;
625
+
626
+ // Make a set with the sources, but exclude .d.ts files since those are type-only.
627
+ const unusedSourceFileNames = new Set ( sourceFiles . filter ( f => ! f . endsWith ( '.d.ts' ) ) ) ;
628
+ // Separately keep track of type-only deps.
629
+ const typeDepFileNames = new Set ( sourceFiles ) ;
615
630
616
- // Start with a set of all the source file names we care about.
617
- const unusedSourceFileNames = new Set (
618
- program . getSourceFiles ( )
619
- . map ( x => this . _compilerHost . denormalizePath ( x . fileName ) )
620
- . filter ( f => ! ( fileExcludeRegExp . test ( f ) || this . _unusedFiles . has ( f ) ) ) ,
621
- ) ;
622
631
// This function removes a source file name and all its dependencies from the set.
623
- const removeSourceFile = ( fileName : string ) => {
624
- if ( unusedSourceFileNames . has ( fileName ) ) {
632
+ const removeSourceFile = ( fileName : string , originalModule = false ) => {
633
+ if ( unusedSourceFileNames . has ( fileName )
634
+ || ( originalModule && typeDepFileNames . has ( fileName ) ) ) {
625
635
unusedSourceFileNames . delete ( fileName ) ;
636
+ if ( originalModule ) {
637
+ typeDepFileNames . delete ( fileName ) ;
638
+ }
626
639
this . getDependencies ( fileName , false ) . forEach ( f => removeSourceFile ( f ) ) ;
627
640
}
628
641
} ;
629
642
630
- // Go over all the modules in the webpack compilation and remove them from the set .
631
- compilation . modules . forEach ( m => m . resource ? removeSourceFile ( m . resource ) : null ) ;
643
+ // Go over all the modules in the webpack compilation and remove them from the sets .
644
+ compilation . modules . forEach ( m => m . resource ? removeSourceFile ( m . resource , true ) : null ) ;
632
645
633
646
// Anything that remains is unused, because it wasn't referenced directly or transitively
634
647
// on the files in the compilation.
@@ -638,7 +651,15 @@ export class AngularCompilerPlugin {
638
651
`Add only entry points to the 'files' or 'include' properties in your tsconfig.` ,
639
652
) ;
640
653
this . _unusedFiles . add ( fileName ) ;
654
+ // Remove the truly unused from the type dep list.
655
+ typeDepFileNames . delete ( fileName ) ;
641
656
}
657
+
658
+ // At this point we know what the type deps are.
659
+ // These are the TS files that weren't part of the compilation modules, aren't unused, but were
660
+ // part of the TS original source list.
661
+ // Next build we add them to the TS entry points so that they trigger rebuilds.
662
+ this . _typeDeps = typeDepFileNames ;
642
663
}
643
664
644
665
// Registration hook for webpack plugin.
@@ -657,7 +678,7 @@ export class AngularCompilerPlugin {
657
678
// cleanup if not watching
658
679
compiler . hooks . thisCompilation . tap ( 'angular-compiler' , compilation => {
659
680
compilation . hooks . finishModules . tap ( 'angular-compiler' , ( ) => {
660
- this . _warnOnUnusedFiles ( compilation ) ;
681
+ this . _checkUnusedFiles ( compilation ) ;
661
682
662
683
let rootCompiler = compiler ;
663
684
while ( rootCompiler . parentCompilation ) {
@@ -1188,7 +1209,7 @@ export class AngularCompilerPlugin {
1188
1209
let msg = `${ fileName } is missing from the TypeScript compilation. `
1189
1210
+ `Please make sure it is in your tsconfig via the 'files' or 'include' property.` ;
1190
1211
1191
- if ( / ( \\ | \/ ) n o d e _ m o d u l e s ( \\ | \/ ) / . test ( fileName ) ) {
1212
+ if ( this . _nodeModulesRegExp . test ( fileName ) ) {
1192
1213
msg += '\nThe missing file seems to be part of a third party library. '
1193
1214
+ 'TS files in published libraries are often a sign of a badly packaged library. '
1194
1215
+ 'Please open an issue in the library repository to alert its author and ask them '
@@ -1267,6 +1288,18 @@ export class AngularCompilerPlugin {
1267
1288
return this . _resourceLoader . getResourceDependencies ( resolvedFileName ) ;
1268
1289
}
1269
1290
1291
+ getTypeDependencies ( fileName : string ) : string [ ] {
1292
+ // We currently add all type deps directly to the main path.
1293
+ // If there's no main path or the lookup isn't the main path, bail.
1294
+ if ( ! this . _mainPath || this . _compilerHost . resolve ( fileName ) != this . _mainPath ) {
1295
+ return [ ] ;
1296
+ }
1297
+
1298
+ // Note: this set is always for the previous build, not the current build.
1299
+ // It should be better than not having rebuilds on type deps but isn't 100% correct.
1300
+ return Array . from ( this . _typeDeps ) ;
1301
+ }
1302
+
1270
1303
// This code mostly comes from `performCompilation` in `@angular/compiler-cli`.
1271
1304
// It skips the program creation because we need to use `loadNgStructureAsync()`,
1272
1305
// and uses CustomTransformers.
0 commit comments