diff --git a/packages/compiler-cli/src/ngtsc/annotations/src/directive.ts b/packages/compiler-cli/src/ngtsc/annotations/src/directive.ts
index a7a89500b2..27f569af19 100644
--- a/packages/compiler-cli/src/ngtsc/annotations/src/directive.ts
+++ b/packages/compiler-cli/src/ngtsc/annotations/src/directive.ts
@@ -165,6 +165,9 @@ export function extractDirectiveMetadata(
}
selector = resolved;
}
+ if (!selector) {
+ throw new Error(`Directive ${clazz.name !.text} has no selector, please add it!`);
+ }
const host = extractHostBindings(directive, decoratedElements, evaluator, coreModule);
diff --git a/packages/compiler-cli/test/compliance/r3_compiler_compliance_spec.ts b/packages/compiler-cli/test/compliance/r3_compiler_compliance_spec.ts
index 7ad7b35f18..7912501877 100644
--- a/packages/compiler-cli/test/compliance/r3_compiler_compliance_spec.ts
+++ b/packages/compiler-cli/test/compliance/r3_compiler_compliance_spec.ts
@@ -640,9 +640,6 @@ describe('compiler compliance', () => {
'spec.ts': `
import {Component, Directive, NgModule} from '@angular/core';
- @Directive({})
- export class EmptyOutletDirective {}
-
@Component({template: ''})
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:
const EmptyOutletComponentDefinition = `
…
@@ -683,13 +670,48 @@ describe('compiler compliance', () => {
const result = compile(files, angularFiles);
const source = result.source;
- expectEmit(
- source, EmptyOutletDirectiveDefinition,
- 'Incorrect EmptyOutletDirective.ngDirectiveDefDef');
expectEmit(
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',
() => {
const files = {
diff --git a/packages/compiler/src/render3/view/compiler.ts b/packages/compiler/src/render3/view/compiler.ts
index 93bf899c89..a326749a94 100644
--- a/packages/compiler/src/render3/view/compiler.ts
+++ b/packages/compiler/src/render3/view/compiler.ts
@@ -164,9 +164,9 @@ export function compileDirectiveFromMetadata(
addFeatures(definitionMap, meta);
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
- // string literal, which must be on one line.
- const selectorForType = (meta.selector || '').replace(/\n/g, '');
+ if (!meta.selector) {
+ throw new Error(`Directive ${meta.name} has no selector, please add it!`);
+ }
const type = createTypeForDef(meta, R3.DirectiveDefWithMeta);
return {expression, type, statements};
diff --git a/packages/core/test/linker/integration_spec.ts b/packages/core/test/linker/integration_spec.ts
index 679794e9c3..ade34f8038 100644
--- a/packages/core/test/linker/integration_spec.ts
+++ b/packages/core/test/linker/integration_spec.ts
@@ -1423,21 +1423,19 @@ function declareTests(config?: {useJit: boolean}) {
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', () => {
- @Directive({})
- class SomeDirective {
- }
+ it('should throw when using directives without selector', () => {
+ @Directive({})
+ class SomeDirective {
+ }
- @Component({selector: 'comp', template: ''})
- class SomeComponent {
- }
+ @Component({selector: 'comp', template: ''})
+ class SomeComponent {
+ }
- TestBed.configureTestingModule({declarations: [MyComp, SomeDirective, SomeComponent]});
- expect(() => TestBed.createComponent(MyComp))
- .toThrowError(
- `Directive ${stringify(SomeDirective)} has no selector, please add it!`);
- });
+ TestBed.configureTestingModule({declarations: [MyComp, SomeDirective, SomeComponent]});
+ expect(() => TestBed.createComponent(MyComp))
+ .toThrowError(`Directive ${stringify(SomeDirective)} has no selector, please add it!`);
+ });
it('should use a default element name for components without selectors', () => {
let noSelectorComponentFactory: ComponentFactory = undefined !;