2016-05-03 20:31:40 -04:00
|
|
|
|
#!/usr/bin/env node
|
2016-06-23 12:47:54 -04:00
|
|
|
|
/**
|
|
|
|
|
* @license
|
|
|
|
|
* Copyright Google Inc. 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
|
|
|
|
|
*/
|
|
|
|
|
|
2017-03-27 12:44:35 -04:00
|
|
|
|
// Must be imported first, because Angular decorators throw on load.
|
2016-04-29 13:35:36 -04:00
|
|
|
|
import 'reflect-metadata';
|
|
|
|
|
|
|
|
|
|
import * as ts from 'typescript';
|
2017-08-16 18:35:19 -04:00
|
|
|
|
import * as tsickle from 'tsickle';
|
2018-08-23 17:33:38 -04:00
|
|
|
|
|
|
|
|
|
import {replaceTsWithNgInErrors} from './ngtsc/diagnostics';
|
2017-08-09 16:45:45 -04:00
|
|
|
|
import * as api from './transformers/api';
|
2017-09-15 21:02:22 -04:00
|
|
|
|
import {GENERATED_FILES} from './transformers/util';
|
2017-08-02 14:20:07 -04:00
|
|
|
|
|
2019-06-06 15:22:32 -04:00
|
|
|
|
import {exitCodeFromResult, performCompilation, readConfiguration, formatDiagnostics, Diagnostics, ParsedConfiguration, filterErrorsAndWarnings} from './perform_compile';
|
2017-08-18 17:03:59 -04:00
|
|
|
|
import {performWatchCompilation, createPerformWatchHost} from './perform_watch';
|
2019-06-06 15:22:32 -04:00
|
|
|
|
import {NodeJSFileSystem, setFileSystem} from './ngtsc/file_system';
|
2016-05-01 14:22:39 -04:00
|
|
|
|
|
2017-08-09 16:45:45 -04:00
|
|
|
|
export function main(
|
2017-09-12 18:53:17 -04:00
|
|
|
|
args: string[], consoleError: (s: string) => void = console.error,
|
2019-03-18 14:21:29 -04:00
|
|
|
|
config?: NgcParsedConfiguration, customTransformers?: api.CustomTransformers, programReuse?: {
|
|
|
|
|
program: api.Program | undefined,
|
2019-06-10 11:22:56 -04:00
|
|
|
|
},
|
2019-06-27 19:25:00 -04:00
|
|
|
|
modifiedResourceFiles?: Set<string>| null): number {
|
2017-09-12 18:53:17 -04:00
|
|
|
|
let {project, rootNames, options, errors: configErrors, watch, emitFlags} =
|
|
|
|
|
config || readNgcCommandLineAndConfiguration(args);
|
2017-08-09 16:45:45 -04:00
|
|
|
|
if (configErrors.length) {
|
2017-10-20 12:46:41 -04:00
|
|
|
|
return reportErrorsAndExit(configErrors, /*options*/ undefined, consoleError);
|
2017-08-09 16:45:45 -04:00
|
|
|
|
}
|
2017-09-12 18:53:17 -04:00
|
|
|
|
if (watch) {
|
|
|
|
|
const result = watchMode(project, options, consoleError);
|
2017-10-20 12:46:41 -04:00
|
|
|
|
return reportErrorsAndExit(result.firstCompileResult, options, consoleError);
|
2017-09-12 18:53:17 -04:00
|
|
|
|
}
|
2019-03-18 14:21:29 -04:00
|
|
|
|
|
|
|
|
|
let oldProgram: api.Program|undefined;
|
|
|
|
|
if (programReuse !== undefined) {
|
|
|
|
|
oldProgram = programReuse.program;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const {diagnostics: compileDiags, program} = performCompilation({
|
2019-01-03 05:23:00 -05:00
|
|
|
|
rootNames,
|
|
|
|
|
options,
|
|
|
|
|
emitFlags,
|
2019-03-18 14:21:29 -04:00
|
|
|
|
oldProgram,
|
2019-06-10 11:22:56 -04:00
|
|
|
|
emitCallback: createEmitCallback(options), customTransformers, modifiedResourceFiles
|
2019-01-03 05:23:00 -05:00
|
|
|
|
});
|
2019-03-18 14:21:29 -04:00
|
|
|
|
if (programReuse !== undefined) {
|
|
|
|
|
programReuse.program = program;
|
|
|
|
|
}
|
2017-10-20 12:46:41 -04:00
|
|
|
|
return reportErrorsAndExit(compileDiags, options, consoleError);
|
2017-08-09 16:45:45 -04:00
|
|
|
|
}
|
|
|
|
|
|
2018-09-25 18:35:03 -04:00
|
|
|
|
export function mainDiagnosticsForTest(
|
2019-11-12 16:38:23 -05:00
|
|
|
|
args: string[], config?: NgcParsedConfiguration,
|
|
|
|
|
programReuse?: {program: api.Program | undefined},
|
|
|
|
|
modifiedResourceFiles?: Set<string>| null): ReadonlyArray<ts.Diagnostic|api.Diagnostic> {
|
2018-09-25 18:35:03 -04:00
|
|
|
|
let {project, rootNames, options, errors: configErrors, watch, emitFlags} =
|
|
|
|
|
config || readNgcCommandLineAndConfiguration(args);
|
|
|
|
|
if (configErrors.length) {
|
|
|
|
|
return configErrors;
|
|
|
|
|
}
|
2019-11-12 16:38:23 -05:00
|
|
|
|
|
|
|
|
|
let oldProgram: api.Program|undefined;
|
|
|
|
|
if (programReuse !== undefined) {
|
|
|
|
|
oldProgram = programReuse.program;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const {diagnostics: compileDiags, program} = performCompilation({
|
|
|
|
|
rootNames,
|
|
|
|
|
options,
|
|
|
|
|
emitFlags,
|
|
|
|
|
oldProgram,
|
|
|
|
|
modifiedResourceFiles,
|
|
|
|
|
emitCallback: createEmitCallback(options),
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (programReuse !== undefined) {
|
|
|
|
|
programReuse.program = program;
|
|
|
|
|
}
|
|
|
|
|
|
2018-09-25 18:35:03 -04:00
|
|
|
|
return compileDiags;
|
|
|
|
|
}
|
2017-12-22 12:36:47 -05:00
|
|
|
|
|
2017-10-12 19:09:49 -04:00
|
|
|
|
function createEmitCallback(options: api.CompilerOptions): api.TsEmitCallback|undefined {
|
2019-08-20 13:52:31 -04:00
|
|
|
|
const transformDecorators =
|
|
|
|
|
(options.enableIvy === false && options.annotationsAs !== 'decorators');
|
2017-09-15 21:02:22 -04:00
|
|
|
|
const transformTypesToClosure = options.annotateForClosureCompiler;
|
|
|
|
|
if (!transformDecorators && !transformTypesToClosure) {
|
|
|
|
|
return undefined;
|
|
|
|
|
}
|
2017-10-25 11:13:56 -04:00
|
|
|
|
if (transformDecorators) {
|
|
|
|
|
// This is needed as a workaround for https://github.com/angular/tsickle/issues/635
|
|
|
|
|
// Otherwise tsickle might emit references to non imported values
|
|
|
|
|
// as TypeScript elided the import.
|
|
|
|
|
options.emitDecoratorMetadata = true;
|
|
|
|
|
}
|
2017-12-22 12:36:47 -05:00
|
|
|
|
const tsickleHost: Pick<
|
|
|
|
|
tsickle.TsickleHost, 'shouldSkipTsickleProcessing'|'pathToModuleName'|
|
|
|
|
|
'shouldIgnoreWarningsForPath'|'fileNameToModuleId'|'googmodule'|'untyped'|
|
|
|
|
|
'convertIndexImportShorthand'|'transformDecorators'|'transformTypesToClosure'> = {
|
2017-09-15 21:02:22 -04:00
|
|
|
|
shouldSkipTsickleProcessing: (fileName) =>
|
|
|
|
|
/\.d\.ts$/.test(fileName) || GENERATED_FILES.test(fileName),
|
2017-09-08 21:40:32 -04:00
|
|
|
|
pathToModuleName: (context, importPath) => '',
|
|
|
|
|
shouldIgnoreWarningsForPath: (filePath) => false,
|
|
|
|
|
fileNameToModuleId: (fileName) => fileName,
|
2017-08-16 18:35:19 -04:00
|
|
|
|
googmodule: false,
|
|
|
|
|
untyped: true,
|
2017-09-28 12:31:28 -04:00
|
|
|
|
convertIndexImportShorthand: false, transformDecorators, transformTypesToClosure,
|
2017-08-16 18:35:19 -04:00
|
|
|
|
};
|
|
|
|
|
|
2018-08-27 14:04:48 -04:00
|
|
|
|
if (options.annotateForClosureCompiler || options.annotationsAs === 'static fields') {
|
|
|
|
|
return ({
|
|
|
|
|
program,
|
|
|
|
|
targetSourceFile,
|
|
|
|
|
writeFile,
|
|
|
|
|
cancellationToken,
|
|
|
|
|
emitOnlyDtsFiles,
|
|
|
|
|
customTransformers = {},
|
|
|
|
|
host,
|
|
|
|
|
options
|
|
|
|
|
}) =>
|
|
|
|
|
// tslint:disable-next-line:no-require-imports only depend on tsickle if requested
|
|
|
|
|
require('tsickle').emitWithTsickle(
|
2019-07-17 20:49:16 -04:00
|
|
|
|
program, {...tsickleHost, options, host, moduleResolutionHost: host}, host, options,
|
|
|
|
|
targetSourceFile, writeFile, cancellationToken, emitOnlyDtsFiles, {
|
2018-08-27 14:04:48 -04:00
|
|
|
|
beforeTs: customTransformers.before,
|
|
|
|
|
afterTs: customTransformers.after,
|
|
|
|
|
});
|
|
|
|
|
} else {
|
|
|
|
|
return ({
|
|
|
|
|
program,
|
|
|
|
|
targetSourceFile,
|
|
|
|
|
writeFile,
|
|
|
|
|
cancellationToken,
|
|
|
|
|
emitOnlyDtsFiles,
|
|
|
|
|
customTransformers = {},
|
|
|
|
|
}) =>
|
|
|
|
|
program.emit(
|
|
|
|
|
targetSourceFile, writeFile, cancellationToken, emitOnlyDtsFiles,
|
|
|
|
|
{after: customTransformers.after, before: customTransformers.before});
|
|
|
|
|
}
|
2017-08-16 18:35:19 -04:00
|
|
|
|
}
|
|
|
|
|
|
2017-09-12 18:53:17 -04:00
|
|
|
|
export interface NgcParsedConfiguration extends ParsedConfiguration { watch?: boolean; }
|
|
|
|
|
|
2018-11-16 11:54:43 -05:00
|
|
|
|
export function readNgcCommandLineAndConfiguration(args: string[]): NgcParsedConfiguration {
|
2017-09-12 18:53:17 -04:00
|
|
|
|
const options: api.CompilerOptions = {};
|
|
|
|
|
const parsedArgs = require('minimist')(args);
|
|
|
|
|
if (parsedArgs.i18nFile) options.i18nInFile = parsedArgs.i18nFile;
|
|
|
|
|
if (parsedArgs.i18nFormat) options.i18nInFormat = parsedArgs.i18nFormat;
|
|
|
|
|
if (parsedArgs.locale) options.i18nInLocale = parsedArgs.locale;
|
|
|
|
|
const mt = parsedArgs.missingTranslation;
|
|
|
|
|
if (mt === 'error' || mt === 'warning' || mt === 'ignore') {
|
|
|
|
|
options.i18nInMissingTranslations = mt;
|
|
|
|
|
}
|
|
|
|
|
const config = readCommandLineAndConfiguration(
|
|
|
|
|
args, options, ['i18nFile', 'i18nFormat', 'locale', 'missingTranslation', 'watch']);
|
|
|
|
|
const watch = parsedArgs.w || parsedArgs.watch;
|
|
|
|
|
return {...config, watch};
|
2017-08-18 17:03:59 -04:00
|
|
|
|
}
|
|
|
|
|
|
2017-09-12 18:53:17 -04:00
|
|
|
|
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
|
|
|
|
|
};
|
|
|
|
|
}
|
2017-08-09 16:45:45 -04:00
|
|
|
|
const allDiagnostics: Diagnostics = [];
|
2017-09-12 18:53:17 -04:00
|
|
|
|
const config = readConfiguration(project, cmdConfig.options);
|
|
|
|
|
const options = {...config.options, ...existingOptions};
|
2017-08-31 17:11:29 -04:00
|
|
|
|
if (options.locale) {
|
|
|
|
|
options.i18nInLocale = options.locale;
|
|
|
|
|
}
|
2017-09-12 18:53:17 -04:00
|
|
|
|
return {
|
|
|
|
|
project,
|
|
|
|
|
rootNames: config.rootNames, options,
|
|
|
|
|
errors: config.errors,
|
|
|
|
|
emitFlags: config.emitFlags
|
|
|
|
|
};
|
2017-08-09 16:45:45 -04:00
|
|
|
|
}
|
|
|
|
|
|
2019-01-25 12:40:51 -05:00
|
|
|
|
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;
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2017-08-09 16:45:45 -04:00
|
|
|
|
function reportErrorsAndExit(
|
2017-10-20 12:46:41 -04:00
|
|
|
|
allDiagnostics: Diagnostics, options?: api.CompilerOptions,
|
|
|
|
|
consoleError: (s: string) => void = console.error): number {
|
2017-09-29 18:02:11 -04:00
|
|
|
|
const errorsAndWarnings = filterErrorsAndWarnings(allDiagnostics);
|
2019-11-15 19:53:33 -05:00
|
|
|
|
printDiagnostics(errorsAndWarnings, options, consoleError);
|
2017-09-12 18:53:17 -04:00
|
|
|
|
return exitCodeFromResult(allDiagnostics);
|
2017-08-18 17:03:59 -04:00
|
|
|
|
}
|
|
|
|
|
|
2017-09-12 18:53:17 -04:00
|
|
|
|
export function watchMode(
|
|
|
|
|
project: string, options: api.CompilerOptions, consoleError: (s: string) => void) {
|
|
|
|
|
return performWatchCompilation(createPerformWatchHost(project, diagnostics => {
|
2019-11-15 19:53:33 -05:00
|
|
|
|
printDiagnostics(diagnostics, options, consoleError);
|
2017-09-12 18:53:17 -04:00
|
|
|
|
}, options, options => createEmitCallback(options)));
|
2017-08-09 16:45:45 -04:00
|
|
|
|
}
|
2016-05-01 14:22:39 -04:00
|
|
|
|
|
2019-11-15 19:53:33 -05:00
|
|
|
|
function printDiagnostics(
|
|
|
|
|
diagnostics: ReadonlyArray<ts.Diagnostic|api.Diagnostic>,
|
|
|
|
|
options: api.CompilerOptions | undefined, consoleError: (s: string) => void): void {
|
|
|
|
|
if (diagnostics.length === 0) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
const formatHost = getFormatDiagnosticsHost(options);
|
2019-12-04 13:44:32 -05:00
|
|
|
|
consoleError(formatDiagnostics(diagnostics, formatHost));
|
2019-11-15 19:53:33 -05:00
|
|
|
|
}
|
|
|
|
|
|
2016-11-30 16:59:53 -05:00
|
|
|
|
// CLI entry point
|
|
|
|
|
if (require.main === module) {
|
2017-08-02 14:20:07 -04:00
|
|
|
|
const args = process.argv.slice(2);
|
2019-06-06 15:22:32 -04:00
|
|
|
|
// We are running the real compiler so run against the real file-system
|
|
|
|
|
setFileSystem(new NodeJSFileSystem());
|
2017-09-13 19:55:42 -04:00
|
|
|
|
process.exitCode = main(args);
|
2016-11-30 19:45:40 -05:00
|
|
|
|
}
|