@@ -377,10 +377,11 @@ export class AngularWebpackPlugin {
377377 }
378378
379379 private loadConfiguration ( compilation : WebpackCompilation ) {
380- const { options : compilerOptions , rootNames, errors } = readConfiguration (
381- this . pluginOptions . tsconfig ,
382- this . pluginOptions . compilerOptions ,
383- ) ;
380+ const {
381+ options : compilerOptions ,
382+ rootNames,
383+ errors,
384+ } = readConfiguration ( this . pluginOptions . tsconfig , this . pluginOptions . compilerOptions ) ;
384385 compilerOptions . enableIvy = true ;
385386 compilerOptions . noEmitOnError = false ;
386387 compilerOptions . suppressOutputPathCheck = true ;
@@ -425,51 +426,57 @@ export class AngularWebpackPlugin {
425426 const typeScriptProgram = angularProgram . getTsProgram ( ) ;
426427 augmentProgramWithVersioning ( typeScriptProgram ) ;
427428
428- const builder = ts . createEmitAndSemanticDiagnosticsBuilderProgram (
429- typeScriptProgram ,
430- host ,
431- this . builder ,
432- ) ;
433-
434- // Save for next rebuild
429+ let builder : ts . BuilderProgram | ts . EmitAndSemanticDiagnosticsBuilderProgram ;
435430 if ( this . watchMode ) {
436- this . builder = builder ;
431+ builder = this . builder = ts . createEmitAndSemanticDiagnosticsBuilderProgram (
432+ typeScriptProgram ,
433+ host ,
434+ this . builder ,
435+ ) ;
437436 this . ngtscNextProgram = angularProgram ;
437+ } else {
438+ // When not in watch mode, the startup cost of the incremental analysis can be avoided by
439+ // using an abstract builder that only wraps a TypeScript program.
440+ builder = ts . createAbstractBuilder ( typeScriptProgram , host ) ;
438441 }
439442
440443 // Update semantic diagnostics cache
441444 const affectedFiles = new Set < ts . SourceFile > ( ) ;
442- // eslint-disable-next-line no-constant-condition
443- while ( true ) {
444- const result = builder . getSemanticDiagnosticsOfNextAffectedFile ( undefined , ( sourceFile ) => {
445- // If the affected file is a TTC shim, add the shim's original source file.
446- // This ensures that changes that affect TTC are typechecked even when the changes
447- // are otherwise unrelated from a TS perspective and do not result in Ivy codegen changes.
448- // For example, changing @Input property types of a directive used in another component's
449- // template.
450- if (
451- ignoreForDiagnostics . has ( sourceFile ) &&
452- sourceFile . fileName . endsWith ( '.ngtypecheck.ts' )
453- ) {
454- // This file name conversion relies on internal compiler logic and should be converted
455- // to an official method when available. 15 is length of `.ngtypecheck.ts`
456- const originalFilename = sourceFile . fileName . slice ( 0 , - 15 ) + '.ts' ;
457- const originalSourceFile = builder . getSourceFile ( originalFilename ) ;
458- if ( originalSourceFile ) {
459- affectedFiles . add ( originalSourceFile ) ;
445+
446+ // Analyze affected files when in watch mode for incremental type checking
447+ if ( 'getSemanticDiagnosticsOfNextAffectedFile' in builder ) {
448+ // eslint-disable-next-line no-constant-condition
449+ while ( true ) {
450+ const result = builder . getSemanticDiagnosticsOfNextAffectedFile ( undefined , ( sourceFile ) => {
451+ // If the affected file is a TTC shim, add the shim's original source file.
452+ // This ensures that changes that affect TTC are typechecked even when the changes
453+ // are otherwise unrelated from a TS perspective and do not result in Ivy codegen changes.
454+ // For example, changing @Input property types of a directive used in another component's
455+ // template.
456+ if (
457+ ignoreForDiagnostics . has ( sourceFile ) &&
458+ sourceFile . fileName . endsWith ( '.ngtypecheck.ts' )
459+ ) {
460+ // This file name conversion relies on internal compiler logic and should be converted
461+ // to an official method when available. 15 is length of `.ngtypecheck.ts`
462+ const originalFilename = sourceFile . fileName . slice ( 0 , - 15 ) + '.ts' ;
463+ const originalSourceFile = builder . getSourceFile ( originalFilename ) ;
464+ if ( originalSourceFile ) {
465+ affectedFiles . add ( originalSourceFile ) ;
466+ }
467+
468+ return true ;
460469 }
461470
462- return true ;
463- }
471+ return false ;
472+ } ) ;
464473
465- return false ;
466- } ) ;
474+ if ( ! result ) {
475+ break ;
476+ }
467477
468- if ( ! result ) {
469- break ;
478+ affectedFiles . add ( result . affected as ts . SourceFile ) ;
470479 }
471-
472- affectedFiles . add ( result . affected as ts . SourceFile ) ;
473480 }
474481
475482 // Collect non-semantic diagnostics
@@ -581,16 +588,18 @@ export class AngularWebpackPlugin {
581588 host : CompilerHost ,
582589 diagnosticsReporter : DiagnosticsReporter ,
583590 ) {
584- const builder = ts . createEmitAndSemanticDiagnosticsBuilderProgram (
585- rootNames ,
586- compilerOptions ,
587- host ,
588- this . builder ,
589- ) ;
590-
591- // Save for next rebuild
591+ let builder ;
592592 if ( this . watchMode ) {
593- this . builder = builder ;
593+ builder = this . builder = ts . createEmitAndSemanticDiagnosticsBuilderProgram (
594+ rootNames ,
595+ compilerOptions ,
596+ host ,
597+ this . builder ,
598+ ) ;
599+ } else {
600+ // When not in watch mode, the startup cost of the incremental analysis can be avoided by
601+ // using an abstract builder that only wraps a TypeScript program.
602+ builder = ts . createAbstractBuilder ( rootNames , compilerOptions , host ) ;
594603 }
595604
596605 const diagnostics = [
0 commit comments