@@ -90,13 +90,14 @@ function initializeNgccProcessor(
9090 return { processor, errors, warnings } ;
9191}
9292
93- function hashContent ( content : string ) : Uint8Array {
94- return createHash ( 'md5' ) . update ( content ) . digest ( ) ;
95- }
96-
9793const PLUGIN_NAME = 'angular-compiler' ;
9894const compilationFileEmitters = new WeakMap < Compilation , FileEmitterCollection > ( ) ;
9995
96+ interface FileEmitHistoryItem {
97+ length : number ;
98+ hash : Uint8Array ;
99+ }
100+
100101export class AngularWebpackPlugin {
101102 private readonly pluginOptions : AngularWebpackPluginOptions ;
102103 private compilerCliModule ?: typeof import ( '@angular/compiler-cli' ) ;
@@ -105,10 +106,11 @@ export class AngularWebpackPlugin {
105106 private ngtscNextProgram ?: NgtscProgram ;
106107 private builder ?: ts . EmitAndSemanticDiagnosticsBuilderProgram ;
107108 private sourceFileCache ?: SourceFileCache ;
109+ private webpackCache ?: ReturnType < Compilation [ 'getCache' ] > ;
108110 private readonly fileDependencies = new Map < string , Set < string > > ( ) ;
109111 private readonly requiredFilesToEmit = new Set < string > ( ) ;
110112 private readonly requiredFilesToEmitCache = new Map < string , EmitFileResult | undefined > ( ) ;
111- private readonly fileEmitHistory = new Map < string , { length : number ; hash : Uint8Array } > ( ) ;
113+ private readonly fileEmitHistory = new Map < string , FileEmitHistoryItem > ( ) ;
112114
113115 constructor ( options : Partial < AngularWebpackPluginOptions > = { } ) {
114116 this . pluginOptions = {
@@ -136,6 +138,7 @@ export class AngularWebpackPlugin {
136138 return this . pluginOptions ;
137139 }
138140
141+ // eslint-disable-next-line max-lines-per-function
139142 apply ( compiler : Compiler ) : void {
140143 const { NormalModuleReplacementPlugin, util } = compiler . webpack ;
141144
@@ -177,9 +180,13 @@ export class AngularWebpackPlugin {
177180 compiler . hooks . thisCompilation . tap ( PLUGIN_NAME , ( compilation ) => {
178181 // Register plugin to ensure deterministic emit order in multi-plugin usage
179182 const emitRegistration = this . registerWithCompilation ( compilation ) ;
180-
181183 this . watchMode = compiler . watchMode ;
182184
185+ // Initialize webpack cache
186+ if ( ! this . webpackCache && compilation . options . cache ) {
187+ this . webpackCache = compilation . getCache ( PLUGIN_NAME ) ;
188+ }
189+
183190 // Initialize the resource loader if not already setup
184191 if ( ! resourceLoader ) {
185192 resourceLoader = new WebpackResourceLoader ( this . watchMode ) ;
@@ -377,7 +384,7 @@ export class AngularWebpackPlugin {
377384
378385 const filesToRebuild = new Set < string > ( ) ;
379386 for ( const requiredFile of this . requiredFilesToEmit ) {
380- const history = this . fileEmitHistory . get ( requiredFile ) ;
387+ const history = await this . getFileEmitHistory ( requiredFile ) ;
381388 if ( history ) {
382389 const emitResult = await fileEmitter ( requiredFile ) ;
383390 if (
@@ -706,12 +713,8 @@ export class AngularWebpackPlugin {
706713
707714 onAfterEmit ?.( sourceFile ) ;
708715
709- let hash ;
710- if ( content !== undefined && this . watchMode ) {
711- // Capture emit history info for Angular rebuild analysis
712- hash = hashContent ( content ) ;
713- this . fileEmitHistory . set ( filePath , { length : content . length , hash } ) ;
714- }
716+ // Capture emit history info for Angular rebuild analysis
717+ const hash = content ? ( await this . addFileEmitHistory ( filePath , content ) ) . hash : undefined ;
715718
716719 const dependencies = [
717720 ...( this . fileDependencies . get ( filePath ) || [ ] ) ,
@@ -737,4 +740,33 @@ export class AngularWebpackPlugin {
737740 this . compilerCliModule = await new Function ( `return import('@angular/compiler-cli');` ) ( ) ;
738741 this . compilerNgccModule = await new Function ( `return import('@angular/compiler-cli/ngcc');` ) ( ) ;
739742 }
743+
744+ private async addFileEmitHistory (
745+ filePath : string ,
746+ content : string ,
747+ ) : Promise < FileEmitHistoryItem > {
748+ const historyData : FileEmitHistoryItem = {
749+ length : content . length ,
750+ hash : createHash ( 'md5' ) . update ( content ) . digest ( ) ,
751+ } ;
752+
753+ if ( this . webpackCache ) {
754+ const history = await this . getFileEmitHistory ( filePath ) ;
755+ if ( ! history || Buffer . compare ( history . hash , historyData . hash ) !== 0 ) {
756+ // Hash doesn't match or item doesn't exist.
757+ await this . webpackCache . storePromise ( filePath , null , historyData ) ;
758+ }
759+ } else if ( this . watchMode ) {
760+ // The in memory file emit history is only required during watch mode.
761+ this . fileEmitHistory . set ( filePath , historyData ) ;
762+ }
763+
764+ return historyData ;
765+ }
766+
767+ private async getFileEmitHistory ( filePath : string ) : Promise < FileEmitHistoryItem | undefined > {
768+ return this . webpackCache
769+ ? this . webpackCache . getPromise < FileEmitHistoryItem | undefined > ( filePath , null )
770+ : this . fileEmitHistory . get ( filePath ) ;
771+ }
740772}
0 commit comments