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:
parent
8f95b751e0
commit
ad7251c8bb
|
@ -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 = [{
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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);
|
||||||
|
|
Loading…
Reference in New Issue