fix(compiler-cli): Return original sourceFile instead of redirected sourceFile from getSourceFile (#26036)
Closes #22524 PR Close #26036
This commit is contained in:
parent
40705f3366
commit
3166cffd28
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 = [];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue