refactor(ivy): allow for shim generators not based on existing files (#27497)

Previously the ngtsc ShimGenerator interface expected that all shims would
be generated using the contents of existing ts.SourceFiles. This assumption
was true for ngfactory and ngsummary files, but breaks down for flat module
index files, which are standalone.

This commit prepares for flat module index generation by enabling shim
generators which don't require an existing file.

PR Close #27497
This commit is contained in:
Alex Rickabaugh 2018-12-05 15:46:21 -08:00
parent 6bd28e6063
commit 352c582f98
3 changed files with 36 additions and 22 deletions

View File

@ -26,9 +26,16 @@ export class FactoryGenerator implements ShimGenerator {
get factoryFileMap(): Map<string, string> { return this.map; } get factoryFileMap(): Map<string, string> { return this.map; }
getOriginalSourceOfShim(fileName: string): string|null { return this.map.get(fileName) || null; } recognize(fileName: string): boolean { return this.map.has(fileName); }
generate(genFilePath: string, readFile: (fileName: string) => ts.SourceFile | null): ts.SourceFile
|null {
const originalPath = this.map.get(genFilePath) !;
const original = readFile(originalPath);
if (original === null) {
return null;
}
generate(original: ts.SourceFile, genFilePath: string): ts.SourceFile {
const relativePathToSource = const relativePathToSource =
'./' + path.posix.basename(original.fileName).replace(TS_DTS_SUFFIX, ''); './' + path.posix.basename(original.fileName).replace(TS_DTS_SUFFIX, '');
// Collect a list of classes that need to have factory types emitted for them. This list is // Collect a list of classes that need to have factory types emitted for them. This list is

View File

@ -11,17 +11,20 @@ import * as ts from 'typescript';
export interface ShimGenerator { export interface ShimGenerator {
/** /**
* Get the original source file for the given shim path, the contents of which determine the * Returns `true` if this generator is intended to handle the given file.
* contents of the shim file.
*
* If this returns `null` then the given file was not a shim file handled by this generator.
*/ */
getOriginalSourceOfShim(fileName: string): string|null; recognize(fileName: string): boolean;
/** /**
* Generate a shim's `ts.SourceFile` for the given original file. * Generate a shim's `ts.SourceFile` for the given original file.
*
* `readFile` is a function which allows the generator to look up the contents of existing source
* files. It returns null if the requested file doesn't exist.
*
* If `generate` returns null, then the shim generator declines to generate the file after all.
*/ */
generate(original: ts.SourceFile, genFileName: string): ts.SourceFile; generate(genFileName: string, readFile: (fileName: string) => ts.SourceFile | null): ts.SourceFile
|null;
} }
/** /**
@ -55,17 +58,14 @@ export class GeneratedShimsHostWrapper implements ts.CompilerHost {
shouldCreateNewSourceFile?: boolean|undefined): ts.SourceFile|undefined { shouldCreateNewSourceFile?: boolean|undefined): ts.SourceFile|undefined {
for (let i = 0; i < this.shimGenerators.length; i++) { for (let i = 0; i < this.shimGenerators.length; i++) {
const generator = this.shimGenerators[i]; const generator = this.shimGenerators[i];
const originalFile = generator.getOriginalSourceOfShim(fileName); if (generator.recognize(fileName)) {
if (originalFile !== null) { const readFile = (originalFile: string) => {
// This shim generator has recognized the filename being requested, and is now responsible return this.delegate.getSourceFile(
// for generating its contents, based on the contents of the original file it has requested. originalFile, languageVersion, onError, shouldCreateNewSourceFile) ||
const originalSource = this.delegate.getSourceFile( null;
originalFile, languageVersion, onError, shouldCreateNewSourceFile); };
if (originalSource === undefined) {
// The original requested file doesn't exist, so the shim cannot exist either. return generator.generate(fileName, readFile) || undefined;
return undefined;
}
return generator.generate(originalSource, fileName);
} }
} }
return this.delegate.getSourceFile( return this.delegate.getSourceFile(
@ -100,7 +100,7 @@ export class GeneratedShimsHostWrapper implements ts.CompilerHost {
// Consider the file as existing whenever 1) it really does exist in the delegate host, or // Consider the file as existing whenever 1) it really does exist in the delegate host, or
// 2) at least one of the shim generators recognizes it. // 2) at least one of the shim generators recognizes it.
return this.delegate.fileExists(fileName) || return this.delegate.fileExists(fileName) ||
this.shimGenerators.some(gen => gen.getOriginalSourceOfShim(canonical) !== null); this.shimGenerators.some(gen => gen.recognize(canonical));
} }
readFile(fileName: string): string|undefined { return this.delegate.readFile(fileName); } readFile(fileName: string): string|undefined { return this.delegate.readFile(fileName); }

View File

@ -16,9 +16,16 @@ export class SummaryGenerator implements ShimGenerator {
getSummaryFileNames(): string[] { return Array.from(this.map.keys()); } getSummaryFileNames(): string[] { return Array.from(this.map.keys()); }
getOriginalSourceOfShim(fileName: string): string|null { return this.map.get(fileName) || null; } recognize(fileName: string): boolean { return this.map.has(fileName); }
generate(genFilePath: string, readFile: (fileName: string) => ts.SourceFile | null): ts.SourceFile
|null {
const originalPath = this.map.get(genFilePath) !;
const original = readFile(originalPath);
if (original === null) {
return null;
}
generate(original: ts.SourceFile, genFilePath: string): ts.SourceFile {
// Collect a list of classes that need to have factory types emitted for them. This list is // Collect a list of classes that need to have factory types emitted for them. This list is
// overly broad as at this point the ts.TypeChecker has not been created and so it can't be used // overly broad as at this point the ts.TypeChecker has not been created and so it can't be used
// to semantically understand which decorators are Angular decorators. It's okay to output an // to semantically understand which decorators are Angular decorators. It's okay to output an