fix(compiler): correctly derive fileExists
for generated files (#19301)
PR Close #19301
This commit is contained in:
@ -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);
|
||||||
// Note: Don't rely on this.generatedSourceFiles here,
|
if (fileName.endsWith('.ngfactory.d.ts')) {
|
||||||
// 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
|
// Note: the factories of a previous program
|
||||||
// are not reachable via the regular fileExists
|
// are not reachable via the regular fileExists
|
||||||
// as they might be in the outDir. So we derive their
|
// as they might be in the outDir. So we derive their
|
||||||
// fileExist information based on the .ngsummary.json file.
|
// fileExist information based on the .ngsummary.json file.
|
||||||
return this.fileExists(summaryFileName(baseFileName));
|
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._getBaseNameForGeneratedSourceFile(fileName)) {
|
||||||
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) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
const [, base, genSuffix, suffix] = genMatch;
|
const [, base, genSuffix, suffix] = genMatch;
|
||||||
let baseNames: string[] = [];
|
if (suffix !== 'ts') {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
if (genSuffix.indexOf('ngstyle') >= 0) {
|
if (genSuffix.indexOf('ngstyle') >= 0) {
|
||||||
// Note: ngstlye files have names like `afile.css.ngstyle.ts`
|
// Note: ngstyle files have names like `afile.css.ngstyle.ts`
|
||||||
baseNames = [base];
|
return base;
|
||||||
} else if (suffix === 'd.ts') {
|
} else {
|
||||||
baseNames = [base + '.d.ts'];
|
|
||||||
} else if (suffix === 'ts') {
|
|
||||||
// Note: on-the-fly generated files always have a `.ts` suffix,
|
// 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`
|
// but the file from which we generated it can be a `.ts`/ `.d.ts`
|
||||||
// (see options.generateCodeForLibraries).
|
// (see options.generateCodeForLibraries).
|
||||||
baseNames = [`${base}.ts`, `${base}.d.ts`];
|
return [`${base}.ts`, `${base}.d.ts`].find(
|
||||||
|
baseFileName => this.isSourceFile(baseFileName) && this.originalFileExists(baseFileName));
|
||||||
}
|
}
|
||||||
return baseNames;
|
|
||||||
}
|
|
||||||
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';
|
||||||
}
|
}
|
@ -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);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
Reference in New Issue
Block a user