66 * found in the LICENSE file at https://angular.io/license
77 */
88
9- import type { CompilerHost } from '@angular/compiler-cli' ;
9+ import type { CompilerHost , NgtscProgram } from '@angular/compiler-cli' ;
1010import { transformAsync } from '@babel/core' ;
1111import * as assert from 'assert' ;
1212import type {
@@ -199,6 +199,10 @@ export function createCompilerPlugin(
199199 // The stylesheet resources from component stylesheets that will be added to the build results output files
200200 let stylesheetResourceFiles : OutputFile [ ] ;
201201
202+ let previousBuilder : ts . EmitAndSemanticDiagnosticsBuilderProgram | undefined ;
203+ let previousAngularProgram : NgtscProgram | undefined ;
204+ const babelMemoryCache = new Map < string , string > ( ) ;
205+
202206 build . onStart ( async ( ) => {
203207 const result : OnStartResult = { } ;
204208
@@ -256,26 +260,43 @@ export function createCompilerPlugin(
256260 return { content : contents } ;
257261 } ;
258262
263+ // Temporary deep import for host augmentation support
264+ const {
265+ augmentHostWithReplacements,
266+ augmentProgramWithVersioning,
267+ } = require ( '@ngtools/webpack/src/ivy/host' ) ;
268+
259269 // Augment TypeScript Host for file replacements option
260270 if ( pluginOptions . fileReplacements ) {
261- // Temporary deep import for file replacements support
262- const { augmentHostWithReplacements } = require ( '@ngtools/webpack/src/ivy/host' ) ;
263271 augmentHostWithReplacements ( host , pluginOptions . fileReplacements ) ;
264272 }
265273
266274 // Create the Angular specific program that contains the Angular compiler
267- const angularProgram = new compilerCli . NgtscProgram ( rootNames , compilerOptions , host ) ;
275+ const angularProgram = new compilerCli . NgtscProgram (
276+ rootNames ,
277+ compilerOptions ,
278+ host ,
279+ previousAngularProgram ,
280+ ) ;
281+ previousAngularProgram = angularProgram ;
268282 const angularCompiler = angularProgram . compiler ;
269283 const { ignoreForDiagnostics } = angularCompiler ;
270284 const typeScriptProgram = angularProgram . getTsProgram ( ) ;
285+ augmentProgramWithVersioning ( typeScriptProgram ) ;
271286
272- const builder = ts . createAbstractBuilder ( typeScriptProgram , host ) ;
287+ const builder = ts . createEmitAndSemanticDiagnosticsBuilderProgram (
288+ typeScriptProgram ,
289+ host ,
290+ previousBuilder ,
291+ configurationDiagnostics ,
292+ ) ;
293+ previousBuilder = builder ;
273294
274295 await angularCompiler . analyzeAsync ( ) ;
275296
276- function * collectDiagnostics ( ) {
297+ function * collectDiagnostics ( ) : Iterable < ts . Diagnostic > {
277298 // Collect program level diagnostics
278- yield * configurationDiagnostics ;
299+ yield * builder . getConfigFileParsingDiagnostics ( ) ;
279300 yield * angularCompiler . getOptionDiagnostics ( ) ;
280301 yield * builder . getOptionsDiagnostics ( ) ;
281302 yield * builder . getGlobalDiagnostics ( ) ;
@@ -312,7 +333,7 @@ export function createCompilerPlugin(
312333 mergeTransformers ( angularCompiler . prepareEmit ( ) . transformers , {
313334 before : [ replaceBootstrap ( ( ) => builder . getProgram ( ) . getTypeChecker ( ) ) ] ,
314335 } ) ,
315- ( ) => [ ] ,
336+ ( sourceFile ) => angularCompiler . incrementalDriver . recordSuccessfulEmit ( sourceFile ) ,
316337 ) ;
317338
318339 return result ;
@@ -349,10 +370,11 @@ export function createCompilerPlugin(
349370 }
350371
351372 return {
352- contents : await transformWithBabel (
373+ contents : await transformWithBabelCached (
353374 args . path ,
354375 typescriptResult . content ?? '' ,
355376 pluginOptions ,
377+ babelMemoryCache ,
356378 ) ,
357379 loader : 'js' ,
358380 } ;
@@ -363,7 +385,12 @@ export function createCompilerPlugin(
363385 const data = await fs . readFile ( args . path , 'utf-8' ) ;
364386
365387 return {
366- contents : await transformWithBabel ( args . path , data , pluginOptions ) ,
388+ contents : await transformWithBabelCached (
389+ args . path ,
390+ data ,
391+ pluginOptions ,
392+ babelMemoryCache ,
393+ ) ,
367394 loader : 'js' ,
368395 } ;
369396 } ) ;
@@ -465,3 +492,32 @@ async function transformWithBabel(
465492
466493 return result ?. code ?? data ;
467494}
495+
496+ /**
497+ * Transforms JavaScript file data using the babel transforms setup in transformWithBabel. The
498+ * supplied cache will be used to avoid repeating the transforms for data that has previously
499+ * been transformed such as in a previous rebuild cycle.
500+ * @param filename The file path of the data to be transformed.
501+ * @param data The file data that will be transformed.
502+ * @param pluginOptions Compiler plugin options that will be used to control the transformation.
503+ * @param cache A cache of previously transformed data that will be used to avoid repeat transforms.
504+ * @returns A promise containing the transformed data.
505+ */
506+ async function transformWithBabelCached (
507+ filename : string ,
508+ data : string ,
509+ pluginOptions : CompilerPluginOptions ,
510+ cache : Map < string , string > ,
511+ ) : Promise < string > {
512+ // The pre-transformed data is used as a cache key. Since the cache is memory only,
513+ // the options cannot change and do not need to be represented in the key. If the
514+ // cache is later stored to disk, then the options that affect transform output
515+ // would need to be added to the key as well.
516+ let result = cache . get ( data ) ;
517+ if ( result === undefined ) {
518+ result = await transformWithBabel ( filename , data , pluginOptions ) ;
519+ cache . set ( data , result ) ;
520+ }
521+
522+ return result ;
523+ }
0 commit comments