refactor(compiler): introduce `EmitFlags.CodeGen` (#19275)

This flag controls whether the compiler emits generated files.
It is initially calculated via `skipTemplateCodegen` from the
compiler options.

Also:
- adds a small performance improvement to not generate the files
  at all if we don’t emit generated code.
- removes `EmitFlags.Summaries` as we never used it.


PR Close #19275
This commit is contained in:
Tobias Bosch 2017-09-19 11:41:47 -07:00 committed by Igor Minar
parent 8f95b751e0
commit ad7251c8bb
3 changed files with 45 additions and 43 deletions

View File

@ -101,6 +101,9 @@ export function readConfiguration(
if (!(options.skipMetadataEmit || options.flatModuleOutFile)) {
emitFlags |= api.EmitFlags.Metadata;
}
if (options.skipTemplateCodegen) {
emitFlags = emitFlags & ~api.EmitFlags.Codegen;
}
return {project: projectFile, rootNames, options, errors: parsed.errors, emitFlags};
} catch (e) {
const errors: Diagnostics = [{

View File

@ -174,10 +174,10 @@ export enum EmitFlags {
JS = 1 << 1,
Metadata = 1 << 2,
I18nBundle = 1 << 3,
Summary = 1 << 4,
Codegen = 1 << 4,
Default = DTS | JS,
All = DTS | JS | Metadata | I18nBundle | Summary
Default = DTS | JS | Codegen,
All = DTS | JS | Metadata | I18nBundle | Codegen,
}
export interface CustomTransformers {

View File

@ -35,6 +35,8 @@ const defaultEmitCallback: TsEmitCallback =
class AngularCompilerProgram implements Program {
private metadataCache: LowerMetadataCache;
private _emittedGenFiles: GeneratedFile[]|undefined;
// Lazily initialized fields
private _typeCheckHost: TypeCheckHost;
private _compiler: AotCompiler;
@ -42,8 +44,6 @@ class AngularCompilerProgram implements Program {
private _analyzedModules: NgAnalyzedModules|undefined;
private _structuralDiagnostics: Diagnostic[]|undefined;
private _programWithStubs: ts.Program|undefined;
private _generatedFiles: GeneratedFile[]|undefined;
private _generatedFileDiagnostics: Diagnostic[]|undefined;
private _semanticDiagnostics: {ts: ts.Diagnostic[], ng: Diagnostic[]}|undefined;
private _optionsDiagnostics: Diagnostic[] = [];
@ -101,11 +101,6 @@ class AngularCompilerProgram implements Program {
getNgSemanticDiagnostics(fileName?: string, cancellationToken?: ts.CancellationToken):
Diagnostic[] {
const compilerDiagnostics = this.generatedFileDiagnostics;
// If we have diagnostics during the parser phase the type check phase is not meaningful so skip
// it.
if (compilerDiagnostics && compilerDiagnostics.length) return compilerDiagnostics;
return this.semanticDiagnostics.ng;
}
@ -140,28 +135,37 @@ class AngularCompilerProgram implements Program {
i18nExtract(format, file, this.host, this.options, bundle);
}
const outSrcMapping: Array<{sourceFile: ts.SourceFile, outFileName: string}> = [];
if ((emitFlags & (EmitFlags.JS | EmitFlags.DTS | EmitFlags.Metadata | EmitFlags.Summary)) ===
if ((emitFlags & (EmitFlags.JS | EmitFlags.DTS | EmitFlags.Metadata | EmitFlags.Codegen)) ===
0) {
return {emitSkipped: true, diagnostics: [], emittedFiles: []};
}
const {genFiles, genDiags} = this.generateFilesForEmit(emitFlags);
if (genDiags.length) {
return {
diagnostics: genDiags,
emitSkipped: true,
emittedFiles: [],
};
}
const emitResult = emitCallback({
program: this.tsProgram,
host: this.host,
options: this.options,
targetSourceFile: undefined,
writeFile: createWriteFileCallback(this.options, this.host, outSrcMapping), cancellationToken,
writeFile: createWriteFileCallback(emitFlags, this.host, outSrcMapping),
emitOnlyDtsFiles: (emitFlags & (EmitFlags.DTS | EmitFlags.JS)) == EmitFlags.DTS,
customTransformers: this.calculateTransforms(customTransformers)
customTransformers: this.calculateTransforms(genFiles, customTransformers)
});
if (!outSrcMapping.length) {
// if no files were emitted by TypeScript, also don't emit .json files
return emitResult;
}
const srcToOutPath = this.createSrcToOutPathMapper(outSrcMapping);
if (!this.options.skipTemplateCodegen) {
this.generatedFiles.forEach(gf => {
if (emitFlags & EmitFlags.Codegen) {
genFiles.forEach(gf => {
if (gf.source) {
this.host.writeFile(srcToOutPath(gf.genFileUrl), gf.source, false);
}
@ -216,31 +220,18 @@ class AngularCompilerProgram implements Program {
return this._typeCheckHost !;
}
private get generatedFiles(): GeneratedFile[] {
if (!this._generatedFiles) {
this.generateFiles();
}
return this._generatedFiles !;
}
private get generatedFileDiagnostics(): Diagnostic[]|undefined {
if (!this._generatedFileDiagnostics) {
this.generateFiles();
}
return this._generatedFileDiagnostics !;
}
private get semanticDiagnostics(): {ts: ts.Diagnostic[], ng: Diagnostic[]} {
return this._semanticDiagnostics ||
(this._semanticDiagnostics = this.generateSemanticDiagnostics());
}
private calculateTransforms(customTransformers?: CustomTransformers): ts.CustomTransformers {
private calculateTransforms(genFiles: GeneratedFile[], customTransformers?: CustomTransformers):
ts.CustomTransformers {
const beforeTs: ts.TransformerFactory<ts.SourceFile>[] = [];
if (!this.options.disableExpressionLowering) {
beforeTs.push(getExpressionLoweringTransformFactory(this.metadataCache));
}
beforeTs.push(getAngularEmitterTransformFactory(this.generatedFiles));
beforeTs.push(getAngularEmitterTransformFactory(genFiles));
if (customTransformers && customTransformers.beforeTs) {
beforeTs.push(...customTransformers.beforeTs);
}
@ -358,20 +349,30 @@ class AngularCompilerProgram implements Program {
throw e;
}
private generateFiles() {
const diags: Diagnostic[] = this._generatedFileDiagnostics = [];
// Note: this returns a ts.Diagnostic so that we
// can return errors in a ts.EmitResult
private generateFilesForEmit(emitFlags: EmitFlags):
{genFiles: GeneratedFile[], genDiags: ts.Diagnostic[]} {
try {
this._generatedFiles = this.compiler.emitAllImpls(this.analyzedModules);
if (!(emitFlags & EmitFlags.Codegen)) {
return {genFiles: [], genDiags: []};
}
const genFiles = this._emittedGenFiles = this.compiler.emitAllImpls(this.analyzedModules);
return {genFiles, genDiags: []};
} catch (e) {
this._generatedFiles = [];
// TODO(tbosch): check whether we can actually have syntax errors here,
// as we already parsed the metadata and templates before to create the type check block.
if (isSyntaxError(e)) {
diags.push({
const genDiags: ts.Diagnostic[] = [{
file: undefined,
start: undefined,
length: undefined,
messageText: e.message,
category: ts.DiagnosticCategory.Error,
source: SOURCE,
code: DEFAULT_ERROR_CODE
});
return [];
}];
return {genFiles: [], genDiags};
}
throw e;
}
@ -426,7 +427,7 @@ function getAotCompilerOptions(options: CompilerOptions): AotCompilerOptions {
}
function createWriteFileCallback(
options: {skipTemplateCodegen?: boolean}, host: ts.CompilerHost,
emitFlags: EmitFlags, host: ts.CompilerHost,
outSrcMapping: Array<{sourceFile: ts.SourceFile, outFileName: string}>) {
return (fileName: string, data: string, writeByteOrderMark: boolean,
onError?: (message: string) => void, sourceFiles?: ts.SourceFile[]) => {
@ -435,9 +436,7 @@ function createWriteFileCallback(
if (sourceFile) {
outSrcMapping.push({outFileName: fileName, sourceFile});
}
if (isGenerated && options.skipTemplateCodegen) {
// Always generate the files if requested to ensure we capture any diagnostic errors but only
// don't emit them.
if (isGenerated && !(emitFlags & EmitFlags.Codegen)) {
return;
}
host.writeFile(fileName, data, writeByteOrderMark, onError, sourceFiles);