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(
|
sf = this.delegate.getSourceFile(
|
||||||
fileName, languageVersion, onError, shouldCreateNewSourceFile);
|
fileName, languageVersion, onError, shouldCreateNewSourceFile);
|
||||||
sf && this.sfMap.set(fileName, sf);
|
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;
|
return sf;
|
||||||
}
|
}
|
||||||
|
|
|
@ -441,6 +441,12 @@ export class TsCompilerAotCompilerTypeCheckHostAdapter implements ts.CompilerHos
|
||||||
fileName, summary.text, this.options.target || ts.ScriptTarget.Latest);
|
fileName, summary.text, this.options.target || ts.ScriptTarget.Latest);
|
||||||
}
|
}
|
||||||
sf = summary.sourceFile;
|
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 = [];
|
genFileNames = [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -202,6 +202,37 @@ runInEachFileSystem(() => {
|
||||||
// If program reuse were configured incorrectly (as was responsible for
|
// 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/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) {
|
function setupFooBarProgram(env: NgtscTestEnvironment) {
|
||||||
|
|
|
@ -107,6 +107,47 @@ describe('perform watch', () => {
|
||||||
expect(getSourceFileSpy !).toHaveBeenCalledWith(utilTsPath, ts.ScriptTarget.ES5);
|
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', () => {
|
it('should recover from static analysis errors', () => {
|
||||||
const config = createConfig();
|
const config = createConfig();
|
||||||
const host = new MockWatchHost(config);
|
const host = new MockWatchHost(config);
|
||||||
|
|
Loading…
Reference in New Issue