refactor(compiler): don't print stack trace on template parse errors (#13390)

This commit is contained in:
Bowen Ni 2016-12-15 13:07:12 -08:00 committed by Chuck Jazdzewski
parent 14e785f5b7
commit f0e092515c
13 changed files with 102 additions and 71 deletions

View File

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

View File

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

View File

@ -88,7 +88,7 @@ export class StaticSymbolResolver {
}
} else {
let value = baseMetadata;
for (var i = 0; i < members.length && value; i++) {
for (let i = 0; i < members.length && value; i++) {
value = value[members[i]];
}
return new ResolvedStaticSymbol(staticSymbol, value);

View File

@ -127,7 +127,7 @@ export enum CompileSummaryKind {
* the directive / module itself.
*/
export interface CompileTypeSummary {
summaryKind: CompileSummaryKind
summaryKind: CompileSummaryKind;
type: CompileTypeMetadata;
}

View File

@ -19,7 +19,7 @@ import {ResourceLoader} from './resource_loader';
import {extractStyleUrls, isStyleUrlResolvable} from './style_url_resolver';
import {PreparsedElementType, preparseElement} from './template_parser/template_preparser';
import {UrlResolver} from './url_resolver';
import {SyncAsyncResult} from './util';
import {SyncAsyncResult, SyntaxError} from './util';
export interface PrenormalizedTemplateMetadata {
componentType: any;
@ -71,7 +71,7 @@ export class DirectiveNormalizer {
} else if (prenormData.templateUrl) {
normalizedTemplateAsync = this.normalizeTemplateAsync(prenormData);
} else {
throw new Error(
throw new SyntaxError(
`No template specified for component ${stringify(prenormData.componentType)}`);
}
@ -105,7 +105,7 @@ export class DirectiveNormalizer {
template, stringify(prenomData.componentType), false, interpolationConfig);
if (rootNodesAndErrors.errors.length > 0) {
const errorString = rootNodesAndErrors.errors.join('\n');
throw new Error(`Template parse errors:\n${errorString}`);
throw new SyntaxError(`Template parse errors:\n${errorString}`);
}
const templateMetadataStyles = this.normalizeStylesheet(new CompileStylesheetMetadata({
styles: prenomData.styles,

View File

@ -24,7 +24,7 @@ import {ComponentStillLoadingError, LIFECYCLE_HOOKS_VALUES, ReflectorReader, ref
import {ElementSchemaRegistry} from './schema/element_schema_registry';
import {SummaryResolver} from './summary_resolver';
import {getUrlScheme} from './url_resolver';
import {MODULE_SUFFIX, SyncAsyncResult, ValueTransformer, visitValue} from './util';
import {MODULE_SUFFIX, SyncAsyncResult, SyntaxError, ValueTransformer, visitValue} from './util';
export type ErrorCollector = (error: any, type?: any) => void;
export const ERROR_COLLECTOR_TOKEN = new OpaqueToken('ErrorCollector');
@ -253,7 +253,7 @@ export class CompileMetadataResolver {
// Directive
if (!selector) {
this._reportError(
new Error(`Directive ${stringifyType(directiveType)} has no selector, please add it!`),
new SyntaxError(`Directive ${stringifyType(directiveType)} has no selector, please add it!`),
directiveType);
selector = 'error';
}
@ -299,7 +299,7 @@ export class CompileMetadataResolver {
const dirMeta = this._directiveCache.get(directiveType);
if (!dirMeta) {
this._reportError(
new Error(
new SyntaxError(
`Illegal state: getDirectiveMetadata can only be called after loadNgModuleMetadata for a module that declares it. Directive ${stringifyType(directiveType)}.`),
directiveType);
}
@ -311,7 +311,7 @@ export class CompileMetadataResolver {
<cpl.CompileDirectiveSummary>this._loadSummary(dirType, cpl.CompileSummaryKind.Directive);
if (!dirSummary) {
this._reportError(
new Error(
new SyntaxError(
`Illegal state: Could not load the summary for directive ${stringifyType(dirType)}.`),
dirType);
}
@ -394,7 +394,7 @@ export class CompileMetadataResolver {
const importedModuleSummary = this.getNgModuleSummary(importedModuleType);
if (!importedModuleSummary) {
this._reportError(
new Error(
new SyntaxError(
`Unexpected ${this._getTypeDescriptor(importedType)} '${stringifyType(importedType)}' imported by the module '${stringifyType(moduleType)}'`),
moduleType);
return;
@ -402,7 +402,7 @@ export class CompileMetadataResolver {
importedModules.push(importedModuleSummary);
} else {
this._reportError(
new Error(
new SyntaxError(
`Unexpected value '${stringifyType(importedType)}' imported by the module '${stringifyType(moduleType)}'`),
moduleType);
return;
@ -414,7 +414,7 @@ export class CompileMetadataResolver {
flattenAndDedupeArray(meta.exports).forEach((exportedType) => {
if (!isValidType(exportedType)) {
this._reportError(
new Error(
new SyntaxError(
`Unexpected value '${stringifyType(exportedType)}' exported by the module '${stringifyType(moduleType)}'`),
moduleType);
return;
@ -435,7 +435,7 @@ export class CompileMetadataResolver {
flattenAndDedupeArray(meta.declarations).forEach((declaredType) => {
if (!isValidType(declaredType)) {
this._reportError(
new Error(
new SyntaxError(
`Unexpected value '${stringifyType(declaredType)}' declared by the module '${stringifyType(moduleType)}'`),
moduleType);
return;
@ -452,7 +452,7 @@ export class CompileMetadataResolver {
this._addTypeToModule(declaredType, moduleType);
} else {
this._reportError(
new Error(
new SyntaxError(
`Unexpected ${this._getTypeDescriptor(declaredType)} '${stringifyType(declaredType)}' declared by the module '${stringifyType(moduleType)}'`),
moduleType);
return;
@ -471,7 +471,7 @@ export class CompileMetadataResolver {
transitiveModule.addExportedPipe(exportedId);
} else {
this._reportError(
new Error(
new SyntaxError(
`Can't export ${this._getTypeDescriptor(exportedId.reference)} ${stringifyType(exportedId.reference)} from ${stringifyType(moduleType)} as it was neither declared nor imported!`),
moduleType);
}
@ -494,7 +494,7 @@ export class CompileMetadataResolver {
flattenAndDedupeArray(meta.bootstrap).forEach(type => {
if (!isValidType(type)) {
this._reportError(
new Error(
new SyntaxError(
`Unexpected value '${stringifyType(type)}' used in the bootstrap property of module '${stringifyType(moduleType)}'`),
moduleType);
return;
@ -557,7 +557,7 @@ export class CompileMetadataResolver {
const oldModule = this._ngModuleOfTypes.get(type);
if (oldModule && oldModule !== moduleType) {
this._reportError(
new Error(
new SyntaxError(
`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)}. ` +
`You can also create a new NgModule that exports and includes ${stringifyType(type)} then import that NgModule in ${stringifyType(oldModule)} and ${stringifyType(moduleType)}.`),
@ -653,7 +653,7 @@ export class CompileMetadataResolver {
const pipeMeta = this._pipeCache.get(pipeType);
if (!pipeMeta) {
this._reportError(
new Error(
new SyntaxError(
`Illegal state: getPipeMetadata can only be called after loadNgModuleMetadata for a module that declares it. Pipe ${stringifyType(pipeType)}.`),
pipeType);
}
@ -665,7 +665,7 @@ export class CompileMetadataResolver {
<cpl.CompilePipeSummary>this._loadSummary(pipeType, cpl.CompileSummaryKind.Pipe);
if (!pipeSummary) {
this._reportError(
new Error(
new SyntaxError(
`Illegal state: Could not load the summary for pipe ${stringifyType(pipeType)}.`),
pipeType);
}
@ -748,7 +748,7 @@ export class CompileMetadataResolver {
const depsTokens =
dependenciesMetadata.map((dep) => dep ? stringifyType(dep.token) : '?').join(', ');
this._reportError(
new Error(
new SyntaxError(
`Can't resolve all parameters for ${stringifyType(typeOrFunc)}: (${depsTokens}).`),
typeOrFunc);
}
@ -797,7 +797,7 @@ export class CompileMetadataResolver {
[]))
.join(', ');
this._reportError(
new Error(
new SyntaxError(
`Invalid ${debugInfo ? debugInfo : 'provider'} - only instances of Provider and Type are allowed, got: [${providersInfo}]`),
type);
}
@ -818,13 +818,14 @@ export class CompileMetadataResolver {
if (provider.useFactory || provider.useExisting || provider.useClass) {
this._reportError(
new Error(`The ANALYZE_FOR_ENTRY_COMPONENTS token only supports useValue!`), type);
new SyntaxError(`The ANALYZE_FOR_ENTRY_COMPONENTS token only supports useValue!`), type);
return [];
}
if (!provider.multi) {
this._reportError(
new Error(`The ANALYZE_FOR_ENTRY_COMPONENTS token only supports 'multi = true'!`), type);
new SyntaxError(`The ANALYZE_FOR_ENTRY_COMPONENTS token only supports 'multi = true'!`),
type);
return [];
}
@ -893,7 +894,7 @@ export class CompileMetadataResolver {
} else {
if (!q.selector) {
this._reportError(
new Error(
new SyntaxError(
`Can't construct a query for the property "${propertyName}" of "${stringifyType(typeOrFunc)}" since the query selector wasn't defined.`),
typeOrFunc);
}
@ -961,7 +962,7 @@ export function componentModuleUrl(
const scheme = getUrlScheme(moduleId);
return scheme ? moduleId : `package:${moduleId}${MODULE_SUFFIX}`;
} else if (moduleId !== null && moduleId !== void 0) {
throw new Error(
throw new SyntaxError(
`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.`);
}

View File

@ -24,7 +24,7 @@ import {ProviderElementContext, ProviderViewContext} from '../provider_analyzer'
import {ElementSchemaRegistry} from '../schema/element_schema_registry';
import {CssSelector, SelectorMatcher} from '../selector';
import {isStyleUrlResolvable} from '../style_url_resolver';
import {SyntaxError} from '../util';
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 {PreparsedElementType, preparseElement} from './template_preparser';
@ -99,7 +99,7 @@ export class TemplateParser {
if (errors.length > 0) {
const errorString = errors.join('\n');
throw new Error(`Template parse errors:\n${errorString}`);
throw new SyntaxError(`Template parse errors:\n${errorString}`);
}
return result.templateAst;

View File

@ -6,8 +6,8 @@
* found in the LICENSE file at https://angular.io/license
*/
import {BaseError} from './facade/errors';
import {isPrimitive, isStrictStringMap} from './facade/lang';
export const MODULE_SUFFIX = '';
const CAMEL_CASE_REGEXP = /([A-Z])/g;
@ -78,3 +78,5 @@ export class SyncAsyncResult<T> {
}
}
}
export class SyntaxError extends BaseError {}

View File

@ -7,8 +7,7 @@
*/
import {AotSummaryResolver, AotSummaryResolverHost, CompileSummaryKind, CompileTypeSummary, ResolvedStaticSymbol, StaticSymbol, StaticSymbolCache, StaticSymbolResolver} from '@angular/compiler';
import {AotSummarySerializerHost} from '@angular/compiler/src/aot/summary_serializer';
import {deserializeSummaries, serializeSummaries} from '@angular/compiler/src/aot/summary_serializer';
import {AotSummarySerializerHost, deserializeSummaries, serializeSummaries} from '@angular/compiler/src/aot/summary_serializer';
import * as path from 'path';
import {MockStaticSymbolResolverHost, MockSummaryResolver} from './static_symbol_resolver_spec';

View File

@ -5,7 +5,7 @@
* 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 {SyntaxError} from '@angular/compiler';
import {CompileDirectiveMetadata, CompileStylesheetMetadata, CompileTemplateMetadata, CompileTypeMetadata} from '@angular/compiler/src/compile_metadata';
import {CompilerConfig} from '@angular/compiler/src/config';
import {DirectiveNormalizer} from '@angular/compiler/src/directive_normalizer';
@ -31,7 +31,7 @@ export function main() {
expect(() => normalizer.normalizeTemplate({
componentType: SomeComp,
moduleUrl: SOME_MODULE_URL,
})).toThrowError('No template specified for component SomeComp');
})).toThrowError(SyntaxError, 'No template specified for component SomeComp');
}));
});

View File

@ -15,8 +15,8 @@ import {identifierName} from '../src/compile_metadata';
import {stringify} from '../src/facade/lang';
import {CompileMetadataResolver} from '../src/metadata_resolver';
import {ResourceLoader} from '../src/resource_loader';
import {SyntaxError} from '../src/util';
import {MockResourceLoader} from '../testing/resource_loader_mock';
import {MalformedStylesComponent} from './metadata_resolver_fixture';
export function main() {
@ -34,8 +34,9 @@ export function main() {
}
expect(() => resolver.getDirectiveMetadata(ComponentWithEverythingInline))
.toThrowError(/Illegal state/);
expect(() => resolver.getPipeMetadata(SomePipe)).toThrowError(/Illegal state/);
.toThrowError(SyntaxError, /Illegal state/);
expect(() => resolver.getPipeMetadata(SomePipe))
.toThrowError(SyntaxError, /Illegal state/);
}));
it('should read metadata in sync for components with inline resources',
@ -121,7 +122,7 @@ export function main() {
expect(() => resolver.loadNgModuleDirectiveAndPipeMetadata(SomeModule, true))
.toThrowError(
`moduleId should be a string in "ComponentWithInvalidModuleId". See` +
SyntaxError, `moduleId should be a string in "ComponentWithInvalidModuleId". 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.`);
@ -145,7 +146,7 @@ export function main() {
}
expect(() => resolver.loadNgModuleDirectiveAndPipeMetadata(SomeModule, true))
.toThrowError(`Can't resolve all parameters for MyBrokenComp1: (?).`);
.toThrowError(SyntaxError, `Can't resolve all parameters for MyBrokenComp1: (?).`);
}));
it('should throw with descriptive error message when a directive is passed to imports',
inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => {
@ -155,6 +156,7 @@ export function main() {
expect(
() => resolver.loadNgModuleDirectiveAndPipeMetadata(ModuleWithImportedComponent, true))
.toThrowError(
SyntaxError,
`Unexpected directive 'ComponentWithoutModuleId' imported by the module 'ModuleWithImportedComponent'`);
}));
@ -168,6 +170,7 @@ export function main() {
}
expect(() => resolver.loadNgModuleDirectiveAndPipeMetadata(ModuleWithImportedPipe, true))
.toThrowError(
SyntaxError,
`Unexpected pipe 'SomePipe' imported by the module 'ModuleWithImportedPipe'`);
}));
@ -181,6 +184,7 @@ export function main() {
}
expect(() => resolver.loadNgModuleDirectiveAndPipeMetadata(ModuleWithDeclaredModule, true))
.toThrowError(
SyntaxError,
`Unexpected module 'SomeModule' declared by the module 'ModuleWithDeclaredModule'`);
}));
@ -191,6 +195,7 @@ export function main() {
}
expect(() => resolver.loadNgModuleDirectiveAndPipeMetadata(ModuleWithNullDeclared, true))
.toThrowError(
SyntaxError,
`Unexpected value 'null' declared by the module 'ModuleWithNullDeclared'`);
}));
@ -201,6 +206,7 @@ export function main() {
}
expect(() => resolver.loadNgModuleDirectiveAndPipeMetadata(ModuleWithNullImported, true))
.toThrowError(
SyntaxError,
`Unexpected value 'null' imported by the module 'ModuleWithNullImported'`);
}));
@ -212,7 +218,8 @@ export function main() {
}
expect(() => resolver.loadNgModuleDirectiveAndPipeMetadata(SomeModule, true))
.toThrowError(`Can't resolve all parameters for NonAnnotatedService: (?).`);
.toThrowError(
SyntaxError, `Can't resolve all parameters for NonAnnotatedService: (?).`);
}));
it('should throw with descriptive error message when one of providers is not present',
@ -223,6 +230,7 @@ export function main() {
expect(() => resolver.loadNgModuleDirectiveAndPipeMetadata(SomeModule, true))
.toThrowError(
SyntaxError,
`Invalid providers for "MyBrokenComp3" - only instances of Provider and Type are allowed, got: [SimpleService, ?null?, ...]`);
}));
@ -234,6 +242,7 @@ export function main() {
expect(() => resolver.loadNgModuleDirectiveAndPipeMetadata(SomeModule, true))
.toThrowError(
SyntaxError,
`Invalid viewProviders for "MyBrokenComp4" - only instances of Provider and Type are allowed, got: [?null?, ...]`);
}));
@ -248,11 +257,13 @@ export function main() {
expect(() => resolver.loadNgModuleDirectiveAndPipeMetadata(ModuleWithNullBootstrap, true))
.toThrowError(
SyntaxError,
`Unexpected value 'null' used in the bootstrap property of module 'ModuleWithNullBootstrap'`);
expect(
() =>
resolver.loadNgModuleDirectiveAndPipeMetadata(ModuleWithUndefinedBootstrap, true))
.toThrowError(
SyntaxError,
`Unexpected value 'undefined' used in the bootstrap property of module 'ModuleWithUndefinedBootstrap'`);
}));

View File

@ -5,7 +5,7 @@
* 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 {SyntaxError} from '@angular/compiler';
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 {ElementSchemaRegistry} from '@angular/compiler/src/schema/element_schema_registry';
@ -374,7 +374,7 @@ export function main() {
describe('errors', () => {
it('should throw error when binding to an unknown property', () => {
expect(() => parse('<my-component [invalidProp]="bar"></my-component>', []))
.toThrowError(`Template parse errors:
.toThrowError(SyntaxError, `Template parse errors:
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.
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,14 +382,16 @@ 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', () => {
expect(() => parse('<unknown></unknown>', [])).toThrowError(`Template parse errors:
expect(() => parse('<unknown></unknown>', []))
.toThrowError(SyntaxError, `Template parse errors:
'unknown' is not a known element:
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`);
});
it('should throw error when binding to an unknown custom element w/o bindings', () => {
expect(() => parse('<un-known></un-known>', [])).toThrowError(`Template parse errors:
expect(() => parse('<un-known></un-known>', []))
.toThrowError(SyntaxError, `Template parse errors:
'un-known' is not a known element:
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`);
@ -397,13 +399,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', () => {
expect(() => parse('<my-component [onEvent]="bar"></my-component>', []))
.toThrowError(`Template parse errors:
.toThrowError(SyntaxError, `Template parse errors:
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', () => {
expect(() => parse('<my-component [attr.onEvent]="bar"></my-component>', []))
.toThrowError(`Template parse errors:
.toThrowError(SyntaxError, `Template parse errors:
Binding to attribute 'onEvent' is disallowed for security reasons ("<my-component [ERROR ->][attr.onEvent]="bar"></my-component>"): TestComp@0:14`);
});
});
@ -445,6 +447,7 @@ Binding to attribute 'onEvent' is disallowed for security reasons ("<my-componen
() => {
expect(() => { parse('<div @someAnimation="value2">', [], [], []); })
.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/);
});
@ -479,6 +482,7 @@ Binding to attribute 'onEvent' is disallowed for security reasons ("<my-componen
expect(() => { parse('<broken></broken>', [dirA]); })
.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`);
});
@ -494,6 +498,7 @@ Binding to attribute 'onEvent' is disallowed for security reasons ("<my-componen
expect(() => { parse('<broken></broken>', [dirA]); })
.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`);
});
@ -940,7 +945,7 @@ Binding to attribute 'onEvent' is disallowed for security reasons ("<my-componen
const dirB = createDir('[dirB]', {providers: [provider1]});
expect(() => parse('<div dirA dirB>', [dirA, dirB]))
.toThrowError(
`Template parse errors:\n` +
SyntaxError, `Template parse errors:\n` +
`Mixing multi and non multi provider is not possible for token service0 ("[ERROR ->]<div dirA dirB>"): TestComp@0:0`);
});
@ -1033,6 +1038,7 @@ Binding to attribute 'onEvent' is disallowed for security reasons ("<my-componen
const dirA = createDir('[dirA]', {deps: ['self:provider0']});
expect(() => parse('<div dirA></div>', [dirA]))
.toThrowError(
SyntaxError,
'Template parse errors:\nNo provider for provider0 ("[ERROR ->]<div dirA></div>"): TestComp@0:0');
});
@ -1047,6 +1053,7 @@ Binding to attribute 'onEvent' is disallowed for security reasons ("<my-componen
const dirA = createDir('[dirA]', {deps: ['host:provider0']});
expect(() => parse('<div dirA></div>', [dirA]))
.toThrowError(
SyntaxError,
'Template parse errors:\nNo provider for provider0 ("[ERROR ->]<div dirA></div>"): TestComp@0:0');
});
@ -1098,23 +1105,26 @@ Binding to attribute 'onEvent' is disallowed for security reasons ("<my-componen
});
it('should report references with values that dont match a directive as errors', () => {
expect(() => parse('<div #a="dirA"></div>', [])).toThrowError(`Template parse errors:
expect(() => parse('<div #a="dirA"></div>', []))
.toThrowError(SyntaxError, `Template parse errors:
There is no directive with "exportAs" set to "dirA" ("<div [ERROR ->]#a="dirA"></div>"): TestComp@0:5`);
});
it('should report invalid reference names', () => {
expect(() => parse('<div #a-b></div>', [])).toThrowError(`Template parse errors:
expect(() => parse('<div #a-b></div>', []))
.toThrowError(SyntaxError, `Template parse errors:
"-" is not allowed in reference names ("<div [ERROR ->]#a-b></div>"): TestComp@0:5`);
});
it('should report variables as errors', () => {
expect(() => parse('<div let-a></div>', [])).toThrowError(`Template parse errors:
expect(() => parse('<div let-a></div>', []))
.toThrowError(SyntaxError, `Template parse errors:
"let-" is only supported on template elements. ("<div [ERROR ->]let-a></div>"): TestComp@0:5`);
});
it('should report duplicate reference names', () => {
expect(() => parse('<div #a></div><div #a></div>', []))
.toThrowError(`Template parse errors:
.toThrowError(SyntaxError, `Template parse errors:
Reference "#a" is defined several times ("<div #a></div><div [ERROR ->]#a></div>"): TestComp@0:19`);
});
@ -1466,7 +1476,7 @@ Reference "#a" is defined several times ("<div #a></div><div [ERROR ->]#a></div>
it('should report when ng-content has non WS content', () => {
expect(() => parse('<ng-content>content</ng-content>', []))
.toThrowError(
`Template parse errors:\n` +
SyntaxError, `Template parse errors:\n` +
`<ng-content> element cannot have content. ("[ERROR ->]<ng-content>content</ng-content>"): TestComp@0:0`);
});
@ -1477,18 +1487,20 @@ Reference "#a" is defined several times ("<div #a></div><div [ERROR ->]#a></div>
() => { expect(() => parse('<template template="ngIf">', [])).not.toThrowError(); });
it('should report when mutliple *attrs are used on the same element', () => {
expect(() => parse('<div *ngIf *ngFor>', [])).toThrowError(`Template parse errors:
expect(() => parse('<div *ngIf *ngFor>', []))
.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`);
});
it('should report when mix of template and *attrs are used on the same element', () => {
expect(() => parse('<span template="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 * ("<span template="ngIf" [ERROR ->]*ngFor>"): TestComp@0:22`);
});
it('should report invalid property names', () => {
expect(() => parse('<div [invalidProp]></div>', [])).toThrowError(`Template parse errors:
expect(() => parse('<div [invalidProp]></div>', []))
.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`);
});
@ -1501,12 +1513,13 @@ Can't bind to 'invalidProp' since it isn't a known property of 'div'. ("<div [ER
host: {'[invalidProp]': 'someProp'}
})
.toSummary();
expect(() => parse('<div></div>', [dirA])).toThrowError(`Template parse errors:
expect(() => parse('<div></div>', [dirA])).toThrowError(SyntaxError, `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`);
});
it('should report errors in expressions', () => {
expect(() => parse('<div [prop]="a b"></div>', [])).toThrowError(`Template parse errors:
expect(() => parse('<div [prop]="a b"></div>', []))
.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`);
});
@ -1544,7 +1557,7 @@ Parser Error: Unexpected token 'b' at column 3 in [a b] in TestComp@0:5 ("<div [
.toSummary();
expect(() => parse('<div>', [dirB, dirA]))
.toThrowError(
`Template parse errors:\n` +
SyntaxError, `Template parse errors:\n` +
`More than one component matched on this 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`);
@ -1562,7 +1575,7 @@ Parser Error: Unexpected token 'b' at column 3 in [a b] in TestComp@0:5 ("<div [
})
.toSummary();
expect(() => parse('<template [a]="b" (e)="f"></template>', [dirA]))
.toThrowError(`Template parse errors:
.toThrowError(SyntaxError, `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
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`);
@ -1578,7 +1591,8 @@ Property binding a not used by any directive on an embedded template. Make sure
template: new CompileTemplateMetadata({ngContentSelectors: []})
})
.toSummary();
expect(() => parse('<div *a="b"></div>', [dirA])).toThrowError(`Template parse errors:
expect(() => parse('<div *a="b"></div>', [dirA]))
.toThrowError(SyntaxError, `Template parse errors:
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`);
});
@ -1838,7 +1852,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', () => {
expect(() => parse('{{a | test}}', [])).toThrowError(`Template parse errors:
expect(() => parse('{{a | test}}', [])).toThrowError(SyntaxError, `Template parse errors:
The pipe 'test' could not be found ("{{[ERROR ->]a | test}}"): TestComp@0:2`);
});

View File

@ -51,13 +51,15 @@ export function main() {
}));
it('should output an error message to the console and re-throw', fakeAsync(() => {
spyOn(console, 'error');
let consoleErrorSpy: jasmine.Spy = spyOn(console, 'error');
expect(() => {
adapter.bootstrap(html('<ng2></ng2>'), ['ng1']);
flushMicrotasks();
}).toThrowError();
expect(console.error).toHaveBeenCalled();
expect(console.error).toHaveBeenCalledWith(jasmine.any(Error), jasmine.any(String));
let args: any[] = consoleErrorSpy.calls.mostRecent().args;
expect(consoleErrorSpy).toHaveBeenCalled();
expect(args.length).toBeGreaterThan(0);
expect(args[0]).toEqual(jasmine.any(Error));
}));
});