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:
Pete Bacon Darwin
2019-06-10 16:22:56 +01:00
committed by Kara Erickson
parent dc613b336d
commit 48def92cad
13 changed files with 159 additions and 43 deletions

View File

@ -8,6 +8,7 @@ ts_library(
"//packages/compiler",
"//packages/compiler-cli",
"//packages/compiler-cli/src/ngtsc/diagnostics",
"//packages/compiler-cli/src/ngtsc/path",
"//packages/compiler-cli/src/ngtsc/routing",
"//packages/compiler-cli/src/ngtsc/util",
"//packages/compiler-cli/test:test_utils",

View File

@ -39,6 +39,7 @@ function setupFakeCore(support: TestSupport): void {
export class NgtscTestEnvironment {
private multiCompileHostExt: MultiCompileHostExt|null = null;
private oldProgram: Program|null = null;
private changedResources: Set<string>|undefined = undefined;
private constructor(private support: TestSupport, readonly outDir: string) {}
@ -106,6 +107,7 @@ export class NgtscTestEnvironment {
}
enableMultipleCompilations(): void {
this.changedResources = new Set();
this.multiCompileHostExt = new MultiCompileHostExt();
setWrapHostForTest(makeWrapHost(this.multiCompileHostExt));
}
@ -114,6 +116,7 @@ export class NgtscTestEnvironment {
if (this.multiCompileHostExt === null) {
throw new Error(`Not tracking written files - call enableMultipleCompilations()`);
}
this.changedResources !.clear();
this.multiCompileHostExt.flushWrittenFileTracking();
}
@ -133,8 +136,9 @@ export class NgtscTestEnvironment {
write(fileName: string, content: string) {
if (this.multiCompileHostExt !== null) {
const absFilePath = path.posix.resolve(this.support.basePath, fileName);
const absFilePath = path.resolve(this.support.basePath, fileName).replace(/\\/g, '/');
this.multiCompileHostExt.invalidate(absFilePath);
this.changedResources !.add(absFilePath);
}
this.support.write(fileName, content);
}
@ -175,8 +179,9 @@ export class NgtscTestEnvironment {
program: this.oldProgram || undefined,
};
}
const exitCode =
main(['-p', this.basePath], errorSpy, undefined, customTransformers, reuseProgram);
const exitCode = main(
['-p', this.basePath], errorSpy, undefined, customTransformers, reuseProgram,
this.changedResources);
expect(errorSpy).not.toHaveBeenCalled();
expect(exitCode).toBe(0);
if (this.multiCompileHostExt !== null) {

View File

@ -6,6 +6,8 @@
* found in the LICENSE file at https://angular.io/license
*/
import {AbsoluteFsPath} from '@angular/compiler-cli/src/ngtsc/path';
import {NgtscTestEnvironment} from './env';
describe('ngtsc incremental compilation', () => {
@ -69,6 +71,33 @@ describe('ngtsc incremental compilation', () => {
expect(written).not.toContain('/component2.js');
});
it('should rebuild components whose templates have changed', () => {
env.write('component1.ts', `
import {Component} from '@angular/core';
@Component({selector: 'cmp', templateUrl: './component1.template.html'})
export class Cmp1 {}
`);
env.write('component1.template.html', 'cmp1');
env.write('component2.ts', `
import {Component} from '@angular/core';
@Component({selector: 'cmp2', templateUrl: './component2.template.html'})
export class Cmp2 {}
`);
env.write('component2.template.html', 'cmp2');
env.driveMain();
// Make a change to Cmp1 template
env.flushWrittenFileTracking();
env.write('component1.template.html', 'changed');
env.driveMain();
const written = env.getFilesWrittenSinceLastFlush();
expect(written).toContain('/component1.js');
expect(written).not.toContain('/component2.js');
});
it('should rebuild components whose partial-evaluation dependencies have changed', () => {
env.write('component1.ts', `
import {Component} from '@angular/core';
@ -222,4 +251,4 @@ function setupFooBarProgram(env: NgtscTestEnvironment) {
`);
env.driveMain();
env.flushWrittenFileTracking();
}
}