Skip to content

Commit 75998eb

Browse files
clydinalan-agius4
authored andcommitted
perf(@angular/build): reuse TS package.json cache when rebuilding
TypeScript 5.6 and higher added functionality that will search for a `package.json` file for source files that are part of the program (e.g., `.d.ts`) and within a node modules directory. This can be an expensive tasks especially considering the large amount of `.d.ts` files within packages. TypeScript supports using a cache of known `package.json` files to improve the performance of this task. The Angular CLI will now provide and reuse this cache across rebuilds during watch mode. This includes the use of `ng serve`. The performance difference is most apparent for the Angular template diagnostic step of the build. Internally the Angular compiler creates a new template typechecking program which causes the `package.json` search process to occur. By leveraging the cache, this process becomes a series of cache hits. In the event that files are modified within the node modules directory, the cache is invalidated and the following rebuild may be longer as a result.
1 parent 15a48a3 commit 75998eb

File tree

3 files changed

+32
-16
lines changed

3 files changed

+32
-16
lines changed

packages/angular/build/src/tools/angular/angular-host.ts

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,7 @@ export function createAngularCompilerHost(
164164
typescript: typeof ts,
165165
compilerOptions: AngularCompilerOptions,
166166
hostOptions: AngularHostOptions,
167+
packageJsonCache: ts.PackageJsonInfoCache | undefined,
167168
): AngularCompilerHost {
168169
// Create TypeScript compiler host
169170
const host: AngularCompilerHost = typescript.createIncrementalCompilerHost(compilerOptions);
@@ -229,16 +230,17 @@ export function createAngularCompilerHost(
229230
return hostOptions.modifiedFiles;
230231
};
231232

233+
// Provide a resolution cache to ensure package.json lookups are cached
234+
const resolutionCache = typescript.createModuleResolutionCache(
235+
host.getCurrentDirectory(),
236+
host.getCanonicalFileName.bind(host),
237+
compilerOptions,
238+
packageJsonCache,
239+
);
240+
host.getModuleResolutionCache = () => resolutionCache;
241+
232242
// Augment TypeScript Host for file replacements option
233243
if (hostOptions.fileReplacements) {
234-
// Provide a resolution cache since overriding resolution prevents automatic creation
235-
const resolutionCache = typescript.createModuleResolutionCache(
236-
host.getCurrentDirectory(),
237-
host.getCanonicalFileName.bind(host),
238-
compilerOptions,
239-
);
240-
host.getModuleResolutionCache = () => resolutionCache;
241-
242244
augmentHostWithReplacements(typescript, host, hostOptions.fileReplacements, resolutionCache);
243245
}
244246

packages/angular/build/src/tools/angular/compilation/aot-compilation.ts

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -74,25 +74,39 @@ export class AotCompilation extends AngularCompilation {
7474
hostOptions.externalStylesheets ??= new Map();
7575
}
7676

77+
// Reuse the package.json cache from the previous compilation
78+
const packageJsonCache = this.#state?.compilerHost
79+
.getModuleResolutionCache?.()
80+
?.getPackageJsonInfoCache();
81+
7782
const useHmr =
7883
compilerOptions['_enableHmr'] &&
7984
hostOptions.modifiedFiles &&
8085
hostOptions.modifiedFiles.size <= HMR_MODIFIED_FILE_LIMIT;
8186

82-
// Collect stale source files for HMR analysis of inline component resources
8387
let staleSourceFiles;
84-
if (useHmr && hostOptions.modifiedFiles && this.#state) {
88+
let clearPackageJsonCache = false;
89+
if (hostOptions.modifiedFiles && this.#state) {
8590
for (const modifiedFile of hostOptions.modifiedFiles) {
86-
const sourceFile = this.#state.typeScriptProgram.getSourceFile(modifiedFile);
87-
if (sourceFile) {
88-
staleSourceFiles ??= new Map<string, ts.SourceFile>();
89-
staleSourceFiles.set(modifiedFile, sourceFile);
91+
// Clear package.json cache if a node modules file was modified
92+
if (!clearPackageJsonCache && modifiedFile.includes('node_modules')) {
93+
clearPackageJsonCache = true;
94+
packageJsonCache?.clear();
95+
}
96+
97+
// Collect stale source files for HMR analysis of inline component resources
98+
if (useHmr) {
99+
const sourceFile = this.#state.typeScriptProgram.getSourceFile(modifiedFile);
100+
if (sourceFile) {
101+
staleSourceFiles ??= new Map<string, ts.SourceFile>();
102+
staleSourceFiles.set(modifiedFile, sourceFile);
103+
}
90104
}
91105
}
92106
}
93107

94108
// Create Angular compiler host
95-
const host = createAngularCompilerHost(ts, compilerOptions, hostOptions);
109+
const host = createAngularCompilerHost(ts, compilerOptions, hostOptions, packageJsonCache);
96110

97111
// Create the Angular specific program that contains the Angular compiler
98112
const angularProgram = profileSync(

packages/angular/build/src/tools/angular/compilation/jit-compilation.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ export class JitCompilation extends AngularCompilation {
5353
compilerOptionsTransformer?.(originalCompilerOptions) ?? originalCompilerOptions;
5454

5555
// Create Angular compiler host
56-
const host = createAngularCompilerHost(ts, compilerOptions, hostOptions);
56+
const host = createAngularCompilerHost(ts, compilerOptions, hostOptions, undefined);
5757

5858
// Create the TypeScript Program
5959
const typeScriptProgram = profileSync('TS_CREATE_PROGRAM', () =>

0 commit comments

Comments
 (0)