fix(compiler-cli): remove minimist dependency of compiler-cli/index (#18532)
Indirectly removes the minimist dependency in the language service package was added with the addition of `ngc.ts`.
This commit is contained in:
parent
04b18a9f46
commit
5b7432b6ea
@ -20,8 +20,7 @@ export {BuiltinType, DeclarationKind, Definition, PipeInfo, Pipes, Signature, Sp
|
|||||||
export * from './src/transformers/api';
|
export * from './src/transformers/api';
|
||||||
export * from './src/transformers/entry_points';
|
export * from './src/transformers/entry_points';
|
||||||
|
|
||||||
export {main as ngc} from './src/ngc';
|
export {performCompilation} from './src/perform-compile';
|
||||||
export {performCompilation} from './src/ngc';
|
|
||||||
|
|
||||||
// TODO(hansl): moving to Angular 4 need to update this API.
|
// TODO(hansl): moving to Angular 4 need to update this API.
|
||||||
export {NgTools_InternalApi_NG_2 as __NGTOOLS_PRIVATE_API_2} from './src/ngtools_api';
|
export {NgTools_InternalApi_NG_2 as __NGTOOLS_PRIVATE_API_2} from './src/ngtools_api';
|
||||||
|
@ -10,185 +10,14 @@
|
|||||||
import 'reflect-metadata';
|
import 'reflect-metadata';
|
||||||
|
|
||||||
import {isSyntaxError, syntaxError} from '@angular/compiler';
|
import {isSyntaxError, syntaxError} from '@angular/compiler';
|
||||||
import {MetadataBundler, 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 ts from 'typescript';
|
|
||||||
import * as api from './transformers/api';
|
|
||||||
import * as ng from './transformers/entry_points';
|
|
||||||
|
|
||||||
const TS_EXT = /\.ts$/;
|
|
||||||
|
|
||||||
type Diagnostics = ts.Diagnostic[] | api.Diagnostic[];
|
|
||||||
|
|
||||||
function isTsDiagnostics(diagnostics: any): diagnostics is ts.Diagnostic[] {
|
|
||||||
return diagnostics && diagnostics[0] && (diagnostics[0].file || diagnostics[0].messageText);
|
|
||||||
}
|
|
||||||
|
|
||||||
function formatDiagnostics(cwd: string, diags: Diagnostics): string {
|
|
||||||
if (diags && diags.length) {
|
|
||||||
if (isTsDiagnostics(diags)) {
|
|
||||||
return ts.formatDiagnostics(diags, {
|
|
||||||
getCurrentDirectory: () => cwd,
|
|
||||||
getCanonicalFileName: fileName => fileName,
|
|
||||||
getNewLine: () => ts.sys.newLine
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
return diags
|
|
||||||
.map(d => {
|
|
||||||
let res = api.DiagnosticCategory[d.category];
|
|
||||||
if (d.span) {
|
|
||||||
res +=
|
|
||||||
` at ${d.span.start.file.url}(${d.span.start.line + 1},${d.span.start.col + 1})`;
|
|
||||||
}
|
|
||||||
if (d.span && d.span.details) {
|
|
||||||
res += `: ${d.span.details}, ${d.message}\n`;
|
|
||||||
} else {
|
|
||||||
res += `: ${d.message}\n`;
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
})
|
|
||||||
.join();
|
|
||||||
}
|
|
||||||
} else
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
function check(cwd: string, ...args: Diagnostics[]) {
|
|
||||||
if (args.some(diags => !!(diags && diags[0]))) {
|
|
||||||
throw syntaxError(args.map(diags => {
|
|
||||||
if (diags && diags[0]) {
|
|
||||||
return formatDiagnostics(cwd, diags);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.filter(message => !!message)
|
|
||||||
.join(''));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function syntheticError(message: string): ts.Diagnostic {
|
|
||||||
return {
|
|
||||||
file: null as any as ts.SourceFile,
|
|
||||||
start: 0,
|
|
||||||
length: 0,
|
|
||||||
messageText: message,
|
|
||||||
category: ts.DiagnosticCategory.Error,
|
|
||||||
code: 0
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function readConfiguration(
|
|
||||||
project: string, basePath: string, checkFunc: (cwd: string, ...args: any[]) => void = check,
|
|
||||||
existingOptions?: ts.CompilerOptions) {
|
|
||||||
// Allow a directory containing tsconfig.json as the project value
|
|
||||||
// Note, TS@next returns an empty array, while earlier versions throw
|
|
||||||
const projectFile =
|
|
||||||
fs.lstatSync(project).isDirectory() ? path.join(project, 'tsconfig.json') : project;
|
|
||||||
let {config, error} = ts.readConfigFile(projectFile, ts.sys.readFile);
|
|
||||||
|
|
||||||
if (error) checkFunc(basePath, [error]);
|
|
||||||
const parseConfigHost = {
|
|
||||||
useCaseSensitiveFileNames: true,
|
|
||||||
fileExists: fs.existsSync,
|
|
||||||
readDirectory: ts.sys.readDirectory,
|
|
||||||
readFile: ts.sys.readFile
|
|
||||||
};
|
|
||||||
const parsed = ts.parseJsonConfigFileContent(config, parseConfigHost, basePath, existingOptions);
|
|
||||||
|
|
||||||
checkFunc(basePath, parsed.errors);
|
|
||||||
|
|
||||||
// Default codegen goes to the current directory
|
|
||||||
// Parsed options are already converted to absolute paths
|
|
||||||
const ngOptions = config.angularCompilerOptions || {};
|
|
||||||
// Ignore the genDir option
|
|
||||||
ngOptions.genDir = basePath;
|
|
||||||
|
|
||||||
return {parsed, ngOptions};
|
|
||||||
}
|
|
||||||
|
|
||||||
function getProjectDirectory(project: string): string {
|
|
||||||
let isFile: boolean;
|
|
||||||
try {
|
|
||||||
isFile = fs.lstatSync(project).isFile();
|
|
||||||
} catch (e) {
|
|
||||||
// Project doesn't exist. Assume it is a file has an extension. This case happens
|
|
||||||
// when the project file is passed to set basePath but no tsconfig.json file exists.
|
|
||||||
// It is used in tests to ensure that the options can be passed in without there being
|
|
||||||
// an actual config file.
|
|
||||||
isFile = path.extname(project) !== '';
|
|
||||||
}
|
|
||||||
|
|
||||||
// If project refers to a file, the project directory is the file's parent directory
|
|
||||||
// otherwise project is the project directory.
|
|
||||||
return isFile ? path.dirname(project) : project;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function performCompilation(
|
|
||||||
basePath: string, files: string[], options: ts.CompilerOptions, ngOptions: any,
|
|
||||||
consoleError: (s: string) => void = console.error,
|
|
||||||
checkFunc: (cwd: string, ...args: any[]) => void = check, tsCompilerHost?: ts.CompilerHost) {
|
|
||||||
try {
|
|
||||||
ngOptions.basePath = basePath;
|
|
||||||
ngOptions.genDir = basePath;
|
|
||||||
|
|
||||||
let host = tsCompilerHost || ts.createCompilerHost(options, true);
|
|
||||||
host.realpath = p => p;
|
|
||||||
|
|
||||||
const rootFileNames = files.map(f => path.normalize(f));
|
|
||||||
|
|
||||||
const addGeneratedFileName = (fileName: string) => {
|
|
||||||
if (fileName.startsWith(basePath) && TS_EXT.exec(fileName)) {
|
|
||||||
rootFileNames.push(fileName);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if (ngOptions.flatModuleOutFile && !ngOptions.skipMetadataEmit) {
|
|
||||||
const {host: bundleHost, indexName, errors} =
|
|
||||||
createBundleIndexHost(ngOptions, rootFileNames, host);
|
|
||||||
if (errors) checkFunc(basePath, errors);
|
|
||||||
if (indexName) addGeneratedFileName(indexName);
|
|
||||||
host = bundleHost;
|
|
||||||
}
|
|
||||||
|
|
||||||
const ngHostOptions = {...options, ...ngOptions};
|
|
||||||
const ngHost = ng.createHost({tsHost: host, options: ngHostOptions});
|
|
||||||
|
|
||||||
const ngProgram =
|
|
||||||
ng.createProgram({rootNames: rootFileNames, host: ngHost, options: ngHostOptions});
|
|
||||||
|
|
||||||
// Check parameter diagnostics
|
|
||||||
checkFunc(basePath, ngProgram.getTsOptionDiagnostics(), ngProgram.getNgOptionDiagnostics());
|
|
||||||
|
|
||||||
// Check syntactic diagnostics
|
|
||||||
checkFunc(basePath, ngProgram.getTsSyntacticDiagnostics());
|
|
||||||
|
|
||||||
// Check TypeScript semantic and Angular structure diagnostics
|
|
||||||
checkFunc(
|
|
||||||
basePath, ngProgram.getTsSemanticDiagnostics(), ngProgram.getNgStructuralDiagnostics());
|
|
||||||
|
|
||||||
// Check Angular semantic diagnostics
|
|
||||||
checkFunc(basePath, ngProgram.getNgSemanticDiagnostics());
|
|
||||||
|
|
||||||
ngProgram.emit({
|
|
||||||
emitFlags: api.EmitFlags.Default |
|
|
||||||
((ngOptions.skipMetadataEmit || ngOptions.flatModuleOutFile) ? 0 : api.EmitFlags.Metadata)
|
|
||||||
});
|
|
||||||
} catch (e) {
|
|
||||||
if (isSyntaxError(e)) {
|
|
||||||
console.error(e.message);
|
|
||||||
consoleError(e.message);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
import {performCompilation, readConfiguration, throwOnDiagnostics} from './perform-compile';
|
||||||
|
|
||||||
export function main(
|
export function main(
|
||||||
args: string[], consoleError: (s: string) => void = console.error,
|
args: string[], consoleError: (s: string) => void = console.error,
|
||||||
checkFunc: (cwd: string, ...args: any[]) => void = check): number {
|
checkFunc: (cwd: string, ...args: any[]) => void = throwOnDiagnostics): number {
|
||||||
try {
|
try {
|
||||||
const parsedArgs = require('minimist')(args);
|
const parsedArgs = require('minimist')(args);
|
||||||
const project = parsedArgs.p || parsedArgs.project || '.';
|
const project = parsedArgs.p || parsedArgs.project || '.';
|
||||||
|
192
packages/compiler-cli/src/perform-compile.ts
Normal file
192
packages/compiler-cli/src/perform-compile.ts
Normal file
@ -0,0 +1,192 @@
|
|||||||
|
/**
|
||||||
|
* @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
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {isSyntaxError, syntaxError} from '@angular/compiler';
|
||||||
|
import {MetadataBundler, createBundleIndexHost} from '@angular/tsc-wrapped';
|
||||||
|
import * as fs from 'fs';
|
||||||
|
import * as path from 'path';
|
||||||
|
import * as ts from 'typescript';
|
||||||
|
import * as api from './transformers/api';
|
||||||
|
import * as ng from './transformers/entry_points';
|
||||||
|
|
||||||
|
const TS_EXT = /\.ts$/;
|
||||||
|
|
||||||
|
export type Diagnostics = ts.Diagnostic[] | api.Diagnostic[];
|
||||||
|
|
||||||
|
function isTsDiagnostics(diagnostics: any): diagnostics is ts.Diagnostic[] {
|
||||||
|
return diagnostics && diagnostics[0] && (diagnostics[0].file || diagnostics[0].messageText);
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatDiagnostics(cwd: string, diags: Diagnostics): string {
|
||||||
|
if (diags && diags.length) {
|
||||||
|
if (isTsDiagnostics(diags)) {
|
||||||
|
return ts.formatDiagnostics(diags, {
|
||||||
|
getCurrentDirectory: () => cwd,
|
||||||
|
getCanonicalFileName: fileName => fileName,
|
||||||
|
getNewLine: () => ts.sys.newLine
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return diags
|
||||||
|
.map(d => {
|
||||||
|
let res = api.DiagnosticCategory[d.category];
|
||||||
|
if (d.span) {
|
||||||
|
res +=
|
||||||
|
` at ${d.span.start.file.url}(${d.span.start.line + 1},${d.span.start.col + 1})`;
|
||||||
|
}
|
||||||
|
if (d.span && d.span.details) {
|
||||||
|
res += `: ${d.span.details}, ${d.message}\n`;
|
||||||
|
} else {
|
||||||
|
res += `: ${d.message}\n`;
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
})
|
||||||
|
.join();
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Throw a syntax error exception with a message formatted for output
|
||||||
|
* if the args parameter contains diagnostics errors.
|
||||||
|
*
|
||||||
|
* @param cwd The directory to report error as relative to.
|
||||||
|
* @param args A list of potentially empty diagnostic errors.
|
||||||
|
*/
|
||||||
|
export function throwOnDiagnostics(cwd: string, ...args: Diagnostics[]) {
|
||||||
|
if (args.some(diags => !!(diags && diags[0]))) {
|
||||||
|
throw syntaxError(args.map(diags => {
|
||||||
|
if (diags && diags[0]) {
|
||||||
|
return formatDiagnostics(cwd, diags);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.filter(message => !!message)
|
||||||
|
.join(''));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function syntheticError(message: string): ts.Diagnostic {
|
||||||
|
return {
|
||||||
|
file: null as any as ts.SourceFile,
|
||||||
|
start: 0,
|
||||||
|
length: 0,
|
||||||
|
messageText: message,
|
||||||
|
category: ts.DiagnosticCategory.Error,
|
||||||
|
code: 0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function readConfiguration(
|
||||||
|
project: string, basePath: string,
|
||||||
|
checkFunc: (cwd: string, ...args: any[]) => void = throwOnDiagnostics,
|
||||||
|
existingOptions?: ts.CompilerOptions) {
|
||||||
|
// Allow a directory containing tsconfig.json as the project value
|
||||||
|
// Note, TS@next returns an empty array, while earlier versions throw
|
||||||
|
const projectFile =
|
||||||
|
fs.lstatSync(project).isDirectory() ? path.join(project, 'tsconfig.json') : project;
|
||||||
|
let {config, error} = ts.readConfigFile(projectFile, ts.sys.readFile);
|
||||||
|
|
||||||
|
if (error) checkFunc(basePath, [error]);
|
||||||
|
const parseConfigHost = {
|
||||||
|
useCaseSensitiveFileNames: true,
|
||||||
|
fileExists: fs.existsSync,
|
||||||
|
readDirectory: ts.sys.readDirectory,
|
||||||
|
readFile: ts.sys.readFile
|
||||||
|
};
|
||||||
|
const parsed = ts.parseJsonConfigFileContent(config, parseConfigHost, basePath, existingOptions);
|
||||||
|
|
||||||
|
checkFunc(basePath, parsed.errors);
|
||||||
|
|
||||||
|
// Default codegen goes to the current directory
|
||||||
|
// Parsed options are already converted to absolute paths
|
||||||
|
const ngOptions = config.angularCompilerOptions || {};
|
||||||
|
// Ignore the genDir option
|
||||||
|
ngOptions.genDir = basePath;
|
||||||
|
|
||||||
|
return {parsed, ngOptions};
|
||||||
|
}
|
||||||
|
|
||||||
|
function getProjectDirectory(project: string): string {
|
||||||
|
let isFile: boolean;
|
||||||
|
try {
|
||||||
|
isFile = fs.lstatSync(project).isFile();
|
||||||
|
} catch (e) {
|
||||||
|
// Project doesn't exist. Assume it is a file has an extension. This case happens
|
||||||
|
// when the project file is passed to set basePath but no tsconfig.json file exists.
|
||||||
|
// It is used in tests to ensure that the options can be passed in without there being
|
||||||
|
// an actual config file.
|
||||||
|
isFile = path.extname(project) !== '';
|
||||||
|
}
|
||||||
|
|
||||||
|
// If project refers to a file, the project directory is the file's parent directory
|
||||||
|
// otherwise project is the project directory.
|
||||||
|
return isFile ? path.dirname(project) : project;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function performCompilation(
|
||||||
|
basePath: string, files: string[], options: ts.CompilerOptions, ngOptions: any,
|
||||||
|
consoleError: (s: string) => void = console.error,
|
||||||
|
checkFunc: (cwd: string, ...args: any[]) => void = throwOnDiagnostics,
|
||||||
|
tsCompilerHost?: ts.CompilerHost) {
|
||||||
|
try {
|
||||||
|
ngOptions.basePath = basePath;
|
||||||
|
ngOptions.genDir = basePath;
|
||||||
|
|
||||||
|
let host = tsCompilerHost || ts.createCompilerHost(options, true);
|
||||||
|
host.realpath = p => p;
|
||||||
|
|
||||||
|
const rootFileNames = files.map(f => path.normalize(f));
|
||||||
|
|
||||||
|
const addGeneratedFileName = (fileName: string) => {
|
||||||
|
if (fileName.startsWith(basePath) && TS_EXT.exec(fileName)) {
|
||||||
|
rootFileNames.push(fileName);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (ngOptions.flatModuleOutFile && !ngOptions.skipMetadataEmit) {
|
||||||
|
const {host: bundleHost, indexName, errors} =
|
||||||
|
createBundleIndexHost(ngOptions, rootFileNames, host);
|
||||||
|
if (errors) checkFunc(basePath, errors);
|
||||||
|
if (indexName) addGeneratedFileName(indexName);
|
||||||
|
host = bundleHost;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ngHostOptions = {...options, ...ngOptions};
|
||||||
|
const ngHost = ng.createHost({tsHost: host, options: ngHostOptions});
|
||||||
|
|
||||||
|
const ngProgram =
|
||||||
|
ng.createProgram({rootNames: rootFileNames, host: ngHost, options: ngHostOptions});
|
||||||
|
|
||||||
|
// Check parameter diagnostics
|
||||||
|
checkFunc(basePath, ngProgram.getTsOptionDiagnostics(), ngProgram.getNgOptionDiagnostics());
|
||||||
|
|
||||||
|
// Check syntactic diagnostics
|
||||||
|
checkFunc(basePath, ngProgram.getTsSyntacticDiagnostics());
|
||||||
|
|
||||||
|
// Check TypeScript semantic and Angular structure diagnostics
|
||||||
|
checkFunc(
|
||||||
|
basePath, ngProgram.getTsSemanticDiagnostics(), ngProgram.getNgStructuralDiagnostics());
|
||||||
|
|
||||||
|
// Check Angular semantic diagnostics
|
||||||
|
checkFunc(basePath, ngProgram.getNgSemanticDiagnostics());
|
||||||
|
|
||||||
|
ngProgram.emit({
|
||||||
|
emitFlags: api.EmitFlags.Default |
|
||||||
|
((ngOptions.skipMetadataEmit || ngOptions.flatModuleOutFile) ? 0 : api.EmitFlags.Metadata)
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
if (isSyntaxError(e)) {
|
||||||
|
console.error(e.message);
|
||||||
|
consoleError(e.message);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
@ -11,7 +11,8 @@ import * as fs from 'fs';
|
|||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import * as ts from 'typescript';
|
import * as ts from 'typescript';
|
||||||
|
|
||||||
import {main, performCompilation} from '../src/ngc';
|
import {main} from '../src/ngc';
|
||||||
|
import {performCompilation} from '../src/perform-compile';
|
||||||
|
|
||||||
function getNgRootDir() {
|
function getNgRootDir() {
|
||||||
const moduleFilename = module.filename.replace(/\\/g, '/');
|
const moduleFilename = module.filename.replace(/\\/g, '/');
|
||||||
|
Loading…
x
Reference in New Issue
Block a user