perf: Don’t subclass Error; resulting in smaller binary (#14160)

Subclassing errors is problematic since Error returns a
new instance. All of the patching which we do than prevent
proper application of source maps.

PR Close #14160
This commit is contained in:
Miško Hevery 2017-01-27 13:19:00 -08:00 committed by Miško Hevery
parent 3c2842be96
commit c33fda2607
51 changed files with 407 additions and 500 deletions

View File

@ -9,7 +9,7 @@
import {ChangeDetectorRef, OnDestroy, Pipe, PipeTransform, WrappedValue} from '@angular/core'; import {ChangeDetectorRef, OnDestroy, Pipe, PipeTransform, WrappedValue} from '@angular/core';
import {EventEmitter, Observable} from '../facade/async'; import {EventEmitter, Observable} from '../facade/async';
import {isObservable, isPromise} from '../private_import_core'; import {isObservable, isPromise} from '../private_import_core';
import {InvalidPipeArgumentError} from './invalid_pipe_argument_error'; import {invalidPipeArgumentError} from './invalid_pipe_argument_error';
interface SubscriptionStrategy { interface SubscriptionStrategy {
createSubscription(async: any, updateLatestValue: any): any; createSubscription(async: any, updateLatestValue: any): any;
@ -120,7 +120,7 @@ export class AsyncPipe implements OnDestroy, PipeTransform {
return _observableStrategy; return _observableStrategy;
} }
throw new InvalidPipeArgumentError(AsyncPipe, obj); throw invalidPipeArgumentError(AsyncPipe, obj);
} }
private _dispose(): void { private _dispose(): void {

View File

@ -7,7 +7,7 @@
*/ */
import {Pipe, PipeTransform} from '@angular/core'; import {Pipe, PipeTransform} from '@angular/core';
import {InvalidPipeArgumentError} from './invalid_pipe_argument_error'; import {invalidPipeArgumentError} from './invalid_pipe_argument_error';
/** /**
* Transforms text to lowercase. * Transforms text to lowercase.
@ -21,7 +21,7 @@ export class LowerCasePipe implements PipeTransform {
transform(value: string): string { transform(value: string): string {
if (!value) return value; if (!value) return value;
if (typeof value !== 'string') { if (typeof value !== 'string') {
throw new InvalidPipeArgumentError(LowerCasePipe, value); throw invalidPipeArgumentError(LowerCasePipe, value);
} }
return value.toLowerCase(); return value.toLowerCase();
} }
@ -48,7 +48,7 @@ export class TitleCasePipe implements PipeTransform {
transform(value: string): string { transform(value: string): string {
if (!value) return value; if (!value) return value;
if (typeof value !== 'string') { if (typeof value !== 'string') {
throw new InvalidPipeArgumentError(TitleCasePipe, value); throw invalidPipeArgumentError(TitleCasePipe, value);
} }
return value.split(/\b/g).map(word => titleCaseWord(word)).join(''); return value.split(/\b/g).map(word => titleCaseWord(word)).join('');
@ -65,7 +65,7 @@ export class UpperCasePipe implements PipeTransform {
transform(value: string): string { transform(value: string): string {
if (!value) return value; if (!value) return value;
if (typeof value !== 'string') { if (typeof value !== 'string') {
throw new InvalidPipeArgumentError(UpperCasePipe, value); throw invalidPipeArgumentError(UpperCasePipe, value);
} }
return value.toUpperCase(); return value.toUpperCase();
} }

View File

@ -9,7 +9,7 @@
import {Inject, LOCALE_ID, Pipe, PipeTransform} from '@angular/core'; import {Inject, LOCALE_ID, Pipe, PipeTransform} from '@angular/core';
import {NumberWrapper} from '../facade/lang'; import {NumberWrapper} from '../facade/lang';
import {DateFormatter} from './intl'; import {DateFormatter} from './intl';
import {InvalidPipeArgumentError} from './invalid_pipe_argument_error'; import {invalidPipeArgumentError} from './invalid_pipe_argument_error';
const ISO8601_DATE_REGEX = const ISO8601_DATE_REGEX =
/^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(Z|([+-])(\d\d):?(\d\d))?)?$/; /^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(Z|([+-])(\d\d):?(\d\d))?)?$/;
@ -134,7 +134,7 @@ export class DatePipe implements PipeTransform {
if ((typeof value === 'string') && (match = value.match(ISO8601_DATE_REGEX))) { if ((typeof value === 'string') && (match = value.match(ISO8601_DATE_REGEX))) {
date = isoStringToDate(match); date = isoStringToDate(match);
} else { } else {
throw new InvalidPipeArgumentError(DatePipe, value); throw invalidPipeArgumentError(DatePipe, value);
} }
} }

View File

@ -8,7 +8,7 @@
import {Pipe, PipeTransform} from '@angular/core'; import {Pipe, PipeTransform} from '@angular/core';
import {NgLocalization, getPluralCategory} from '../localization'; import {NgLocalization, getPluralCategory} from '../localization';
import {InvalidPipeArgumentError} from './invalid_pipe_argument_error'; import {invalidPipeArgumentError} from './invalid_pipe_argument_error';
const _INTERPOLATION_REGEXP: RegExp = /#/g; const _INTERPOLATION_REGEXP: RegExp = /#/g;
@ -37,7 +37,7 @@ export class I18nPluralPipe implements PipeTransform {
if (value == null) return ''; if (value == null) return '';
if (typeof pluralMap !== 'object' || pluralMap === null) { if (typeof pluralMap !== 'object' || pluralMap === null) {
throw new InvalidPipeArgumentError(I18nPluralPipe, pluralMap); throw invalidPipeArgumentError(I18nPluralPipe, pluralMap);
} }
const key = getPluralCategory(value, Object.keys(pluralMap), this._localization); const key = getPluralCategory(value, Object.keys(pluralMap), this._localization);

View File

@ -7,7 +7,7 @@
*/ */
import {Pipe, PipeTransform} from '@angular/core'; import {Pipe, PipeTransform} from '@angular/core';
import {InvalidPipeArgumentError} from './invalid_pipe_argument_error'; import {invalidPipeArgumentError} from './invalid_pipe_argument_error';
/** /**
* @ngModule CommonModule * @ngModule CommonModule
@ -32,7 +32,7 @@ export class I18nSelectPipe implements PipeTransform {
if (value == null) return ''; if (value == null) return '';
if (typeof mapping !== 'object' || typeof value !== 'string') { if (typeof mapping !== 'object' || typeof value !== 'string') {
throw new InvalidPipeArgumentError(I18nSelectPipe, mapping); throw invalidPipeArgumentError(I18nSelectPipe, mapping);
} }
if (mapping.hasOwnProperty(value)) { if (mapping.hasOwnProperty(value)) {

View File

@ -8,11 +8,8 @@
import {Type} from '@angular/core'; import {Type} from '@angular/core';
import {BaseError} from '../facade/errors';
import {stringify} from '../facade/lang'; import {stringify} from '../facade/lang';
export class InvalidPipeArgumentError extends BaseError { export function invalidPipeArgumentError(type: Type<any>, value: Object) {
constructor(type: Type<any>, value: Object) { return Error(`InvalidPipeArgument: '${value}' for pipe '${stringify(type)}'`);
super(`Invalid argument '${value}' for pipe '${stringify(type)}'`);
}
} }

View File

@ -11,7 +11,7 @@ import {Inject, LOCALE_ID, Pipe, PipeTransform, Type} from '@angular/core';
import {NumberWrapper} from '../facade/lang'; import {NumberWrapper} from '../facade/lang';
import {NumberFormatStyle, NumberFormatter} from './intl'; import {NumberFormatStyle, NumberFormatter} from './intl';
import {InvalidPipeArgumentError} from './invalid_pipe_argument_error'; import {invalidPipeArgumentError} from './invalid_pipe_argument_error';
const _NUMBER_FORMAT_REGEXP = /^(\d+)?\.((\d+)(-(\d+))?)?$/; const _NUMBER_FORMAT_REGEXP = /^(\d+)?\.((\d+)(-(\d+))?)?$/;
@ -23,7 +23,7 @@ function formatNumber(
// Convert strings to numbers // Convert strings to numbers
value = typeof value === 'string' && NumberWrapper.isNumeric(value) ? +value : value; value = typeof value === 'string' && NumberWrapper.isNumeric(value) ? +value : value;
if (typeof value !== 'number') { if (typeof value !== 'number') {
throw new InvalidPipeArgumentError(pipe, value); throw invalidPipeArgumentError(pipe, value);
} }
let minInt: number; let minInt: number;

View File

@ -7,7 +7,7 @@
*/ */
import {Pipe, PipeTransform} from '@angular/core'; import {Pipe, PipeTransform} from '@angular/core';
import {InvalidPipeArgumentError} from './invalid_pipe_argument_error'; import {invalidPipeArgumentError} from './invalid_pipe_argument_error';
/** /**
* @ngModule CommonModule * @ngModule CommonModule
@ -60,7 +60,7 @@ export class SlicePipe implements PipeTransform {
if (value == null) return value; if (value == null) return value;
if (!this.supports(value)) { if (!this.supports(value)) {
throw new InvalidPipeArgumentError(SlicePipe, value); throw invalidPipeArgumentError(SlicePipe, value);
} }
return value.slice(start, end); return value.slice(start, end);

View File

@ -59,7 +59,7 @@ export function main() {
() => { expect(() => pipe.transform(isoStringWithoutTime)).not.toThrow(); }); () => { expect(() => pipe.transform(isoStringWithoutTime)).not.toThrow(); });
it('should not support other objects', it('should not support other objects',
() => expect(() => pipe.transform({})).toThrowError(/Invalid argument/)); () => expect(() => pipe.transform({})).toThrowError(/InvalidPipeArgument/));
}); });
describe('transform', () => { describe('transform', () => {

View File

@ -13,8 +13,8 @@ import 'reflect-metadata';
import * as ts from 'typescript'; import * as ts from 'typescript';
import * as tsc from '@angular/tsc-wrapped'; import * as tsc from '@angular/tsc-wrapped';
import {isSyntaxError} from '@angular/compiler';
import {SyntaxError} from '@angular/compiler';
import {CodeGenerator} from './codegen'; import {CodeGenerator} from './codegen';
function codegen( function codegen(
@ -29,7 +29,7 @@ export function main(
const cliOptions = new tsc.NgcCliOptions(args); const cliOptions = new tsc.NgcCliOptions(args);
return tsc.main(project, cliOptions, codegen).then(() => 0).catch(e => { return tsc.main(project, cliOptions, codegen).then(() => 0).catch(e => {
if (e instanceof tsc.UserError || e instanceof SyntaxError) { if (e instanceof tsc.UserError || isSyntaxError(e)) {
consoleError(e.message); consoleError(e.message);
return Promise.resolve(1); return Promise.resolve(1);
} else { } else {

View File

@ -62,5 +62,5 @@ export * from './src/style_compiler';
export * from './src/template_parser/template_parser'; export * from './src/template_parser/template_parser';
export {ViewCompiler} from './src/view_compiler/view_compiler'; export {ViewCompiler} from './src/view_compiler/view_compiler';
export {AnimationParser} from './src/animation/animation_parser'; export {AnimationParser} from './src/animation/animation_parser';
export {SyntaxError} from './src/util'; export {isSyntaxError, syntaxError} from './src/util';
// This file only reexports content of the `src` folder. Keep it that way. // This file only reexports content of the `src` folder. Keep it that way.

View File

@ -9,7 +9,7 @@
import {Attribute, Component, ContentChild, ContentChildren, Directive, Host, HostBinding, HostListener, Inject, Injectable, Input, NgModule, Optional, Output, Pipe, Self, SkipSelf, ViewChild, ViewChildren, animate, group, keyframes, sequence, state, style, transition, trigger} from '@angular/core'; import {Attribute, Component, ContentChild, ContentChildren, Directive, Host, HostBinding, HostListener, Inject, Injectable, Input, NgModule, Optional, Output, Pipe, Self, SkipSelf, ViewChild, ViewChildren, animate, group, keyframes, sequence, state, style, transition, trigger} from '@angular/core';
import {ReflectorReader} from '../private_import_core'; import {ReflectorReader} from '../private_import_core';
import {SyntaxError} from '../util'; import {syntaxError} from '../util';
import {StaticSymbol} from './static_symbol'; import {StaticSymbol} from './static_symbol';
import {StaticSymbolResolver} from './static_symbol_resolver'; import {StaticSymbolResolver} from './static_symbol_resolver';
@ -566,7 +566,7 @@ export class StaticReflector implements ReflectorReader {
if (e.fileName) { if (e.fileName) {
throw positionalError(message, e.fileName, e.line, e.column); throw positionalError(message, e.fileName, e.line, e.column);
} }
throw new SyntaxError(message); throw syntaxError(message);
} }
} }

View File

@ -8,7 +8,6 @@
import * as chars from '../chars'; import * as chars from '../chars';
import {BaseError} from '../facade/errors';
import {isPresent} from '../facade/lang'; import {isPresent} from '../facade/lang';
export enum CssTokenType { export enum CssTokenType {
@ -42,7 +41,7 @@ export enum CssLexerMode {
} }
export class LexedCssResult { export class LexedCssResult {
constructor(public error: CssScannerError, public token: CssToken) {} constructor(public error: Error, public token: CssToken) {}
} }
export function generateErrorMessage( export function generateErrorMessage(
@ -86,16 +85,22 @@ export class CssLexer {
} }
} }
export class CssScannerError extends BaseError { export function cssScannerError(token: CssToken, message: string): Error {
public rawMessage: string; const error = Error('CssParseError: ' + message);
public message: string; (error as any)[ERROR_RAW_MESSAGE] = message;
(error as any)[ERROR_TOKEN] = token;
return error;
}
constructor(public token: CssToken, message: string) { const ERROR_TOKEN = 'ngToken';
super('Css Parse Error: ' + message); const ERROR_RAW_MESSAGE = 'ngRawMessage';
this.rawMessage = message;
}
toString(): string { return this.message; } export function getRawMessage(error: Error): string {
return (error as any)[ERROR_RAW_MESSAGE];
}
export function getToken(error: Error): CssToken {
return (error as any)[ERROR_TOKEN];
} }
function _trackWhitespace(mode: CssLexerMode) { function _trackWhitespace(mode: CssLexerMode) {
@ -122,7 +127,7 @@ export class CssScanner {
/** @internal */ /** @internal */
_currentMode: CssLexerMode = CssLexerMode.BLOCK; _currentMode: CssLexerMode = CssLexerMode.BLOCK;
/** @internal */ /** @internal */
_currentError: CssScannerError = null; _currentError: Error = null;
constructor(public input: string, private _trackComments: boolean = false) { constructor(public input: string, private _trackComments: boolean = false) {
this.length = this.input.length; this.length = this.input.length;
@ -221,7 +226,7 @@ export class CssScanner {
// mode so that the parser can recover... // mode so that the parser can recover...
this.setMode(mode); this.setMode(mode);
let error: CssScannerError = null; let error: Error = null;
if (!isMatchingType || (isPresent(value) && value != next.strValue)) { if (!isMatchingType || (isPresent(value) && value != next.strValue)) {
let errorMessage = let errorMessage =
CssTokenType[next.type] + ' does not match expected ' + CssTokenType[type] + ' value'; CssTokenType[next.type] + ' does not match expected ' + CssTokenType[type] + ' value';
@ -230,7 +235,7 @@ export class CssScanner {
errorMessage += ' ("' + next.strValue + '" should match "' + value + '")'; errorMessage += ' ("' + next.strValue + '" should match "' + value + '")';
} }
error = new CssScannerError( error = cssScannerError(
next, generateErrorMessage( next, generateErrorMessage(
this.input, errorMessage, next.strValue, previousIndex, previousLine, this.input, errorMessage, next.strValue, previousIndex, previousLine,
previousColumn)); previousColumn));
@ -483,7 +488,7 @@ export class CssScanner {
if (!doNotAdvance) { if (!doNotAdvance) {
this.advance(); this.advance();
} }
this._currentError = new CssScannerError(invalidToken, errorMessage); this._currentError = cssScannerError(invalidToken, errorMessage);
return invalidToken; return invalidToken;
} }
} }

View File

@ -11,7 +11,7 @@ import {isPresent} from '../facade/lang';
import {ParseError, ParseLocation, ParseSourceFile, ParseSourceSpan} from '../parse_util'; import {ParseError, ParseLocation, ParseSourceFile, ParseSourceSpan} from '../parse_util';
import {BlockType, CssAst, CssAtRulePredicateAst, CssBlockAst, CssBlockDefinitionRuleAst, CssBlockRuleAst, CssDefinitionAst, CssInlineRuleAst, CssKeyframeDefinitionAst, CssKeyframeRuleAst, CssMediaQueryRuleAst, CssPseudoSelectorAst, CssRuleAst, CssSelectorAst, CssSelectorRuleAst, CssSimpleSelectorAst, CssStyleSheetAst, CssStyleValueAst, CssStylesBlockAst, CssUnknownRuleAst, CssUnknownTokenListAst, mergeTokens} from './css_ast'; import {BlockType, CssAst, CssAtRulePredicateAst, CssBlockAst, CssBlockDefinitionRuleAst, CssBlockRuleAst, CssDefinitionAst, CssInlineRuleAst, CssKeyframeDefinitionAst, CssKeyframeRuleAst, CssMediaQueryRuleAst, CssPseudoSelectorAst, CssRuleAst, CssSelectorAst, CssSelectorRuleAst, CssSimpleSelectorAst, CssStyleSheetAst, CssStyleValueAst, CssStylesBlockAst, CssUnknownRuleAst, CssUnknownTokenListAst, mergeTokens} from './css_ast';
import {CssLexer, CssLexerMode, CssScanner, CssToken, CssTokenType, generateErrorMessage, isNewline} from './css_lexer'; import {CssLexer, CssLexerMode, CssScanner, CssToken, CssTokenType, generateErrorMessage, getRawMessage, isNewline} from './css_lexer';
const SPACE_OPERATOR = ' '; const SPACE_OPERATOR = ' ';
@ -378,7 +378,7 @@ export class CssParser {
const token = output.token; const token = output.token;
const error = output.error; const error = output.error;
if (isPresent(error)) { if (isPresent(error)) {
this._error(error.rawMessage, token); this._error(getRawMessage(error), token);
} }
this._lastToken = token; this._lastToken = token;
return token; return token;
@ -393,7 +393,7 @@ export class CssParser {
const token = output.token; const token = output.token;
const error = output.error; const error = output.error;
if (isPresent(error)) { if (isPresent(error)) {
this._error(error.rawMessage, token); this._error(getRawMessage(error), token);
} }
this._lastToken = token; this._lastToken = token;
return token; return token;

View File

@ -19,7 +19,7 @@ import {ResourceLoader} from './resource_loader';
import {extractStyleUrls, isStyleUrlResolvable} from './style_url_resolver'; import {extractStyleUrls, isStyleUrlResolvable} from './style_url_resolver';
import {PreparsedElementType, preparseElement} from './template_parser/template_preparser'; import {PreparsedElementType, preparseElement} from './template_parser/template_preparser';
import {UrlResolver} from './url_resolver'; import {UrlResolver} from './url_resolver';
import {SyncAsyncResult, SyntaxError} from './util'; import {SyncAsyncResult, syntaxError} from './util';
export interface PrenormalizedTemplateMetadata { export interface PrenormalizedTemplateMetadata {
componentType: any; componentType: any;
@ -67,19 +67,19 @@ export class DirectiveNormalizer {
let normalizedTemplateAsync: Promise<CompileTemplateMetadata>; let normalizedTemplateAsync: Promise<CompileTemplateMetadata>;
if (prenormData.template != null) { if (prenormData.template != null) {
if (typeof prenormData.template !== 'string') { if (typeof prenormData.template !== 'string') {
throw new SyntaxError( throw syntaxError(
`The template specified for component ${stringify(prenormData.componentType)} is not a string`); `The template specified for component ${stringify(prenormData.componentType)} is not a string`);
} }
normalizedTemplateSync = this.normalizeTemplateSync(prenormData); normalizedTemplateSync = this.normalizeTemplateSync(prenormData);
normalizedTemplateAsync = Promise.resolve(normalizedTemplateSync); normalizedTemplateAsync = Promise.resolve(normalizedTemplateSync);
} else if (prenormData.templateUrl) { } else if (prenormData.templateUrl) {
if (typeof prenormData.templateUrl !== 'string') { if (typeof prenormData.templateUrl !== 'string') {
throw new SyntaxError( throw syntaxError(
`The templateUrl specified for component ${stringify(prenormData.componentType)} is not a string`); `The templateUrl specified for component ${stringify(prenormData.componentType)} is not a string`);
} }
normalizedTemplateAsync = this.normalizeTemplateAsync(prenormData); normalizedTemplateAsync = this.normalizeTemplateAsync(prenormData);
} else { } else {
throw new SyntaxError( throw syntaxError(
`No template specified for component ${stringify(prenormData.componentType)}`); `No template specified for component ${stringify(prenormData.componentType)}`);
} }
@ -113,7 +113,7 @@ export class DirectiveNormalizer {
template, stringify(prenomData.componentType), true, interpolationConfig); template, stringify(prenomData.componentType), true, interpolationConfig);
if (rootNodesAndErrors.errors.length > 0) { if (rootNodesAndErrors.errors.length > 0) {
const errorString = rootNodesAndErrors.errors.join('\n'); const errorString = rootNodesAndErrors.errors.join('\n');
throw new SyntaxError(`Template parse errors:\n${errorString}`); throw syntaxError(`Template parse errors:\n${errorString}`);
} }
const templateMetadataStyles = this.normalizeStylesheet(new CompileStylesheetMetadata({ const templateMetadataStyles = this.normalizeStylesheet(new CompileStylesheetMetadata({

View File

@ -20,11 +20,11 @@ import {CompilerInjectable} from './injectable';
import {hasLifecycleHook} from './lifecycle_reflector'; import {hasLifecycleHook} from './lifecycle_reflector';
import {NgModuleResolver} from './ng_module_resolver'; import {NgModuleResolver} from './ng_module_resolver';
import {PipeResolver} from './pipe_resolver'; import {PipeResolver} from './pipe_resolver';
import {ComponentStillLoadingError, LIFECYCLE_HOOKS_VALUES, ReflectorReader, reflector} from './private_import_core'; import {ERROR_COMPONENT_TYPE, LIFECYCLE_HOOKS_VALUES, ReflectorReader, reflector} from './private_import_core';
import {ElementSchemaRegistry} from './schema/element_schema_registry'; import {ElementSchemaRegistry} from './schema/element_schema_registry';
import {SummaryResolver} from './summary_resolver'; import {SummaryResolver} from './summary_resolver';
import {getUrlScheme} from './url_resolver'; import {getUrlScheme} from './url_resolver';
import {MODULE_SUFFIX, SyntaxError, ValueTransformer, visitValue} from './util'; import {MODULE_SUFFIX, ValueTransformer, syntaxError, visitValue} from './util';
export type ErrorCollector = (error: any, type?: any) => void; export type ErrorCollector = (error: any, type?: any) => void;
export const ERROR_COLLECTOR_TOKEN = new InjectionToken('ErrorCollector'); export const ERROR_COLLECTOR_TOKEN = new InjectionToken('ErrorCollector');
@ -254,7 +254,7 @@ export class CompileMetadataResolver {
return null; return null;
} else { } else {
if (isSync) { if (isSync) {
this._reportError(new ComponentStillLoadingError(directiveType), directiveType); this._reportError(componentStillLoadingError(directiveType), directiveType);
return null; return null;
} }
return templateMeta.asyncResult.then(createDirectiveMetadata); return templateMeta.asyncResult.then(createDirectiveMetadata);
@ -328,7 +328,7 @@ export class CompileMetadataResolver {
// Directive // Directive
if (!selector) { if (!selector) {
this._reportError( this._reportError(
new SyntaxError( syntaxError(
`Directive ${stringifyType(directiveType)} has no selector, please add it!`), `Directive ${stringifyType(directiveType)} has no selector, please add it!`),
directiveType); directiveType);
selector = 'error'; selector = 'error';
@ -383,7 +383,7 @@ export class CompileMetadataResolver {
const dirMeta = this._directiveCache.get(directiveType); const dirMeta = this._directiveCache.get(directiveType);
if (!dirMeta) { if (!dirMeta) {
this._reportError( this._reportError(
new SyntaxError( syntaxError(
`Illegal state: getDirectiveMetadata can only be called after loadNgModuleDirectiveAndPipeMetadata for a module that declares it. Directive ${stringifyType(directiveType)}.`), `Illegal state: getDirectiveMetadata can only be called after loadNgModuleDirectiveAndPipeMetadata for a module that declares it. Directive ${stringifyType(directiveType)}.`),
directiveType); directiveType);
} }
@ -395,7 +395,7 @@ export class CompileMetadataResolver {
<cpl.CompileDirectiveSummary>this._loadSummary(dirType, cpl.CompileSummaryKind.Directive); <cpl.CompileDirectiveSummary>this._loadSummary(dirType, cpl.CompileSummaryKind.Directive);
if (!dirSummary) { if (!dirSummary) {
this._reportError( this._reportError(
new SyntaxError( syntaxError(
`Illegal state: Could not load the summary for directive ${stringifyType(dirType)}.`), `Illegal state: Could not load the summary for directive ${stringifyType(dirType)}.`),
dirType); dirType);
} }
@ -478,7 +478,7 @@ export class CompileMetadataResolver {
const importedModuleSummary = this.getNgModuleSummary(importedModuleType); const importedModuleSummary = this.getNgModuleSummary(importedModuleType);
if (!importedModuleSummary) { if (!importedModuleSummary) {
this._reportError( this._reportError(
new SyntaxError( syntaxError(
`Unexpected ${this._getTypeDescriptor(importedType)} '${stringifyType(importedType)}' imported by the module '${stringifyType(moduleType)}'`), `Unexpected ${this._getTypeDescriptor(importedType)} '${stringifyType(importedType)}' imported by the module '${stringifyType(moduleType)}'`),
moduleType); moduleType);
return; return;
@ -486,7 +486,7 @@ export class CompileMetadataResolver {
importedModules.push(importedModuleSummary); importedModules.push(importedModuleSummary);
} else { } else {
this._reportError( this._reportError(
new SyntaxError( syntaxError(
`Unexpected value '${stringifyType(importedType)}' imported by the module '${stringifyType(moduleType)}'`), `Unexpected value '${stringifyType(importedType)}' imported by the module '${stringifyType(moduleType)}'`),
moduleType); moduleType);
return; return;
@ -498,7 +498,7 @@ export class CompileMetadataResolver {
flattenAndDedupeArray(meta.exports).forEach((exportedType) => { flattenAndDedupeArray(meta.exports).forEach((exportedType) => {
if (!isValidType(exportedType)) { if (!isValidType(exportedType)) {
this._reportError( this._reportError(
new SyntaxError( syntaxError(
`Unexpected value '${stringifyType(exportedType)}' exported by the module '${stringifyType(moduleType)}'`), `Unexpected value '${stringifyType(exportedType)}' exported by the module '${stringifyType(moduleType)}'`),
moduleType); moduleType);
return; return;
@ -519,7 +519,7 @@ export class CompileMetadataResolver {
flattenAndDedupeArray(meta.declarations).forEach((declaredType) => { flattenAndDedupeArray(meta.declarations).forEach((declaredType) => {
if (!isValidType(declaredType)) { if (!isValidType(declaredType)) {
this._reportError( this._reportError(
new SyntaxError( syntaxError(
`Unexpected value '${stringifyType(declaredType)}' declared by the module '${stringifyType(moduleType)}'`), `Unexpected value '${stringifyType(declaredType)}' declared by the module '${stringifyType(moduleType)}'`),
moduleType); moduleType);
return; return;
@ -536,7 +536,7 @@ export class CompileMetadataResolver {
this._addTypeToModule(declaredType, moduleType); this._addTypeToModule(declaredType, moduleType);
} else { } else {
this._reportError( this._reportError(
new SyntaxError( syntaxError(
`Unexpected ${this._getTypeDescriptor(declaredType)} '${stringifyType(declaredType)}' declared by the module '${stringifyType(moduleType)}'`), `Unexpected ${this._getTypeDescriptor(declaredType)} '${stringifyType(declaredType)}' declared by the module '${stringifyType(moduleType)}'`),
moduleType); moduleType);
return; return;
@ -555,7 +555,7 @@ export class CompileMetadataResolver {
transitiveModule.addExportedPipe(exportedId); transitiveModule.addExportedPipe(exportedId);
} else { } else {
this._reportError( this._reportError(
new SyntaxError( syntaxError(
`Can't export ${this._getTypeDescriptor(exportedId.reference)} ${stringifyType(exportedId.reference)} from ${stringifyType(moduleType)} as it was neither declared nor imported!`), `Can't export ${this._getTypeDescriptor(exportedId.reference)} ${stringifyType(exportedId.reference)} from ${stringifyType(moduleType)} as it was neither declared nor imported!`),
moduleType); moduleType);
} }
@ -578,7 +578,7 @@ export class CompileMetadataResolver {
flattenAndDedupeArray(meta.bootstrap).forEach(type => { flattenAndDedupeArray(meta.bootstrap).forEach(type => {
if (!isValidType(type)) { if (!isValidType(type)) {
this._reportError( this._reportError(
new SyntaxError( syntaxError(
`Unexpected value '${stringifyType(type)}' used in the bootstrap property of module '${stringifyType(moduleType)}'`), `Unexpected value '${stringifyType(type)}' used in the bootstrap property of module '${stringifyType(moduleType)}'`),
moduleType); moduleType);
return; return;
@ -642,7 +642,7 @@ export class CompileMetadataResolver {
const oldModule = this._ngModuleOfTypes.get(type); const oldModule = this._ngModuleOfTypes.get(type);
if (oldModule && oldModule !== moduleType) { if (oldModule && oldModule !== moduleType) {
this._reportError( this._reportError(
new SyntaxError( syntaxError(
`Type ${stringifyType(type)} is part of the declarations of 2 modules: ${stringifyType(oldModule)} and ${stringifyType(moduleType)}! ` + `Type ${stringifyType(type)} is part of the declarations of 2 modules: ${stringifyType(oldModule)} and ${stringifyType(moduleType)}! ` +
`Please consider moving ${stringifyType(type)} to a higher module that imports ${stringifyType(oldModule)} and ${stringifyType(moduleType)}. ` + `Please consider moving ${stringifyType(type)} to a higher module that imports ${stringifyType(oldModule)} and ${stringifyType(moduleType)}. ` +
`You can also create a new NgModule that exports and includes ${stringifyType(type)} then import that NgModule in ${stringifyType(oldModule)} and ${stringifyType(moduleType)}.`), `You can also create a new NgModule that exports and includes ${stringifyType(type)} then import that NgModule in ${stringifyType(oldModule)} and ${stringifyType(moduleType)}.`),
@ -738,7 +738,7 @@ export class CompileMetadataResolver {
const pipeMeta = this._pipeCache.get(pipeType); const pipeMeta = this._pipeCache.get(pipeType);
if (!pipeMeta) { if (!pipeMeta) {
this._reportError( this._reportError(
new SyntaxError( syntaxError(
`Illegal state: getPipeMetadata can only be called after loadNgModuleDirectiveAndPipeMetadata for a module that declares it. Pipe ${stringifyType(pipeType)}.`), `Illegal state: getPipeMetadata can only be called after loadNgModuleDirectiveAndPipeMetadata for a module that declares it. Pipe ${stringifyType(pipeType)}.`),
pipeType); pipeType);
} }
@ -750,7 +750,7 @@ export class CompileMetadataResolver {
<cpl.CompilePipeSummary>this._loadSummary(pipeType, cpl.CompileSummaryKind.Pipe); <cpl.CompilePipeSummary>this._loadSummary(pipeType, cpl.CompileSummaryKind.Pipe);
if (!pipeSummary) { if (!pipeSummary) {
this._reportError( this._reportError(
new SyntaxError( syntaxError(
`Illegal state: Could not load the summary for pipe ${stringifyType(pipeType)}.`), `Illegal state: Could not load the summary for pipe ${stringifyType(pipeType)}.`),
pipeType); pipeType);
} }
@ -833,7 +833,7 @@ export class CompileMetadataResolver {
const depsTokens = const depsTokens =
dependenciesMetadata.map((dep) => dep ? stringifyType(dep.token) : '?').join(', '); dependenciesMetadata.map((dep) => dep ? stringifyType(dep.token) : '?').join(', ');
this._reportError( this._reportError(
new SyntaxError( syntaxError(
`Can't resolve all parameters for ${stringifyType(typeOrFunc)}: (${depsTokens}).`), `Can't resolve all parameters for ${stringifyType(typeOrFunc)}: (${depsTokens}).`),
typeOrFunc); typeOrFunc);
} }
@ -868,7 +868,7 @@ export class CompileMetadataResolver {
} else if (isValidType(provider)) { } else if (isValidType(provider)) {
providerMeta = new cpl.ProviderMeta(provider, {useClass: provider}); providerMeta = new cpl.ProviderMeta(provider, {useClass: provider});
} else if (provider === void 0) { } else if (provider === void 0) {
this._reportError(new SyntaxError( this._reportError(syntaxError(
`Encountered undefined provider! Usually this means you have a circular dependencies (might be caused by using 'barrel' index.ts files.`)); `Encountered undefined provider! Usually this means you have a circular dependencies (might be caused by using 'barrel' index.ts files.`));
} else { } else {
const providersInfo = const providersInfo =
@ -886,7 +886,7 @@ export class CompileMetadataResolver {
[])) []))
.join(', '); .join(', ');
this._reportError( this._reportError(
new SyntaxError( syntaxError(
`Invalid ${debugInfo ? debugInfo : 'provider'} - only instances of Provider and Type are allowed, got: [${providersInfo}]`), `Invalid ${debugInfo ? debugInfo : 'provider'} - only instances of Provider and Type are allowed, got: [${providersInfo}]`),
type); type);
} }
@ -902,7 +902,7 @@ export class CompileMetadataResolver {
private _validateProvider(provider: any): void { private _validateProvider(provider: any): void {
if (provider.hasOwnProperty('useClass') && provider.useClass == null) { if (provider.hasOwnProperty('useClass') && provider.useClass == null) {
this._reportError(new SyntaxError( this._reportError(syntaxError(
`Invalid provider for ${stringifyType(provider.provide)}. useClass cannot be ${provider.useClass}. `Invalid provider for ${stringifyType(provider.provide)}. useClass cannot be ${provider.useClass}.
Usually it happens when: Usually it happens when:
1. There's a circular dependency (might be caused by using index.ts (barrel) files). 1. There's a circular dependency (might be caused by using index.ts (barrel) files).
@ -917,13 +917,13 @@ export class CompileMetadataResolver {
if (provider.useFactory || provider.useExisting || provider.useClass) { if (provider.useFactory || provider.useExisting || provider.useClass) {
this._reportError( this._reportError(
new SyntaxError(`The ANALYZE_FOR_ENTRY_COMPONENTS token only supports useValue!`), type); syntaxError(`The ANALYZE_FOR_ENTRY_COMPONENTS token only supports useValue!`), type);
return []; return [];
} }
if (!provider.multi) { if (!provider.multi) {
this._reportError( this._reportError(
new SyntaxError(`The ANALYZE_FOR_ENTRY_COMPONENTS token only supports 'multi = true'!`), syntaxError(`The ANALYZE_FOR_ENTRY_COMPONENTS token only supports 'multi = true'!`),
type); type);
return []; return [];
} }
@ -1006,7 +1006,7 @@ export class CompileMetadataResolver {
} else { } else {
if (!q.selector) { if (!q.selector) {
this._reportError( this._reportError(
new SyntaxError( syntaxError(
`Can't construct a query for the property "${propertyName}" of "${stringifyType(typeOrFunc)}" since the query selector wasn't defined.`), `Can't construct a query for the property "${propertyName}" of "${stringifyType(typeOrFunc)}" since the query selector wasn't defined.`),
typeOrFunc); typeOrFunc);
} }
@ -1074,7 +1074,7 @@ export function componentModuleUrl(
const scheme = getUrlScheme(moduleId); const scheme = getUrlScheme(moduleId);
return scheme ? moduleId : `package:${moduleId}${MODULE_SUFFIX}`; return scheme ? moduleId : `package:${moduleId}${MODULE_SUFFIX}`;
} else if (moduleId !== null && moduleId !== void 0) { } else if (moduleId !== null && moduleId !== void 0) {
throw new SyntaxError( throw syntaxError(
`moduleId should be a string in "${stringifyType(type)}". See https://goo.gl/wIDDiL for more information.\n` + `moduleId should be a string in "${stringifyType(type)}". See https://goo.gl/wIDDiL for more information.\n` +
`If you're using Webpack you should inline the template and the styles, see https://goo.gl/X2J8zc.`); `If you're using Webpack you should inline the template and the styles, see https://goo.gl/X2J8zc.`);
} }
@ -1099,3 +1099,14 @@ function stringifyType(type: any): string {
return stringify(type); return stringify(type);
} }
} }
/**
* Indicates that a component is still being loaded in a synchronous compile.
*/
function componentStillLoadingError(compType: Type<any>) {
debugger;
const error =
Error(`Can't compile synchronously as ${stringify(compType)} is still being loaded!`);
(error as any)[ERROR_COMPONENT_TYPE] = compType;
return error;
}

View File

@ -58,6 +58,7 @@ export const AnimationStyles: typeof r.AnimationStyles = r.AnimationStyles;
export const ANY_STATE = r.ANY_STATE; export const ANY_STATE = r.ANY_STATE;
export const DEFAULT_STATE = r.DEFAULT_STATE; export const DEFAULT_STATE = r.DEFAULT_STATE;
export const EMPTY_STATE = r.EMPTY_STATE; export const EMPTY_STATE = r.EMPTY_STATE;
export const ERROR_COMPONENT_TYPE = r.ERROR_COMPONENT_TYPE;
export const FILL_STYLE_FLAG = r.FILL_STYLE_FLAG; export const FILL_STYLE_FLAG = r.FILL_STYLE_FLAG;
export const prepareFinalAnimationStyles: typeof r.prepareFinalAnimationStyles = export const prepareFinalAnimationStyles: typeof r.prepareFinalAnimationStyles =
r.prepareFinalAnimationStyles; r.prepareFinalAnimationStyles;
@ -68,9 +69,6 @@ export const collectAndResolveStyles: typeof r.collectAndResolveStyles = r.colle
export const renderStyles: typeof r.renderStyles = r.renderStyles; export const renderStyles: typeof r.renderStyles = r.renderStyles;
export type ViewMetadata = typeof r._ViewMetadata; export type ViewMetadata = typeof r._ViewMetadata;
export const ViewMetadata: typeof r.ViewMetadata = r.ViewMetadata; export const ViewMetadata: typeof r.ViewMetadata = r.ViewMetadata;
export type ComponentStillLoadingError = typeof r._ComponentStillLoadingError;
export const ComponentStillLoadingError: typeof r.ComponentStillLoadingError =
r.ComponentStillLoadingError;
export const AnimationTransition: typeof r.AnimationTransition = r.AnimationTransition; export const AnimationTransition: typeof r.AnimationTransition = r.AnimationTransition;
export type SetterFn = typeof r._SetterFn; export type SetterFn = typeof r._SetterFn;
export type GetterFn = typeof r._GetterFn; export type GetterFn = typeof r._GetterFn;

View File

@ -24,7 +24,7 @@ import {ProviderElementContext, ProviderViewContext} from '../provider_analyzer'
import {ElementSchemaRegistry} from '../schema/element_schema_registry'; import {ElementSchemaRegistry} from '../schema/element_schema_registry';
import {CssSelector, SelectorMatcher} from '../selector'; import {CssSelector, SelectorMatcher} from '../selector';
import {isStyleUrlResolvable} from '../style_url_resolver'; import {isStyleUrlResolvable} from '../style_url_resolver';
import {SyntaxError} from '../util'; import {syntaxError} from '../util';
import {BindingParser, BoundProperty} from './binding_parser'; import {BindingParser, BoundProperty} from './binding_parser';
import {AttrAst, BoundDirectivePropertyAst, BoundElementPropertyAst, BoundEventAst, BoundTextAst, DirectiveAst, ElementAst, EmbeddedTemplateAst, NgContentAst, PropertyBindingType, ReferenceAst, TemplateAst, TemplateAstVisitor, TextAst, VariableAst, templateVisitAll} from './template_ast'; import {AttrAst, BoundDirectivePropertyAst, BoundElementPropertyAst, BoundEventAst, BoundTextAst, DirectiveAst, ElementAst, EmbeddedTemplateAst, NgContentAst, PropertyBindingType, ReferenceAst, TemplateAst, TemplateAstVisitor, TextAst, VariableAst, templateVisitAll} from './template_ast';
import {PreparsedElementType, preparseElement} from './template_preparser'; import {PreparsedElementType, preparseElement} from './template_preparser';
@ -99,7 +99,7 @@ export class TemplateParser {
if (errors.length > 0) { if (errors.length > 0) {
const errorString = errors.join('\n'); const errorString = errors.join('\n');
throw new SyntaxError(`Template parse errors:\n${errorString}`); throw syntaxError(`Template parse errors:\n${errorString}`);
} }
return result.templateAst; return result.templateAst;

View File

@ -6,7 +6,6 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {BaseError} from './facade/errors';
import {isPrimitive, isStrictStringMap} from './facade/lang'; import {isPrimitive, isStrictStringMap} from './facade/lang';
export const MODULE_SUFFIX = ''; export const MODULE_SUFFIX = '';
@ -79,4 +78,14 @@ export class SyncAsyncResult<T> {
} }
} }
export class SyntaxError extends BaseError {} export function syntaxError(msg: string): Error {
const error = Error(msg);
(error as any)[ERROR_SYNTAX_ERROR] = true;
return error;
}
const ERROR_SYNTAX_ERROR = 'ngSyntaxError';
export function isSyntaxError(error: Error): boolean {
return (error as any)[ERROR_SYNTAX_ERROR];
}

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {StaticReflector, StaticSymbol, StaticSymbolCache, StaticSymbolResolver, StaticSymbolResolverHost, SyntaxError} from '@angular/compiler'; import {StaticReflector, StaticSymbol, StaticSymbolCache, StaticSymbolResolver, StaticSymbolResolverHost} from '@angular/compiler';
import {HostListener, Inject, animate, group, keyframes, sequence, state, style, transition, trigger} from '@angular/core'; import {HostListener, Inject, animate, group, keyframes, sequence, state, style, transition, trigger} from '@angular/core';
import {MockStaticSymbolResolverHost, MockSummaryResolver} from './static_symbol_resolver_spec'; import {MockStaticSymbolResolverHost, MockSummaryResolver} from './static_symbol_resolver_spec';
@ -354,7 +354,6 @@ describe('StaticReflector', () => {
'Could not resolve ./does-not-exist.component relative to /tmp/src/function-reference.ts' 'Could not resolve ./does-not-exist.component relative to /tmp/src/function-reference.ts'
}))) })))
.toThrowError( .toThrowError(
SyntaxError,
'Error encountered resolving symbol values statically. Could not resolve ./does-not-exist.component relative to /tmp/src/function-reference.ts, resolving symbol AppModule in /tmp/src/function-reference.ts'); 'Error encountered resolving symbol values statically. Could not resolve ./does-not-exist.component relative to /tmp/src/function-reference.ts, resolving symbol AppModule in /tmp/src/function-reference.ts');
}); });

View File

@ -7,7 +7,7 @@
*/ */
import {describe, expect, it} from '../../../core/testing/testing_internal'; import {describe, expect, it} from '../../../core/testing/testing_internal';
import {CssLexer, CssLexerMode, CssScannerError, CssToken, CssTokenType} from '../../src/css_parser/css_lexer'; import {CssLexer, CssLexerMode, CssToken, CssTokenType, cssScannerError, getRawMessage, getToken} from '../../src/css_parser/css_lexer';
import {isPresent} from '../../src/facade/lang'; import {isPresent} from '../../src/facade/lang';
export function main() { export function main() {
@ -22,7 +22,7 @@ export function main() {
while (output != null) { while (output != null) {
const error = output.error; const error = output.error;
if (isPresent(error)) { if (isPresent(error)) {
throw new CssScannerError(error.token, error.rawMessage); throw cssScannerError(getToken(error), getRawMessage(error));
} }
tokens.push(output.token); tokens.push(output.token);
output = scanner.scan(); output = scanner.scan();
@ -279,7 +279,7 @@ export function main() {
try { try {
tokenize(cssCode, false, CssLexerMode.STYLE_BLOCK); tokenize(cssCode, false, CssLexerMode.STYLE_BLOCK);
} catch (e) { } catch (e) {
capturedMessage = e.rawMessage; capturedMessage = getRawMessage(e);
} }
expect(capturedMessage).toMatch(/Unexpected character \[\>\] at column 0:7 in expression/g); expect(capturedMessage).toMatch(/Unexpected character \[\>\] at column 0:7 in expression/g);
@ -288,7 +288,7 @@ export function main() {
try { try {
tokenize(cssCode, false, CssLexerMode.SELECTOR); tokenize(cssCode, false, CssLexerMode.SELECTOR);
} catch (e) { } catch (e) {
capturedMessage = e.rawMessage; capturedMessage = getRawMessage(e);
} }
expect(capturedMessage).toEqual(null); expect(capturedMessage).toEqual(null);

View File

@ -5,7 +5,6 @@
* Use of this source code is governed by an MIT-style license that can be * 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 * found in the LICENSE file at https://angular.io/license
*/ */
import {SyntaxError} from '@angular/compiler';
import {CompileDirectiveMetadata, CompileStylesheetMetadata, CompileTemplateMetadata, CompileTypeMetadata} from '@angular/compiler/src/compile_metadata'; import {CompileDirectiveMetadata, CompileStylesheetMetadata, CompileTemplateMetadata, CompileTypeMetadata} from '@angular/compiler/src/compile_metadata';
import {CompilerConfig} from '@angular/compiler/src/config'; import {CompilerConfig} from '@angular/compiler/src/config';
import {DirectiveNormalizer} from '@angular/compiler/src/directive_normalizer'; import {DirectiveNormalizer} from '@angular/compiler/src/directive_normalizer';
@ -31,23 +30,23 @@ export function main() {
expect(() => normalizer.normalizeTemplate({ expect(() => normalizer.normalizeTemplate({
componentType: SomeComp, componentType: SomeComp,
moduleUrl: SOME_MODULE_URL, moduleUrl: SOME_MODULE_URL,
})).toThrowError(SyntaxError, 'No template specified for component SomeComp'); })).toThrowError('No template specified for component SomeComp');
})); }));
it('should throw if template is not a string', it('should throw if template is not a string',
inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => { inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => {
expect( expect(() => normalizer.normalizeTemplate({
() => normalizer.normalizeTemplate( componentType: SomeComp,
{componentType: SomeComp, moduleUrl: SOME_MODULE_URL, template: <any>{}})) moduleUrl: SOME_MODULE_URL,
.toThrowError( template: <any>{}
SyntaxError, 'The template specified for component SomeComp is not a string'); })).toThrowError('The template specified for component SomeComp is not a string');
})); }));
it('should throw if templateUrl is not a string', it('should throw if templateUrl is not a string',
inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => { inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => {
expect( expect(() => normalizer.normalizeTemplate({
() => normalizer.normalizeTemplate( componentType: SomeComp,
{componentType: SomeComp, moduleUrl: SOME_MODULE_URL, templateUrl: <any>{}})) moduleUrl: SOME_MODULE_URL,
.toThrowError( templateUrl: <any>{}
SyntaxError, 'The templateUrl specified for component SomeComp is not a string'); })).toThrowError('The templateUrl specified for component SomeComp is not a string');
})); }));
}); });

View File

@ -15,7 +15,6 @@ import {identifierName} from '../src/compile_metadata';
import {stringify} from '../src/facade/lang'; import {stringify} from '../src/facade/lang';
import {CompileMetadataResolver} from '../src/metadata_resolver'; import {CompileMetadataResolver} from '../src/metadata_resolver';
import {ResourceLoader} from '../src/resource_loader'; import {ResourceLoader} from '../src/resource_loader';
import {SyntaxError} from '../src/util';
import {MockResourceLoader} from '../testing/resource_loader_mock'; import {MockResourceLoader} from '../testing/resource_loader_mock';
import {MalformedStylesComponent} from './metadata_resolver_fixture'; import {MalformedStylesComponent} from './metadata_resolver_fixture';
@ -34,9 +33,8 @@ export function main() {
} }
expect(() => resolver.getDirectiveMetadata(ComponentWithEverythingInline)) expect(() => resolver.getDirectiveMetadata(ComponentWithEverythingInline))
.toThrowError(SyntaxError, /Illegal state/); .toThrowError(/Illegal state/);
expect(() => resolver.getPipeMetadata(SomePipe)) expect(() => resolver.getPipeMetadata(SomePipe)).toThrowError(/Illegal state/);
.toThrowError(SyntaxError, /Illegal state/);
})); }));
it('should read metadata in sync for components with inline resources', it('should read metadata in sync for components with inline resources',
@ -122,7 +120,7 @@ export function main() {
expect(() => resolver.loadNgModuleDirectiveAndPipeMetadata(SomeModule, true)) expect(() => resolver.loadNgModuleDirectiveAndPipeMetadata(SomeModule, true))
.toThrowError( .toThrowError(
SyntaxError, `moduleId should be a string in "ComponentWithInvalidModuleId". See` + `moduleId should be a string in "ComponentWithInvalidModuleId". See` +
` https://goo.gl/wIDDiL for more information.\n` + ` https://goo.gl/wIDDiL for more information.\n` +
`If you're using Webpack you should inline the template and the styles, see` + `If you're using Webpack you should inline the template and the styles, see` +
` https://goo.gl/X2J8zc.`); ` https://goo.gl/X2J8zc.`);
@ -146,7 +144,7 @@ export function main() {
} }
expect(() => resolver.loadNgModuleDirectiveAndPipeMetadata(SomeModule, true)) expect(() => resolver.loadNgModuleDirectiveAndPipeMetadata(SomeModule, true))
.toThrowError(SyntaxError, `Can't resolve all parameters for MyBrokenComp1: (?).`); .toThrowError(`Can't resolve all parameters for MyBrokenComp1: (?).`);
})); }));
it('should throw with descriptive error message when a directive is passed to imports', it('should throw with descriptive error message when a directive is passed to imports',
inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => { inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => {
@ -156,7 +154,6 @@ export function main() {
expect( expect(
() => resolver.loadNgModuleDirectiveAndPipeMetadata(ModuleWithImportedComponent, true)) () => resolver.loadNgModuleDirectiveAndPipeMetadata(ModuleWithImportedComponent, true))
.toThrowError( .toThrowError(
SyntaxError,
`Unexpected directive 'ComponentWithoutModuleId' imported by the module 'ModuleWithImportedComponent'`); `Unexpected directive 'ComponentWithoutModuleId' imported by the module 'ModuleWithImportedComponent'`);
})); }));
@ -170,7 +167,6 @@ export function main() {
} }
expect(() => resolver.loadNgModuleDirectiveAndPipeMetadata(ModuleWithImportedPipe, true)) expect(() => resolver.loadNgModuleDirectiveAndPipeMetadata(ModuleWithImportedPipe, true))
.toThrowError( .toThrowError(
SyntaxError,
`Unexpected pipe 'SomePipe' imported by the module 'ModuleWithImportedPipe'`); `Unexpected pipe 'SomePipe' imported by the module 'ModuleWithImportedPipe'`);
})); }));
@ -184,7 +180,6 @@ export function main() {
} }
expect(() => resolver.loadNgModuleDirectiveAndPipeMetadata(ModuleWithDeclaredModule, true)) expect(() => resolver.loadNgModuleDirectiveAndPipeMetadata(ModuleWithDeclaredModule, true))
.toThrowError( .toThrowError(
SyntaxError,
`Unexpected module 'SomeModule' declared by the module 'ModuleWithDeclaredModule'`); `Unexpected module 'SomeModule' declared by the module 'ModuleWithDeclaredModule'`);
})); }));
@ -195,7 +190,6 @@ export function main() {
} }
expect(() => resolver.loadNgModuleDirectiveAndPipeMetadata(ModuleWithNullDeclared, true)) expect(() => resolver.loadNgModuleDirectiveAndPipeMetadata(ModuleWithNullDeclared, true))
.toThrowError( .toThrowError(
SyntaxError,
`Unexpected value 'null' declared by the module 'ModuleWithNullDeclared'`); `Unexpected value 'null' declared by the module 'ModuleWithNullDeclared'`);
})); }));
@ -206,7 +200,6 @@ export function main() {
} }
expect(() => resolver.loadNgModuleDirectiveAndPipeMetadata(ModuleWithNullImported, true)) expect(() => resolver.loadNgModuleDirectiveAndPipeMetadata(ModuleWithNullImported, true))
.toThrowError( .toThrowError(
SyntaxError,
`Unexpected value 'null' imported by the module 'ModuleWithNullImported'`); `Unexpected value 'null' imported by the module 'ModuleWithNullImported'`);
})); }));
@ -218,8 +211,7 @@ export function main() {
} }
expect(() => resolver.loadNgModuleDirectiveAndPipeMetadata(SomeModule, true)) expect(() => resolver.loadNgModuleDirectiveAndPipeMetadata(SomeModule, true))
.toThrowError( .toThrowError(`Can't resolve all parameters for NonAnnotatedService: (?).`);
SyntaxError, `Can't resolve all parameters for NonAnnotatedService: (?).`);
})); }));
it('should throw with descriptive error message when encounter invalid provider', it('should throw with descriptive error message when encounter invalid provider',
@ -229,8 +221,7 @@ export function main() {
} }
expect(() => resolver.loadNgModuleDirectiveAndPipeMetadata(SomeModule, true)) expect(() => resolver.loadNgModuleDirectiveAndPipeMetadata(SomeModule, true))
.toThrowError( .toThrowError(/Invalid provider for SimpleService. useClass cannot be undefined./);
SyntaxError, /Invalid provider for SimpleService. useClass cannot be undefined./);
})); }));
it('should throw with descriptive error message when provider is undefined', it('should throw with descriptive error message when provider is undefined',
@ -240,7 +231,7 @@ export function main() {
} }
expect(() => resolver.loadNgModuleDirectiveAndPipeMetadata(SomeModule, true)) expect(() => resolver.loadNgModuleDirectiveAndPipeMetadata(SomeModule, true))
.toThrowError(SyntaxError, /Encountered undefined provider!/); .toThrowError(/Encountered undefined provider!/);
})); }));
it('should throw with descriptive error message when one of providers is not present', it('should throw with descriptive error message when one of providers is not present',
@ -251,7 +242,6 @@ export function main() {
expect(() => resolver.loadNgModuleDirectiveAndPipeMetadata(SomeModule, true)) expect(() => resolver.loadNgModuleDirectiveAndPipeMetadata(SomeModule, true))
.toThrowError( .toThrowError(
SyntaxError,
`Invalid providers for "MyBrokenComp3" - only instances of Provider and Type are allowed, got: [SimpleService, ?null?, ...]`); `Invalid providers for "MyBrokenComp3" - only instances of Provider and Type are allowed, got: [SimpleService, ?null?, ...]`);
})); }));
@ -263,7 +253,6 @@ export function main() {
expect(() => resolver.loadNgModuleDirectiveAndPipeMetadata(SomeModule, true)) expect(() => resolver.loadNgModuleDirectiveAndPipeMetadata(SomeModule, true))
.toThrowError( .toThrowError(
SyntaxError,
`Invalid viewProviders for "MyBrokenComp4" - only instances of Provider and Type are allowed, got: [?null?, ...]`); `Invalid viewProviders for "MyBrokenComp4" - only instances of Provider and Type are allowed, got: [?null?, ...]`);
})); }));
@ -278,13 +267,11 @@ export function main() {
expect(() => resolver.loadNgModuleDirectiveAndPipeMetadata(ModuleWithNullBootstrap, true)) expect(() => resolver.loadNgModuleDirectiveAndPipeMetadata(ModuleWithNullBootstrap, true))
.toThrowError( .toThrowError(
SyntaxError,
`Unexpected value 'null' used in the bootstrap property of module 'ModuleWithNullBootstrap'`); `Unexpected value 'null' used in the bootstrap property of module 'ModuleWithNullBootstrap'`);
expect( expect(
() => () =>
resolver.loadNgModuleDirectiveAndPipeMetadata(ModuleWithUndefinedBootstrap, true)) resolver.loadNgModuleDirectiveAndPipeMetadata(ModuleWithUndefinedBootstrap, true))
.toThrowError( .toThrowError(
SyntaxError,
`Unexpected value 'undefined' used in the bootstrap property of module 'ModuleWithUndefinedBootstrap'`); `Unexpected value 'undefined' used in the bootstrap property of module 'ModuleWithUndefinedBootstrap'`);
})); }));

View File

@ -5,7 +5,6 @@
* Use of this source code is governed by an MIT-style license that can be * 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 * found in the LICENSE file at https://angular.io/license
*/ */
import {SyntaxError} from '@angular/compiler';
import {CompileAnimationEntryMetadata, CompileDiDependencyMetadata, CompileDirectiveMetadata, CompileDirectiveSummary, CompilePipeMetadata, CompilePipeSummary, CompileProviderMetadata, CompileTemplateMetadata, CompileTokenMetadata, CompileTypeMetadata, tokenReference} from '@angular/compiler/src/compile_metadata'; import {CompileAnimationEntryMetadata, CompileDiDependencyMetadata, CompileDirectiveMetadata, CompileDirectiveSummary, CompilePipeMetadata, CompilePipeSummary, CompileProviderMetadata, CompileTemplateMetadata, CompileTokenMetadata, CompileTypeMetadata, tokenReference} from '@angular/compiler/src/compile_metadata';
import {DomElementSchemaRegistry} from '@angular/compiler/src/schema/dom_element_schema_registry'; import {DomElementSchemaRegistry} from '@angular/compiler/src/schema/dom_element_schema_registry';
import {ElementSchemaRegistry} from '@angular/compiler/src/schema/element_schema_registry'; import {ElementSchemaRegistry} from '@angular/compiler/src/schema/element_schema_registry';
@ -374,7 +373,7 @@ export function main() {
describe('errors', () => { describe('errors', () => {
it('should throw error when binding to an unknown property', () => { it('should throw error when binding to an unknown property', () => {
expect(() => parse('<my-component [invalidProp]="bar"></my-component>', [])) expect(() => parse('<my-component [invalidProp]="bar"></my-component>', []))
.toThrowError(SyntaxError, `Template parse errors: .toThrowError(`Template parse errors:
Can't bind to 'invalidProp' since it isn't a known property of 'my-component'. Can't bind to 'invalidProp' since it isn't a known property of 'my-component'.
1. If 'my-component' is an Angular component and it has 'invalidProp' input, then verify that it is part of this module. 1. If 'my-component' is an Angular component and it has 'invalidProp' input, then verify that it is part of this module.
2. If 'my-component' is a Web Component then add "CUSTOM_ELEMENTS_SCHEMA" to the '@NgModule.schemas' of this component to suppress this message. 2. If 'my-component' is a Web Component then add "CUSTOM_ELEMENTS_SCHEMA" to the '@NgModule.schemas' of this component to suppress this message.
@ -382,16 +381,14 @@ Can't bind to 'invalidProp' since it isn't a known property of 'my-component'.
}); });
it('should throw error when binding to an unknown element w/o bindings', () => { it('should throw error when binding to an unknown element w/o bindings', () => {
expect(() => parse('<unknown></unknown>', [])) expect(() => parse('<unknown></unknown>', [])).toThrowError(`Template parse errors:
.toThrowError(SyntaxError, `Template parse errors:
'unknown' is not a known element: 'unknown' is not a known element:
1. If 'unknown' is an Angular component, then verify that it is part of this module. 1. If 'unknown' is an Angular component, then verify that it is part of this module.
2. If 'unknown' is a Web Component then add "CUSTOM_ELEMENTS_SCHEMA" to the '@NgModule.schemas' of this component to suppress this message. ("[ERROR ->]<unknown></unknown>"): TestComp@0:0`); 2. If 'unknown' is a Web Component then add "CUSTOM_ELEMENTS_SCHEMA" to the '@NgModule.schemas' of this component to suppress this message. ("[ERROR ->]<unknown></unknown>"): TestComp@0:0`);
}); });
it('should throw error when binding to an unknown custom element w/o bindings', () => { it('should throw error when binding to an unknown custom element w/o bindings', () => {
expect(() => parse('<un-known></un-known>', [])) expect(() => parse('<un-known></un-known>', [])).toThrowError(`Template parse errors:
.toThrowError(SyntaxError, `Template parse errors:
'un-known' is not a known element: 'un-known' is not a known element:
1. If 'un-known' is an Angular component, then verify that it is part of this module. 1. If 'un-known' is an Angular component, then verify that it is part of this module.
2. If 'un-known' is a Web Component then add "CUSTOM_ELEMENTS_SCHEMA" to the '@NgModule.schemas' of this component to suppress this message. ("[ERROR ->]<un-known></un-known>"): TestComp@0:0`); 2. If 'un-known' is a Web Component then add "CUSTOM_ELEMENTS_SCHEMA" to the '@NgModule.schemas' of this component to suppress this message. ("[ERROR ->]<un-known></un-known>"): TestComp@0:0`);
@ -399,13 +396,13 @@ Can't bind to 'invalidProp' since it isn't a known property of 'my-component'.
it('should throw error when binding to an invalid property', () => { it('should throw error when binding to an invalid property', () => {
expect(() => parse('<my-component [onEvent]="bar"></my-component>', [])) expect(() => parse('<my-component [onEvent]="bar"></my-component>', []))
.toThrowError(SyntaxError, `Template parse errors: .toThrowError(`Template parse errors:
Binding to property 'onEvent' is disallowed for security reasons ("<my-component [ERROR ->][onEvent]="bar"></my-component>"): TestComp@0:14`); Binding to property 'onEvent' is disallowed for security reasons ("<my-component [ERROR ->][onEvent]="bar"></my-component>"): TestComp@0:14`);
}); });
it('should throw error when binding to an invalid attribute', () => { it('should throw error when binding to an invalid attribute', () => {
expect(() => parse('<my-component [attr.onEvent]="bar"></my-component>', [])) expect(() => parse('<my-component [attr.onEvent]="bar"></my-component>', []))
.toThrowError(SyntaxError, `Template parse errors: .toThrowError(`Template parse errors:
Binding to attribute 'onEvent' is disallowed for security reasons ("<my-component [ERROR ->][attr.onEvent]="bar"></my-component>"): TestComp@0:14`); Binding to attribute 'onEvent' is disallowed for security reasons ("<my-component [ERROR ->][attr.onEvent]="bar"></my-component>"): TestComp@0:14`);
}); });
}); });
@ -447,7 +444,6 @@ Binding to attribute 'onEvent' is disallowed for security reasons ("<my-componen
() => { () => {
expect(() => { parse('<div @someAnimation="value2">', [], [], []); }) expect(() => { parse('<div @someAnimation="value2">', [], [], []); })
.toThrowError( .toThrowError(
SyntaxError,
/Assigning animation triggers via @prop="exp" attributes with an expression is invalid. Use property bindings \(e.g. \[@prop\]="exp"\) or use an attribute without a value \(e.g. @prop\) instead. \("<div \[ERROR ->\]@someAnimation="value2">"\): TestComp@0:5/); /Assigning animation triggers via @prop="exp" attributes with an expression is invalid. Use property bindings \(e.g. \[@prop\]="exp"\) or use an attribute without a value \(e.g. @prop\) instead. \("<div \[ERROR ->\]@someAnimation="value2">"\): TestComp@0:5/);
}); });
@ -482,7 +478,6 @@ Binding to attribute 'onEvent' is disallowed for security reasons ("<my-componen
expect(() => { parse('<broken></broken>', [dirA]); }) expect(() => { parse('<broken></broken>', [dirA]); })
.toThrowError( .toThrowError(
SyntaxError,
`Template parse errors:\nValue of the host property binding "class.foo" needs to be a string representing an expression but got "null" (object) ("[ERROR ->]<broken></broken>"): TestComp@0:0, Directive DirA`); `Template parse errors:\nValue of the host property binding "class.foo" needs to be a string representing an expression but got "null" (object) ("[ERROR ->]<broken></broken>"): TestComp@0:0, Directive DirA`);
}); });
@ -498,7 +493,6 @@ Binding to attribute 'onEvent' is disallowed for security reasons ("<my-componen
expect(() => { parse('<broken></broken>', [dirA]); }) expect(() => { parse('<broken></broken>', [dirA]); })
.toThrowError( .toThrowError(
SyntaxError,
`Template parse errors:\nValue of the host listener "click" needs to be a string representing an expression but got "null" (object) ("[ERROR ->]<broken></broken>"): TestComp@0:0, Directive DirA`); `Template parse errors:\nValue of the host listener "click" needs to be a string representing an expression but got "null" (object) ("[ERROR ->]<broken></broken>"): TestComp@0:0, Directive DirA`);
}); });
@ -962,7 +956,7 @@ Binding to attribute 'onEvent' is disallowed for security reasons ("<my-componen
const dirB = createDir('[dirB]', {providers: [provider1]}); const dirB = createDir('[dirB]', {providers: [provider1]});
expect(() => parse('<div dirA dirB>', [dirA, dirB])) expect(() => parse('<div dirA dirB>', [dirA, dirB]))
.toThrowError( .toThrowError(
SyntaxError, `Template parse errors:\n` + `Template parse errors:\n` +
`Mixing multi and non multi provider is not possible for token service0 ("[ERROR ->]<div dirA dirB>"): TestComp@0:0`); `Mixing multi and non multi provider is not possible for token service0 ("[ERROR ->]<div dirA dirB>"): TestComp@0:0`);
}); });
@ -1055,7 +1049,6 @@ Binding to attribute 'onEvent' is disallowed for security reasons ("<my-componen
const dirA = createDir('[dirA]', {deps: ['self:provider0']}); const dirA = createDir('[dirA]', {deps: ['self:provider0']});
expect(() => parse('<div dirA></div>', [dirA])) expect(() => parse('<div dirA></div>', [dirA]))
.toThrowError( .toThrowError(
SyntaxError,
'Template parse errors:\nNo provider for provider0 ("[ERROR ->]<div dirA></div>"): TestComp@0:0'); 'Template parse errors:\nNo provider for provider0 ("[ERROR ->]<div dirA></div>"): TestComp@0:0');
}); });
@ -1070,7 +1063,6 @@ Binding to attribute 'onEvent' is disallowed for security reasons ("<my-componen
const dirA = createDir('[dirA]', {deps: ['host:provider0']}); const dirA = createDir('[dirA]', {deps: ['host:provider0']});
expect(() => parse('<div dirA></div>', [dirA])) expect(() => parse('<div dirA></div>', [dirA]))
.toThrowError( .toThrowError(
SyntaxError,
'Template parse errors:\nNo provider for provider0 ("[ERROR ->]<div dirA></div>"): TestComp@0:0'); 'Template parse errors:\nNo provider for provider0 ("[ERROR ->]<div dirA></div>"): TestComp@0:0');
}); });
@ -1122,26 +1114,23 @@ Binding to attribute 'onEvent' is disallowed for security reasons ("<my-componen
}); });
it('should report references with values that dont match a directive as errors', () => { it('should report references with values that dont match a directive as errors', () => {
expect(() => parse('<div #a="dirA"></div>', [])) expect(() => parse('<div #a="dirA"></div>', [])).toThrowError(`Template parse errors:
.toThrowError(SyntaxError, `Template parse errors:
There is no directive with "exportAs" set to "dirA" ("<div [ERROR ->]#a="dirA"></div>"): TestComp@0:5`); There is no directive with "exportAs" set to "dirA" ("<div [ERROR ->]#a="dirA"></div>"): TestComp@0:5`);
}); });
it('should report invalid reference names', () => { it('should report invalid reference names', () => {
expect(() => parse('<div #a-b></div>', [])) expect(() => parse('<div #a-b></div>', [])).toThrowError(`Template parse errors:
.toThrowError(SyntaxError, `Template parse errors:
"-" is not allowed in reference names ("<div [ERROR ->]#a-b></div>"): TestComp@0:5`); "-" is not allowed in reference names ("<div [ERROR ->]#a-b></div>"): TestComp@0:5`);
}); });
it('should report variables as errors', () => { it('should report variables as errors', () => {
expect(() => parse('<div let-a></div>', [])) expect(() => parse('<div let-a></div>', [])).toThrowError(`Template parse errors:
.toThrowError(SyntaxError, `Template parse errors:
"let-" is only supported on template elements. ("<div [ERROR ->]let-a></div>"): TestComp@0:5`); "let-" is only supported on template elements. ("<div [ERROR ->]let-a></div>"): TestComp@0:5`);
}); });
it('should report duplicate reference names', () => { it('should report duplicate reference names', () => {
expect(() => parse('<div #a></div><div #a></div>', [])) expect(() => parse('<div #a></div><div #a></div>', []))
.toThrowError(SyntaxError, `Template parse errors: .toThrowError(`Template parse errors:
Reference "#a" is defined several times ("<div #a></div><div [ERROR ->]#a></div>"): TestComp@0:19`); Reference "#a" is defined several times ("<div #a></div><div [ERROR ->]#a></div>"): TestComp@0:19`);
}); });
@ -1513,7 +1502,7 @@ Reference "#a" is defined several times ("<div #a></div><div [ERROR ->]#a></div>
it('should report when ng-content has non WS content', () => { it('should report when ng-content has non WS content', () => {
expect(() => parse('<ng-content>content</ng-content>', [])) expect(() => parse('<ng-content>content</ng-content>', []))
.toThrowError( .toThrowError(
SyntaxError, `Template parse errors:\n` + `Template parse errors:\n` +
`<ng-content> element cannot have content. ("[ERROR ->]<ng-content>content</ng-content>"): TestComp@0:0`); `<ng-content> element cannot have content. ("[ERROR ->]<ng-content>content</ng-content>"): TestComp@0:0`);
}); });
@ -1524,20 +1513,18 @@ Reference "#a" is defined several times ("<div #a></div><div [ERROR ->]#a></div>
() => { expect(() => parse('<template template="ngIf">', [])).not.toThrowError(); }); () => { expect(() => parse('<template template="ngIf">', [])).not.toThrowError(); });
it('should report when mutliple *attrs are used on the same element', () => { it('should report when mutliple *attrs are used on the same element', () => {
expect(() => parse('<div *ngIf *ngFor>', [])) expect(() => parse('<div *ngIf *ngFor>', [])).toThrowError(`Template parse errors:
.toThrowError(SyntaxError, `Template parse errors:
Can't have multiple template bindings on one element. Use only one attribute named 'template' or prefixed with * ("<div *ngIf [ERROR ->]*ngFor>"): TestComp@0:11`); Can't have multiple template bindings on one element. Use only one attribute named 'template' or prefixed with * ("<div *ngIf [ERROR ->]*ngFor>"): TestComp@0:11`);
}); });
it('should report when mix of template and *attrs are used on the same element', () => { it('should report when mix of template and *attrs are used on the same element', () => {
expect(() => parse('<span template="ngIf" *ngFor>', [])) expect(() => parse('<span template="ngIf" *ngFor>', []))
.toThrowError(SyntaxError, `Template parse errors: .toThrowError(`Template parse errors:
Can't have multiple template bindings on one element. Use only one attribute named 'template' or prefixed with * ("<span template="ngIf" [ERROR ->]*ngFor>"): TestComp@0:22`); Can't have multiple template bindings on one element. Use only one attribute named 'template' or prefixed with * ("<span template="ngIf" [ERROR ->]*ngFor>"): TestComp@0:22`);
}); });
it('should report invalid property names', () => { it('should report invalid property names', () => {
expect(() => parse('<div [invalidProp]></div>', [])) expect(() => parse('<div [invalidProp]></div>', [])).toThrowError(`Template parse errors:
.toThrowError(SyntaxError, `Template parse errors:
Can't bind to 'invalidProp' since it isn't a known property of 'div'. ("<div [ERROR ->][invalidProp]></div>"): TestComp@0:5`); Can't bind to 'invalidProp' since it isn't a known property of 'div'. ("<div [ERROR ->][invalidProp]></div>"): TestComp@0:5`);
}); });
@ -1550,13 +1537,12 @@ Can't bind to 'invalidProp' since it isn't a known property of 'div'. ("<div [ER
host: {'[invalidProp]': 'someProp'} host: {'[invalidProp]': 'someProp'}
}) })
.toSummary(); .toSummary();
expect(() => parse('<div></div>', [dirA])).toThrowError(SyntaxError, `Template parse errors: expect(() => parse('<div></div>', [dirA])).toThrowError(`Template parse errors:
Can't bind to 'invalidProp' since it isn't a known property of 'div'. ("[ERROR ->]<div></div>"): TestComp@0:0, Directive DirA`); Can't bind to 'invalidProp' since it isn't a known property of 'div'. ("[ERROR ->]<div></div>"): TestComp@0:0, Directive DirA`);
}); });
it('should report errors in expressions', () => { it('should report errors in expressions', () => {
expect(() => parse('<div [prop]="a b"></div>', [])) expect(() => parse('<div [prop]="a b"></div>', [])).toThrowError(`Template parse errors:
.toThrowError(SyntaxError, `Template parse errors:
Parser Error: Unexpected token 'b' at column 3 in [a b] in TestComp@0:5 ("<div [ERROR ->][prop]="a b"></div>"): TestComp@0:5`); Parser Error: Unexpected token 'b' at column 3 in [a b] in TestComp@0:5 ("<div [ERROR ->][prop]="a b"></div>"): TestComp@0:5`);
}); });
@ -1594,7 +1580,7 @@ Parser Error: Unexpected token 'b' at column 3 in [a b] in TestComp@0:5 ("<div [
.toSummary(); .toSummary();
expect(() => parse('<div>', [dirB, dirA])) expect(() => parse('<div>', [dirB, dirA]))
.toThrowError( .toThrowError(
SyntaxError, `Template parse errors:\n` + `Template parse errors:\n` +
`More than one component matched on this element.\n` + `More than one component matched on this element.\n` +
`Make sure that only one component's selector can match a given element.\n` + `Make sure that only one component's selector can match a given element.\n` +
`Conflicting components: DirB,DirA ("[ERROR ->]<div>"): TestComp@0:0`); `Conflicting components: DirB,DirA ("[ERROR ->]<div>"): TestComp@0:0`);
@ -1612,7 +1598,7 @@ Parser Error: Unexpected token 'b' at column 3 in [a b] in TestComp@0:5 ("<div [
}) })
.toSummary(); .toSummary();
expect(() => parse('<template [a]="b" (e)="f"></template>', [dirA])) expect(() => parse('<template [a]="b" (e)="f"></template>', [dirA]))
.toThrowError(SyntaxError, `Template parse errors: .toThrowError(`Template parse errors:
Event binding e not emitted by any directive on an embedded template. Make sure that the event name is spelled correctly and all directives are listed in the "@NgModule.declarations". ("<template [a]="b" [ERROR ->](e)="f"></template>"): TestComp@0:18 Event binding e not emitted by any directive on an embedded template. Make sure that the event name is spelled correctly and all directives are listed in the "@NgModule.declarations". ("<template [a]="b" [ERROR ->](e)="f"></template>"): TestComp@0:18
Components on an embedded template: DirA ("[ERROR ->]<template [a]="b" (e)="f"></template>"): TestComp@0:0 Components on an embedded template: DirA ("[ERROR ->]<template [a]="b" (e)="f"></template>"): TestComp@0:0
Property binding a not used by any directive on an embedded template. Make sure that the property name is spelled correctly and all directives are listed in the "@NgModule.declarations". ("[ERROR ->]<template [a]="b" (e)="f"></template>"): TestComp@0:0`); Property binding a not used by any directive on an embedded template. Make sure that the property name is spelled correctly and all directives are listed in the "@NgModule.declarations". ("[ERROR ->]<template [a]="b" (e)="f"></template>"): TestComp@0:0`);
@ -1628,8 +1614,7 @@ Property binding a not used by any directive on an embedded template. Make sure
template: new CompileTemplateMetadata({ngContentSelectors: []}) template: new CompileTemplateMetadata({ngContentSelectors: []})
}) })
.toSummary(); .toSummary();
expect(() => parse('<div *a="b"></div>', [dirA])) expect(() => parse('<div *a="b"></div>', [dirA])).toThrowError(`Template parse errors:
.toThrowError(SyntaxError, `Template parse errors:
Components on an embedded template: DirA ("[ERROR ->]<div *a="b"></div>"): TestComp@0:0 Components on an embedded template: DirA ("[ERROR ->]<div *a="b"></div>"): TestComp@0:0
Property binding a not used by any directive on an embedded template. Make sure that the property name is spelled correctly and all directives are listed in the "@NgModule.declarations". ("[ERROR ->]<div *a="b"></div>"): TestComp@0:0`); Property binding a not used by any directive on an embedded template. Make sure that the property name is spelled correctly and all directives are listed in the "@NgModule.declarations". ("[ERROR ->]<div *a="b"></div>"): TestComp@0:0`);
}); });
@ -1889,7 +1874,7 @@ Property binding a not used by any directive on an embedded template. Make sure
}); });
it('should report pipes as error that have not been defined as dependencies', () => { it('should report pipes as error that have not been defined as dependencies', () => {
expect(() => parse('{{a | test}}', [])).toThrowError(SyntaxError, `Template parse errors: expect(() => parse('{{a | test}}', [])).toThrowError(`Template parse errors:
The pipe 'test' could not be found ("{{[ERROR ->]a | test}}"): TestComp@0:2`); The pipe 'test' could not be found ("{{[ERROR ->]a | test}}"): TestComp@0:2`);
}); });

View File

@ -20,7 +20,7 @@ import * as constants from './change_detection/constants';
import * as console from './console'; import * as console from './console';
import * as debug from './debug/debug_renderer'; import * as debug from './debug/debug_renderer';
import * as reflective_provider from './di/reflective_provider'; import * as reflective_provider from './di/reflective_provider';
import {ComponentStillLoadingError} from './linker/compiler'; import {ERROR_COMPONENT_TYPE} from './errors';
import * as component_factory from './linker/component_factory'; import * as component_factory from './linker/component_factory';
import * as component_factory_resolver from './linker/component_factory_resolver'; import * as component_factory_resolver from './linker/component_factory_resolver';
import * as debug_context from './linker/debug_context'; import * as debug_context from './linker/debug_context';
@ -104,12 +104,11 @@ export const __core_private__: {
DEFAULT_STATE: typeof DEFAULT_STATE_, DEFAULT_STATE: typeof DEFAULT_STATE_,
EMPTY_STATE: typeof EMPTY_STATE_, EMPTY_STATE: typeof EMPTY_STATE_,
FILL_STYLE_FLAG: typeof FILL_STYLE_FLAG_, FILL_STYLE_FLAG: typeof FILL_STYLE_FLAG_,
_ComponentStillLoadingError?: ComponentStillLoadingError,
ComponentStillLoadingError: typeof ComponentStillLoadingError,
isPromise: typeof isPromise, isPromise: typeof isPromise,
isObservable: typeof isObservable, isObservable: typeof isObservable,
AnimationTransition: typeof AnimationTransition AnimationTransition: typeof AnimationTransition
view_utils: typeof view_utils, view_utils: typeof view_utils,
ERROR_COMPONENT_TYPE: typeof ERROR_COMPONENT_TYPE,
} = { } = {
isDefaultChangeDetectionStrategy: constants.isDefaultChangeDetectionStrategy, isDefaultChangeDetectionStrategy: constants.isDefaultChangeDetectionStrategy,
ChangeDetectorStatus: constants.ChangeDetectorStatus, ChangeDetectorStatus: constants.ChangeDetectorStatus,
@ -156,8 +155,8 @@ export const __core_private__: {
DEFAULT_STATE: DEFAULT_STATE_, DEFAULT_STATE: DEFAULT_STATE_,
EMPTY_STATE: EMPTY_STATE_, EMPTY_STATE: EMPTY_STATE_,
FILL_STYLE_FLAG: FILL_STYLE_FLAG_, FILL_STYLE_FLAG: FILL_STYLE_FLAG_,
ComponentStillLoadingError: ComponentStillLoadingError,
isPromise: isPromise, isPromise: isPromise,
isObservable: isObservable, isObservable: isObservable,
AnimationTransition: AnimationTransition AnimationTransition: AnimationTransition,
ERROR_COMPONENT_TYPE: ERROR_COMPONENT_TYPE
}; };

View File

@ -6,7 +6,8 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {BaseError, WrappedError} from '../facade/errors'; import {wrappedError} from '../error_handler';
import {ERROR_ORIGINAL_ERROR, getOriginalError} from '../errors';
import {stringify} from '../facade/lang'; import {stringify} from '../facade/lang';
import {Type} from '../type'; import {Type} from '../type';
@ -35,38 +36,31 @@ function constructResolvingPath(keys: any[]): string {
return ''; return '';
} }
export interface InjectionError extends Error {
/**
* Base class for all errors arising from misconfigured providers.
* @stable
*/
export class AbstractProviderError extends BaseError {
/** @internal */
message: string;
/** @internal */
keys: ReflectiveKey[]; keys: ReflectiveKey[];
/** @internal */
injectors: ReflectiveInjector[]; injectors: ReflectiveInjector[];
constructResolvingMessage: (this: InjectionError) => string;
addKey(injector: ReflectiveInjector, key: ReflectiveKey): void;
}
/** @internal */ function injectionError(
constructResolvingMessage: Function; injector: ReflectiveInjector, key: ReflectiveKey,
constructResolvingMessage: (this: InjectionError) => string,
originalError?: Error): InjectionError {
const error = (originalError ? wrappedError('', originalError) : Error()) as InjectionError;
error.addKey = addKey;
error.keys = [key];
error.injectors = [injector];
error.constructResolvingMessage = constructResolvingMessage;
error.message = error.constructResolvingMessage();
(error as any)[ERROR_ORIGINAL_ERROR] = originalError;
return error;
}
constructor( function addKey(this: InjectionError, injector: ReflectiveInjector, key: ReflectiveKey): void {
injector: ReflectiveInjector, key: ReflectiveKey, constructResolvingMessage: Function) {
super('DI Error');
this.keys = [key];
this.injectors = [injector];
this.constructResolvingMessage = constructResolvingMessage;
this.message = this.constructResolvingMessage(this.keys);
}
addKey(injector: ReflectiveInjector, key: ReflectiveKey): void {
this.injectors.push(injector); this.injectors.push(injector);
this.keys.push(key); this.keys.push(key);
this.message = this.constructResolvingMessage(this.keys); this.message = this.constructResolvingMessage();
}
} }
/** /**
@ -82,15 +76,12 @@ export class AbstractProviderError extends BaseError {
* *
* expect(() => Injector.resolveAndCreate([A])).toThrowError(); * expect(() => Injector.resolveAndCreate([A])).toThrowError();
* ``` * ```
* @stable
*/ */
export class NoProviderError extends AbstractProviderError { export function noProviderError(injector: ReflectiveInjector, key: ReflectiveKey): InjectionError {
constructor(injector: ReflectiveInjector, key: ReflectiveKey) { return injectionError(injector, key, function(this: InjectionError) {
super(injector, key, function(keys: any[]) { const first = stringify(this.keys[0].token);
const first = stringify(keys[0].token); return `No provider for ${first}!${constructResolvingPath(this.keys)}`;
return `No provider for ${first}!${constructResolvingPath(keys)}`;
}); });
}
} }
/** /**
@ -108,14 +99,12 @@ export class NoProviderError extends AbstractProviderError {
* ``` * ```
* *
* Retrieving `A` or `B` throws a `CyclicDependencyError` as the graph above cannot be constructed. * Retrieving `A` or `B` throws a `CyclicDependencyError` as the graph above cannot be constructed.
* @stable
*/ */
export class CyclicDependencyError extends AbstractProviderError { export function cyclicDependencyError(
constructor(injector: ReflectiveInjector, key: ReflectiveKey) { injector: ReflectiveInjector, key: ReflectiveKey): InjectionError {
super(injector, key, function(keys: any[]) { return injectionError(injector, key, function(this: InjectionError) {
return `Cannot instantiate cyclic dependency!${constructResolvingPath(keys)}`; return `Cannot instantiate cyclic dependency!${constructResolvingPath(this.keys)}`;
}); });
}
} }
/** /**
@ -143,34 +132,14 @@ export class CyclicDependencyError extends AbstractProviderError {
* expect(e.originalStack).toBeDefined(); * expect(e.originalStack).toBeDefined();
* } * }
* ``` * ```
* @stable
*/ */
export class InstantiationError extends WrappedError { export function instantiationError(
/** @internal */
keys: ReflectiveKey[];
/** @internal */
injectors: ReflectiveInjector[];
constructor(
injector: ReflectiveInjector, originalException: any, originalStack: any, injector: ReflectiveInjector, originalException: any, originalStack: any,
key: ReflectiveKey) { key: ReflectiveKey): InjectionError {
super('DI Error', originalException); return injectionError(injector, key, function(this: InjectionError) {
this.keys = [key];
this.injectors = [injector];
}
addKey(injector: ReflectiveInjector, key: ReflectiveKey): void {
this.injectors.push(injector);
this.keys.push(key);
}
get message(): string {
const first = stringify(this.keys[0].token); const first = stringify(this.keys[0].token);
return `${this.originalError.message}: Error during instantiation of ${first}!${constructResolvingPath(this.keys)}.`; return `${getOriginalError(this).message}: Error during instantiation of ${first}!${constructResolvingPath(this.keys)}.`;
} }, originalException);
get causeKey(): ReflectiveKey { return this.keys[0]; }
} }
/** /**
@ -182,12 +151,10 @@ export class InstantiationError extends WrappedError {
* ```typescript * ```typescript
* expect(() => Injector.resolveAndCreate(["not a type"])).toThrowError(); * expect(() => Injector.resolveAndCreate(["not a type"])).toThrowError();
* ``` * ```
* @stable
*/ */
export class InvalidProviderError extends BaseError { export function invalidProviderError(provider: any) {
constructor(provider: any) { return Error(
super(`Invalid provider - only instances of Provider and Type are allowed, got: ${provider}`); `Invalid provider - only instances of Provider and Type are allowed, got: ${provider}`);
}
} }
/** /**
@ -219,12 +186,7 @@ export class InvalidProviderError extends BaseError {
* ``` * ```
* @stable * @stable
*/ */
export class NoAnnotationError extends BaseError { export function noAnnotationError(typeOrFunc: Type<any>| Function, params: any[][]): Error {
constructor(typeOrFunc: Type<any>|Function, params: any[][]) {
super(NoAnnotationError._genMessage(typeOrFunc, params));
}
private static _genMessage(typeOrFunc: Type<any>|Function, params: any[][]) {
const signature: string[] = []; const signature: string[] = [];
for (let i = 0, ii = params.length; i < ii; i++) { for (let i = 0, ii = params.length; i < ii; i++) {
const parameter = params[i]; const parameter = params[i];
@ -234,11 +196,11 @@ export class NoAnnotationError extends BaseError {
signature.push(parameter.map(stringify).join(' ')); signature.push(parameter.map(stringify).join(' '));
} }
} }
return 'Cannot resolve all parameters for \'' + stringify(typeOrFunc) + '\'(' + return Error(
'Cannot resolve all parameters for \'' + stringify(typeOrFunc) + '\'(' +
signature.join(', ') + '). ' + signature.join(', ') + '). ' +
'Make sure that all the parameters are decorated with Inject or have valid type annotations and that \'' + 'Make sure that all the parameters are decorated with Inject or have valid type annotations and that \'' +
stringify(typeOrFunc) + '\' is decorated with Injectable.'; stringify(typeOrFunc) + '\' is decorated with Injectable.');
}
} }
/** /**
@ -255,8 +217,8 @@ export class NoAnnotationError extends BaseError {
* ``` * ```
* @stable * @stable
*/ */
export class OutOfBoundsError extends BaseError { export function outOfBoundsError(index: number) {
constructor(index: number) { super(`Index ${index} is out-of-bounds.`); } return Error(`Index ${index} is out-of-bounds.`);
} }
// TODO: add a working example after alpha38 is released // TODO: add a working example after alpha38 is released
@ -272,10 +234,7 @@ export class OutOfBoundsError extends BaseError {
* ])).toThrowError(); * ])).toThrowError();
* ``` * ```
*/ */
export class MixingMultiProvidersWithRegularProvidersError extends BaseError { export function mixingMultiProvidersWithRegularProvidersError(
constructor(provider1: any, provider2: any) { provider1: any, provider2: any): Error {
super( return Error(`Cannot mix multi providers and regular providers, got: ${provider1} ${provider2}`);
'Cannot mix multi providers and regular providers, got: ' + provider1.toString() + ' ' +
provider2.toString());
}
} }

View File

@ -9,7 +9,7 @@
import {Injector, THROW_IF_NOT_FOUND} from './injector'; import {Injector, THROW_IF_NOT_FOUND} from './injector';
import {Self, SkipSelf} from './metadata'; import {Self, SkipSelf} from './metadata';
import {Provider} from './provider'; import {Provider} from './provider';
import {AbstractProviderError, CyclicDependencyError, InstantiationError, NoProviderError, OutOfBoundsError} from './reflective_errors'; import {cyclicDependencyError, instantiationError, noProviderError, outOfBoundsError} from './reflective_errors';
import {ReflectiveKey} from './reflective_key'; import {ReflectiveKey} from './reflective_key';
import {ReflectiveDependency, ResolvedReflectiveFactory, ResolvedReflectiveProvider, resolveReflectiveProviders} from './reflective_provider'; import {ReflectiveDependency, ResolvedReflectiveFactory, ResolvedReflectiveProvider, resolveReflectiveProviders} from './reflective_provider';
@ -331,7 +331,7 @@ export class ReflectiveInjector_ implements ReflectiveInjector {
getProviderAtIndex(index: number): ResolvedReflectiveProvider { getProviderAtIndex(index: number): ResolvedReflectiveProvider {
if (index < 0 || index >= this._providers.length) { if (index < 0 || index >= this._providers.length) {
throw new OutOfBoundsError(index); throw outOfBoundsError(index);
} }
return this._providers[index]; return this._providers[index];
} }
@ -339,7 +339,7 @@ export class ReflectiveInjector_ implements ReflectiveInjector {
/** @internal */ /** @internal */
_new(provider: ResolvedReflectiveProvider): any { _new(provider: ResolvedReflectiveProvider): any {
if (this._constructionCounter++ > this._getMaxNumberOfObjects()) { if (this._constructionCounter++ > this._getMaxNumberOfObjects()) {
throw new CyclicDependencyError(this, provider.key); throw cyclicDependencyError(this, provider.key);
} }
return this._instantiateProvider(provider); return this._instantiateProvider(provider);
} }
@ -368,7 +368,7 @@ export class ReflectiveInjector_ implements ReflectiveInjector {
deps = deps =
ResolvedReflectiveFactory.dependencies.map(dep => this._getByReflectiveDependency(dep)); ResolvedReflectiveFactory.dependencies.map(dep => this._getByReflectiveDependency(dep));
} catch (e) { } catch (e) {
if (e instanceof AbstractProviderError || e instanceof InstantiationError) { if (e.addKey) {
e.addKey(this, provider.key); e.addKey(this, provider.key);
} }
throw e; throw e;
@ -378,7 +378,7 @@ export class ReflectiveInjector_ implements ReflectiveInjector {
try { try {
obj = factory(...deps); obj = factory(...deps);
} catch (e) { } catch (e) {
throw new InstantiationError(this, e, e.stack, provider.key); throw instantiationError(this, e, e.stack, provider.key);
} }
return obj; return obj;
@ -420,7 +420,7 @@ export class ReflectiveInjector_ implements ReflectiveInjector {
if (notFoundValue !== THROW_IF_NOT_FOUND) { if (notFoundValue !== THROW_IF_NOT_FOUND) {
return notFoundValue; return notFoundValue;
} else { } else {
throw new NoProviderError(this, key); throw noProviderError(this, key);
} }
} }

View File

@ -12,7 +12,7 @@ import {Type} from '../type';
import {resolveForwardRef} from './forward_ref'; import {resolveForwardRef} from './forward_ref';
import {Host, Inject, Optional, Self, SkipSelf} from './metadata'; import {Host, Inject, Optional, Self, SkipSelf} from './metadata';
import {ClassProvider, ExistingProvider, FactoryProvider, Provider, TypeProvider, ValueProvider} from './provider'; import {ClassProvider, ExistingProvider, FactoryProvider, Provider, TypeProvider, ValueProvider} from './provider';
import {InvalidProviderError, MixingMultiProvidersWithRegularProvidersError, NoAnnotationError} from './reflective_errors'; import {invalidProviderError, mixingMultiProvidersWithRegularProvidersError, noAnnotationError} from './reflective_errors';
import {ReflectiveKey} from './reflective_key'; import {ReflectiveKey} from './reflective_key';
@ -154,7 +154,7 @@ export function mergeResolvedReflectiveProviders(
const existing = normalizedProvidersMap.get(provider.key.id); const existing = normalizedProvidersMap.get(provider.key.id);
if (existing) { if (existing) {
if (provider.multiProvider !== existing.multiProvider) { if (provider.multiProvider !== existing.multiProvider) {
throw new MixingMultiProvidersWithRegularProvidersError(existing, provider); throw mixingMultiProvidersWithRegularProvidersError(existing, provider);
} }
if (provider.multiProvider) { if (provider.multiProvider) {
for (let j = 0; j < provider.resolvedFactories.length; j++) { for (let j = 0; j < provider.resolvedFactories.length; j++) {
@ -189,7 +189,7 @@ function _normalizeProviders(providers: Provider[], res: Provider[]): Provider[]
_normalizeProviders(b, res); _normalizeProviders(b, res);
} else { } else {
throw new InvalidProviderError(b); throw invalidProviderError(b);
} }
}); });
@ -211,7 +211,7 @@ function _dependenciesFor(typeOrFunc: any): ReflectiveDependency[] {
if (!params) return []; if (!params) return [];
if (params.some(p => p == null)) { if (params.some(p => p == null)) {
throw new NoAnnotationError(typeOrFunc, params); throw noAnnotationError(typeOrFunc, params);
} }
return params.map(p => _extractToken(typeOrFunc, p, params)); return params.map(p => _extractToken(typeOrFunc, p, params));
} }
@ -253,7 +253,7 @@ function _extractToken(
if (token != null) { if (token != null) {
return _createDependency(token, optional, visibility); return _createDependency(token, optional, visibility);
} else { } else {
throw new NoAnnotationError(typeOrFunc, params); throw noAnnotationError(typeOrFunc, params);
} }
} }

View File

@ -6,7 +6,8 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {WrappedError} from './facade/errors'; import {ERROR_ORIGINAL_ERROR, getDebugContext, getOriginalError} from './errors';
/** /**
* @whatItDoes Provides a hook for centralized exception handling. * @whatItDoes Provides a hook for centralized exception handling.
@ -48,12 +49,13 @@ export class ErrorHandler {
constructor(rethrowError: boolean = true) { this.rethrowError = rethrowError; } constructor(rethrowError: boolean = true) { this.rethrowError = rethrowError; }
handleError(error: any): void { handleError(error: any): void {
this._console.error(`EXCEPTION: ${this._extractMessage(error)}`);
if (error instanceof Error) {
const originalError = this._findOriginalError(error); const originalError = this._findOriginalError(error);
const originalStack = this._findOriginalStack(error); const originalStack = this._findOriginalStack(error);
const context = this._findContext(error); const context = this._findContext(error);
this._console.error(`EXCEPTION: ${this._extractMessage(error)}`);
if (originalError) { if (originalError) {
this._console.error(`ORIGINAL EXCEPTION: ${this._extractMessage(originalError)}`); this._console.error(`ORIGINAL EXCEPTION: ${this._extractMessage(originalError)}`);
} }
@ -67,6 +69,7 @@ export class ErrorHandler {
this._console.error('ERROR CONTEXT:'); this._console.error('ERROR CONTEXT:');
this._console.error(context); this._console.error(context);
} }
}
// We rethrow exceptions, so operations like 'bootstrap' will result in an error // We rethrow exceptions, so operations like 'bootstrap' will result in an error
// when an error happens. If we do not rethrow, bootstrap will always succeed. // when an error happens. If we do not rethrow, bootstrap will always succeed.
@ -81,31 +84,29 @@ export class ErrorHandler {
/** @internal */ /** @internal */
_findContext(error: any): any { _findContext(error: any): any {
if (error) { if (error) {
return error.context ? error.context : return getDebugContext(error) ? getDebugContext(error) :
this._findContext((error as WrappedError).originalError); this._findContext(getOriginalError(error));
} }
return null; return null;
} }
/** @internal */ /** @internal */
_findOriginalError(error: any): any { _findOriginalError(error: Error): any {
let e = (error as WrappedError).originalError; let e = getOriginalError(error);
while (e && (e as WrappedError).originalError) { while (e && getOriginalError(e)) {
e = (e as WrappedError).originalError; e = getOriginalError(e);
} }
return e; return e;
} }
/** @internal */ /** @internal */
_findOriginalStack(error: any): string { _findOriginalStack(error: Error): string {
if (!(error instanceof Error)) return null;
let e: any = error; let e: any = error;
let stack: string = e.stack; let stack: string = e.stack;
while (e instanceof Error && (e as WrappedError).originalError) { while (e instanceof Error && getOriginalError(e)) {
e = (e as WrappedError).originalError; e = getOriginalError(e);
if (e instanceof Error && e.stack) { if (e instanceof Error && e.stack) {
stack = e.stack; stack = e.stack;
} }
@ -114,3 +115,11 @@ export class ErrorHandler {
return stack; return stack;
} }
} }
export function wrappedError(message: string, originalError: any): Error {
const msg =
`${message} caused by: ${originalError instanceof Error ? originalError.message: originalError }`;
const error = Error(msg);
(error as any)[ERROR_ORIGINAL_ERROR] = originalError;
return error;
}

View File

@ -0,0 +1,28 @@
/**
* @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 {Type} from './type';
import {DebugContext} from './view';
export const ERROR_TYPE = 'ngType';
export const ERROR_COMPONENT_TYPE = 'ngComponentType';
export const ERROR_DEBUG_CONTEXT = 'ngDebugContext';
export const ERROR_ORIGINAL_ERROR = 'ngOriginalError';
export function getType(error: Error): Function {
return (error as any)[ERROR_TYPE];
}
export function getDebugContext(error: Error): DebugContext {
return (error as any)[ERROR_DEBUG_CONTEXT];
}
export function getOriginalError(error: Error): Error {
return (error as any)[ERROR_ORIGINAL_ERROR];
}

View File

@ -7,7 +7,6 @@
*/ */
import {Injectable, InjectionToken} from '../di'; import {Injectable, InjectionToken} from '../di';
import {BaseError} from '../facade/errors';
import {stringify} from '../facade/lang'; import {stringify} from '../facade/lang';
import {MissingTranslationStrategy} from '../i18n/tokens'; import {MissingTranslationStrategy} from '../i18n/tokens';
import {ViewEncapsulation} from '../metadata'; import {ViewEncapsulation} from '../metadata';
@ -16,19 +15,6 @@ import {Type} from '../type';
import {ComponentFactory} from './component_factory'; import {ComponentFactory} from './component_factory';
import {NgModuleFactory} from './ng_module_factory'; import {NgModuleFactory} from './ng_module_factory';
/**
* Indicates that a component is still being loaded in a synchronous compile.
*
* @stable
*/
export class ComponentStillLoadingError extends BaseError {
constructor(public compType: Type<any>) {
super(`Can't compile synchronously as ${stringify(compType)} is still being loaded!`);
}
}
/** /**
* Combination of NgModuleFactory and ComponentFactorys. * Combination of NgModuleFactory and ComponentFactorys.
* *
@ -59,8 +45,7 @@ function _throwError() {
export class Compiler { export class Compiler {
/** /**
* Compiles the given NgModule and all of its components. All templates of the components listed * Compiles the given NgModule and all of its components. All templates of the components listed
* in `entryComponents` * in `entryComponents` have to be inlined.
* have to be inlined. Otherwise throws a {@link ComponentStillLoadingError}.
*/ */
compileModuleSync<T>(moduleType: Type<T>): NgModuleFactory<T> { throw _throwError(); } compileModuleSync<T>(moduleType: Type<T>): NgModuleFactory<T> { throw _throwError(); }

View File

@ -6,7 +6,6 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {BaseError} from '../facade/errors';
import {stringify} from '../facade/lang'; import {stringify} from '../facade/lang';
import {Type} from '../type'; import {Type} from '../type';
@ -14,19 +13,23 @@ import {ComponentFactory} from './component_factory';
/** export function noComponentFactoryError(component: Function) {
* @stable const error = Error(
*/
export class NoComponentFactoryError extends BaseError {
constructor(public component: Function) {
super(
`No component factory found for ${stringify(component)}. Did you add it to @NgModule.entryComponents?`); `No component factory found for ${stringify(component)}. Did you add it to @NgModule.entryComponents?`);
} (error as any)[ERROR_COMPONENT] = component;
return error;
} }
const ERROR_COMPONENT = 'ngComponent';
export function getComponent(error: Error): Type<any> {
return (error as any)[ERROR_COMPONENT];
}
class _NullComponentFactoryResolver implements ComponentFactoryResolver { class _NullComponentFactoryResolver implements ComponentFactoryResolver {
resolveComponentFactory<T>(component: {new (...args: any[]): T}): ComponentFactory<T> { resolveComponentFactory<T>(component: {new (...args: any[]): T}): ComponentFactory<T> {
throw new NoComponentFactoryError(component); throw noComponentFactoryError(component);
} }
} }

View File

@ -6,8 +6,8 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {BaseError, WrappedError} from '../facade/errors'; import {wrappedError} from '../error_handler';
import {ERROR_DEBUG_CONTEXT, ERROR_TYPE} from '../errors';
import {DebugContext} from './debug_context'; import {DebugContext} from './debug_context';
@ -41,10 +41,9 @@ import {DebugContext} from './debug_context';
* } * }
* } * }
* ``` * ```
* @stable
*/ */
export class ExpressionChangedAfterItHasBeenCheckedError extends BaseError { export function expressionChangedAfterItHasBeenCheckedError(
constructor(oldValue: any, currValue: any, isFirstCheck: boolean) { oldValue: any, currValue: any, isFirstCheck: boolean) {
let msg = let msg =
`Expression has changed after it was checked. Previous value: '${oldValue}'. Current value: '${currValue}'.`; `Expression has changed after it was checked. Previous value: '${oldValue}'. Current value: '${currValue}'.`;
if (isFirstCheck) { if (isFirstCheck) {
@ -52,8 +51,9 @@ export class ExpressionChangedAfterItHasBeenCheckedError extends BaseError {
` It seems like the view has been created after its parent and its children have been dirty checked.` + ` It seems like the view has been created after its parent and its children have been dirty checked.` +
` Has it been created in a change detection hook ?`; ` Has it been created in a change detection hook ?`;
} }
super(msg); const error = Error(msg);
} (error as any)[ERROR_TYPE] = expressionChangedAfterItHasBeenCheckedError;
return error;
} }
/** /**
@ -61,28 +61,21 @@ export class ExpressionChangedAfterItHasBeenCheckedError extends BaseError {
* *
* This error wraps the original exception to attach additional contextual information that can * This error wraps the original exception to attach additional contextual information that can
* be useful for debugging. * be useful for debugging.
* @stable
*/ */
export class ViewWrappedError extends WrappedError { export function viewWrappedError(originalError: any, context: DebugContext): Error {
/** const error = wrappedError(`Error in ${context.source}`, originalError);
* DebugContext (error as any)[ERROR_DEBUG_CONTEXT] = context;
*/ (error as any)[ERROR_TYPE] = viewWrappedError;
context: DebugContext; return error;
constructor(originalError: any, context: DebugContext) {
super(`Error in ${context.source}`, originalError);
this.context = context;
}
} }
/** /**
* Thrown when a destroyed view is used. * Thrown when a destroyed view is used.
* *
* This error indicates a bug in the framework. * This error indicates a bug in the framework.
* *
* This is an internal Angular error. * This is an internal Angular error.
* @stable */
*/ export function viewDestroyedError(details: string) {
export class ViewDestroyedError extends BaseError { return Error(`Attempt to use a destroyed view: ${details}`);
constructor(details: string) { super(`Attempt to use a destroyed view: ${details}`); }
} }

View File

@ -6,9 +6,11 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {ApplicationRef} from '../application_ref'; import {ApplicationRef} from '../application_ref';
import {ChangeDetectorRef, ChangeDetectorStatus} from '../change_detection/change_detection'; import {ChangeDetectorRef, ChangeDetectorStatus} from '../change_detection/change_detection';
import {Injector, THROW_IF_NOT_FOUND} from '../di/injector'; import {Injector, THROW_IF_NOT_FOUND} from '../di/injector';
import {getType} from '../errors';
import {isPresent} from '../facade/lang'; import {isPresent} from '../facade/lang';
import {WtfScopeFn, wtfCreateScope, wtfLeave} from '../profile/profile'; import {WtfScopeFn, wtfCreateScope, wtfLeave} from '../profile/profile';
import {DirectRenderer, RenderComponentType, Renderer} from '../render/api'; import {DirectRenderer, RenderComponentType, Renderer} from '../render/api';
@ -17,7 +19,7 @@ import {AnimationViewContext} from './animation_view_context';
import {ComponentRef} from './component_factory'; import {ComponentRef} from './component_factory';
import {DebugContext, StaticNodeDebugInfo} from './debug_context'; import {DebugContext, StaticNodeDebugInfo} from './debug_context';
import {ElementInjector} from './element_injector'; import {ElementInjector} from './element_injector';
import {ExpressionChangedAfterItHasBeenCheckedError, ViewDestroyedError, ViewWrappedError} from './errors'; import {expressionChangedAfterItHasBeenCheckedError, viewDestroyedError, viewWrappedError} from './errors';
import {ViewContainer} from './view_container'; import {ViewContainer} from './view_container';
import {ViewRef_} from './view_ref'; import {ViewRef_} from './view_ref';
import {ViewType} from './view_type'; import {ViewType} from './view_type';
@ -360,7 +362,7 @@ export abstract class AppView<T> {
return cb; return cb;
} }
throwDestroyedError(details: string): void { throw new ViewDestroyedError(details); } throwDestroyedError(details: string): void { throw viewDestroyedError(details); }
} }
export class DebugAppView<T> extends AppView<T> { export class DebugAppView<T> extends AppView<T> {
@ -445,12 +447,12 @@ export class DebugAppView<T> extends AppView<T> {
} }
private _rethrowWithContext(e: any) { private _rethrowWithContext(e: any) {
if (!(e instanceof ViewWrappedError)) { if (!(getType(e) == viewWrappedError)) {
if (!(e instanceof ExpressionChangedAfterItHasBeenCheckedError)) { if (!(getType(e) == expressionChangedAfterItHasBeenCheckedError)) {
this.cdMode = ChangeDetectorStatus.Errored; this.cdMode = ChangeDetectorStatus.Errored;
} }
if (isPresent(this._currentDebugContext)) { if (isPresent(this._currentDebugContext)) {
throw new ViewWrappedError(e, this._currentDebugContext); throw viewWrappedError(e, this._currentDebugContext);
} }
} }
} }

View File

@ -17,7 +17,7 @@ import {Type} from '../type';
import {VERSION} from '../version'; import {VERSION} from '../version';
import {ComponentFactory} from './component_factory'; import {ComponentFactory} from './component_factory';
import {ExpressionChangedAfterItHasBeenCheckedError} from './errors'; import {expressionChangedAfterItHasBeenCheckedError} from './errors';
import {AppView} from './view'; import {AppView} from './view';
@Injectable() @Injectable()
@ -104,7 +104,7 @@ export function checkBinding(
const isFirstCheck = view.numberOfChecks === 0; const isFirstCheck = view.numberOfChecks === 0;
if (view.throwOnChange) { if (view.throwOnChange) {
if (isFirstCheck || !devModeEqual(oldValue, newValue)) { if (isFirstCheck || !devModeEqual(oldValue, newValue)) {
throw new ExpressionChangedAfterItHasBeenCheckedError(oldValue, newValue, isFirstCheck); throw expressionChangedAfterItHasBeenCheckedError(oldValue, newValue, isFirstCheck);
} }
return false; return false;
} else { } else {

View File

@ -6,12 +6,11 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {BaseError, WrappedError} from '../facade/errors'; import {ERROR_DEBUG_CONTEXT, ERROR_ORIGINAL_ERROR, getDebugContext} from '../errors';
import {DebugContext, EntryAction, ViewState} from './types'; import {DebugContext, EntryAction, ViewState} from './types';
export function expressionChangedAfterItHasBeenCheckedError( export function expressionChangedAfterItHasBeenCheckedError(
context: DebugContext, oldValue: any, currValue: any, isFirstCheck: boolean): ViewDebugError { context: DebugContext, oldValue: any, currValue: any, isFirstCheck: boolean): Error {
let msg = let msg =
`Expression has changed after it was checked. Previous value: '${oldValue}'. Current value: '${currValue}'.`; `Expression has changed after it was checked. Previous value: '${oldValue}'. Current value: '${currValue}'.`;
if (isFirstCheck) { if (isFirstCheck) {
@ -22,25 +21,22 @@ export function expressionChangedAfterItHasBeenCheckedError(
return viewDebugError(msg, context); return viewDebugError(msg, context);
} }
export function viewWrappedDebugError(originalError: any, context: DebugContext): WrappedError& export function viewWrappedDebugError(originalError: any, context: DebugContext): Error {
ViewDebugError { const err = viewDebugError(originalError.message, context);
const err = viewDebugError(originalError.message, context) as WrappedError & ViewDebugError; (err as any)[ERROR_ORIGINAL_ERROR] = originalError;
err.originalError = originalError;
return err; return err;
} }
export interface ViewDebugError { context: DebugContext; } export function viewDebugError(msg: string, context: DebugContext): Error {
const err = new Error(msg);
export function viewDebugError(msg: string, context: DebugContext): ViewDebugError { (err as any)[ERROR_DEBUG_CONTEXT] = context;
const err = new Error(msg) as any;
err.context = context;
err.stack = context.source; err.stack = context.source;
context.view.state |= ViewState.Errored; context.view.state |= ViewState.Errored;
return err; return err;
} }
export function isViewDebugError(err: any): boolean { export function isViewDebugError(err: Error): boolean {
return err.context; return !!getDebugContext(err);
} }
export function viewDestroyedError(action: EntryAction): Error { export function viewDestroyedError(action: EntryAction): Error {

View File

@ -6,9 +6,7 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {isDevMode} from '../application_ref';
import {RenderComponentType, Renderer} from '../render/api'; import {RenderComponentType, Renderer} from '../render/api';
import {checkAndUpdateElementDynamic, checkAndUpdateElementInline, createElement} from './element'; import {checkAndUpdateElementDynamic, checkAndUpdateElementInline, createElement} from './element';
import {expressionChangedAfterItHasBeenCheckedError} from './errors'; import {expressionChangedAfterItHasBeenCheckedError} from './errors';
import {appendNgContent} from './ng_content'; import {appendNgContent} from './ng_content';

View File

@ -9,6 +9,7 @@
import {Inject, Injectable, Injector, Optional, Provider, ReflectiveInjector, ReflectiveKey, Self, forwardRef} from '@angular/core'; import {Inject, Injectable, Injector, Optional, Provider, ReflectiveInjector, ReflectiveKey, Self, forwardRef} from '@angular/core';
import {ReflectiveInjector_} from '@angular/core/src/di/reflective_injector'; import {ReflectiveInjector_} from '@angular/core/src/di/reflective_injector';
import {ResolvedReflectiveProvider_} from '@angular/core/src/di/reflective_provider'; import {ResolvedReflectiveProvider_} from '@angular/core/src/di/reflective_provider';
import {getOriginalError} from '@angular/core/src/errors';
import {expect} from '@angular/platform-browser/testing/matchers'; import {expect} from '@angular/platform-browser/testing/matchers';
import {isPresent, stringify} from '../../src/facade/lang'; import {isPresent, stringify} from '../../src/facade/lang';
@ -293,8 +294,8 @@ export function main() {
} catch (e) { } catch (e) {
expect(e.message).toContain( expect(e.message).toContain(
`Error during instantiation of Engine! (${stringify(Car)} -> Engine)`); `Error during instantiation of Engine! (${stringify(Car)} -> Engine)`);
expect(e.originalError instanceof Error).toBeTruthy(); expect(getOriginalError(e) instanceof Error).toBeTruthy();
expect(e.causeKey.token).toEqual(Engine); expect(e.keys[0].token).toEqual(Engine);
} }
}); });

View File

@ -6,22 +6,17 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {WrappedError} from '@angular/core/src/facade/errors'; import {ERROR_DEBUG_CONTEXT} from '@angular/core/src/errors';
import {DebugContext} from '@angular/core/src/linker/debug_context'; import {DebugContext} from '@angular/core/src/linker/debug_context';
import {ViewWrappedError} from '@angular/core/src/linker/errors'; import {viewWrappedError} from '@angular/core/src/linker/errors';
import {ErrorHandler} from '../src/error_handler'; import {ErrorHandler, wrappedError} from '../src/error_handler';
class MockConsole { class MockConsole {
res: any[] = []; res: any[] = [];
error(s: any): void { this.res.push(s); } error(s: any): void { this.res.push(s); }
} }
class _CustomException {
context = 'some context';
toString(): string { return 'custom'; }
}
export function main() { export function main() {
function errorToString(error: any) { function errorToString(error: any) {
const logger = new MockConsole(); const logger = new MockConsole();
@ -58,12 +53,9 @@ export function main() {
it('should print nested context', () => { it('should print nested context', () => {
const cause = new Error('message!'); const cause = new Error('message!');
const stack = getStack(cause); const stack = getStack(cause);
const context = { const context = { source: 'context!', toString() { return 'Context'; } } as any;
source: 'context!', const original = viewWrappedError(cause, context);
toString() { return 'Context'; } const e = errorToString(wrappedError('message', original));
} as any as DebugContext;
const original = new ViewWrappedError(cause, context);
const e = errorToString(new WrappedError('message', original));
expect(e).toEqual( expect(e).toEqual(
stack ? `EXCEPTION: message caused by: Error in context! caused by: message! stack ? `EXCEPTION: message caused by: Error in context! caused by: message!
ORIGINAL EXCEPTION: message! ORIGINAL EXCEPTION: message!
@ -81,15 +73,15 @@ Context`);
describe('original exception', () => { describe('original exception', () => {
it('should print original exception message if available (original is Error)', () => { it('should print original exception message if available (original is Error)', () => {
const realOriginal = new Error('inner'); const realOriginal = new Error('inner');
const original = new WrappedError('wrapped', realOriginal); const original = wrappedError('wrapped', realOriginal);
const e = errorToString(new WrappedError('wrappedwrapped', original)); const e = errorToString(wrappedError('wrappedwrapped', original));
expect(e).toContain('inner'); expect(e).toContain('inner');
}); });
it('should print original exception message if available (original is not Error)', () => { it('should print original exception message if available (original is not Error)', () => {
const realOriginal = new _CustomException(); const realOriginal = new Error('custom');
const original = new WrappedError('wrapped', realOriginal); const original = wrappedError('wrapped', realOriginal);
const e = errorToString(new WrappedError('wrappedwrapped', original)); const e = errorToString(wrappedError('wrappedwrapped', original));
expect(e).toContain('custom'); expect(e).toContain('custom');
}); });
}); });
@ -99,8 +91,8 @@ Context`);
const realOriginal = new Error('inner'); const realOriginal = new Error('inner');
const stack = getStack(realOriginal); const stack = getStack(realOriginal);
if (stack) { if (stack) {
const original = new WrappedError('wrapped', realOriginal); const original = wrappedError('wrapped', realOriginal);
const e = errorToString(new WrappedError('wrappedwrapped', original)); const e = errorToString(wrappedError('wrappedwrapped', original));
expect(e).toContain(stack); expect(e).toContain(stack);
} }
}); });

View File

@ -7,7 +7,7 @@
*/ */
import {ANALYZE_FOR_ENTRY_COMPONENTS, Component, ComponentFactoryResolver} from '@angular/core'; import {ANALYZE_FOR_ENTRY_COMPONENTS, Component, ComponentFactoryResolver} from '@angular/core';
import {NoComponentFactoryError} from '@angular/core/src/linker/component_factory_resolver'; import {noComponentFactoryError} from '@angular/core/src/linker/component_factory_resolver';
import {TestBed} from '@angular/core/testing'; import {TestBed} from '@angular/core/testing';
import {Console} from '../../src/console'; import {Console} from '../../src/console';
@ -77,7 +77,7 @@ function declareTests({useJit}: {useJit: boolean}) {
const nestedChildComp: NestedChildComp = nestedChildCompEl.componentInstance; const nestedChildComp: NestedChildComp = nestedChildCompEl.componentInstance;
expect(nestedChildComp.cfr.resolveComponentFactory(ChildComp).componentType).toBe(ChildComp); expect(nestedChildComp.cfr.resolveComponentFactory(ChildComp).componentType).toBe(ChildComp);
expect(() => nestedChildComp.cfr.resolveComponentFactory(NestedChildComp)) expect(() => nestedChildComp.cfr.resolveComponentFactory(NestedChildComp))
.toThrow(new NoComponentFactoryError(NestedChildComp)); .toThrow(noComponentFactoryError(NestedChildComp));
}); });
}); });

View File

@ -9,6 +9,7 @@
import {CommonModule} from '@angular/common'; import {CommonModule} from '@angular/common';
import {ComponentFactory, Host, Inject, Injectable, InjectionToken, Injector, NO_ERRORS_SCHEMA, NgModule, OnDestroy, ReflectiveInjector, SkipSelf} from '@angular/core'; import {ComponentFactory, Host, Inject, Injectable, InjectionToken, Injector, NO_ERRORS_SCHEMA, NgModule, OnDestroy, ReflectiveInjector, SkipSelf} from '@angular/core';
import {ChangeDetectionStrategy, ChangeDetectorRef, PipeTransform} from '@angular/core/src/change_detection/change_detection'; import {ChangeDetectionStrategy, ChangeDetectorRef, PipeTransform} from '@angular/core/src/change_detection/change_detection';
import {getDebugContext} from '@angular/core/src/errors';
import {ComponentFactoryResolver} from '@angular/core/src/linker/component_factory_resolver'; import {ComponentFactoryResolver} from '@angular/core/src/linker/component_factory_resolver';
import {ElementRef} from '@angular/core/src/linker/element_ref'; import {ElementRef} from '@angular/core/src/linker/element_ref';
import {QueryList} from '@angular/core/src/linker/query_list'; import {QueryList} from '@angular/core/src/linker/query_list';
@ -1295,7 +1296,7 @@ function declareTests({useJit}: {useJit: boolean}) {
TestBed.createComponent(MyComp); TestBed.createComponent(MyComp);
throw 'Should throw'; throw 'Should throw';
} catch (e) { } catch (e) {
const c = e.context; const c = getDebugContext(e);
expect(getDOM().nodeName(c.componentRenderElement).toUpperCase()).toEqual('DIV'); expect(getDOM().nodeName(c.componentRenderElement).toUpperCase()).toEqual('DIV');
expect((<Injector>c.injector).get).toBeTruthy(); expect((<Injector>c.injector).get).toBeTruthy();
} }
@ -1310,7 +1311,7 @@ function declareTests({useJit}: {useJit: boolean}) {
fixture.detectChanges(); fixture.detectChanges();
throw 'Should throw'; throw 'Should throw';
} catch (e) { } catch (e) {
const c = e.context; const c = getDebugContext(e);
expect(getDOM().nodeName(c.renderNode).toUpperCase()).toEqual('INPUT'); expect(getDOM().nodeName(c.renderNode).toUpperCase()).toEqual('INPUT');
expect(getDOM().nodeName(c.componentRenderElement).toUpperCase()).toEqual('DIV'); expect(getDOM().nodeName(c.componentRenderElement).toUpperCase()).toEqual('DIV');
expect((<Injector>c.injector).get).toBeTruthy(); expect((<Injector>c.injector).get).toBeTruthy();
@ -1330,7 +1331,7 @@ function declareTests({useJit}: {useJit: boolean}) {
fixture.detectChanges(); fixture.detectChanges();
throw 'Should throw'; throw 'Should throw';
} catch (e) { } catch (e) {
const c = e.context; const c = getDebugContext(e);
expect(c.renderNode).toBeTruthy(); expect(c.renderNode).toBeTruthy();
expect(c.source).toContain(':0:5'); expect(c.source).toContain(':0:5');
} }
@ -1353,7 +1354,7 @@ function declareTests({useJit}: {useJit: boolean}) {
try { try {
tc.injector.get(DirectiveEmittingEvent).fireEvent('boom'); tc.injector.get(DirectiveEmittingEvent).fireEvent('boom');
} catch (e) { } catch (e) {
const c = e.context; const c = getDebugContext(e);
expect(getDOM().nodeName(c.renderNode).toUpperCase()).toEqual('SPAN'); expect(getDOM().nodeName(c.renderNode).toUpperCase()).toEqual('SPAN');
expect(getDOM().nodeName(c.componentRenderElement).toUpperCase()).toEqual('DIV'); expect(getDOM().nodeName(c.componentRenderElement).toUpperCase()).toEqual('DIV');
expect((<Injector>c.injector).get).toBeTruthy(); expect((<Injector>c.injector).get).toBeTruthy();

View File

@ -7,6 +7,7 @@
*/ */
import {Injector, RenderComponentType, RootRenderer, Sanitizer, SecurityContext, ViewEncapsulation, WrappedValue, getDebugNode} from '@angular/core'; import {Injector, RenderComponentType, RootRenderer, Sanitizer, SecurityContext, ViewEncapsulation, WrappedValue, getDebugNode} from '@angular/core';
import {getDebugContext} from '@angular/core/src/errors';
import {BindingType, DebugContext, NodeDef, NodeFlags, RootData, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewUpdateFn, anchorDef, asElementData, checkAndUpdateView, checkNoChangesView, checkNodeDynamic, checkNodeInline, createRootView, destroyView, elementDef, rootRenderNodes, setCurrentNode, textDef, viewDef} from '@angular/core/src/view/index'; import {BindingType, DebugContext, NodeDef, NodeFlags, RootData, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewUpdateFn, anchorDef, asElementData, checkAndUpdateView, checkNoChangesView, checkNodeDynamic, checkNodeInline, createRootView, destroyView, elementDef, rootRenderNodes, setCurrentNode, textDef, viewDef} from '@angular/core/src/view/index';
import {inject} from '@angular/core/testing'; import {inject} from '@angular/core/testing';
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter'; import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
@ -363,7 +364,7 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
} }
expect(err).toBeTruthy(); expect(err).toBeTruthy();
expect(err.message).toBe('Test'); expect(err.message).toBe('Test');
const debugCtx = <DebugContext>err.context; const debugCtx = getDebugContext(err);
expect(debugCtx.view).toBe(view); expect(debugCtx.view).toBe(view);
expect(debugCtx.nodeIndex).toBe(0); expect(debugCtx.nodeIndex).toBe(0);
}); });

View File

@ -7,6 +7,7 @@
*/ */
import {AfterContentChecked, AfterContentInit, AfterViewChecked, AfterViewInit, ChangeDetectorRef, DoCheck, ElementRef, EventEmitter, Injector, OnChanges, OnDestroy, OnInit, RenderComponentType, Renderer, RootRenderer, Sanitizer, SecurityContext, SimpleChange, TemplateRef, ViewContainerRef, ViewEncapsulation, WrappedValue, getDebugNode} from '@angular/core'; import {AfterContentChecked, AfterContentInit, AfterViewChecked, AfterViewInit, ChangeDetectorRef, DoCheck, ElementRef, EventEmitter, Injector, OnChanges, OnDestroy, OnInit, RenderComponentType, Renderer, RootRenderer, Sanitizer, SecurityContext, SimpleChange, TemplateRef, ViewContainerRef, ViewEncapsulation, WrappedValue, getDebugNode} from '@angular/core';
import {getDebugContext} from '@angular/core/src/errors';
import {BindingType, DebugContext, DepFlags, NodeDef, NodeFlags, ProviderType, RootData, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewUpdateFn, anchorDef, asElementData, asProviderData, checkAndUpdateView, checkNoChangesView, checkNodeDynamic, checkNodeInline, createRootView, destroyView, directiveDef, elementDef, providerDef, rootRenderNodes, setCurrentNode, textDef, viewDef} from '@angular/core/src/view/index'; import {BindingType, DebugContext, DepFlags, NodeDef, NodeFlags, ProviderType, RootData, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewUpdateFn, anchorDef, asElementData, asProviderData, checkAndUpdateView, checkNoChangesView, checkNodeDynamic, checkNodeInline, createRootView, destroyView, directiveDef, elementDef, providerDef, rootRenderNodes, setCurrentNode, textDef, viewDef} from '@angular/core/src/view/index';
import {inject} from '@angular/core/testing'; import {inject} from '@angular/core/testing';
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter'; import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
@ -135,7 +136,7 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
} }
expect(err).toBeTruthy(); expect(err).toBeTruthy();
expect(err.message).toBe('Test'); expect(err.message).toBe('Test');
const debugCtx = <DebugContext>err.context; const debugCtx = getDebugContext(err);
expect(debugCtx.view).toBeTruthy(); expect(debugCtx.view).toBeTruthy();
// errors should point to the already existing element // errors should point to the already existing element
expect(debugCtx.nodeIndex).toBe(0); expect(debugCtx.nodeIndex).toBe(0);
@ -447,7 +448,7 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
err = e; err = e;
} }
expect(err).toBeTruthy(); expect(err).toBeTruthy();
const debugCtx = <DebugContext>err.context; const debugCtx = getDebugContext(err);
expect(debugCtx.view).toBe(view); expect(debugCtx.view).toBe(view);
// events are emitted with the index of the element, not the index of the provider. // events are emitted with the index of the element, not the index of the provider.
expect(debugCtx.nodeIndex).toBe(0); expect(debugCtx.nodeIndex).toBe(0);
@ -576,7 +577,7 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
} }
expect(err).toBeTruthy(); expect(err).toBeTruthy();
expect(err.message).toBe('Test'); expect(err.message).toBe('Test');
const debugCtx = <DebugContext>err.context; const debugCtx = getDebugContext(err);
expect(debugCtx.view).toBe(view); expect(debugCtx.view).toBe(view);
expect(debugCtx.nodeIndex).toBe(1); expect(debugCtx.nodeIndex).toBe(1);
}); });
@ -599,7 +600,7 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
} }
expect(err).toBeTruthy(); expect(err).toBeTruthy();
expect(err.message).toBe('Test'); expect(err.message).toBe('Test');
const debugCtx = <DebugContext>err.context; const debugCtx = getDebugContext(err);
expect(debugCtx.view).toBe(view); expect(debugCtx.view).toBe(view);
expect(debugCtx.nodeIndex).toBe(1); expect(debugCtx.nodeIndex).toBe(1);
}); });

View File

@ -7,6 +7,7 @@
*/ */
import {ElementRef, Injector, QueryList, RenderComponentType, RootRenderer, Sanitizer, SecurityContext, TemplateRef, ViewContainerRef, ViewEncapsulation, getDebugNode} from '@angular/core'; import {ElementRef, Injector, QueryList, RenderComponentType, RootRenderer, Sanitizer, SecurityContext, TemplateRef, ViewContainerRef, ViewEncapsulation, getDebugNode} from '@angular/core';
import {getDebugContext} from '@angular/core/src/errors';
import {BindingType, DebugContext, NodeDef, NodeFlags, QueryBindingType, QueryValueType, RootData, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewUpdateFn, anchorDef, asElementData, asProviderData, attachEmbeddedView, checkAndUpdateView, checkNoChangesView, checkNodeDynamic, checkNodeInline, createEmbeddedView, createRootView, destroyView, detachEmbeddedView, directiveDef, elementDef, queryDef, rootRenderNodes, setCurrentNode, textDef, viewDef} from '@angular/core/src/view/index'; import {BindingType, DebugContext, NodeDef, NodeFlags, QueryBindingType, QueryValueType, RootData, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewUpdateFn, anchorDef, asElementData, asProviderData, attachEmbeddedView, checkAndUpdateView, checkNoChangesView, checkNodeDynamic, checkNodeInline, createEmbeddedView, createRootView, destroyView, detachEmbeddedView, directiveDef, elementDef, queryDef, rootRenderNodes, setCurrentNode, textDef, viewDef} from '@angular/core/src/view/index';
import {inject} from '@angular/core/testing'; import {inject} from '@angular/core/testing';
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter'; import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
@ -395,7 +396,7 @@ export function main() {
expect(err.message) expect(err.message)
.toBe( .toBe(
`Expression has changed after it was checked. Previous value: 'Query query1 not dirty'. Current value: 'Query query1 dirty'.`); `Expression has changed after it was checked. Previous value: 'Query query1 not dirty'. Current value: 'Query query1 dirty'.`);
const debugCtx = <DebugContext>err.context; const debugCtx = getDebugContext(err);
expect(debugCtx.view).toBe(view); expect(debugCtx.view).toBe(view);
expect(debugCtx.nodeIndex).toBe(2); expect(debugCtx.nodeIndex).toBe(2);
}); });
@ -421,7 +422,7 @@ export function main() {
} }
expect(err).toBeTruthy(); expect(err).toBeTruthy();
expect(err.message).toBe('Test'); expect(err.message).toBe('Test');
const debugCtx = <DebugContext>err.context; const debugCtx = getDebugContext(err);
expect(debugCtx.view).toBe(view); expect(debugCtx.view).toBe(view);
expect(debugCtx.nodeIndex).toBe(2); expect(debugCtx.nodeIndex).toBe(2);
}); });

View File

@ -10,7 +10,6 @@ import {NgZone} from '@angular/core/src/zone/ng_zone';
import {async, fakeAsync, flushMicrotasks} from '@angular/core/testing'; import {async, fakeAsync, flushMicrotasks} from '@angular/core/testing';
import {AsyncTestCompleter, Log, beforeEach, describe, expect, inject, it, xit} from '@angular/core/testing/testing_internal'; import {AsyncTestCompleter, Log, beforeEach, describe, expect, inject, it, xit} from '@angular/core/testing/testing_internal';
import {browserDetection} from '@angular/platform-browser/testing/browser_util'; import {browserDetection} from '@angular/platform-browser/testing/browser_util';
import {BaseError} from '../../src/facade/errors';
import {isPresent, scheduleMicroTask} from '../../src/facade/lang'; import {isPresent, scheduleMicroTask} from '../../src/facade/lang';
const needsLongerTimers = browserDetection.isSlow || browserDetection.isEdge; const needsLongerTimers = browserDetection.isSlow || browserDetection.isEdge;
@ -94,7 +93,7 @@ export function main() {
setTimeout(() => { setTimeout(() => {
setTimeout(() => { setTimeout(() => {
resolve(null); resolve(null);
throw new BaseError('ccc'); throw new Error('ccc');
}, 0); }, 0);
}, 0); }, 0);
}); });
@ -117,7 +116,7 @@ export function main() {
scheduleMicroTask(() => { scheduleMicroTask(() => {
scheduleMicroTask(() => { scheduleMicroTask(() => {
resolve(null); resolve(null);
throw new BaseError('ddd'); throw new Error('ddd');
}); });
}); });
}); });
@ -152,7 +151,7 @@ export function main() {
setTimeout(() => { setTimeout(() => {
setTimeout(() => { setTimeout(() => {
resolve(null); resolve(null);
throw new BaseError('ccc'); throw new Error('ccc');
}, 0); }, 0);
}, 0); }, 0);
}); });
@ -719,7 +718,7 @@ function commonTests() {
it('should call the on error callback when it is invoked via zone.runGuarded', it('should call the on error callback when it is invoked via zone.runGuarded',
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
macroTask(() => { macroTask(() => {
const exception = new BaseError('sync'); const exception = new Error('sync');
_zone.runGuarded(() => { throw exception; }); _zone.runGuarded(() => { throw exception; });
@ -732,7 +731,7 @@ function commonTests() {
it('should not call the on error callback but rethrow when it is invoked via zone.run', it('should not call the on error callback but rethrow when it is invoked via zone.run',
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
macroTask(() => { macroTask(() => {
const exception = new BaseError('sync'); const exception = new Error('sync');
expect(() => _zone.run(() => { throw exception; })).toThrowError('sync'); expect(() => _zone.run(() => { throw exception; })).toThrowError('sync');
expect(_errors.length).toBe(0); expect(_errors.length).toBe(0);
@ -742,7 +741,7 @@ function commonTests() {
it('should call onError for errors from microtasks', it('should call onError for errors from microtasks',
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
const exception = new BaseError('async'); const exception = new Error('async');
macroTask(() => { _zone.run(() => { scheduleMicroTask(() => { throw exception; }); }); }); macroTask(() => { _zone.run(() => { scheduleMicroTask(() => { throw exception; }); }); });

View File

@ -6,8 +6,7 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {CompilerOptions, Component, Directive, InjectionToken, Injector, ModuleWithComponentFactories, NgModule, NgModuleRef, NgZone, Pipe, PlatformRef, Provider, ReflectiveInjector, SchemaMetadata, Type} from '@angular/core'; import {CompilerOptions, Component, Directive, InjectionToken, Injector, ModuleWithComponentFactories, NgModule, NgModuleRef, NgZone, Pipe, PlatformRef, Provider, ReflectiveInjector, SchemaMetadata, Type, __core_private__} from '@angular/core';
import {AsyncTestCompleter} from './async_test_completer'; import {AsyncTestCompleter} from './async_test_completer';
import {ComponentFixture} from './component_fixture'; import {ComponentFixture} from './component_fixture';
import {stringify} from './facade/lang'; import {stringify} from './facade/lang';
@ -266,9 +265,9 @@ export class TestBed implements Injector {
this._moduleWithComponentFactories = this._moduleWithComponentFactories =
this._compiler.compileModuleAndAllComponentsSync(moduleType); this._compiler.compileModuleAndAllComponentsSync(moduleType);
} catch (e) { } catch (e) {
if (e.compType) { if (getComponentType(e)) {
throw new Error( throw new Error(
`This test module uses the component ${stringify(e.compType)} which is using a "templateUrl" or "styleUrls", but they were never compiled. ` + `This test module uses the component ${stringify(getComponentType(e))} which is using a "templateUrl" or "styleUrls", but they were never compiled. ` +
`Please call "TestBed.compileComponents" before your test.`); `Please call "TestBed.compileComponents" before your test.`);
} else { } else {
throw e; throw e;
@ -471,3 +470,7 @@ export function withModule(moduleDef: TestModuleMetadata, fn: Function = null):
} }
return new InjectSetupWrapper(() => moduleDef); return new InjectSetupWrapper(() => moduleDef);
} }
function getComponentType(error: Error): Function {
return (error as any)[__core_private__.ERROR_COMPONENT_TYPE];
}

View File

@ -8,9 +8,11 @@
import {Compiler, CompilerOptions, Component, Directive, Injector, NgModule, Pipe, Type} from '@angular/core'; import {Compiler, CompilerOptions, Component, Directive, Injector, NgModule, Pipe, Type} from '@angular/core';
import {unimplemented} from './facade/errors';
import {MetadataOverride} from './metadata_override'; import {MetadataOverride} from './metadata_override';
function unimplemented(): any {
throw Error('unimplemented');
}
/** /**
* Special interface to the compiler only used by testing * Special interface to the compiler only used by testing

View File

@ -1,56 +0,0 @@
/**
* @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
*/
/**
* Convenience to throw an Error with 'unimplemented' as the message.
*/
export function unimplemented(): any {
throw new Error('unimplemented');
}
/**
* @stable
*/
export class BaseError extends Error {
/** @internal **/
_nativeError: Error;
constructor(message: string) {
super(message);
// Errors don't use current this, instead they create a new instance.
// We have to do forward all of our api to the nativeInstance.
// TODO(bradfordcsmith): Remove this hack when
// google/closure-compiler/issues/2102 is fixed.
const nativeError = new Error(message) as any as Error;
this._nativeError = nativeError;
}
get message() { return this._nativeError.message; }
set message(message) { this._nativeError.message = message; }
get name() { return this._nativeError.name; }
get stack() { return (this._nativeError as any).stack; }
set stack(value) { (this._nativeError as any).stack = value; }
toString() { return this._nativeError.toString(); }
}
/**
* @stable
*/
export class WrappedError extends BaseError {
originalError: any;
constructor(message: string, error: any) {
super(`${message} caused by: ${error instanceof Error ? error.message: error }`);
this.originalError = error;
}
get stack() {
return ((this.originalError instanceof Error ? this.originalError : this._nativeError) as any)
.stack;
}
}

View File

@ -1436,21 +1436,21 @@ export function main() {
bootstrap(platformBrowserDynamic(), Ng2Module, elementA, ng1Module).then(() => { bootstrap(platformBrowserDynamic(), Ng2Module, elementA, ng1Module).then(() => {
expect(mockExceptionHandler).toHaveBeenCalledWith(jasmine.objectContaining({ expect(mockExceptionHandler).toHaveBeenCalledWith(jasmine.objectContaining({
originalError: new Error( ngOriginalError: new Error(
'Unable to find required \'iDoNotExist\' in upgraded directive \'ng1A\'.') 'Unable to find required \'iDoNotExist\' in upgraded directive \'ng1A\'.')
})); }));
}); });
bootstrap(platformBrowserDynamic(), Ng2Module, elementB, ng1Module).then(() => { bootstrap(platformBrowserDynamic(), Ng2Module, elementB, ng1Module).then(() => {
expect(mockExceptionHandler).toHaveBeenCalledWith(jasmine.objectContaining({ expect(mockExceptionHandler).toHaveBeenCalledWith(jasmine.objectContaining({
originalError: new Error( ngOriginalError: new Error(
'Unable to find required \'^iDoNotExist\' in upgraded directive \'ng1B\'.') 'Unable to find required \'^iDoNotExist\' in upgraded directive \'ng1B\'.')
})); }));
}); });
bootstrap(platformBrowserDynamic(), Ng2Module, elementC, ng1Module).then(() => { bootstrap(platformBrowserDynamic(), Ng2Module, elementC, ng1Module).then(() => {
expect(mockExceptionHandler).toHaveBeenCalledWith(jasmine.objectContaining({ expect(mockExceptionHandler).toHaveBeenCalledWith(jasmine.objectContaining({
originalError: new Error( ngOriginalError: new Error(
'Unable to find required \'^^iDoNotExist\' in upgraded directive \'ng1C\'.') 'Unable to find required \'^^iDoNotExist\' in upgraded directive \'ng1C\'.')
})); }));
}); });

View File

@ -8,7 +8,7 @@
import {ComponentFactory, ComponentRef, ElementRef, Injector, TemplateRef, ViewContainerRef, ViewRef} from '@angular/core'; import {ComponentFactory, ComponentRef, ElementRef, Injector, TemplateRef, ViewContainerRef, ViewRef} from '@angular/core';
import {devModeEqual, looseIdentical} from '@angular/core/src/change_detection/change_detection_util'; import {devModeEqual, looseIdentical} from '@angular/core/src/change_detection/change_detection_util';
import {ExpressionChangedAfterItHasBeenCheckedError} from '@angular/core/src/linker/errors'; import {expressionChangedAfterItHasBeenCheckedError} from '@angular/core/src/linker/errors';
export function unimplemented(): any { export function unimplemented(): any {
@ -212,7 +212,7 @@ export function createAnchorAndAppend(parent: any) {
export function checkBinding(throwOnChange: boolean, oldValue: any, newValue: any): boolean { export function checkBinding(throwOnChange: boolean, oldValue: any, newValue: any): boolean {
if (throwOnChange) { if (throwOnChange) {
if (!devModeEqual(oldValue, newValue)) { if (!devModeEqual(oldValue, newValue)) {
throw new ExpressionChangedAfterItHasBeenCheckedError(oldValue, newValue, false); throw expressionChangedAfterItHasBeenCheckedError(oldValue, newValue, false);
} }
return false; return false;
} else { } else {

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {devModeEqual, looseIdentical} from '@angular/core/src/change_detection/change_detection_util'; import {devModeEqual, looseIdentical} from '@angular/core/src/change_detection/change_detection_util';
import {ExpressionChangedAfterItHasBeenCheckedError} from '@angular/core/src/linker/errors'; import {expressionChangedAfterItHasBeenCheckedError} from '@angular/core/src/linker/errors';
export function createElementAndAppend(parent: any, name: string) { export function createElementAndAppend(parent: any, name: string) {
const el = document.createElement(name); const el = document.createElement(name);
@ -29,7 +29,7 @@ export function createAnchorAndAppend(parent: any) {
export function checkBinding(throwOnChange: boolean, oldValue: any, newValue: any): boolean { export function checkBinding(throwOnChange: boolean, oldValue: any, newValue: any): boolean {
if (throwOnChange) { if (throwOnChange) {
if (!devModeEqual(oldValue, newValue)) { if (!devModeEqual(oldValue, newValue)) {
throw new ExpressionChangedAfterItHasBeenCheckedError(oldValue, newValue, false); throw expressionChangedAfterItHasBeenCheckedError(oldValue, newValue, false);
} }
return false; return false;
} else { } else {