fix(ivy): ensure that changes to component resources trigger incremental builds (#30954)
Optimizations to skip compiling source files that had not changed did not account for the case where only a resource file changes, such as an external template or style file. Now we track such dependencies and trigger a recompilation if any of the previously tracked resources have changed. This will require a change on the CLI side to provide the list of resource files that changed to trigger the current compilation by implementing `CompilerHost.getModifiedResourceFiles()`. Closes #30947 PR Close #30954
This commit is contained in:

committed by
Kara Erickson

parent
dc613b336d
commit
48def92cad
@ -12,6 +12,7 @@ ts_library(
|
||||
"//packages/compiler-cli/src/ngtsc/metadata",
|
||||
"//packages/compiler-cli/src/ngtsc/partial_evaluator",
|
||||
"//packages/compiler-cli/src/ngtsc/reflection",
|
||||
"//packages/compiler-cli/src/ngtsc/util",
|
||||
"@npm//typescript",
|
||||
],
|
||||
)
|
||||
|
@ -7,21 +7,26 @@
|
||||
*/
|
||||
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {Reference} from '../../imports';
|
||||
import {DirectiveMeta, MetadataReader, MetadataRegistry, NgModuleMeta, PipeMeta} from '../../metadata';
|
||||
import {DependencyTracker} from '../../partial_evaluator';
|
||||
import {ClassDeclaration} from '../../reflection';
|
||||
import {ResourceDependencyRecorder} from '../../util/src/resource_recorder';
|
||||
|
||||
/**
|
||||
* Accumulates state between compilations.
|
||||
*/
|
||||
export class IncrementalState implements DependencyTracker, MetadataReader, MetadataRegistry {
|
||||
export class IncrementalState implements DependencyTracker, MetadataReader, MetadataRegistry,
|
||||
ResourceDependencyRecorder {
|
||||
private constructor(
|
||||
private unchangedFiles: Set<ts.SourceFile>,
|
||||
private metadata: Map<ts.SourceFile, FileMetadata>) {}
|
||||
private metadata: Map<ts.SourceFile, FileMetadata>,
|
||||
private modifiedResourceFiles: Set<string>|null) {}
|
||||
|
||||
static reconcile(previousState: IncrementalState, oldProgram: ts.Program, newProgram: ts.Program):
|
||||
IncrementalState {
|
||||
static reconcile(
|
||||
previousState: IncrementalState, oldProgram: ts.Program, newProgram: ts.Program,
|
||||
modifiedResourceFiles: Set<string>|null): IncrementalState {
|
||||
const unchangedFiles = new Set<ts.SourceFile>();
|
||||
const metadata = new Map<ts.SourceFile, FileMetadata>();
|
||||
const oldFiles = new Set<ts.SourceFile>(oldProgram.getSourceFiles());
|
||||
@ -46,14 +51,17 @@ export class IncrementalState implements DependencyTracker, MetadataReader, Meta
|
||||
}
|
||||
}
|
||||
|
||||
return new IncrementalState(unchangedFiles, metadata);
|
||||
return new IncrementalState(unchangedFiles, metadata, modifiedResourceFiles);
|
||||
}
|
||||
|
||||
static fresh(): IncrementalState {
|
||||
return new IncrementalState(new Set<ts.SourceFile>(), new Map<ts.SourceFile, FileMetadata>());
|
||||
return new IncrementalState(
|
||||
new Set<ts.SourceFile>(), new Map<ts.SourceFile, FileMetadata>(), null);
|
||||
}
|
||||
|
||||
safeToSkip(sf: ts.SourceFile): boolean { return this.unchangedFiles.has(sf); }
|
||||
safeToSkip(sf: ts.SourceFile): boolean|Promise<boolean> {
|
||||
return this.unchangedFiles.has(sf) && !this.hasChangedResourceDependencies(sf);
|
||||
}
|
||||
|
||||
trackFileDependency(dep: ts.SourceFile, src: ts.SourceFile) {
|
||||
const metadata = this.ensureMetadata(src);
|
||||
@ -92,11 +100,25 @@ export class IncrementalState implements DependencyTracker, MetadataReader, Meta
|
||||
metadata.pipeMeta.set(meta.ref.node, meta);
|
||||
}
|
||||
|
||||
recordResourceDependency(file: ts.SourceFile, resourcePath: string): void {
|
||||
const metadata = this.ensureMetadata(file);
|
||||
metadata.resourcePaths.add(resourcePath);
|
||||
}
|
||||
|
||||
private ensureMetadata(sf: ts.SourceFile): FileMetadata {
|
||||
const metadata = this.metadata.get(sf) || new FileMetadata();
|
||||
this.metadata.set(sf, metadata);
|
||||
return metadata;
|
||||
}
|
||||
|
||||
private hasChangedResourceDependencies(sf: ts.SourceFile): boolean {
|
||||
if (this.modifiedResourceFiles === undefined || !this.metadata.has(sf)) {
|
||||
return false;
|
||||
}
|
||||
const resourceDeps = this.metadata.get(sf) !.resourcePaths;
|
||||
return Array.from(resourceDeps.keys())
|
||||
.some(resourcePath => this.modifiedResourceFiles !.has(resourcePath));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -105,6 +127,7 @@ export class IncrementalState implements DependencyTracker, MetadataReader, Meta
|
||||
class FileMetadata {
|
||||
/** A set of source files that this file depends upon. */
|
||||
fileDependencies = new Set<ts.SourceFile>();
|
||||
resourcePaths = new Set<string>();
|
||||
directiveMeta = new Map<ClassDeclaration, DirectiveMeta>();
|
||||
ngModuleMeta = new Map<ClassDeclaration, NgModuleMeta>();
|
||||
pipeMeta = new Map<ClassDeclaration, PipeMeta>();
|
||||
|
Reference in New Issue
Block a user