fix(compiler-cli): Return original sourceFile instead of redirected sourceFile from getSourceFile (#26036)

Closes #22524

PR Close #26036
This commit is contained in:
Jon Wallsten 2019-07-08 16:50:19 +02:00 committed by Matias Niemelä
parent 40705f3366
commit 3166cffd28
4 changed files with 85 additions and 0 deletions

View File

@ -39,6 +39,13 @@ export class TypeCheckProgramHost implements ts.CompilerHost {
sf = this.delegate.getSourceFile(
fileName, languageVersion, onError, shouldCreateNewSourceFile);
sf && this.sfMap.set(fileName, sf);
} else {
// TypeScript doesn't allow returning redirect source files. To avoid unforseen errors we
// return the original source file instead of the redirect target.
const redirectInfo = (sf as any).redirectInfo;
if (redirectInfo !== undefined) {
sf = redirectInfo.unredirected;
}
}
return sf;
}

View File

@ -441,6 +441,12 @@ export class TsCompilerAotCompilerTypeCheckHostAdapter implements ts.CompilerHos
fileName, summary.text, this.options.target || ts.ScriptTarget.Latest);
}
sf = summary.sourceFile;
// TypeScript doesn't allow returning redirect source files. To avoid unforseen errors we
// return the original source file instead of the redirect target.
const redirectInfo = (sf as any).redirectInfo;
if (redirectInfo !== undefined) {
sf = redirectInfo.unredirected;
}
genFileNames = [];
}
}

View File

@ -202,6 +202,37 @@ runInEachFileSystem(() => {
// If program reuse were configured incorrectly (as was responsible for
// https://github.com/angular/angular/issues/30079), this would have crashed.
});
// https://github.com/angular/angular/pull/26036
it('should handle redirected source files', () => {
env.tsconfig({fullTemplateTypeCheck: true});
// This file structure has an identical version of "a" under the root node_modules and inside
// of "b". Because their package.json file indicates it is the exact same version of "a",
// TypeScript will transform the source file of "node_modules/b/node_modules/a/index.d.ts"
// into a redirect to "node_modules/a/index.d.ts". During incremental compilations, we must
// assure not to reintroduce "node_modules/b/node_modules/a/index.d.ts" as its redirected
// source file, but instead use its original file.
env.write('node_modules/a/index.js', `export class ServiceA {}`);
env.write('node_modules/a/index.d.ts', `export declare class ServiceA {}`);
env.write('node_modules/a/package.json', `{"name": "a", "version": "1.0"}`);
env.write('node_modules/b/node_modules/a/index.js', `export class ServiceA {}`);
env.write('node_modules/b/node_modules/a/index.d.ts', `export declare class ServiceA {}`);
env.write('node_modules/b/node_modules/a/package.json', `{"name": "a", "version": "1.0"}`);
env.write('node_modules/b/index.js', `export {ServiceA as ServiceB} from 'a';`);
env.write('node_modules/b/index.d.ts', `export {ServiceA as ServiceB} from 'a';`);
env.write('test.ts', `
import {ServiceA} from 'a';
import {ServiceB} from 'b';
`);
env.driveMain();
env.flushWrittenFileTracking();
// Pretend a change was made to test.ts. If redirect sources were introduced into the new
// program, this would fail due to an assertion failure in TS.
env.invalidateCachedFile('test.ts');
env.driveMain();
});
});
function setupFooBarProgram(env: NgtscTestEnvironment) {

View File

@ -107,6 +107,47 @@ describe('perform watch', () => {
expect(getSourceFileSpy !).toHaveBeenCalledWith(utilTsPath, ts.ScriptTarget.ES5);
});
// https://github.com/angular/angular/pull/26036
it('should handle redirected source files', () => {
const config = createConfig();
const host = new MockWatchHost(config);
host.createCompilerHost = (options: ng.CompilerOptions) => {
const ngHost = ng.createCompilerHost({options});
return ngHost;
};
// This file structure has an identical version of "a" under the root node_modules and inside
// of "b". Because their package.json file indicates it is the exact same version of "a",
// TypeScript will transform the source file of "node_modules/b/node_modules/a/index.d.ts"
// into a redirect to "node_modules/a/index.d.ts". During watch compilations, we must assure
// not to reintroduce "node_modules/b/node_modules/a/index.d.ts" as its redirected source file,
// but instead using its original file.
testSupport.writeFiles({
'node_modules/a/index.js': `export class ServiceA {}`,
'node_modules/a/index.d.ts': `export declare class ServiceA {}`,
'node_modules/a/package.json': `{"name": "a", "version": "1.0"}`,
'node_modules/b/node_modules/a/index.js': `export class ServiceA {}`,
'node_modules/b/node_modules/a/index.d.ts': `export declare class ServiceA {}`,
'node_modules/b/node_modules/a/package.json': `{"name": "a", "version": "1.0"}`,
'node_modules/b/index.js': `export {ServiceA as ServiceB} from 'a';`,
'node_modules/b/index.d.ts': `export {ServiceA as ServiceB} from 'a';`,
'src/index.ts': `
import {ServiceA} from 'a';
import {ServiceB} from 'b';
`,
});
const indexTsPath = path.posix.join(testSupport.basePath, 'src', 'index.ts');
performWatchCompilation(host);
// Trigger a file change. This recreates the program from the old program. If redirect sources
// were introduced into the new program, this would fail due to an assertion failure in TS.
host.triggerFileChange(FileChangeEvent.Change, indexTsPath);
expectNoDiagnostics(config.options, host.diagnostics);
});
it('should recover from static analysis errors', () => {
const config = createConfig();
const host = new MockWatchHost(config);