perf(@ngtools/webpack): cache results of processed inline resources

When in watch mode, both the file and inline resources will now be cached between rebuilds. This removes the need to reprocess inline resources that have not changed even if the containing TypeScript file has changed.
This commit is contained in:
Charles Lyding 2021-04-20 16:23:05 -04:00 committed by Joey Perrott
parent d92805e361
commit 8dfc8e73f3
2 changed files with 32 additions and 9 deletions

View File

@ -150,7 +150,7 @@ export class AngularWebpackPlugin {
});
let ngccProcessor: NgccProcessor | undefined;
const resourceLoader = new WebpackResourceLoader();
let resourceLoader: WebpackResourceLoader | undefined;
let previousUnused: Set<string> | undefined;
compiler.hooks.thisCompilation.tap(PLUGIN_NAME, (thisCompilation) => {
const compilation = thisCompilation as WebpackCompilation;
@ -164,6 +164,11 @@ export class AngularWebpackPlugin {
// Store watch mode; assume true if not present (webpack < 4.23.0)
this.watchMode = compiler.watchMode ?? true;
// Initialize the resource loader if not already setup
if (!resourceLoader) {
resourceLoader = new WebpackResourceLoader(this.watchMode);
}
// Initialize and process eager ngcc if not already setup
if (!ngccProcessor) {
const { processor, errors, warnings } = initializeNgccProcessor(
@ -266,7 +271,7 @@ export class AngularWebpackPlugin {
await this.rebuildRequiredFiles(modules, compilation, fileEmitter);
// Clear out the Webpack compilation to avoid an extra retaining reference
resourceLoader.clearParentCompilation();
resourceLoader?.clearParentCompilation();
// Analyze program for unused files
if (compilation.errors.length > 0) {

View File

@ -5,6 +5,7 @@
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import { createHash } from 'crypto';
import * as vm from 'vm';
import { Compilation, EntryPlugin, NormalModule, library, node, sources } from 'webpack';
import { normalizePath } from './ivy/paths';
@ -20,10 +21,18 @@ export class WebpackResourceLoader {
private _fileDependencies = new Map<string, Set<string>>();
private _reverseDependencies = new Map<string, Set<string>>();
private cache = new Map<string, CompilationOutput>();
private fileCache?: Map<string, CompilationOutput>;
private inlineCache?: Map<string, CompilationOutput>;
private modifiedResources = new Set<string>();
private outputPathCounter = 1;
constructor(shouldCache: boolean) {
if (shouldCache) {
this.fileCache = new Map();
this.inlineCache = new Map();
}
}
update(
parentCompilation: Compilation,
changedFiles?: Iterable<string>,
@ -35,12 +44,12 @@ export class WebpackResourceLoader {
if (changedFiles) {
for (const changedFile of changedFiles) {
for (const affectedResource of this.getAffectedResources(changedFile)) {
this.cache.delete(normalizePath(affectedResource));
this.fileCache?.delete(normalizePath(affectedResource));
this.modifiedResources.add(affectedResource);
}
}
} else {
this.cache.clear();
this.fileCache?.clear();
}
}
@ -236,15 +245,15 @@ export class WebpackResourceLoader {
async get(filePath: string): Promise<string> {
const normalizedFile = normalizePath(filePath);
let compilationResult = this.cache.get(normalizedFile);
let compilationResult = this.fileCache?.get(normalizedFile);
if (compilationResult === undefined) {
// cache miss so compile resource
compilationResult = await this._compile(filePath);
// Only cache if compilation was successful
if (compilationResult.success) {
this.cache.set(normalizedFile, compilationResult);
if (this.fileCache && compilationResult.success) {
this.fileCache.set(normalizedFile, compilationResult);
}
}
@ -256,7 +265,16 @@ export class WebpackResourceLoader {
return '';
}
const compilationResult = await this._compile(undefined, data, mimeType);
const cacheKey = createHash('md5').update(data).digest('hex');
let compilationResult = this.inlineCache?.get(cacheKey);
if (compilationResult === undefined) {
compilationResult = await this._compile(undefined, data, mimeType);
if (this.inlineCache && compilationResult.success) {
this.inlineCache.set(cacheKey, compilationResult);
}
}
return compilationResult.content;
}