fix(DirectiveResolver): throw on duplicate Input & Output names

This commit is contained in:
Victor Berchet 2016-07-11 16:01:49 -07:00
parent 93d0a01d3d
commit d1a3e3aff1
2 changed files with 63 additions and 12 deletions

View File

@ -7,11 +7,13 @@
*/ */
import {ComponentMetadata, DirectiveMetadata, HostBindingMetadata, HostListenerMetadata, Injectable, InputMetadata, OutputMetadata, QueryMetadata, resolveForwardRef} from '@angular/core'; import {ComponentMetadata, DirectiveMetadata, HostBindingMetadata, HostListenerMetadata, Injectable, InputMetadata, OutputMetadata, QueryMetadata, resolveForwardRef} from '@angular/core';
import {ReflectorReader, reflector} from '../core_private';
import {ListWrapper, StringMapWrapper} from '../src/facade/collection';
import {BaseException} from '../src/facade/exceptions';
import {Type, isPresent, stringify} from '../src/facade/lang';
import {ReflectorReader, reflector} from '../core_private';
import {StringMapWrapper} from './facade/collection';
import {BaseException} from './facade/exceptions';
import {Type, isPresent, stringify} from './facade/lang';
import {splitAtColon} from './util';
function _isDirectiveMetadata(type: any): type is DirectiveMetadata { function _isDirectiveMetadata(type: any): type is DirectiveMetadata {
return type instanceof DirectiveMetadata; return type instanceof DirectiveMetadata;
@ -91,20 +93,42 @@ export class DirectiveResolver {
return this._merge(dm, inputs, outputs, host, queries, directiveType); return this._merge(dm, inputs, outputs, host, queries, directiveType);
} }
private _extractPublicName(def: string) { return splitAtColon(def, [null, def])[1].trim(); }
private _merge( private _merge(
dm: DirectiveMetadata, inputs: string[], outputs: string[], host: {[key: string]: string}, dm: DirectiveMetadata, inputs: string[], outputs: string[], host: {[key: string]: string},
queries: {[key: string]: any}, directiveType: Type): DirectiveMetadata { queries: {[key: string]: any}, directiveType: Type): DirectiveMetadata {
var mergedInputs = isPresent(dm.inputs) ? ListWrapper.concat(dm.inputs, inputs) : inputs; let mergedInputs: string[];
var mergedOutputs: string[]; if (isPresent(dm.inputs)) {
if (isPresent(dm.outputs)) { const inputNames: string[] =
dm.outputs.forEach((propName: string) => { dm.inputs.map((def: string): string => this._extractPublicName(def));
if (ListWrapper.contains(outputs, propName)) { inputs.forEach((inputDef: string) => {
const publicName = this._extractPublicName(inputDef);
if (inputNames.indexOf(publicName) > -1) {
throw new BaseException( throw new BaseException(
`Output event '${propName}' defined multiple times in '${stringify(directiveType)}'`); `Input '${publicName}' defined multiple times in '${stringify(directiveType)}'`);
} }
}); });
mergedOutputs = ListWrapper.concat(dm.outputs, outputs); mergedInputs = dm.inputs.concat(inputs);
} else {
mergedInputs = inputs;
}
let mergedOutputs: string[];
if (isPresent(dm.outputs)) {
const outputNames: string[] =
dm.outputs.map((def: string): string => this._extractPublicName(def));
outputs.forEach((outputDef: string) => {
const publicName = this._extractPublicName(outputDef);
if (outputNames.indexOf(publicName) > -1) {
throw new BaseException(
`Output event '${publicName}' defined multiple times in '${stringify(directiveType)}'`);
}
});
mergedOutputs = dm.outputs.concat(outputs);
} else { } else {
mergedOutputs = outputs; mergedOutputs = outputs;
} }

View File

@ -31,7 +31,6 @@ class SomeDirectiveWithOutputs {
c: any; c: any;
} }
@Directive({selector: 'someDirective', outputs: ['a']}) @Directive({selector: 'someDirective', outputs: ['a']})
class SomeDirectiveWithDuplicateOutputs { class SomeDirectiveWithDuplicateOutputs {
@Output() a: any; @Output() a: any;
@ -43,6 +42,17 @@ class SomeDirectiveWithDuplicateRenamedOutputs {
localA: any; localA: any;
} }
@Directive({selector: 'someDirective', inputs: ['a']})
class SomeDirectiveWithDuplicateInputs {
@Input() a: any;
}
@Directive({selector: 'someDirective', inputs: ['localA: a']})
class SomeDirectiveWithDuplicateRenamedInputs {
@Input() a: any;
localA: any;
}
@Directive({selector: 'someDirective'}) @Directive({selector: 'someDirective'})
class SomeDirectiveWithSetterProps { class SomeDirectiveWithSetterProps {
@Input('renamed') @Input('renamed')
@ -133,6 +143,17 @@ export function main() {
expect(directiveMetadata.inputs).toEqual(['a: renamed']); expect(directiveMetadata.inputs).toEqual(['a: renamed']);
}); });
it('should throw if duplicate inputs', () => {
expect(() => {
resolver.resolve(SomeDirectiveWithDuplicateInputs);
}).toThrowError(`Input 'a' defined multiple times in 'SomeDirectiveWithDuplicateInputs'`);
});
it('should throw if duplicate inputs (with rename)', () => {
expect(() => { resolver.resolve(SomeDirectiveWithDuplicateRenamedInputs); })
.toThrowError(
`Input 'a' defined multiple times in 'SomeDirectiveWithDuplicateRenamedInputs'`);
});
}); });
describe('outputs', () => { describe('outputs', () => {
@ -151,6 +172,12 @@ export function main() {
.toThrowError( .toThrowError(
`Output event 'a' defined multiple times in 'SomeDirectiveWithDuplicateOutputs'`); `Output event 'a' defined multiple times in 'SomeDirectiveWithDuplicateOutputs'`);
}); });
it('should throw if duplicate outputs (with rename)', () => {
expect(() => { resolver.resolve(SomeDirectiveWithDuplicateRenamedOutputs); })
.toThrowError(
`Output event 'a' defined multiple times in 'SomeDirectiveWithDuplicateRenamedOutputs'`);
});
}); });
describe('host', () => { describe('host', () => {