feat(ivy): generate .ngfactory stubs if requested (#25176)

Existing bootstrap code in the wild depends on the existence of
.ngfactory files, which Ivy does not need. This commit adds the
capability in ngtsc to generate .ngfactory files which bridge
existing bootstrap code with Ivy.

This is an initial step. Remaining work includes complying with
the compiler option to specify a generated file directory, as well
as presumably testing in g3.

PR Close #25176
This commit is contained in:
Alex Rickabaugh
2018-07-27 22:57:44 -07:00
committed by Kara Erickson
parent 728d98d3a9
commit 0822dc70f2
13 changed files with 338 additions and 10 deletions

View File

@ -13,6 +13,7 @@ import * as ts from 'typescript';
import * as api from '../transformers/api';
import {ComponentDecoratorHandler, DirectiveDecoratorHandler, InjectableDecoratorHandler, NgModuleDecoratorHandler, PipeDecoratorHandler, ResourceLoader, SelectorScopeRegistry} from './annotations';
import {FactoryGenerator, FactoryInfo, GeneratedFactoryHostWrapper, generatedFactoryTransform} from './factories';
import {TypeScriptReflectionHost} from './metadata';
import {FileResourceLoader, HostResourceLoader} from './resource_loader';
import {IvyCompilation, ivyTransformFactory} from './transform';
@ -21,20 +22,39 @@ export class NgtscProgram implements api.Program {
private tsProgram: ts.Program;
private resourceLoader: ResourceLoader;
private compilation: IvyCompilation|undefined = undefined;
private factoryToSourceInfo: Map<string, FactoryInfo>|null = null;
private sourceToFactorySymbols: Map<string, Set<string>>|null = null;
private host: ts.CompilerHost;
private _coreImportsFrom: ts.SourceFile|null|undefined = undefined;
private _reflector: TypeScriptReflectionHost|undefined = undefined;
private _isCore: boolean|undefined = undefined;
constructor(
rootNames: ReadonlyArray<string>, private options: api.CompilerOptions,
private host: api.CompilerHost, oldProgram?: api.Program) {
host: api.CompilerHost, oldProgram?: api.Program) {
this.resourceLoader = host.readResource !== undefined ?
new HostResourceLoader(host.readResource.bind(host)) :
new FileResourceLoader();
const shouldGenerateFactories = options.allowEmptyCodegenFiles || false;
this.host = host;
let rootFiles = [...rootNames];
if (shouldGenerateFactories) {
const generator = new FactoryGenerator();
const factoryFileMap = generator.computeFactoryFileMap(rootNames);
rootFiles.push(...Array.from(factoryFileMap.keys()));
this.factoryToSourceInfo = new Map<string, FactoryInfo>();
this.sourceToFactorySymbols = new Map<string, Set<string>>();
factoryFileMap.forEach((sourceFilePath, factoryPath) => {
const moduleSymbolNames = new Set<string>();
this.sourceToFactorySymbols !.set(sourceFilePath, moduleSymbolNames);
this.factoryToSourceInfo !.set(factoryPath, {sourceFilePath, moduleSymbolNames});
});
this.host = new GeneratedFactoryHostWrapper(host, generator, factoryFileMap);
}
this.tsProgram =
ts.createProgram(rootNames, options, host, oldProgram && oldProgram.getTsProgram());
ts.createProgram(rootFiles, options, this.host, oldProgram && oldProgram.getTsProgram());
}
getTsProgram(): ts.Program { return this.tsProgram; }
@ -125,7 +145,11 @@ export class NgtscProgram implements api.Program {
this.host.writeFile(fileName, data, writeByteOrderMark, onError, sourceFiles);
};
const transforms =
[ivyTransformFactory(this.compilation !, this.reflector, this.coreImportsFrom)];
if (this.factoryToSourceInfo !== null) {
transforms.push(generatedFactoryTransform(this.factoryToSourceInfo, this.coreImportsFrom));
}
// Run the emit, including a custom transformer that will downlevel the Ivy decorators in code.
const emitResult = emitCallback({
program: this.tsProgram,
@ -133,7 +157,7 @@ export class NgtscProgram implements api.Program {
options: this.options,
emitOnlyDtsFiles: false, writeFile,
customTransformers: {
before: [ivyTransformFactory(this.compilation !, this.reflector, this.coreImportsFrom)],
before: transforms,
},
});
return emitResult;
@ -153,7 +177,8 @@ export class NgtscProgram implements api.Program {
new PipeDecoratorHandler(checker, this.reflector, scopeRegistry, this.isCore),
];
return new IvyCompilation(handlers, checker, this.reflector, this.coreImportsFrom);
return new IvyCompilation(
handlers, checker, this.reflector, this.coreImportsFrom, this.sourceToFactorySymbols);
}
private get reflector(): TypeScriptReflectionHost {