fix(compiler): correctly derive fileExists for generated files (#19301)

PR Close #19301
This commit is contained in:
Tobias Bosch
2017-09-20 16:31:32 -07:00
committed by Igor Minar
parent 1a647c399b
commit f2bad195bc
2 changed files with 81 additions and 34 deletions

View File

@ -49,6 +49,7 @@ export class TsCompilerAotCompilerTypeCheckHostAdapter extends
private rootDirs: string[]; private rootDirs: string[];
private moduleResolutionCache: ts.ModuleResolutionCache; private moduleResolutionCache: ts.ModuleResolutionCache;
private originalSourceFiles = new Map<string, ts.SourceFile>(); private originalSourceFiles = new Map<string, ts.SourceFile>();
private originalFileExistsCache = new Map<string, boolean>();
private generatedSourceFiles = new Map<string, GenSourceFile>(); private generatedSourceFiles = new Map<string, GenSourceFile>();
private generatedCodeFor = new Set<string>(); private generatedCodeFor = new Set<string>();
private emitter = new TypeScriptEmitter(); private emitter = new TypeScriptEmitter();
@ -293,12 +294,10 @@ export class TsCompilerAotCompilerTypeCheckHostAdapter extends
} }
this.generatedCodeFor.add(fileName); this.generatedCodeFor.add(fileName);
const baseNameFromGeneratedFile = this._getBaseNamesForGeneratedFile(fileName).find( const baseNameFromGeneratedFile = this._getBaseNameForGeneratedSourceFile(fileName);
fileName => this.isSourceFile(fileName) && this.fileExists(fileName));
if (baseNameFromGeneratedFile) { if (baseNameFromGeneratedFile) {
return this.ensureCodeGeneratedFor(baseNameFromGeneratedFile); return this.ensureCodeGeneratedFor(baseNameFromGeneratedFile);
} }
const sf = this.getOriginalSourceFile(fileName, this.options.target || ts.ScriptTarget.Latest); const sf = this.getOriginalSourceFile(fileName, this.options.target || ts.ScriptTarget.Latest);
if (!sf) { if (!sf) {
return; return;
@ -334,46 +333,54 @@ export class TsCompilerAotCompilerTypeCheckHostAdapter extends
return this.getOriginalSourceFile(fileName, languageVersion, onError) !; return this.getOriginalSourceFile(fileName, languageVersion, onError) !;
} }
private originalFileExists(fileName: string): boolean {
let fileExists = this.originalFileExistsCache.get(fileName);
if (fileExists == null) {
fileExists = this.context.fileExists(fileName);
this.originalFileExistsCache.set(fileName, fileExists);
}
return fileExists;
}
fileExists(fileName: string): boolean { fileExists(fileName: string): boolean {
fileName = stripNgResourceSuffix(fileName); fileName = stripNgResourceSuffix(fileName);
if (fileName.endsWith('.ngfactory.d.ts')) {
// Note: the factories of a previous program
// are not reachable via the regular fileExists
// as they might be in the outDir. So we derive their
// fileExist information based on the .ngsummary.json file.
if (this.summariesFromPreviousCompilations.has(summaryFileName(fileName))) {
return true;
}
}
// Note: Don't rely on this.generatedSourceFiles here, // Note: Don't rely on this.generatedSourceFiles here,
// as it might not have been filled yet. // as it might not have been filled yet.
if (this._getBaseNamesForGeneratedFile(fileName).find(baseFileName => { if (this._getBaseNameForGeneratedSourceFile(fileName)) {
if (this.isSourceFile(baseFileName)) {
return this.fileExists(baseFileName);
} else {
// Note: the factories of a previous program
// are not reachable via the regular fileExists
// as they might be in the outDir. So we derive their
// fileExist information based on the .ngsummary.json file.
return this.fileExists(summaryFileName(baseFileName));
}
})) {
return true; return true;
} }
return this.summariesFromPreviousCompilations.has(fileName) || return this.summariesFromPreviousCompilations.has(fileName) ||
this.originalSourceFiles.has(fileName) || this.context.fileExists(fileName); this.originalFileExists(fileName);
} }
private _getBaseNamesForGeneratedFile(genFileName: string): string[] { private _getBaseNameForGeneratedSourceFile(genFileName: string): string|undefined {
const genMatch = GENERATED_FILES.exec(genFileName); const genMatch = GENERATED_FILES.exec(genFileName);
if (genMatch) { if (!genMatch) {
const [, base, genSuffix, suffix] = genMatch; return undefined;
let baseNames: string[] = []; }
if (genSuffix.indexOf('ngstyle') >= 0) { const [, base, genSuffix, suffix] = genMatch;
// Note: ngstlye files have names like `afile.css.ngstyle.ts` if (suffix !== 'ts') {
baseNames = [base]; return undefined;
} else if (suffix === 'd.ts') { }
baseNames = [base + '.d.ts']; if (genSuffix.indexOf('ngstyle') >= 0) {
} else if (suffix === 'ts') { // Note: ngstyle files have names like `afile.css.ngstyle.ts`
// Note: on-the-fly generated files always have a `.ts` suffix, return base;
// but the file from which we generated it can be a `.ts`/ `.d.ts` } else {
// (see options.generateCodeForLibraries). // Note: on-the-fly generated files always have a `.ts` suffix,
baseNames = [`${base}.ts`, `${base}.d.ts`]; // but the file from which we generated it can be a `.ts`/ `.d.ts`
} // (see options.generateCodeForLibraries).
return baseNames; return [`${base}.ts`, `${base}.d.ts`].find(
baseFileName => this.isSourceFile(baseFileName) && this.originalFileExists(baseFileName));
} }
return [];
} }
loadSummary(filePath: string): string|null { loadSummary(filePath: string): string|null {
@ -464,5 +471,10 @@ function addNgResourceSuffix(fileName: string): string {
} }
function summaryFileName(fileName: string): string { function summaryFileName(fileName: string): string {
const genFileMatch = GENERATED_FILES.exec(fileName);
if (genFileMatch) {
const base = genFileMatch[1];
return base + '.ngsummary.json';
}
return fileName.replace(EXT, '') + '.ngsummary.json'; return fileName.replace(EXT, '') + '.ngsummary.json';
} }

View File

@ -37,9 +37,16 @@ describe('NgCompilerHost', () => {
moduleResolution: ts.ModuleResolutionKind.NodeJs, moduleResolution: ts.ModuleResolutionKind.NodeJs,
}, },
ngHost = createNgHost({files}), ngHost = createNgHost({files}),
}: {files?: Directory, options?: CompilerOptions, ngHost?: CompilerHost} = {}) { summariesFromPreviousCompilations = new Map<string, string>(),
}: {
files?: Directory,
options?: CompilerOptions,
ngHost?: CompilerHost,
summariesFromPreviousCompilations?: Map<string, string>
} = {}) {
return new TsCompilerAotCompilerTypeCheckHostAdapter( return new TsCompilerAotCompilerTypeCheckHostAdapter(
['/tmp/index.ts'], options, ngHost, new MetadataCollector(), codeGenerator, new Map()); ['/tmp/index.ts'], options, ngHost, new MetadataCollector(), codeGenerator,
summariesFromPreviousCompilations);
} }
describe('fileNameToModuleName', () => { describe('fileNameToModuleName', () => {
@ -285,4 +292,32 @@ describe('NgCompilerHost', () => {
].join('\n')); ].join('\n'));
}); });
}); });
describe('fileExists', () => {
it('should cache calls', () => {
const ngHost = createNgHost({files: {'tmp': {'src': {'index.ts': ``}}}});
spyOn(ngHost, 'fileExists').and.callThrough();
const host = createHost({ngHost});
expect(host.fileExists('/tmp/src/index.ts')).toBe(true);
expect(host.fileExists('/tmp/src/index.ts')).toBe(true);
expect(ngHost.fileExists).toHaveBeenCalledTimes(1);
});
it(`should not derive the existence of generated files baesd on summaries on disc`, () => {
const host = createHost({files: {'tmp': {'lib': {'module.ngsummary.json': ``}}}});
expect(host.fileExists('/tmp/lib/module.ngfactory.ts')).toBe(false);
expect(host.fileExists('/tmp/lib/module.ngfactory.d.ts')).toBe(false);
});
it(`should derive the existence of generated .d.ts files based on the summaries from an old program`,
() => {
const summariesFromPreviousCompilations = new Map<string, string>();
summariesFromPreviousCompilations.set('/tmp/lib/module.ngsummary.json', `{}`);
const host = createHost({summariesFromPreviousCompilations});
expect(host.fileExists('/tmp/lib/module.ngfactory.ts')).toBe(false);
expect(host.fileExists('/tmp/lib/module.ngfactory.d.ts')).toBe(true);
});
});
}); });