diff --git a/packages/compiler-cli/src/transformers/compiler_host.ts b/packages/compiler-cli/src/transformers/compiler_host.ts index 90caee9df3..aa27c24e3e 100644 --- a/packages/compiler-cli/src/transformers/compiler_host.ts +++ b/packages/compiler-cli/src/transformers/compiler_host.ts @@ -49,6 +49,7 @@ export class TsCompilerAotCompilerTypeCheckHostAdapter extends private rootDirs: string[]; private moduleResolutionCache: ts.ModuleResolutionCache; private originalSourceFiles = new Map(); + private originalFileExistsCache = new Map(); private generatedSourceFiles = new Map(); private generatedCodeFor = new Set(); private emitter = new TypeScriptEmitter(); @@ -293,12 +294,10 @@ export class TsCompilerAotCompilerTypeCheckHostAdapter extends } this.generatedCodeFor.add(fileName); - const baseNameFromGeneratedFile = this._getBaseNamesForGeneratedFile(fileName).find( - fileName => this.isSourceFile(fileName) && this.fileExists(fileName)); + const baseNameFromGeneratedFile = this._getBaseNameForGeneratedSourceFile(fileName); if (baseNameFromGeneratedFile) { return this.ensureCodeGeneratedFor(baseNameFromGeneratedFile); } - const sf = this.getOriginalSourceFile(fileName, this.options.target || ts.ScriptTarget.Latest); if (!sf) { return; @@ -334,46 +333,54 @@ export class TsCompilerAotCompilerTypeCheckHostAdapter extends 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 { 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, // as it might not have been filled yet. - if (this._getBaseNamesForGeneratedFile(fileName).find(baseFileName => { - 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)); - } - })) { + if (this._getBaseNameForGeneratedSourceFile(fileName)) { return true; } 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); - if (genMatch) { - const [, base, genSuffix, suffix] = genMatch; - let baseNames: string[] = []; - if (genSuffix.indexOf('ngstyle') >= 0) { - // Note: ngstlye files have names like `afile.css.ngstyle.ts` - baseNames = [base]; - } else if (suffix === 'd.ts') { - baseNames = [base + '.d.ts']; - } else if (suffix === 'ts') { - // Note: on-the-fly generated files always have a `.ts` suffix, - // but the file from which we generated it can be a `.ts`/ `.d.ts` - // (see options.generateCodeForLibraries). - baseNames = [`${base}.ts`, `${base}.d.ts`]; - } - return baseNames; + if (!genMatch) { + return undefined; + } + const [, base, genSuffix, suffix] = genMatch; + if (suffix !== 'ts') { + return undefined; + } + if (genSuffix.indexOf('ngstyle') >= 0) { + // Note: ngstyle files have names like `afile.css.ngstyle.ts` + return base; + } else { + // Note: on-the-fly generated files always have a `.ts` suffix, + // but the file from which we generated it can be a `.ts`/ `.d.ts` + // (see options.generateCodeForLibraries). + return [`${base}.ts`, `${base}.d.ts`].find( + baseFileName => this.isSourceFile(baseFileName) && this.originalFileExists(baseFileName)); } - return []; } loadSummary(filePath: string): string|null { @@ -464,5 +471,10 @@ function addNgResourceSuffix(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'; } \ No newline at end of file diff --git a/packages/compiler-cli/test/transformers/compiler_host_spec.ts b/packages/compiler-cli/test/transformers/compiler_host_spec.ts index 404040c31b..8784ff0c6a 100644 --- a/packages/compiler-cli/test/transformers/compiler_host_spec.ts +++ b/packages/compiler-cli/test/transformers/compiler_host_spec.ts @@ -37,9 +37,16 @@ describe('NgCompilerHost', () => { moduleResolution: ts.ModuleResolutionKind.NodeJs, }, ngHost = createNgHost({files}), - }: {files?: Directory, options?: CompilerOptions, ngHost?: CompilerHost} = {}) { + summariesFromPreviousCompilations = new Map(), + }: { + files?: Directory, + options?: CompilerOptions, + ngHost?: CompilerHost, + summariesFromPreviousCompilations?: Map + } = {}) { return new TsCompilerAotCompilerTypeCheckHostAdapter( - ['/tmp/index.ts'], options, ngHost, new MetadataCollector(), codeGenerator, new Map()); + ['/tmp/index.ts'], options, ngHost, new MetadataCollector(), codeGenerator, + summariesFromPreviousCompilations); } describe('fileNameToModuleName', () => { @@ -285,4 +292,32 @@ describe('NgCompilerHost', () => { ].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(); + 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); + }); + }); });