Skip to content

Commit 3166cff

Browse files
JonWallstenmatsko
authored andcommittedJul 15, 2019
fix(compiler-cli): Return original sourceFile instead of redirected sourceFile from getSourceFile (#26036)
Closes #22524 PR Close #26036
1 parent 40705f3 commit 3166cff

File tree

4 files changed

+85
-0
lines changed

4 files changed

+85
-0
lines changed
 

‎packages/compiler-cli/src/ngtsc/typecheck/src/host.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,13 @@ export class TypeCheckProgramHost implements ts.CompilerHost {
3939
sf = this.delegate.getSourceFile(
4040
fileName, languageVersion, onError, shouldCreateNewSourceFile);
4141
sf && this.sfMap.set(fileName, sf);
42+
} else {
43+
// TypeScript doesn't allow returning redirect source files. To avoid unforseen errors we
44+
// return the original source file instead of the redirect target.
45+
const redirectInfo = (sf as any).redirectInfo;
46+
if (redirectInfo !== undefined) {
47+
sf = redirectInfo.unredirected;
48+
}
4249
}
4350
return sf;
4451
}

‎packages/compiler-cli/src/transformers/compiler_host.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -441,6 +441,12 @@ export class TsCompilerAotCompilerTypeCheckHostAdapter implements ts.CompilerHos
441441
fileName, summary.text, this.options.target || ts.ScriptTarget.Latest);
442442
}
443443
sf = summary.sourceFile;
444+
// TypeScript doesn't allow returning redirect source files. To avoid unforseen errors we
445+
// return the original source file instead of the redirect target.
446+
const redirectInfo = (sf as any).redirectInfo;
447+
if (redirectInfo !== undefined) {
448+
sf = redirectInfo.unredirected;
449+
}
444450
genFileNames = [];
445451
}
446452
}

‎packages/compiler-cli/test/ngtsc/incremental_spec.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,37 @@ runInEachFileSystem(() => {
202202
// If program reuse were configured incorrectly (as was responsible for
203203
// https://github.com/angular/angular/issues/30079), this would have crashed.
204204
});
205+
206+
// https://github.com/angular/angular/pull/26036
207+
it('should handle redirected source files', () => {
208+
env.tsconfig({fullTemplateTypeCheck: true});
209+
210+
// This file structure has an identical version of "a" under the root node_modules and inside
211+
// of "b". Because their package.json file indicates it is the exact same version of "a",
212+
// TypeScript will transform the source file of "node_modules/b/node_modules/a/index.d.ts"
213+
// into a redirect to "node_modules/a/index.d.ts". During incremental compilations, we must
214+
// assure not to reintroduce "node_modules/b/node_modules/a/index.d.ts" as its redirected
215+
// source file, but instead use its original file.
216+
env.write('node_modules/a/index.js', `export class ServiceA {}`);
217+
env.write('node_modules/a/index.d.ts', `export declare class ServiceA {}`);
218+
env.write('node_modules/a/package.json', `{"name": "a", "version": "1.0"}`);
219+
env.write('node_modules/b/node_modules/a/index.js', `export class ServiceA {}`);
220+
env.write('node_modules/b/node_modules/a/index.d.ts', `export declare class ServiceA {}`);
221+
env.write('node_modules/b/node_modules/a/package.json', `{"name": "a", "version": "1.0"}`);
222+
env.write('node_modules/b/index.js', `export {ServiceA as ServiceB} from 'a';`);
223+
env.write('node_modules/b/index.d.ts', `export {ServiceA as ServiceB} from 'a';`);
224+
env.write('test.ts', `
225+
import {ServiceA} from 'a';
226+
import {ServiceB} from 'b';
227+
`);
228+
env.driveMain();
229+
env.flushWrittenFileTracking();
230+
231+
// Pretend a change was made to test.ts. If redirect sources were introduced into the new
232+
// program, this would fail due to an assertion failure in TS.
233+
env.invalidateCachedFile('test.ts');
234+
env.driveMain();
235+
});
205236
});
206237

207238
function setupFooBarProgram(env: NgtscTestEnvironment) {

‎packages/compiler-cli/test/perform_watch_spec.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,47 @@ describe('perform watch', () => {
107107
expect(getSourceFileSpy !).toHaveBeenCalledWith(utilTsPath, ts.ScriptTarget.ES5);
108108
});
109109

110+
// https://github.com/angular/angular/pull/26036
111+
it('should handle redirected source files', () => {
112+
const config = createConfig();
113+
const host = new MockWatchHost(config);
114+
host.createCompilerHost = (options: ng.CompilerOptions) => {
115+
const ngHost = ng.createCompilerHost({options});
116+
return ngHost;
117+
};
118+
119+
// This file structure has an identical version of "a" under the root node_modules and inside
120+
// of "b". Because their package.json file indicates it is the exact same version of "a",
121+
// TypeScript will transform the source file of "node_modules/b/node_modules/a/index.d.ts"
122+
// into a redirect to "node_modules/a/index.d.ts". During watch compilations, we must assure
123+
// not to reintroduce "node_modules/b/node_modules/a/index.d.ts" as its redirected source file,
124+
// but instead using its original file.
125+
testSupport.writeFiles({
126+
'node_modules/a/index.js': `export class ServiceA {}`,
127+
'node_modules/a/index.d.ts': `export declare class ServiceA {}`,
128+
'node_modules/a/package.json': `{"name": "a", "version": "1.0"}`,
129+
'node_modules/b/node_modules/a/index.js': `export class ServiceA {}`,
130+
'node_modules/b/node_modules/a/index.d.ts': `export declare class ServiceA {}`,
131+
'node_modules/b/node_modules/a/package.json': `{"name": "a", "version": "1.0"}`,
132+
'node_modules/b/index.js': `export {ServiceA as ServiceB} from 'a';`,
133+
'node_modules/b/index.d.ts': `export {ServiceA as ServiceB} from 'a';`,
134+
'src/index.ts': `
135+
import {ServiceA} from 'a';
136+
import {ServiceB} from 'b';
137+
`,
138+
});
139+
140+
const indexTsPath = path.posix.join(testSupport.basePath, 'src', 'index.ts');
141+
142+
performWatchCompilation(host);
143+
144+
// Trigger a file change. This recreates the program from the old program. If redirect sources
145+
// were introduced into the new program, this would fail due to an assertion failure in TS.
146+
host.triggerFileChange(FileChangeEvent.Change, indexTsPath);
147+
expectNoDiagnostics(config.options, host.diagnostics);
148+
});
149+
150+
110151
it('should recover from static analysis errors', () => {
111152
const config = createConfig();
112153
const host = new MockWatchHost(config);

0 commit comments

Comments
 (0)
Please sign in to comment.