From ad7251c8bbdffffd31711711f7872c4901e4d668 Mon Sep 17 00:00:00 2001 From: Tobias Bosch Date: Tue, 19 Sep 2017 11:41:47 -0700 Subject: [PATCH] refactor(compiler): introduce `EmitFlags.CodeGen` (#19275) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- packages/compiler-cli/src/perform_compile.ts | 3 + packages/compiler-cli/src/transformers/api.ts | 6 +- .../compiler-cli/src/transformers/program.ts | 79 +++++++++---------- 3 files changed, 45 insertions(+), 43 deletions(-) diff --git a/packages/compiler-cli/src/perform_compile.ts b/packages/compiler-cli/src/perform_compile.ts index e8ef4d13df..2f53d37949 100644 --- a/packages/compiler-cli/src/perform_compile.ts +++ b/packages/compiler-cli/src/perform_compile.ts @@ -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 = [{ diff --git a/packages/compiler-cli/src/transformers/api.ts b/packages/compiler-cli/src/transformers/api.ts index cf7053207d..eed1d0f9af 100644 --- a/packages/compiler-cli/src/transformers/api.ts +++ b/packages/compiler-cli/src/transformers/api.ts @@ -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 { diff --git a/packages/compiler-cli/src/transformers/program.ts b/packages/compiler-cli/src/transformers/program.ts index 66ce9922a2..c256645dcb 100644 --- a/packages/compiler-cli/src/transformers/program.ts +++ b/packages/compiler-cli/src/transformers/program.ts @@ -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[] = []; 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);