From 8dfc8e73f3508e88a1e3d99a8deb006f1bd5706c Mon Sep 17 00:00:00 2001 From: Charles Lyding <19598772+clydin@users.noreply.github.com> Date: Tue, 20 Apr 2021 16:23:05 -0400 Subject: [PATCH] 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. --- packages/ngtools/webpack/src/ivy/plugin.ts | 9 ++++-- .../ngtools/webpack/src/resource_loader.ts | 32 +++++++++++++++---- 2 files changed, 32 insertions(+), 9 deletions(-) diff --git a/packages/ngtools/webpack/src/ivy/plugin.ts b/packages/ngtools/webpack/src/ivy/plugin.ts index ce8b2c5692..5b5b4f5695 100644 --- a/packages/ngtools/webpack/src/ivy/plugin.ts +++ b/packages/ngtools/webpack/src/ivy/plugin.ts @@ -150,7 +150,7 @@ export class AngularWebpackPlugin { }); let ngccProcessor: NgccProcessor | undefined; - const resourceLoader = new WebpackResourceLoader(); + let resourceLoader: WebpackResourceLoader | undefined; let previousUnused: Set | 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) { diff --git a/packages/ngtools/webpack/src/resource_loader.ts b/packages/ngtools/webpack/src/resource_loader.ts index 4b22770f6a..f760a256ac 100644 --- a/packages/ngtools/webpack/src/resource_loader.ts +++ b/packages/ngtools/webpack/src/resource_loader.ts @@ -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>(); private _reverseDependencies = new Map>(); - private cache = new Map(); + private fileCache?: Map; + private inlineCache?: Map; private modifiedResources = new Set(); private outputPathCounter = 1; + constructor(shouldCache: boolean) { + if (shouldCache) { + this.fileCache = new Map(); + this.inlineCache = new Map(); + } + } + update( parentCompilation: Compilation, changedFiles?: Iterable, @@ -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 { 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; }