fix(ivy): directives without selector should not be supported (#28021)

PR Close #28021
This commit is contained in:
Marc Laval 2019-01-09 17:55:23 +01:00 committed by Andrew Kushnir
parent 7374dfd1fa
commit 76ed13bffe
4 changed files with 55 additions and 32 deletions

View File

@ -165,6 +165,9 @@ export function extractDirectiveMetadata(
} }
selector = resolved; selector = resolved;
} }
if (!selector) {
throw new Error(`Directive ${clazz.name !.text} has no selector, please add it!`);
}
const host = extractHostBindings(directive, decoratedElements, evaluator, coreModule); const host = extractHostBindings(directive, decoratedElements, evaluator, coreModule);

View File

@ -640,9 +640,6 @@ describe('compiler compliance', () => {
'spec.ts': ` 'spec.ts': `
import {Component, Directive, NgModule} from '@angular/core'; import {Component, Directive, NgModule} from '@angular/core';
@Directive({})
export class EmptyOutletDirective {}
@Component({template: '<router-outlet></router-outlet>'}) @Component({template: '<router-outlet></router-outlet>'})
export class EmptyOutletComponent {} export class EmptyOutletComponent {}
@ -652,16 +649,6 @@ describe('compiler compliance', () => {
} }
}; };
// EmptyOutletDirective definition should be:
const EmptyOutletDirectiveDefinition = `
EmptyOutletDirective.ngDirectiveDef = $r3$.ɵdefineDirective({
type: EmptyOutletDirective,
selectors: [],
factory: function EmptyOutletDirective_Factory(t) { return new (t || EmptyOutletDirective)(); }
});
`;
// EmptyOutletComponent definition should be: // EmptyOutletComponent definition should be:
const EmptyOutletComponentDefinition = ` const EmptyOutletComponentDefinition = `
@ -683,13 +670,48 @@ describe('compiler compliance', () => {
const result = compile(files, angularFiles); const result = compile(files, angularFiles);
const source = result.source; const source = result.source;
expectEmit(
source, EmptyOutletDirectiveDefinition,
'Incorrect EmptyOutletDirective.ngDirectiveDefDef');
expectEmit( expectEmit(
source, EmptyOutletComponentDefinition, 'Incorrect EmptyOutletComponent.ngComponentDef'); source, EmptyOutletComponentDefinition, 'Incorrect EmptyOutletComponent.ngComponentDef');
}); });
it('should not support directives without selector', () => {
const files = {
app: {
'spec.ts': `
import {Component, Directive, NgModule} from '@angular/core';
@Directive({})
export class EmptyOutletDirective {}
@NgModule({declarations: [EmptyOutletDirective]})
export class MyModule{}
`
}
};
expect(() => compile(files, angularFiles))
.toThrowError('Directive EmptyOutletDirective has no selector, please add it!');
});
it('should not support directives with empty selector', () => {
const files = {
app: {
'spec.ts': `
import {Component, Directive, NgModule} from '@angular/core';
@Directive({selector: ''})
export class EmptyOutletDirective {}
@NgModule({declarations: [EmptyOutletDirective]})
export class MyModule{}
`
}
};
expect(() => compile(files, angularFiles))
.toThrowError('Directive EmptyOutletDirective has no selector, please add it!');
});
it('should not treat ElementRef, ViewContainerRef, or ChangeDetectorRef specially when injecting', it('should not treat ElementRef, ViewContainerRef, or ChangeDetectorRef specially when injecting',
() => { () => {
const files = { const files = {

View File

@ -164,9 +164,9 @@ export function compileDirectiveFromMetadata(
addFeatures(definitionMap, meta); addFeatures(definitionMap, meta);
const expression = o.importExpr(R3.defineDirective).callFn([definitionMap.toLiteralMap()]); const expression = o.importExpr(R3.defineDirective).callFn([definitionMap.toLiteralMap()]);
// On the type side, remove newlines from the selector as it will need to fit into a TypeScript if (!meta.selector) {
// string literal, which must be on one line. throw new Error(`Directive ${meta.name} has no selector, please add it!`);
const selectorForType = (meta.selector || '').replace(/\n/g, ''); }
const type = createTypeForDef(meta, R3.DirectiveDefWithMeta); const type = createTypeForDef(meta, R3.DirectiveDefWithMeta);
return {expression, type, statements}; return {expression, type, statements};

View File

@ -1423,8 +1423,7 @@ function declareTests(config?: {useJit: boolean}) {
expect(getDOM().querySelectorAll(fixture.nativeElement, 'script').length).toEqual(0); expect(getDOM().querySelectorAll(fixture.nativeElement, 'script').length).toEqual(0);
}); });
fixmeIvy('FW-662: Components without selector are not supported') it('should throw when using directives without selector', () => {
.it('should throw when using directives without selector', () => {
@Directive({}) @Directive({})
class SomeDirective { class SomeDirective {
} }
@ -1435,8 +1434,7 @@ function declareTests(config?: {useJit: boolean}) {
TestBed.configureTestingModule({declarations: [MyComp, SomeDirective, SomeComponent]}); TestBed.configureTestingModule({declarations: [MyComp, SomeDirective, SomeComponent]});
expect(() => TestBed.createComponent(MyComp)) expect(() => TestBed.createComponent(MyComp))
.toThrowError( .toThrowError(`Directive ${stringify(SomeDirective)} has no selector, please add it!`);
`Directive ${stringify(SomeDirective)} has no selector, please add it!`);
}); });
it('should use a default element name for components without selectors', () => { it('should use a default element name for components without selectors', () => {