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)) { if (!(options.skipMetadataEmit || options.flatModuleOutFile)) {
emitFlags |= api.EmitFlags.Metadata; emitFlags |= api.EmitFlags.Metadata;
} }
if (options.skipTemplateCodegen) {
emitFlags = emitFlags & ~api.EmitFlags.Codegen;
}
return {project: projectFile, rootNames, options, errors: parsed.errors, emitFlags}; return {project: projectFile, rootNames, options, errors: parsed.errors, emitFlags};
} catch (e) { } catch (e) {
const errors: Diagnostics = [{ const errors: Diagnostics = [{

View File

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

View File

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