Skip to content

Commit 648e6d6

Browse files
committed
refactor(@angular-devkit/build-angular): prepare esbuild angular plugin for incremental rebuilds
This adds the initial infrastructure to support incremental esbuild rebuilds within the Angular compiler plugin. Currently, these changes will not yet have an effect on the watch mode rebuild performance. Integration of esbuild's incremental rebuild mode will be needed within the builder itself to take advantage of these changes.
1 parent 7f27908 commit 648e6d6

File tree

1 file changed

+66
-10
lines changed

1 file changed

+66
-10
lines changed

packages/angular_devkit/build_angular/src/builders/browser-esbuild/compiler-plugin.ts

Lines changed: 66 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
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';
1010
import { transformAsync } from '@babel/core';
1111
import * as assert from 'assert';
1212
import 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

Comments
 (0)