Skip to content

Commit 378624d

Browse files
clydinalan-agius4
authored andcommitted
refactor(@angular/build): add initial component HMR source file analysis
When component template HMR support is enabled (`NG_HMR_TEMPLATES=1`), TypeScript file changes will now be analyzed to determine if Angular component metadata has changed and if the changes can support a hot replacement. Any other changes to a TypeScript file will cause a full page reload to avoid inconsistent state between the code and running application. The analysis currently has an upper limit of 32 modified files at one time to prevent a large of amount of analysis to be performed which may take longer than a full rebuild. This value may be adjusted based on feedback. Component template HMR is currently experimental and may not support all template modifications. Both inline and file-based templates are now supported. However, rebuild times have not yet been optimized.
1 parent a8ea9cf commit 378624d

File tree

2 files changed

+329
-46
lines changed

2 files changed

+329
-46
lines changed

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

Lines changed: 15 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,14 @@ import {
1919
import { replaceBootstrap } from '../transformers/jit-bootstrap-transformer';
2020
import { createWorkerTransformer } from '../transformers/web-worker-transformer';
2121
import { AngularCompilation, DiagnosticModes, EmitFileResult } from './angular-compilation';
22+
import { collectHmrCandidates } from './hmr-candidates';
23+
24+
/**
25+
* The modified files count limit for performing component HMR analysis.
26+
* Performing content analysis for a large amount of files can result in longer rebuild times
27+
* than a full rebuild would entail.
28+
*/
29+
const HMR_MODIFIED_FILE_LIMIT = 32;
2230

2331
class AngularCompilationState {
2432
constructor(
@@ -66,9 +74,14 @@ export class AotCompilation extends AngularCompilation {
6674
hostOptions.externalStylesheets ??= new Map();
6775
}
6876

77+
const useHmr =
78+
compilerOptions['_enableHmr'] &&
79+
hostOptions.modifiedFiles &&
80+
hostOptions.modifiedFiles.size <= HMR_MODIFIED_FILE_LIMIT;
81+
6982
// Collect stale source files for HMR analysis of inline component resources
7083
let staleSourceFiles;
71-
if (compilerOptions['_enableHmr'] && hostOptions.modifiedFiles && this.#state) {
84+
if (useHmr && hostOptions.modifiedFiles && this.#state) {
7285
for (const modifiedFile of hostOptions.modifiedFiles) {
7386
const sourceFile = this.#state.typeScriptProgram.getSourceFile(modifiedFile);
7487
if (sourceFile) {
@@ -107,7 +120,7 @@ export class AotCompilation extends AngularCompilation {
107120
await profileAsync('NG_ANALYZE_PROGRAM', () => angularCompiler.analyzeAsync());
108121

109122
let templateUpdates;
110-
if (compilerOptions['_enableHmr'] && hostOptions.modifiedFiles && this.#state) {
123+
if (useHmr && hostOptions.modifiedFiles && this.#state) {
111124
const componentNodes = collectHmrCandidates(
112125
hostOptions.modifiedFiles,
113126
angularProgram,
@@ -432,47 +445,3 @@ function findAffectedFiles(
432445

433446
return affectedFiles;
434447
}
435-
436-
function collectHmrCandidates(
437-
modifiedFiles: Set<string>,
438-
{ compiler }: ng.NgtscProgram,
439-
staleSourceFiles: Map<string, ts.SourceFile> | undefined,
440-
): Set<ts.ClassDeclaration> {
441-
const candidates = new Set<ts.ClassDeclaration>();
442-
443-
for (const file of modifiedFiles) {
444-
const templateFileNodes = compiler.getComponentsWithTemplateFile(file);
445-
if (templateFileNodes.size) {
446-
templateFileNodes.forEach((node) => candidates.add(node as ts.ClassDeclaration));
447-
continue;
448-
}
449-
450-
const styleFileNodes = compiler.getComponentsWithStyleFile(file);
451-
if (styleFileNodes.size) {
452-
styleFileNodes.forEach((node) => candidates.add(node as ts.ClassDeclaration));
453-
continue;
454-
}
455-
456-
const staleSource = staleSourceFiles?.get(file);
457-
if (staleSource === undefined) {
458-
// Unknown file requires a rebuild so clear out the candidates and stop collecting
459-
candidates.clear();
460-
break;
461-
}
462-
463-
const updatedSource = compiler.getCurrentProgram().getSourceFile(file);
464-
if (updatedSource === undefined) {
465-
// No longer existing program file requires a rebuild so clear out the candidates and stop collecting
466-
candidates.clear();
467-
break;
468-
}
469-
470-
// Compare the stale and updated file for changes
471-
472-
// TODO: Implement -- for now assume a rebuild is needed
473-
candidates.clear();
474-
break;
475-
}
476-
477-
return candidates;
478-
}

0 commit comments

Comments
 (0)