angular/packages/compiler-cli/src/main.ts
Paul Gschwendtner 552ea77854 refactor(compiler-cli): drop tsickle code paths (#50602)
`tsickle` is not used in any code paths in 3P and we can remove
this complexity. The `tsickle` npm package has not been released
in a while and we are risking breakages with e.g. future TypeScript
versions.

Note that the `ng_module` rule was updated to not emit through
tsickle at all. The tsickle in 1P is done directly by `tsc_wrapped`
and our code path in `compiler-cli` is not needed at all.

PR Close #50602
2023-08-17 10:23:49 -07:00

191 lines
6.7 KiB
TypeScript

/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import ts from 'typescript';
import yargs from 'yargs';
import {exitCodeFromResult, formatDiagnostics, ParsedConfiguration, performCompilation, readConfiguration} from './perform_compile';
import {createPerformWatchHost, performWatchCompilation} from './perform_watch';
import * as api from './transformers/api';
export function main(
args: string[], consoleError: (s: string) => void = console.error,
config?: NgcParsedConfiguration, customTransformers?: api.CustomTransformers, programReuse?: {
program: api.Program|undefined,
},
modifiedResourceFiles?: Set<string>|null): number {
let {project, rootNames, options, errors: configErrors, watch, emitFlags} =
config || readNgcCommandLineAndConfiguration(args);
if (configErrors.length) {
return reportErrorsAndExit(configErrors, /*options*/ undefined, consoleError);
}
if (watch) {
const result = watchMode(project, options, consoleError);
return reportErrorsAndExit(result.firstCompileResult, options, consoleError);
}
let oldProgram: api.Program|undefined;
if (programReuse !== undefined) {
oldProgram = programReuse.program;
}
const {diagnostics: compileDiags, program} = performCompilation(
{rootNames, options, emitFlags, oldProgram, customTransformers, modifiedResourceFiles});
if (programReuse !== undefined) {
programReuse.program = program;
}
return reportErrorsAndExit(compileDiags, options, consoleError);
}
export function mainDiagnosticsForTest(
args: string[], config?: NgcParsedConfiguration,
programReuse?: {program: api.Program|undefined}, modifiedResourceFiles?: Set<string>|null): {
exitCode: number,
diagnostics: ReadonlyArray<ts.Diagnostic>,
} {
let {rootNames, options, errors: configErrors, emitFlags} =
config || readNgcCommandLineAndConfiguration(args);
if (configErrors.length) {
return {
exitCode: exitCodeFromResult(configErrors),
diagnostics: configErrors,
};
}
let oldProgram: api.Program|undefined;
if (programReuse !== undefined) {
oldProgram = programReuse.program;
}
const {diagnostics: compileDiags, program} = performCompilation({
rootNames,
options,
emitFlags,
oldProgram,
modifiedResourceFiles,
});
if (programReuse !== undefined) {
programReuse.program = program;
}
return {
exitCode: exitCodeFromResult(compileDiags),
diagnostics: compileDiags,
};
}
export interface NgcParsedConfiguration extends ParsedConfiguration {
watch?: boolean;
}
export function readNgcCommandLineAndConfiguration(args: string[]): NgcParsedConfiguration {
const options: api.CompilerOptions = {};
const parsedArgs =
yargs(args)
.parserConfiguration({'strip-aliased': true})
.option('i18nFile', {type: 'string'})
.option('i18nFormat', {type: 'string'})
.option('locale', {type: 'string'})
.option('missingTranslation', {type: 'string', choices: ['error', 'warning', 'ignore']})
.option('outFile', {type: 'string'})
.option('watch', {type: 'boolean', alias: ['w']})
.parseSync();
if (parsedArgs.i18nFile) options.i18nInFile = parsedArgs.i18nFile;
if (parsedArgs.i18nFormat) options.i18nInFormat = parsedArgs.i18nFormat;
if (parsedArgs.locale) options.i18nInLocale = parsedArgs.locale;
if (parsedArgs.missingTranslation)
options.i18nInMissingTranslations =
parsedArgs.missingTranslation as api.CompilerOptions['i18nInMissingTranslations'];
const config = readCommandLineAndConfiguration(
args, options, ['i18nFile', 'i18nFormat', 'locale', 'missingTranslation', 'watch']);
return {...config, watch: parsedArgs.watch};
}
export function readCommandLineAndConfiguration(
args: string[], existingOptions: api.CompilerOptions = {},
ngCmdLineOptions: string[] = []): ParsedConfiguration {
let cmdConfig = ts.parseCommandLine(args);
const project = cmdConfig.options.project || '.';
const cmdErrors = cmdConfig.errors.filter(e => {
if (typeof e.messageText === 'string') {
const msg = e.messageText;
return !ngCmdLineOptions.some(o => msg.indexOf(o) >= 0);
}
return true;
});
if (cmdErrors.length) {
return {
project,
rootNames: [],
options: cmdConfig.options,
errors: cmdErrors,
emitFlags: api.EmitFlags.Default
};
}
const config = readConfiguration(project, cmdConfig.options);
const options = {...config.options, ...existingOptions};
if (options.locale) {
options.i18nInLocale = options.locale;
}
return {
project,
rootNames: config.rootNames,
options,
errors: config.errors,
emitFlags: config.emitFlags
};
}
function getFormatDiagnosticsHost(options?: api.CompilerOptions): ts.FormatDiagnosticsHost {
const basePath = options ? options.basePath : undefined;
return {
getCurrentDirectory: () => basePath || ts.sys.getCurrentDirectory(),
// We need to normalize the path separators here because by default, TypeScript
// compiler hosts use posix canonical paths. In order to print consistent diagnostics,
// we also normalize the paths.
getCanonicalFileName: fileName => fileName.replace(/\\/g, '/'),
getNewLine: () => {
// Manually determine the proper new line string based on the passed compiler
// options. There is no public TypeScript function that returns the corresponding
// new line string. see: https://github.com/Microsoft/TypeScript/issues/29581
if (options && options.newLine !== undefined) {
return options.newLine === ts.NewLineKind.LineFeed ? '\n' : '\r\n';
}
return ts.sys.newLine;
},
};
}
function reportErrorsAndExit(
allDiagnostics: ReadonlyArray<ts.Diagnostic>, options?: api.CompilerOptions,
consoleError: (s: string) => void = console.error): number {
const errorsAndWarnings =
allDiagnostics.filter(d => d.category !== ts.DiagnosticCategory.Message);
printDiagnostics(errorsAndWarnings, options, consoleError);
return exitCodeFromResult(allDiagnostics);
}
export function watchMode(
project: string, options: api.CompilerOptions, consoleError: (s: string) => void) {
return performWatchCompilation(createPerformWatchHost(project, diagnostics => {
printDiagnostics(diagnostics, options, consoleError);
}, options, undefined));
}
function printDiagnostics(
diagnostics: ReadonlyArray<ts.Diagnostic>, options: api.CompilerOptions|undefined,
consoleError: (s: string) => void): void {
if (diagnostics.length === 0) {
return;
}
const formatHost = getFormatDiagnosticsHost(options);
consoleError(formatDiagnostics(diagnostics, formatHost));
}