fix(ivy): recompile component when template changes in ngc watch mode (#33551)

When the Angular compiler is operated through the ngc binary in watch
mode, changing a template in an external file would not cause the
component to be recompiled if Ivy is enabled.

There was a problem with how a cached compiler host was present that was
unaware of the changed resources, therefore failing to trigger a
recompilation of a component whenever its template changes. This commit
fixes the issue by ensuring that information about modified resources is
correctly available to the cached compiler host.

Fixes #32869

PR Close #33551
This commit is contained in:
JoostK 2019-11-03 00:33:31 +01:00 committed by Andrew Scott
parent 8d72a37f3e
commit 8912b11f56
3 changed files with 39 additions and 6 deletions

View File

@ -153,7 +153,7 @@ export function performWatchCompilation(host: PerformWatchHost):
} }
// Invoked to perform initial compilation or re-compilation in watch mode // Invoked to perform initial compilation or re-compilation in watch mode
function doCompilation(modifiedResourceFiles?: Set<string>): Diagnostics { function doCompilation(): Diagnostics {
if (!cachedOptions) { if (!cachedOptions) {
cachedOptions = host.readConfiguration(); cachedOptions = host.readConfiguration();
} }
@ -197,8 +197,12 @@ export function performWatchCompilation(host: PerformWatchHost):
return ce.content !; return ce.content !;
}; };
// Provide access to the file paths that triggered this rebuild // Provide access to the file paths that triggered this rebuild
cachedCompilerHost.getModifiedResourceFiles = cachedCompilerHost.getModifiedResourceFiles = function() {
modifiedResourceFiles !== undefined ? () => modifiedResourceFiles : undefined; if (timerHandleForRecompilation === undefined) {
return undefined;
}
return timerHandleForRecompilation.modifiedResourceFiles;
};
} }
ignoreFilesForWatch.clear(); ignoreFilesForWatch.clear();
const oldProgram = cachedProgram; const oldProgram = cachedProgram;
@ -287,7 +291,7 @@ export function performWatchCompilation(host: PerformWatchHost):
function recompile() { function recompile() {
host.reportDiagnostics( host.reportDiagnostics(
[createMessageDiagnostic('File change detected. Starting incremental compilation.')]); [createMessageDiagnostic('File change detected. Starting incremental compilation.')]);
doCompilation(timerHandleForRecompilation !.modifiedResourceFiles); doCompilation();
timerHandleForRecompilation = undefined; timerHandleForRecompilation = undefined;
} }
} }

View File

@ -109,6 +109,7 @@ ts_library(
":test_utils", ":test_utils",
"//packages/compiler", "//packages/compiler",
"//packages/compiler-cli", "//packages/compiler-cli",
"//packages/private/testing",
"@npm//typescript", "@npm//typescript",
], ],
) )

View File

@ -6,6 +6,7 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {ivyEnabled} from '@angular/private/testing';
import * as fs from 'fs'; import * as fs from 'fs';
import * as path from 'path'; import * as path from 'path';
import * as ts from 'typescript'; import * as ts from 'typescript';
@ -24,8 +25,8 @@ describe('perform watch', () => {
outDir = path.resolve(testSupport.basePath, 'outDir'); outDir = path.resolve(testSupport.basePath, 'outDir');
}); });
function createConfig(): ng.ParsedConfiguration { function createConfig(overrideOptions: ng.CompilerOptions = {}): ng.ParsedConfiguration {
const options = testSupport.createCompilerOptions({outDir}); const options = testSupport.createCompilerOptions({outDir, ...overrideOptions});
return { return {
options, options,
rootNames: [path.resolve(testSupport.basePath, 'src/index.ts')], rootNames: [path.resolve(testSupport.basePath, 'src/index.ts')],
@ -50,6 +51,33 @@ describe('perform watch', () => {
expect(fs.existsSync(path.resolve(outDir, 'src', 'main.ngfactory.js'))).toBe(true); expect(fs.existsSync(path.resolve(outDir, 'src', 'main.ngfactory.js'))).toBe(true);
}); });
it('should recompile components when its template changes', () => {
const config = createConfig({enableIvy: ivyEnabled});
const host = new MockWatchHost(config);
testSupport.writeFiles({
'src/main.ts': createModuleAndCompSource('main', './main.html'),
'src/main.html': 'initial',
'src/index.ts': `export * from './main'; `,
});
const watchResult = performWatchCompilation(host);
expectNoDiagnostics(config.options, watchResult.firstCompileResult);
const htmlPath = path.resolve(testSupport.basePath, 'src', 'main.html');
const genPath = ivyEnabled ? path.resolve(outDir, 'src', 'main.js') :
path.resolve(outDir, 'src', 'main.ngfactory.js');
const initial = fs.readFileSync(genPath, {encoding: 'utf8'});
expect(initial).toContain('"initial"');
fs.writeFileSync(htmlPath, 'updated');
host.triggerFileChange(FileChangeEvent.Change, htmlPath);
const updated = fs.readFileSync(genPath, {encoding: 'utf8'});
expect(updated).toContain('"updated"');
});
it('should cache files on subsequent runs', () => { it('should cache files on subsequent runs', () => {
const config = createConfig(); const config = createConfig();
const host = new MockWatchHost(config); const host = new MockWatchHost(config);