66 * found in the LICENSE file at https://angular.dev/license
77 */
88
9+ import { toPosixPath } from '../../utils/path' ;
910import { ExecutionResult } from './execution-result' ;
1011import type {
1112 BundleResult ,
@@ -15,10 +16,12 @@ import type {
1516} from './types' ;
1617
1718/**
18- * Encapsulates incremental compilation state, watched compilation file tracking,
19- * and execution results across watch-mode rebuilds.
19+ * Encapsulates incremental compilation state, transitive resource reference tracking,
20+ * watched compilation file tracking, and execution results across watch-mode rebuilds.
2021 */
2122export class BuilderState {
23+ readonly #referencingFiles = new Map < string , Set < string > > ( ) ;
24+ readonly #referencedByFile = new Map < string , Set < string > > ( ) ;
2225 readonly #watchedCompilationFiles = new Set < string > ( ) ;
2326 readonly #pendingCompilationFiles = new Set < string > ( ) ;
2427
@@ -116,6 +119,94 @@ export class BuilderState {
116119 return this . #executionResult;
117120 }
118121
122+ /**
123+ * Records transitive resource dependencies (such as Sass partials) for a containing
124+ * component or stylesheet file and registers them in `watchedCompilationFiles`.
125+ * Multiple calls for the same containing file accumulate references additively.
126+ *
127+ * @param containingFile Path of the referencing component or stylesheet file.
128+ * @param referencedFiles Paths of the referenced files.
129+ */
130+ addFileReferences ( containingFile : string , referencedFiles : Iterable < string > ) : void {
131+ const posixContainingFile = toPosixPath ( containingFile ) ;
132+ let forwardReferences = this . #referencedByFile. get ( posixContainingFile ) ;
133+
134+ for ( const file of referencedFiles ) {
135+ const posixFile = toPosixPath ( file ) ;
136+ this . #watchedCompilationFiles. add ( posixFile ) ;
137+ if ( posixFile === posixContainingFile ) {
138+ continue ;
139+ }
140+
141+ if ( ! forwardReferences ) {
142+ forwardReferences = new Set ( ) ;
143+ this . #referencedByFile. set ( posixContainingFile , forwardReferences ) ;
144+ }
145+ forwardReferences . add ( posixFile ) ;
146+
147+ let referencing = this . #referencingFiles. get ( posixFile ) ;
148+ if ( ! referencing ) {
149+ referencing = new Set ( ) ;
150+ this . #referencingFiles. set ( posixFile , referencing ) ;
151+ }
152+ referencing . add ( posixContainingFile ) ;
153+ }
154+ }
155+
156+ /**
157+ * Clears previously recorded outgoing resource references for the given containing files
158+ * before they are recompiled.
159+ *
160+ * @param containingFiles Paths of the containing or stylesheet files being recompiled.
161+ */
162+ clearFileReferences ( containingFiles : Iterable < string > ) : void {
163+ for ( const containingFile of containingFiles ) {
164+ const posixContainingFile = toPosixPath ( containingFile ) ;
165+ const forwardReferences = this . #referencedByFile. get ( posixContainingFile ) ;
166+ if ( ! forwardReferences ) {
167+ continue ;
168+ }
169+
170+ for ( const prevRef of forwardReferences ) {
171+ const referencing = this . #referencingFiles. get ( prevRef ) ;
172+ if ( referencing ) {
173+ referencing . delete ( posixContainingFile ) ;
174+ if ( referencing . size === 0 ) {
175+ this . #referencingFiles. delete ( prevRef ) ;
176+ }
177+ }
178+ }
179+
180+ this . #referencedByFile. delete ( posixContainingFile ) ;
181+ }
182+ }
183+
184+ /**
185+ * Returns the set of all files affected by the given changed files, including the
186+ * changed files themselves and all transitively referencing files
187+ * (e.g., partial `.scss` -> component `.scss` -> component `.ts`).
188+ *
189+ * @param files Initial changed file paths.
190+ * @returns Set of POSIX paths including all transitively referencing files.
191+ */
192+ getAffectedFiles ( files : Iterable < string > ) : Set < string > {
193+ const affected = new Set < string > ( ) ;
194+ for ( const file of files ) {
195+ affected . add ( toPosixPath ( file ) ) ;
196+ }
197+
198+ for ( const file of affected ) {
199+ const referencing = this . #referencingFiles. get ( file ) ;
200+ if ( referencing ) {
201+ for ( const refFile of referencing ) {
202+ affected . add ( refFile ) ;
203+ }
204+ }
205+ }
206+
207+ return affected ;
208+ }
209+
119210 /**
120211 * Determines whether compilation and bundling need to run for the current build iteration.
121212 * Also records incoming modified files so they are retained if the iteration aborts early.
@@ -235,6 +326,8 @@ export class BuilderState {
235326 this . pendingChangedEsmFiles . clear ( ) ;
236327 this . pendingChangedDtsFiles . clear ( ) ;
237328 this . #pendingCompilationFiles. clear ( ) ;
329+ this . #referencingFiles. clear ( ) ;
330+ this . #referencedByFile. clear ( ) ;
238331 this . #watchedCompilationFiles. clear ( ) ;
239332 }
240333}
0 commit comments