@@ -66,6 +66,18 @@ export class AotCompilation extends AngularCompilation {
66
66
hostOptions . externalStylesheets ??= new Map ( ) ;
67
67
}
68
68
69
+ // Collect stale source files for HMR analysis of inline component resources
70
+ let staleSourceFiles ;
71
+ if ( compilerOptions [ '_enableHmr' ] && hostOptions . modifiedFiles && this . #state) {
72
+ for ( const modifiedFile of hostOptions . modifiedFiles ) {
73
+ const sourceFile = this . #state. typeScriptProgram . getSourceFile ( modifiedFile ) ;
74
+ if ( sourceFile ) {
75
+ staleSourceFiles ??= new Map < string , ts . SourceFile > ( ) ;
76
+ staleSourceFiles . set ( modifiedFile , sourceFile ) ;
77
+ }
78
+ }
79
+ }
80
+
69
81
// Create Angular compiler host
70
82
const host = createAngularCompilerHost ( ts , compilerOptions , hostOptions ) ;
71
83
@@ -95,14 +107,12 @@ export class AotCompilation extends AngularCompilation {
95
107
await profileAsync ( 'NG_ANALYZE_PROGRAM' , ( ) => angularCompiler . analyzeAsync ( ) ) ;
96
108
97
109
let templateUpdates ;
98
- if (
99
- compilerOptions [ '_enableHmr' ] &&
100
- hostOptions . modifiedFiles &&
101
- hasOnlyTemplates ( hostOptions . modifiedFiles )
102
- ) {
103
- const componentNodes = [ ...hostOptions . modifiedFiles ] . flatMap ( ( file ) => [
104
- ...angularCompiler . getComponentsWithTemplateFile ( file ) ,
105
- ] ) ;
110
+ if ( compilerOptions [ '_enableHmr' ] && hostOptions . modifiedFiles && this . #state) {
111
+ const componentNodes = collectHmrCandidates (
112
+ hostOptions . modifiedFiles ,
113
+ angularProgram ,
114
+ staleSourceFiles ,
115
+ ) ;
106
116
107
117
for ( const node of componentNodes ) {
108
118
if ( ! ts . isClassDeclaration ( node ) ) {
@@ -423,15 +433,46 @@ function findAffectedFiles(
423
433
return affectedFiles ;
424
434
}
425
435
426
- function hasOnlyTemplates ( modifiedFiles : Set < string > ) : boolean {
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
+
427
443
for ( const file of modifiedFiles ) {
428
- const lowerFile = file . toLowerCase ( ) ;
429
- if ( lowerFile . endsWith ( '.html' ) || lowerFile . endsWith ( '.svg' ) ) {
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 ) ) ;
430
453
continue ;
431
454
}
432
455
433
- return false ;
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 ;
434
475
}
435
476
436
- return true ;
477
+ return candidates ;
437
478
}
0 commit comments