6
6
* found in the LICENSE file at https://angular.io/license
7
7
*/
8
8
9
- import type { CompilerHost } from '@angular/compiler-cli' ;
9
+ import type { CompilerHost , NgtscProgram } from '@angular/compiler-cli' ;
10
10
import { transformAsync } from '@babel/core' ;
11
11
import * as assert from 'assert' ;
12
12
import type {
@@ -199,6 +199,10 @@ export function createCompilerPlugin(
199
199
// The stylesheet resources from component stylesheets that will be added to the build results output files
200
200
let stylesheetResourceFiles : OutputFile [ ] ;
201
201
202
+ let previousBuilder : ts . EmitAndSemanticDiagnosticsBuilderProgram | undefined ;
203
+ let previousAngularProgram : NgtscProgram | undefined ;
204
+ const babelMemoryCache = new Map < string , string > ( ) ;
205
+
202
206
build . onStart ( async ( ) => {
203
207
const result : OnStartResult = { } ;
204
208
@@ -256,26 +260,43 @@ export function createCompilerPlugin(
256
260
return { content : contents } ;
257
261
} ;
258
262
263
+ // Temporary deep import for host augmentation support
264
+ const {
265
+ augmentHostWithReplacements,
266
+ augmentProgramWithVersioning,
267
+ } = require ( '@ngtools/webpack/src/ivy/host' ) ;
268
+
259
269
// Augment TypeScript Host for file replacements option
260
270
if ( pluginOptions . fileReplacements ) {
261
- // Temporary deep import for file replacements support
262
- const { augmentHostWithReplacements } = require ( '@ngtools/webpack/src/ivy/host' ) ;
263
271
augmentHostWithReplacements ( host , pluginOptions . fileReplacements ) ;
264
272
}
265
273
266
274
// 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 ;
268
282
const angularCompiler = angularProgram . compiler ;
269
283
const { ignoreForDiagnostics } = angularCompiler ;
270
284
const typeScriptProgram = angularProgram . getTsProgram ( ) ;
285
+ augmentProgramWithVersioning ( typeScriptProgram ) ;
271
286
272
- const builder = ts . createAbstractBuilder ( typeScriptProgram , host ) ;
287
+ const builder = ts . createEmitAndSemanticDiagnosticsBuilderProgram (
288
+ typeScriptProgram ,
289
+ host ,
290
+ previousBuilder ,
291
+ configurationDiagnostics ,
292
+ ) ;
293
+ previousBuilder = builder ;
273
294
274
295
await angularCompiler . analyzeAsync ( ) ;
275
296
276
- function * collectDiagnostics ( ) {
297
+ function * collectDiagnostics ( ) : Iterable < ts . Diagnostic > {
277
298
// Collect program level diagnostics
278
- yield * configurationDiagnostics ;
299
+ yield * builder . getConfigFileParsingDiagnostics ( ) ;
279
300
yield * angularCompiler . getOptionDiagnostics ( ) ;
280
301
yield * builder . getOptionsDiagnostics ( ) ;
281
302
yield * builder . getGlobalDiagnostics ( ) ;
@@ -312,7 +333,7 @@ export function createCompilerPlugin(
312
333
mergeTransformers ( angularCompiler . prepareEmit ( ) . transformers , {
313
334
before : [ replaceBootstrap ( ( ) => builder . getProgram ( ) . getTypeChecker ( ) ) ] ,
314
335
} ) ,
315
- ( ) => [ ] ,
336
+ ( sourceFile ) => angularCompiler . incrementalDriver . recordSuccessfulEmit ( sourceFile ) ,
316
337
) ;
317
338
318
339
return result ;
@@ -349,10 +370,11 @@ export function createCompilerPlugin(
349
370
}
350
371
351
372
return {
352
- contents : await transformWithBabel (
373
+ contents : await transformWithBabelCached (
353
374
args . path ,
354
375
typescriptResult . content ?? '' ,
355
376
pluginOptions ,
377
+ babelMemoryCache ,
356
378
) ,
357
379
loader : 'js' ,
358
380
} ;
@@ -363,7 +385,12 @@ export function createCompilerPlugin(
363
385
const data = await fs . readFile ( args . path , 'utf-8' ) ;
364
386
365
387
return {
366
- contents : await transformWithBabel ( args . path , data , pluginOptions ) ,
388
+ contents : await transformWithBabelCached (
389
+ args . path ,
390
+ data ,
391
+ pluginOptions ,
392
+ babelMemoryCache ,
393
+ ) ,
367
394
loader : 'js' ,
368
395
} ;
369
396
} ) ;
@@ -465,3 +492,32 @@ async function transformWithBabel(
465
492
466
493
return result ?. code ?? data ;
467
494
}
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