refactor(compiler): add ability to produce stub .ngfactory / .ngsummary files (#16963)
These files are needed so that: - user code can compile even without real codegen - as tsc transformers cannot create but only change existing files in the transformation pipeline.
This commit is contained in:

committed by
Chuck Jazdzewski

parent
fa809ec8cf
commit
eba59aaf87
@ -24,7 +24,7 @@ import {GeneratedFile} from './generated_file';
|
||||
import {StaticReflector} from './static_reflector';
|
||||
import {StaticSymbol} from './static_symbol';
|
||||
import {ResolvedStaticSymbol, StaticSymbolResolver} from './static_symbol_resolver';
|
||||
import {serializeSummaries} from './summary_serializer';
|
||||
import {createForJitStub, serializeSummaries} from './summary_serializer';
|
||||
import {ngfactoryFilePath, splitTypescriptSuffix, summaryFileName, summaryForJitFileName, summaryForJitName} from './util';
|
||||
|
||||
export class AotCompiler {
|
||||
@ -39,38 +39,86 @@ export class AotCompiler {
|
||||
|
||||
clearCache() { this._metadataResolver.clearCache(); }
|
||||
|
||||
compileAllAsync(rootFiles: string[]): Promise<GeneratedFile[]> {
|
||||
analyzeModulesSync(rootFiles: string[]): NgAnalyzedModules {
|
||||
const programSymbols = extractProgramSymbols(this._symbolResolver, rootFiles, this._host);
|
||||
const {ngModuleByPipeOrDirective, files, ngModules} =
|
||||
const analyzeResult =
|
||||
analyzeAndValidateNgModules(programSymbols, this._host, this._metadataResolver);
|
||||
return Promise
|
||||
.all(ngModules.map(
|
||||
ngModule => this._metadataResolver.loadNgModuleDirectiveAndPipeMetadata(
|
||||
ngModule.type.reference, false)))
|
||||
.then(() => {
|
||||
const sourceModules = files.map(
|
||||
file => this._compileSrcFile(
|
||||
file.srcUrl, ngModuleByPipeOrDirective, file.directives, file.pipes,
|
||||
file.ngModules, file.injectables));
|
||||
return flatten(sourceModules);
|
||||
});
|
||||
}
|
||||
|
||||
compileAllSync(rootFiles: string[]): GeneratedFile[] {
|
||||
const programSymbols = extractProgramSymbols(this._symbolResolver, rootFiles, this._host);
|
||||
const {ngModuleByPipeOrDirective, files, ngModules} =
|
||||
analyzeAndValidateNgModules(programSymbols, this._host, this._metadataResolver);
|
||||
ngModules.forEach(
|
||||
analyzeResult.ngModules.forEach(
|
||||
ngModule => this._metadataResolver.loadNgModuleDirectiveAndPipeMetadata(
|
||||
ngModule.type.reference, true));
|
||||
return analyzeResult;
|
||||
}
|
||||
|
||||
analyzeModulesAsync(rootFiles: string[]): Promise<NgAnalyzedModules> {
|
||||
const programSymbols = extractProgramSymbols(this._symbolResolver, rootFiles, this._host);
|
||||
const analyzeResult =
|
||||
analyzeAndValidateNgModules(programSymbols, this._host, this._metadataResolver);
|
||||
return Promise
|
||||
.all(analyzeResult.ngModules.map(
|
||||
ngModule => this._metadataResolver.loadNgModuleDirectiveAndPipeMetadata(
|
||||
ngModule.type.reference, false)))
|
||||
.then(() => analyzeResult);
|
||||
}
|
||||
|
||||
emitAllStubs(analyzeResult: NgAnalyzedModules): GeneratedFile[] {
|
||||
const {files} = analyzeResult;
|
||||
const sourceModules =
|
||||
files.map(file => this._compileStubFile(file.srcUrl, file.directives, file.ngModules));
|
||||
return flatten(sourceModules);
|
||||
}
|
||||
|
||||
emitAllImpls(analyzeResult: NgAnalyzedModules): GeneratedFile[] {
|
||||
const {ngModuleByPipeOrDirective, files} = analyzeResult;
|
||||
const sourceModules = files.map(
|
||||
file => this._compileSrcFile(
|
||||
file => this._compileImplFile(
|
||||
file.srcUrl, ngModuleByPipeOrDirective, file.directives, file.pipes, file.ngModules,
|
||||
file.injectables));
|
||||
return flatten(sourceModules);
|
||||
}
|
||||
|
||||
private _compileSrcFile(
|
||||
private _compileStubFile(
|
||||
srcFileUrl: string, directives: StaticSymbol[], ngModules: StaticSymbol[]): GeneratedFile[] {
|
||||
const fileSuffix = splitTypescriptSuffix(srcFileUrl, true)[1];
|
||||
const generatedFiles: GeneratedFile[] = [];
|
||||
|
||||
const jitSummaryStmts: o.Statement[] = [];
|
||||
const ngFactoryStms: o.Statement[] = [];
|
||||
|
||||
const ngFactoryOutputCtx = this._createOutputContext(ngfactoryFilePath(srcFileUrl, true));
|
||||
const jitSummaryOutputCtx = this._createOutputContext(summaryForJitFileName(srcFileUrl, true));
|
||||
|
||||
// create exports that user code can reference
|
||||
ngModules.forEach((ngModuleReference) => {
|
||||
this._ngModuleCompiler.createStub(ngFactoryOutputCtx, ngModuleReference);
|
||||
createForJitStub(jitSummaryOutputCtx, ngModuleReference);
|
||||
});
|
||||
// Note: we are creating stub ngfactory/ngsummary for all source files,
|
||||
// as the real calculation requires almost the same logic as producing the real content for
|
||||
// them.
|
||||
// Our pipeline will filter out empty ones at the end.
|
||||
generatedFiles.push(this._codegenSourceModule(srcFileUrl, ngFactoryOutputCtx));
|
||||
generatedFiles.push(this._codegenSourceModule(srcFileUrl, jitSummaryOutputCtx));
|
||||
|
||||
// create stubs for external stylesheets (always empty, as users should not import anything from
|
||||
// the generated code)
|
||||
directives.forEach((dirType) => {
|
||||
const compMeta = this._metadataResolver.getDirectiveMetadata(<any>dirType);
|
||||
if (!compMeta.isComponent) {
|
||||
return;
|
||||
}
|
||||
// Note: compMeta is a component and therefore template is non null.
|
||||
compMeta.template !.externalStylesheets.forEach((stylesheetMeta) => {
|
||||
generatedFiles.push(this._codegenSourceModule(
|
||||
stylesheetMeta.moduleUrl !,
|
||||
this._createOutputContext(_stylesModuleUrl(
|
||||
stylesheetMeta.moduleUrl !, this._styleCompiler.needsStyleShim(compMeta),
|
||||
fileSuffix))));
|
||||
});
|
||||
});
|
||||
return generatedFiles;
|
||||
}
|
||||
|
||||
private _compileImplFile(
|
||||
srcFileUrl: string, ngModuleByPipeOrDirective: Map<StaticSymbol, CompileNgModuleMetadata>,
|
||||
directives: StaticSymbol[], pipes: StaticSymbol[], ngModules: StaticSymbol[],
|
||||
injectables: StaticSymbol[]): GeneratedFile[] {
|
||||
@ -89,7 +137,7 @@ export class AotCompiler {
|
||||
directives.forEach((dirType) => {
|
||||
const compMeta = this._metadataResolver.getDirectiveMetadata(<any>dirType);
|
||||
if (!compMeta.isComponent) {
|
||||
return Promise.resolve(null);
|
||||
return;
|
||||
}
|
||||
const ngModule = ngModuleByPipeOrDirective.get(dirType);
|
||||
if (!ngModule) {
|
||||
@ -97,13 +145,12 @@ export class AotCompiler {
|
||||
`Internal Error: cannot determine the module for component ${identifierName(compMeta.type)}!`);
|
||||
}
|
||||
|
||||
_assertComponent(compMeta);
|
||||
|
||||
// compile styles
|
||||
const componentStylesheet = this._styleCompiler.compileComponent(outputCtx, compMeta);
|
||||
// Note: compMeta is a component and therefore template is non null.
|
||||
compMeta.template !.externalStylesheets.forEach((stylesheetMeta) => {
|
||||
generatedFiles.push(this._codegenStyles(srcFileUrl, compMeta, stylesheetMeta, fileSuffix));
|
||||
generatedFiles.push(
|
||||
this._codegenStyles(stylesheetMeta.moduleUrl !, compMeta, stylesheetMeta, fileSuffix));
|
||||
});
|
||||
|
||||
// compile components
|
||||
@ -149,7 +196,6 @@ export class AotCompiler {
|
||||
}))
|
||||
];
|
||||
const forJitOutputCtx = this._createOutputContext(summaryForJitFileName(srcFileUrl, true));
|
||||
const forJitTargetFilePath = summaryForJitFileName(srcFileUrl, true);
|
||||
const {json, exportAs} = serializeSummaries(
|
||||
forJitOutputCtx, this._summaryResolver, this._symbolResolver, symbolSummaries, typeData);
|
||||
exportAs.forEach((entry) => {
|
||||
@ -305,13 +351,6 @@ function _stylesModuleUrl(stylesheetUrl: string, shim: boolean, suffix: string):
|
||||
return `${stylesheetUrl}${shim ? '.shim' : ''}.ngstyle${suffix}`;
|
||||
}
|
||||
|
||||
function _assertComponent(meta: CompileDirectiveMetadata) {
|
||||
if (!meta.isComponent) {
|
||||
throw new Error(
|
||||
`Could not compile '${identifierName(meta.type)}' because it is not a component.`);
|
||||
}
|
||||
}
|
||||
|
||||
export interface NgAnalyzedModules {
|
||||
ngModules: CompileNgModuleMetadata[];
|
||||
ngModuleByPipeOrDirective: Map<StaticSymbol, CompileNgModuleMetadata>;
|
||||
|
@ -87,6 +87,20 @@ export function deserializeSummaries(symbolCache: StaticSymbolCache, json: strin
|
||||
return deserializer.deserialize(json);
|
||||
}
|
||||
|
||||
export function createForJitStub(outputCtx: OutputContext, reference: StaticSymbol) {
|
||||
return createSummaryForJitFunction(outputCtx, reference, o.NULL_EXPR);
|
||||
}
|
||||
|
||||
function createSummaryForJitFunction(
|
||||
outputCtx: OutputContext, reference: StaticSymbol, value: o.Expression) {
|
||||
const fnName = summaryForJitName(reference.name);
|
||||
outputCtx.statements.push(
|
||||
o.fn([], [new o.ReturnStatement(value)], new o.ArrayType(o.DYNAMIC_TYPE)).toDeclStmt(fnName, [
|
||||
o.StmtModifier.Final, o.StmtModifier.Exported
|
||||
]));
|
||||
}
|
||||
|
||||
|
||||
class ToJsonSerializer extends ValueTransformer {
|
||||
// Note: This only contains symbols without members.
|
||||
symbols: StaticSymbol[] = [];
|
||||
@ -215,10 +229,9 @@ class ForJitSerializer {
|
||||
}
|
||||
if (!isLibrary) {
|
||||
const fnName = summaryForJitName(summary.type.reference.name);
|
||||
this.outputCtx.statements.push(
|
||||
o.fn([], [new o.ReturnStatement(this.serializeSummaryWithDeps(summary, metadata !))],
|
||||
new o.ArrayType(o.DYNAMIC_TYPE))
|
||||
.toDeclStmt(fnName, [o.StmtModifier.Final, o.StmtModifier.Exported]));
|
||||
createSummaryForJitFunction(
|
||||
this.outputCtx, summary.type.reference,
|
||||
this.serializeSummaryWithDeps(summary, metadata !));
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -50,21 +50,13 @@ export class NgModuleCompiler {
|
||||
[new o.FnParam(LOG_VAR.name !)], [new o.ReturnStatement(ngModuleDef)], o.INFERRED_TYPE);
|
||||
|
||||
const ngModuleFactoryVar = `${identifierName(ngModuleMeta.type)}NgFactory`;
|
||||
const ngModuleFactoryStmt =
|
||||
o.variable(ngModuleFactoryVar)
|
||||
.set(o.importExpr(Identifiers.createModuleFactory).callFn([
|
||||
ctx.importExpr(ngModuleMeta.type.reference),
|
||||
o.literalArr(bootstrapComponents.map(id => ctx.importExpr(id.reference))),
|
||||
ngModuleDefFactory
|
||||
]))
|
||||
.toDeclStmt(
|
||||
o.importType(
|
||||
Identifiers.NgModuleFactory,
|
||||
[o.expressionType(ctx.importExpr(ngModuleMeta.type.reference)) !],
|
||||
[o.TypeModifier.Const]),
|
||||
[o.StmtModifier.Final, o.StmtModifier.Exported]);
|
||||
this._createNgModuleFactory(
|
||||
ctx, ngModuleMeta.type.reference, o.importExpr(Identifiers.createModuleFactory).callFn([
|
||||
ctx.importExpr(ngModuleMeta.type.reference),
|
||||
o.literalArr(bootstrapComponents.map(id => ctx.importExpr(id.reference))),
|
||||
ngModuleDefFactory
|
||||
]));
|
||||
|
||||
ctx.statements.push(ngModuleFactoryStmt);
|
||||
if (ngModuleMeta.id) {
|
||||
const registerFactoryStmt =
|
||||
o.importExpr(Identifiers.RegisterModuleFactoryFn)
|
||||
@ -75,4 +67,22 @@ export class NgModuleCompiler {
|
||||
|
||||
return new NgModuleCompileResult(ngModuleFactoryVar);
|
||||
}
|
||||
|
||||
createStub(ctx: OutputContext, ngModuleReference: any) {
|
||||
this._createNgModuleFactory(ctx, ngModuleReference, o.NULL_EXPR);
|
||||
}
|
||||
|
||||
private _createNgModuleFactory(ctx: OutputContext, reference: any, value: o.Expression) {
|
||||
const ngModuleFactoryVar = `${identifierName({reference: reference})}NgFactory`;
|
||||
const ngModuleFactoryStmt =
|
||||
o.variable(ngModuleFactoryVar)
|
||||
.set(value)
|
||||
.toDeclStmt(
|
||||
o.importType(
|
||||
Identifiers.NgModuleFactory, [o.expressionType(ctx.importExpr(reference)) !],
|
||||
[o.TypeModifier.Const]),
|
||||
[o.StmtModifier.Final, o.StmtModifier.Exported]);
|
||||
|
||||
ctx.statements.push(ngModuleFactoryStmt);
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user