Skip to content

Commit

Permalink
fix(compiler-cli): Return original sourceFile instead of redirected s…
Browse files Browse the repository at this point in the history
…ourceFile from getSourceFile

Closes angular#22524
  • Loading branch information
JonWallsten committed Jul 9, 2019
1 parent 6aaca21 commit 70f37a8
Show file tree
Hide file tree
Showing 4 changed files with 91 additions and 3 deletions.
10 changes: 9 additions & 1 deletion packages/compiler-cli/src/ngtsc/typecheck/src/host.ts
Expand Up @@ -38,7 +38,15 @@ export class TypeCheckProgramHost implements ts.CompilerHost {
// a cache miss.
sf = this.delegate.getSourceFile(
fileName, languageVersion, onError, shouldCreateNewSourceFile);
sf && this.sfMap.set(fileName, sf);
if (sf) {
this.sfMap.set(fileName, sf !);
}
} else if ((sf as any).redirectInfo) {
// When redirectInfo is provided on a sourceFile it means the same file exists in another
// location.
// We return 'redirectInfo.unredirected' to return the original file and not the, by
// TypeScript, suggested target file.
sf = (sf as any).redirectInfo.unredirected;
}
return sf;
}
Expand Down
12 changes: 10 additions & 2 deletions packages/compiler-cli/src/transformers/compiler_host.ts
Expand Up @@ -440,8 +440,16 @@ export class TsCompilerAotCompilerTypeCheckHostAdapter implements ts.CompilerHos
summary.sourceFile = ts.createSourceFile(
fileName, summary.text, this.options.target || ts.ScriptTarget.Latest);
}
sf = summary.sourceFile;
genFileNames = [];
// When redirectInfo is provided on a sourceFile it means the same file exists in another
// location.
// We return 'redirectInfo.unredirected' to return the original file and not the, by
// TypeScript, suggested target file.
if ((summary.sourceFile as any).redirectInfo) {
sf = (summary.sourceFile as any).redirectInfo.unredirected;
} else {
sf = summary.sourceFile;
}
if (sf) genFileNames = [];
}
}
if (!sf) {
Expand Down
31 changes: 31 additions & 0 deletions packages/compiler-cli/test/ngtsc/incremental_spec.ts
Expand Up @@ -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) {
Expand Down
41 changes: 41 additions & 0 deletions packages/compiler-cli/test/perform_watch_spec.ts
Expand Up @@ -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);
Expand Down

0 comments on commit 70f37a8

Please sign in to comment.