refactor(compiler): make the new ngc API independent of tsickle (#18739)
This changes `performCompile` / `program.emit` to not tsickle automatically, but allows to pass in an `emitCallback` in which tsickle can be executed.
This commit is contained in:
parent
56a5b02d04
commit
ffb1553282
|
@ -14,6 +14,7 @@ import * as ts from 'typescript';
|
||||||
import * as tsc from '@angular/tsc-wrapped';
|
import * as tsc from '@angular/tsc-wrapped';
|
||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
|
import * as tsickle from 'tsickle';
|
||||||
import * as api from './transformers/api';
|
import * as api from './transformers/api';
|
||||||
import * as ngc from './transformers/entry_points';
|
import * as ngc from './transformers/entry_points';
|
||||||
import {performCompilation, readConfiguration, formatDiagnostics, Diagnostics, ParsedConfiguration} from './perform-compile';
|
import {performCompilation, readConfiguration, formatDiagnostics, Diagnostics, ParsedConfiguration} from './perform-compile';
|
||||||
|
@ -31,7 +32,8 @@ export function main(
|
||||||
if (options.disableTransformerPipeline) {
|
if (options.disableTransformerPipeline) {
|
||||||
return disabledTransformerPipelineNgcMain(parsedArgs, consoleError);
|
return disabledTransformerPipelineNgcMain(parsedArgs, consoleError);
|
||||||
}
|
}
|
||||||
const {diagnostics: compileDiags} = performCompilation(rootNames, options);
|
const {diagnostics: compileDiags} =
|
||||||
|
performCompilation({rootNames, options, emitCallback: createEmitCallback(options)});
|
||||||
return Promise.resolve(reportErrorsAndExit(options, compileDiags, consoleError));
|
return Promise.resolve(reportErrorsAndExit(options, compileDiags, consoleError));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,10 +44,45 @@ export function mainSync(
|
||||||
if (configErrors.length) {
|
if (configErrors.length) {
|
||||||
return reportErrorsAndExit(options, configErrors, consoleError);
|
return reportErrorsAndExit(options, configErrors, consoleError);
|
||||||
}
|
}
|
||||||
const {diagnostics: compileDiags} = performCompilation(rootNames, options);
|
const {diagnostics: compileDiags} =
|
||||||
|
performCompilation({rootNames, options, emitCallback: createEmitCallback(options)});
|
||||||
return reportErrorsAndExit(options, compileDiags, consoleError);
|
return reportErrorsAndExit(options, compileDiags, consoleError);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function createEmitCallback(options: api.CompilerOptions): api.TsEmitCallback {
|
||||||
|
const tsickleOptions: tsickle.TransformerOptions = {
|
||||||
|
googmodule: false,
|
||||||
|
untyped: true,
|
||||||
|
convertIndexImportShorthand: true,
|
||||||
|
transformDecorators: options.annotationsAs !== 'decorators',
|
||||||
|
transformTypesToClosure: options.annotateForClosureCompiler,
|
||||||
|
};
|
||||||
|
|
||||||
|
const tsickleHost: tsickle.TransformerHost = {
|
||||||
|
shouldSkipTsickleProcessing: (fileName) => /\.d\.ts$/.test(fileName),
|
||||||
|
pathToModuleName: (context, importPath) => '',
|
||||||
|
shouldIgnoreWarningsForPath: (filePath) => false,
|
||||||
|
fileNameToModuleId: (fileName) => fileName,
|
||||||
|
};
|
||||||
|
|
||||||
|
return ({
|
||||||
|
program,
|
||||||
|
targetSourceFile,
|
||||||
|
writeFile,
|
||||||
|
cancellationToken,
|
||||||
|
emitOnlyDtsFiles,
|
||||||
|
customTransformers = {},
|
||||||
|
host,
|
||||||
|
options
|
||||||
|
}) =>
|
||||||
|
tsickle.emitWithTsickle(
|
||||||
|
program, tsickleHost, tsickleOptions, host, options, targetSourceFile, writeFile,
|
||||||
|
cancellationToken, emitOnlyDtsFiles, {
|
||||||
|
beforeTs: customTransformers.before,
|
||||||
|
afterTs: customTransformers.after,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function readCommandLineAndConfiguration(args: any): ParsedConfiguration {
|
function readCommandLineAndConfiguration(args: any): ParsedConfiguration {
|
||||||
const project = args.p || args.project || '.';
|
const project = args.p || args.project || '.';
|
||||||
const allDiagnostics: Diagnostics = [];
|
const allDiagnostics: Diagnostics = [];
|
||||||
|
|
|
@ -105,10 +105,16 @@ export function readConfiguration(
|
||||||
}
|
}
|
||||||
|
|
||||||
export function performCompilation(
|
export function performCompilation(
|
||||||
rootNames: string[], options: api.CompilerOptions, host?: api.CompilerHost,
|
{rootNames, options, host, oldProgram, emitCallback, customTransformers}: {
|
||||||
oldProgram?: api.Program): {
|
rootNames: string[],
|
||||||
|
options: api.CompilerOptions,
|
||||||
|
host?: api.CompilerHost,
|
||||||
|
oldProgram?: api.Program,
|
||||||
|
emitCallback?: api.TsEmitCallback,
|
||||||
|
customTransformers?: api.CustomTransformers
|
||||||
|
}): {
|
||||||
program?: api.Program,
|
program?: api.Program,
|
||||||
emitResult?: api.EmitResult,
|
emitResult?: ts.EmitResult,
|
||||||
diagnostics: Diagnostics,
|
diagnostics: Diagnostics,
|
||||||
} {
|
} {
|
||||||
const [major, minor] = ts.version.split('.');
|
const [major, minor] = ts.version.split('.');
|
||||||
|
@ -128,7 +134,7 @@ export function performCompilation(
|
||||||
}
|
}
|
||||||
|
|
||||||
let program: api.Program|undefined;
|
let program: api.Program|undefined;
|
||||||
let emitResult: api.EmitResult|undefined;
|
let emitResult: ts.EmitResult|undefined;
|
||||||
try {
|
try {
|
||||||
if (!host) {
|
if (!host) {
|
||||||
host = ng.createCompilerHost({options});
|
host = ng.createCompilerHost({options});
|
||||||
|
@ -155,7 +161,9 @@ export function performCompilation(
|
||||||
shouldEmit = shouldEmit && checkDiagnostics(program !.getNgSemanticDiagnostics());
|
shouldEmit = shouldEmit && checkDiagnostics(program !.getNgSemanticDiagnostics());
|
||||||
|
|
||||||
if (shouldEmit) {
|
if (shouldEmit) {
|
||||||
const emitResult = program !.emit({
|
emitResult = program !.emit({
|
||||||
|
emitCallback,
|
||||||
|
customTransformers,
|
||||||
emitFlags: api.EmitFlags.Default |
|
emitFlags: api.EmitFlags.Default |
|
||||||
((options.skipMetadataEmit || options.flatModuleOutFile) ? 0 : api.EmitFlags.Metadata)
|
((options.skipMetadataEmit || options.flatModuleOutFile) ? 0 : api.EmitFlags.Metadata)
|
||||||
});
|
});
|
||||||
|
|
|
@ -167,17 +167,24 @@ export enum EmitFlags {
|
||||||
All = DTS | JS | Metadata | I18nBundle | Summary
|
All = DTS | JS | Metadata | I18nBundle | Summary
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(chuckj): Support CustomTransformers once we require TypeScript 2.3+
|
export interface CustomTransformers {
|
||||||
// export interface CustomTransformers {
|
beforeTs?: ts.TransformerFactory<ts.SourceFile>[];
|
||||||
// beforeTs?: ts.TransformerFactory<ts.SourceFile>[];
|
afterTs?: ts.TransformerFactory<ts.SourceFile>[];
|
||||||
// afterTs?: ts.TransformerFactory<ts.SourceFile>[];
|
|
||||||
// }
|
|
||||||
|
|
||||||
export interface EmitResult extends ts.EmitResult {
|
|
||||||
modulesManifest: {modules: string[]; fileNames: string[];};
|
|
||||||
externs: {[fileName: string]: string;};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface TsEmitArguments {
|
||||||
|
program: ts.Program;
|
||||||
|
host: CompilerHost;
|
||||||
|
options: CompilerOptions;
|
||||||
|
targetSourceFile?: ts.SourceFile;
|
||||||
|
writeFile?: ts.WriteFileCallback;
|
||||||
|
cancellationToken?: ts.CancellationToken;
|
||||||
|
emitOnlyDtsFiles?: boolean;
|
||||||
|
customTransformers?: ts.CustomTransformers;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TsEmitCallback { (args: TsEmitArguments): ts.EmitResult; }
|
||||||
|
|
||||||
export interface Program {
|
export interface Program {
|
||||||
/**
|
/**
|
||||||
* Retrieve the TypeScript program used to produce semantic diagnostics and emit the sources.
|
* Retrieve the TypeScript program used to produce semantic diagnostics and emit the sources.
|
||||||
|
@ -254,10 +261,10 @@ export interface Program {
|
||||||
*
|
*
|
||||||
* Angular structural information is required to emit files.
|
* Angular structural information is required to emit files.
|
||||||
*/
|
*/
|
||||||
emit({// transformers,
|
emit({emitFlags, cancellationToken, customTransformers, emitCallback}: {
|
||||||
emitFlags, cancellationToken}: {
|
emitFlags?: EmitFlags,
|
||||||
emitFlags: EmitFlags,
|
|
||||||
// transformers?: CustomTransformers, // See TODO above
|
|
||||||
cancellationToken?: ts.CancellationToken,
|
cancellationToken?: ts.CancellationToken,
|
||||||
}): EmitResult;
|
customTransformers?: CustomTransformers,
|
||||||
|
emitCallback?: TsEmitCallback
|
||||||
|
}): ts.EmitResult;
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,13 +10,12 @@ import {AotCompiler, AotCompilerHost, AotCompilerOptions, GeneratedFile, NgAnaly
|
||||||
import {createBundleIndexHost} from '@angular/tsc-wrapped';
|
import {createBundleIndexHost} from '@angular/tsc-wrapped';
|
||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import * as tsickle from 'tsickle';
|
|
||||||
import * as ts from 'typescript';
|
import * as ts from 'typescript';
|
||||||
|
|
||||||
import {BaseAotCompilerHost} from '../compiler_host';
|
import {BaseAotCompilerHost} from '../compiler_host';
|
||||||
import {TypeChecker} from '../diagnostics/check_types';
|
import {TypeChecker} from '../diagnostics/check_types';
|
||||||
|
|
||||||
import {CompilerHost, CompilerOptions, Diagnostic, EmitFlags, EmitResult, Program} from './api';
|
import {CompilerHost, CompilerOptions, CustomTransformers, Diagnostic, EmitFlags, Program, TsEmitArguments, TsEmitCallback} from './api';
|
||||||
import {LowerMetadataCache, getExpressionLoweringTransformFactory} from './lower_expressions';
|
import {LowerMetadataCache, getExpressionLoweringTransformFactory} from './lower_expressions';
|
||||||
import {getAngularEmitterTransformFactory} from './node_emitter_transform';
|
import {getAngularEmitterTransformFactory} from './node_emitter_transform';
|
||||||
|
|
||||||
|
@ -30,6 +29,13 @@ const emptyModules: NgAnalyzedModules = {
|
||||||
files: []
|
files: []
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const defaultEmitCallback: TsEmitCallback =
|
||||||
|
({program, targetSourceFile, writeFile, cancellationToken, emitOnlyDtsFiles,
|
||||||
|
customTransformers}) =>
|
||||||
|
program.emit(
|
||||||
|
targetSourceFile, writeFile, cancellationToken, emitOnlyDtsFiles, customTransformers);
|
||||||
|
|
||||||
|
|
||||||
class AngularCompilerProgram implements Program {
|
class AngularCompilerProgram implements Program {
|
||||||
private tsProgram: ts.Program;
|
private tsProgram: ts.Program;
|
||||||
private aotCompilerHost: AotCompilerHost;
|
private aotCompilerHost: AotCompilerHost;
|
||||||
|
@ -128,35 +134,35 @@ class AngularCompilerProgram implements Program {
|
||||||
|
|
||||||
getLazyRoutes(cancellationToken?: ts.CancellationToken): {[route: string]: string} { return {}; }
|
getLazyRoutes(cancellationToken?: ts.CancellationToken): {[route: string]: string} { return {}; }
|
||||||
|
|
||||||
emit({emitFlags = EmitFlags.Default, cancellationToken}:
|
emit({emitFlags = EmitFlags.Default, cancellationToken, customTransformers,
|
||||||
{emitFlags?: EmitFlags, cancellationToken?: ts.CancellationToken}): EmitResult {
|
emitCallback = defaultEmitCallback}: {
|
||||||
|
emitFlags?: EmitFlags,
|
||||||
|
cancellationToken?: ts.CancellationToken,
|
||||||
|
customTransformers?: CustomTransformers,
|
||||||
|
emitCallback?: TsEmitCallback
|
||||||
|
}): ts.EmitResult {
|
||||||
const emitMap = new Map<string, string>();
|
const emitMap = new Map<string, string>();
|
||||||
|
|
||||||
const tsickleCompilerHostOptions: tsickle.TransformerOptions = {
|
|
||||||
googmodule: false,
|
|
||||||
untyped: true,
|
|
||||||
convertIndexImportShorthand: true,
|
|
||||||
transformDecorators: this.options.annotationsAs !== 'decorators',
|
|
||||||
transformTypesToClosure: this.options.annotateForClosureCompiler,
|
|
||||||
};
|
|
||||||
|
|
||||||
const tsickleHost: tsickle.TransformerHost = {
|
|
||||||
shouldSkipTsickleProcessing: (fileName) => /\.d\.ts$/.test(fileName),
|
|
||||||
pathToModuleName: (context, importPath) => '',
|
|
||||||
shouldIgnoreWarningsForPath: (filePath) => false,
|
|
||||||
fileNameToModuleId: (fileName) => fileName,
|
|
||||||
};
|
|
||||||
|
|
||||||
const expectedOut = this.options.expectedOut ?
|
const expectedOut = this.options.expectedOut ?
|
||||||
this.options.expectedOut.map(f => path.resolve(process.cwd(), f)) :
|
this.options.expectedOut.map(f => path.resolve(process.cwd(), f)) :
|
||||||
undefined;
|
undefined;
|
||||||
|
|
||||||
const result = tsickle.emitWithTsickle(
|
// Ensure that expected output files exist.
|
||||||
this.programWithStubs, tsickleHost, tsickleCompilerHostOptions, this.host, this.options,
|
for (const out of expectedOut || []) {
|
||||||
/* targetSourceFile */ undefined,
|
this.host.writeFile(out, '', false);
|
||||||
|
}
|
||||||
|
|
||||||
|
const emitResult = emitCallback({
|
||||||
|
program: this.programWithStubs,
|
||||||
|
host: this.host,
|
||||||
|
options: this.options,
|
||||||
|
targetSourceFile: undefined,
|
||||||
|
writeFile:
|
||||||
createWriteFileCallback(emitFlags, this.host, this.metadataCache, emitMap, expectedOut),
|
createWriteFileCallback(emitFlags, this.host, this.metadataCache, emitMap, expectedOut),
|
||||||
cancellationToken, (emitFlags & (EmitFlags.DTS | EmitFlags.JS)) == EmitFlags.DTS,
|
cancellationToken,
|
||||||
this.calculateTransforms());
|
emitOnlyDtsFiles: (emitFlags & (EmitFlags.DTS | EmitFlags.JS)) == EmitFlags.DTS,
|
||||||
|
customTransformers: this.calculateTransforms(customTransformers)
|
||||||
|
});
|
||||||
|
|
||||||
this.generatedFiles.forEach(file => {
|
this.generatedFiles.forEach(file => {
|
||||||
// In order not to replicate the TS calculation of the out folder for files
|
// In order not to replicate the TS calculation of the out folder for files
|
||||||
|
@ -174,12 +180,7 @@ class AngularCompilerProgram implements Program {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Ensure that expected output files exist.
|
return emitResult;
|
||||||
for (const out of expectedOut || []) {
|
|
||||||
fs.appendFileSync(out, '', 'utf8');
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Private members
|
// Private members
|
||||||
|
@ -228,7 +229,7 @@ class AngularCompilerProgram implements Program {
|
||||||
return this.generatedFiles && this._generatedFileDiagnostics !;
|
return this.generatedFiles && this._generatedFileDiagnostics !;
|
||||||
}
|
}
|
||||||
|
|
||||||
private calculateTransforms(): tsickle.EmitTransformers {
|
private calculateTransforms(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));
|
||||||
|
@ -236,7 +237,11 @@ class AngularCompilerProgram implements Program {
|
||||||
if (!this.options.skipTemplateCodegen) {
|
if (!this.options.skipTemplateCodegen) {
|
||||||
beforeTs.push(getAngularEmitterTransformFactory(this.generatedFiles));
|
beforeTs.push(getAngularEmitterTransformFactory(this.generatedFiles));
|
||||||
}
|
}
|
||||||
return {beforeTs};
|
if (customTransformers && customTransformers.beforeTs) {
|
||||||
|
beforeTs.push(...customTransformers.beforeTs);
|
||||||
|
}
|
||||||
|
const afterTs = customTransformers ? customTransformers.afterTs : undefined;
|
||||||
|
return {before: beforeTs, after: afterTs};
|
||||||
}
|
}
|
||||||
|
|
||||||
private catchAnalysisError(e: any): NgAnalyzedModules {
|
private catchAnalysisError(e: any): NgAnalyzedModules {
|
||||||
|
@ -370,7 +375,8 @@ function getAotCompilerOptions(options: CompilerOptions): AotCompilerOptions {
|
||||||
}
|
}
|
||||||
|
|
||||||
function writeMetadata(
|
function writeMetadata(
|
||||||
emitFilePath: string, sourceFile: ts.SourceFile, metadataCache: LowerMetadataCache) {
|
host: ts.CompilerHost, emitFilePath: string, sourceFile: ts.SourceFile,
|
||||||
|
metadataCache: LowerMetadataCache) {
|
||||||
if (/\.js$/.test(emitFilePath)) {
|
if (/\.js$/.test(emitFilePath)) {
|
||||||
const path = emitFilePath.replace(/\.js$/, '.metadata.json');
|
const path = emitFilePath.replace(/\.js$/, '.metadata.json');
|
||||||
|
|
||||||
|
@ -386,7 +392,7 @@ function writeMetadata(
|
||||||
const metadata = metadataCache.getMetadata(collectableFile);
|
const metadata = metadataCache.getMetadata(collectableFile);
|
||||||
if (metadata) {
|
if (metadata) {
|
||||||
const metadataText = JSON.stringify([metadata]);
|
const metadataText = JSON.stringify([metadata]);
|
||||||
fs.writeFileSync(path, metadataText, {encoding: 'utf-8'});
|
host.writeFile(path, metadataText, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -412,7 +418,7 @@ function createWriteFileCallback(
|
||||||
host.writeFile(fileName, data, writeByteOrderMark, onError, sourceFiles);
|
host.writeFile(fileName, data, writeByteOrderMark, onError, sourceFiles);
|
||||||
|
|
||||||
if (srcFile && !generatedFile && (emitFlags & EmitFlags.Metadata) != 0) {
|
if (srcFile && !generatedFile && (emitFlags & EmitFlags.Metadata) != 0) {
|
||||||
writeMetadata(fileName, srcFile, metadataCache);
|
writeMetadata(host, fileName, srcFile, metadataCache);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -23,7 +23,7 @@ function main(args: string[]) {
|
||||||
const {basePath} = calcProjectFileAndBasePath(project);
|
const {basePath} = calcProjectFileAndBasePath(project);
|
||||||
const ngOptions = createNgCompilerOptions(basePath, config, tsOptions);
|
const ngOptions = createNgCompilerOptions(basePath, config, tsOptions);
|
||||||
|
|
||||||
const {diagnostics} = performCompilation(files, ngOptions);
|
const {diagnostics} = performCompilation({rootNames: files, options: ngOptions});
|
||||||
if (diagnostics.length) {
|
if (diagnostics.length) {
|
||||||
console.error(formatDiagnostics(ngOptions, diagnostics));
|
console.error(formatDiagnostics(ngOptions, diagnostics));
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue