refactor(compiler): change ngc error handling

Do not print stack trace for user errors
Print stack trace for compiler internal errors
This commit is contained in:
Bowen Ni 2016-11-30 13:59:53 -08:00 committed by Alex Rickabaugh
parent 75d1617b63
commit 9761db5ac2
6 changed files with 152 additions and 10 deletions

View File

@ -22,14 +22,24 @@ function codegen(
return CodeGenerator.create(ngOptions, cliOptions, program, host).codegen();
}
export function main(args: any, consoleError: any): Promise<number> {
const project = args.p || args.project || '.';
const cliOptions = new tsc.NgcCliOptions(args);
return tsc.main(project, cliOptions, codegen).then(() => 0).catch(e => {
if (e instanceof tsc.UserError) {
consoleError(e.message);
return Promise.resolve(0);
} else {
consoleError(e.stack);
consoleError('Compilation failed');
return Promise.resolve(1);
}
});
}
// CLI entry point
if (require.main === module) {
const args = require('minimist')(process.argv.slice(2));
const project = args.p || args.project || '.';
const cliOptions = new tsc.NgcCliOptions(args);
tsc.main(project, cliOptions, codegen).then(exitCode => process.exit(exitCode)).catch(e => {
console.error(e.stack);
console.error('Compilation failed');
process.exit(1);
});
main(args, console.error).then((exitCode: number) => process.exit(exitCode));
}

View File

@ -0,0 +1,108 @@
/**
* @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 {makeTempDir} from '@angular/tsc-wrapped/test/test_support';
import * as fs from 'fs';
import * as path from 'path';
import {main} from '../src/main';
import {ReflectionCapabilities, reflector} from './private_import_core';
describe('compiler-cli', () => {
let basePath: string;
let write: (fileName: string, content: string) => void;
beforeEach(() => {
basePath = makeTempDir();
write = (fileName: string, content: string) => {
fs.writeFileSync(path.join(basePath, fileName), content, {encoding: 'utf-8'});
};
write('tsconfig.json', `{
"compilerOptions": {
"experimentalDecorators": true,
"types": [],
"outDir": "built",
"declaration": true,
"module": "es2015"
},
"angularCompilerOptions": {
"annotateForClosureCompiler": true
},
"files": ["test.ts"]
}`);
});
// Restore reflector since AoT compiler will update it with a new static reflector
afterEach(() => { reflector.updateCapabilities(new ReflectionCapabilities()); });
it('should compile without errors', (done) => {
write('test.ts', 'export const A = 1;');
const mockConsole = {error: {}};
spyOn(mockConsole, 'error');
main({p: basePath}, mockConsole.error)
.then((exitCode) => {
expect(mockConsole.error).not.toHaveBeenCalled();
expect(exitCode).toEqual(0);
done();
})
.catch(e => done.fail(e));
});
it('should not print the stack trace if user input file does not exist', (done) => {
const mockConsole = {error: {}};
spyOn(mockConsole, 'error');
main({p: basePath}, mockConsole.error)
.then((exitCode) => {
expect(mockConsole.error).toHaveBeenCalled();
expect(mockConsole.error).not.toHaveBeenCalledWith('Compilation failed');
expect(exitCode).toEqual(0);
done();
})
.catch(e => done.fail(e));
});
it('should not print the stack trace if user input file is malformed', (done) => {
write('test.ts', 'foo bar');
const mockConsole = {error: {}};
spyOn(mockConsole, 'error');
main({p: basePath}, mockConsole.error)
.then((exitCode) => {
expect(mockConsole.error).toHaveBeenCalled();
expect(mockConsole.error).not.toHaveBeenCalledWith('Compilation failed');
expect(exitCode).toEqual(0);
done();
})
.catch(e => done.fail(e));
});
it('should print the stack trace on compiler internal errors', (done) => {
write('test.ts', 'export const A = 1;');
const mockConsole = {error: {}};
spyOn(mockConsole, 'error');
main({p: 'not-exist'}, mockConsole.error)
.then((exitCode) => {
expect(mockConsole.error).toHaveBeenCalled();
expect(mockConsole.error).toHaveBeenCalledWith('Compilation failed');
expect(exitCode).toEqual(1);
done();
})
.catch(e => done.fail(e));
});
});

View File

@ -0,0 +1,13 @@
/**
* @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 {__core_private__ as r} from '@angular/core';
export type ReflectionCapabilities = typeof r._ReflectionCapabilities;
export var ReflectionCapabilities: typeof r.ReflectionCapabilities = r.ReflectionCapabilities;
export var reflector: typeof r.reflector = r.reflector;

View File

@ -7,7 +7,7 @@
*/
export {DecoratorDownlevelCompilerHost, MetadataWriterHost} from './src/compiler_host';
export {CodegenExtension, main} from './src/main';
export {CodegenExtension, UserError, main} from './src/main';
export {default as AngularCompilerOptions} from './src/options';
export * from './src/cli_options';

View File

@ -16,6 +16,8 @@ import NgOptions from './options';
import {MetadataWriterHost, DecoratorDownlevelCompilerHost, TsickleCompilerHost} from './compiler_host';
import {CliOptions} from './cli_options';
export {UserError} from './tsc';
export type CodegenExtension =
(ngOptions: NgOptions, cliOptions: CliOptions, program: ts.Program, host: ts.CompilerHost) =>
Promise<void>;

View File

@ -24,6 +24,15 @@ export interface CompilerInterface {
emit(program: ts.Program): number;
}
export class UserError extends Error {
constructor(message: string) {
super(message);
this.message = message;
this.name = 'UserError';
this.stack = new Error().stack;
}
}
const DEBUG = false;
function debug(msg: string, ...o: any[]) {
@ -48,7 +57,7 @@ export function formatDiagnostics(diags: ts.Diagnostic[]): string {
export function check(diags: ts.Diagnostic[]) {
if (diags && diags.length && diags[0]) {
throw new Error(formatDiagnostics(diags));
throw new UserError(formatDiagnostics(diags));
}
}