diff --git a/packages/compiler-cli/ngcc/test/rendering/renderer_spec.ts b/packages/compiler-cli/ngcc/test/rendering/renderer_spec.ts index 5d04b53e08843..aab86206d3f71 100644 --- a/packages/compiler-cli/ngcc/test/rendering/renderer_spec.ts +++ b/packages/compiler-cli/ngcc/test/rendering/renderer_spec.ts @@ -310,7 +310,7 @@ A.ɵdir = ɵngcc0.ɵɵdefineDirective({ type: A, selectors: [["", "a", ""]] }); expect(addDefinitionsSpy.calls.first().args[2]) .toEqual( `UndecoratedBase.ɵfac = function UndecoratedBase_Factory(t) { return new (t || UndecoratedBase)(); }; -UndecoratedBase.ɵdir = ɵngcc0.ɵɵdefineDirective({ type: UndecoratedBase, selectors: [], viewQuery: function UndecoratedBase_Query(rf, ctx) { if (rf & 1) { +UndecoratedBase.ɵdir = ɵngcc0.ɵɵdefineDirective({ type: UndecoratedBase, viewQuery: function UndecoratedBase_Query(rf, ctx) { if (rf & 1) { ɵngcc0.ɵɵstaticViewQuery(_c0, true); } if (rf & 2) { var _t; 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 35d449e44ec80..96b45b9bbd50b 100644 --- a/packages/compiler-cli/test/compliance/r3_compiler_compliance_spec.ts +++ b/packages/compiler-cli/test/compliance/r3_compiler_compliance_spec.ts @@ -2958,6 +2958,28 @@ describe('compiler compliance', () => { expect(() => compile(files, angularFiles)).not.toThrow(); }); + it('should not generate a selectors array if the directive does not have a selector', () => { + const files = { + app: { + 'spec.ts': ` + import {Directive} from '@angular/core'; + + @Directive() + export class AbstractDirective { + } + ` + } + }; + const expectedOutput = ` + // ... + AbstractDirective.ɵdir = $r3$.ɵɵdefineDirective({ + type: AbstractDirective + }); + // ... + `; + const result = compile(files, angularFiles); + expectEmit(result.source, expectedOutput, 'Invalid directive definition'); + }); }); @@ -3004,7 +3026,6 @@ describe('compiler compliance', () => { // ... BaseClass.ɵdir = $r3$.ɵɵdefineDirective({ type: BaseClass, - selectors: [], inputs: { input1: "input1", input2: ["alias2", "input2"] @@ -3013,7 +3034,7 @@ describe('compiler compliance', () => { // ... `; const result = compile(files, angularFiles); - expectEmit(result.source, expectedOutput, 'Invalid base definition'); + expectEmit(result.source, expectedOutput, 'Invalid directive definition'); }); it('should add an abstract directive if one or more @Output is present', () => { @@ -3052,7 +3073,6 @@ describe('compiler compliance', () => { // ... BaseClass.ɵdir = $r3$.ɵɵdefineDirective({ type: BaseClass, - selectors: [], outputs: { output1: "output1", output2: "output2" @@ -3061,7 +3081,7 @@ describe('compiler compliance', () => { // ... `; const result = compile(files, angularFiles); - expectEmit(result.source, expectedOutput, 'Invalid base definition'); + expectEmit(result.source, expectedOutput, 'Invalid directive definition'); }); it('should add an abstract directive if a mixture of @Input and @Output props are present', @@ -3107,7 +3127,6 @@ describe('compiler compliance', () => { // ... BaseClass.ɵdir = $r3$.ɵɵdefineDirective({ type: BaseClass, - selectors: [], inputs: { input1: "input1", input2: ["whatever", "input2"] @@ -3120,7 +3139,7 @@ describe('compiler compliance', () => { // ... `; const result = compile(files, angularFiles); - expectEmit(result.source, expectedOutput, 'Invalid base definition'); + expectEmit(result.source, expectedOutput, 'Invalid directive definition'); }); it('should add an abstract directive if a ViewChild query is present', () => { @@ -3151,7 +3170,6 @@ describe('compiler compliance', () => { // ... BaseClass.ɵdir = $r3$.ɵɵdefineDirective({ type: BaseClass, - selectors: [], viewQuery: function BaseClass_Query(rf, ctx) { if (rf & 1) { $r3$.ɵɵviewQuery($e0_attrs$, true); @@ -3165,7 +3183,7 @@ describe('compiler compliance', () => { // ... `; const result = compile(files, angularFiles); - expectEmit(result.source, expectedOutput, 'Invalid base definition'); + expectEmit(result.source, expectedOutput, 'Invalid directive definition'); }); it('should add an abstract directive if a ViewChildren query is present', () => { @@ -3198,7 +3216,6 @@ describe('compiler compliance', () => { // ... BaseClass.ɵdir = $r3$.ɵɵdefineDirective({ type: BaseClass, - selectors: [], viewQuery: function BaseClass_Query(rf, ctx) { if (rf & 1) { $r3$.ɵɵviewQuery(SomeDirective, true); @@ -3212,7 +3229,7 @@ describe('compiler compliance', () => { // ... `; const result = compile(files, angularFiles); - expectEmit(result.source, expectedOutput, 'Invalid base definition'); + expectEmit(result.source, expectedOutput, 'Invalid directive definition'); }); it('should add an abstract directive if a ContentChild query is present', () => { @@ -3243,7 +3260,6 @@ describe('compiler compliance', () => { // ... BaseClass.ɵdir = $r3$.ɵɵdefineDirective({ type: BaseClass, - selectors: [], contentQueries: function BaseClass_ContentQueries(rf, ctx, dirIndex) { if (rf & 1) { $r3$.ɵɵcontentQuery(dirIndex, $e0_attrs$, true); @@ -3257,7 +3273,7 @@ describe('compiler compliance', () => { // ... `; const result = compile(files, angularFiles); - expectEmit(result.source, expectedOutput, 'Invalid base definition'); + expectEmit(result.source, expectedOutput, 'Invalid directive definition'); }); it('should add an abstract directive if a ContentChildren query is present', () => { @@ -3290,7 +3306,6 @@ describe('compiler compliance', () => { // ... BaseClass.ɵdir = $r3$.ɵɵdefineDirective({ type: BaseClass, - selectors: [], contentQueries: function BaseClass_ContentQueries(rf, ctx, dirIndex) { if (rf & 1) { $r3$.ɵɵcontentQuery(dirIndex, SomeDirective, false); @@ -3304,7 +3319,7 @@ describe('compiler compliance', () => { // ... `; const result = compile(files, angularFiles); - expectEmit(result.source, expectedOutput, 'Invalid base definition'); + expectEmit(result.source, expectedOutput, 'Invalid directive definition'); }); it('should add an abstract directive if a host binding is present', () => { @@ -3335,7 +3350,6 @@ describe('compiler compliance', () => { // ... BaseClass.ɵdir = $r3$.ɵɵdefineDirective({ type: BaseClass, - selectors: [], hostBindings: function BaseClass_HostBindings(rf, ctx, elIndex) { if (rf & 1) { $r3$.ɵɵallocHostVars(1); @@ -3348,7 +3362,7 @@ describe('compiler compliance', () => { // ... `; const result = compile(files, angularFiles); - expectEmit(result.source, expectedOutput, 'Invalid base definition'); + expectEmit(result.source, expectedOutput, 'Invalid directive definition'); }); it('should add an abstract directive if a host listener is present', () => { @@ -3379,7 +3393,6 @@ describe('compiler compliance', () => { // ... BaseClass.ɵdir = $r3$.ɵɵdefineDirective({ type: BaseClass, - selectors: [], hostBindings: function BaseClass_HostBindings(rf, ctx, elIndex) { if (rf & 1) { $r3$.ɵɵlistener("mousedown", function BaseClass_mousedown_HostBindingHandler($event) { @@ -3391,7 +3404,7 @@ describe('compiler compliance', () => { // ... `; const result = compile(files, angularFiles); - expectEmit(result.source, expectedOutput, 'Invalid base definition'); + expectEmit(result.source, expectedOutput, 'Invalid directive definition'); }); it('should add an abstract directive when using any lifecycle hook', () => { @@ -3420,13 +3433,12 @@ describe('compiler compliance', () => { const expectedOutput = ` // ... BaseClass.ɵdir = $r3$.ɵɵdefineDirective({ - type: BaseClass, - selectors: [] + type: BaseClass }); // ... `; const result = compile(files, angularFiles); - expectEmit(result.source, expectedOutput, 'Invalid base definition'); + expectEmit(result.source, expectedOutput, 'Invalid directive definition'); }); @@ -3457,13 +3469,12 @@ describe('compiler compliance', () => { // ... BaseClass.ɵdir = $r3$.ɵɵdefineDirective({ type: BaseClass, - selectors: [], features: [$r3$.ɵɵNgOnChangesFeature()] }); // ... `; const result = compile(files, angularFiles); - expectEmit(result.source, expectedOutput, 'Invalid base definition'); + expectEmit(result.source, expectedOutput, 'Invalid directive definition'); }); it('should NOT add an abstract directive if @Component is present', () => { diff --git a/packages/compiler-cli/test/ngtsc/ngtsc_spec.ts b/packages/compiler-cli/test/ngtsc/ngtsc_spec.ts index c203010f4f60b..df808e4bdbaa8 100644 --- a/packages/compiler-cli/test/ngtsc/ngtsc_spec.ts +++ b/packages/compiler-cli/test/ngtsc/ngtsc_spec.ts @@ -516,8 +516,7 @@ runInEachFileSystem(os => { const jsContents = env.getContents('test.js'); expect(jsContents) - .toContain( - 'i0.ɵɵdefineDirective({ type: TestBase, selectors: [], inputs: { input: "input" } });'); + .toContain('i0.ɵɵdefineDirective({ type: TestBase, inputs: { input: "input" } });'); const dtsContents = env.getContents('test.d.ts'); expect(dtsContents) diff --git a/packages/compiler/src/render3/view/compiler.ts b/packages/compiler/src/render3/view/compiler.ts index 15880b8f2df78..bf8878ce40cc5 100644 --- a/packages/compiler/src/render3/view/compiler.ts +++ b/packages/compiler/src/render3/view/compiler.ts @@ -38,20 +38,19 @@ const EMPTY_ARRAY: any[] = []; // If there is a match, the first matching group will contain the attribute name to bind. const ATTR_REGEX = /attr\.([^\]]+)/; -function getStylingPrefix(name: string): string { - return name.substring(0, 5); // style or class -} - function baseDirectiveFields( meta: R3DirectiveMetadata, constantPool: ConstantPool, bindingParser: BindingParser): DefinitionMap { const definitionMap = new DefinitionMap(); + const selectors = core.parseSelectorToR3Selector(meta.selector); // e.g. `type: MyDirective` definitionMap.set('type', meta.type); // e.g. `selectors: [['', 'someDir', '']]` - definitionMap.set('selectors', createDirectiveSelector(meta.selector)); + if (selectors.length > 0) { + definitionMap.set('selectors', asLiteral(selectors)); + } if (meta.queries.length > 0) { // e.g. `contentQueries: (rf, ctx, dirIndex) => { ... } @@ -406,11 +405,6 @@ function prepareQueryParams(query: R3QueryMetadata, constantPool: ConstantPool): return parameters; } -// Turn a directive selector into an R3-compatible selector for directive def -function createDirectiveSelector(selector: string | null): o.Expression { - return asLiteral(core.parseSelectorToR3Selector(selector)); -} - function convertAttributesToExpressions(attributes: {[name: string]: o.Expression}): o.Expression[] { const values: o.Expression[] = []; diff --git a/packages/core/src/render3/definition.ts b/packages/core/src/render3/definition.ts index 5ac3be85e8c9d..228ac7599c3d1 100644 --- a/packages/core/src/render3/definition.ts +++ b/packages/core/src/render3/definition.ts @@ -48,7 +48,7 @@ export function ɵɵdefineComponent(componentDefinition: { type: Type; /** The selectors that will be used to match nodes to this component. */ - selectors: CssSelectorList; + selectors?: CssSelectorList; /** * The number of nodes, local refs, and pipes in this component template. @@ -275,7 +275,7 @@ export function ɵɵdefineComponent(componentDefinition: { onPush: componentDefinition.changeDetection === ChangeDetectionStrategy.OnPush, directiveDefs: null !, // assigned in noSideEffects pipeDefs: null !, // assigned in noSideEffects - selectors: componentDefinition.selectors, + selectors: componentDefinition.selectors || EMPTY_ARRAY, viewQuery: componentDefinition.viewQuery || null, features: componentDefinition.features as DirectiveDefFeature[] || null, data: componentDefinition.data || {}, @@ -507,7 +507,7 @@ export const ɵɵdefineDirective = ɵɵdefineComponent as any as(directiveDef type: Type; /** The selectors that will be used to match nodes to this directive. */ - selectors: CssSelectorList; + selectors?: CssSelectorList; /** * A map of input names. diff --git a/packages/core/test/render3/render_util.ts b/packages/core/test/render3/render_util.ts index 66dd809329c21..6f34eb2b46e65 100644 --- a/packages/core/test/render3/render_util.ts +++ b/packages/core/test/render3/render_util.ts @@ -261,7 +261,6 @@ export function renderTemplate( enterView(hostLView, null); const def: ComponentDef = ɵɵdefineComponent({ - selectors: [], type: Object, template: templateFn, decls: decls, diff --git a/tools/public_api_guard/core/core.d.ts b/tools/public_api_guard/core/core.d.ts index 2b75ccae93f33..fe70ea1d1230f 100644 --- a/tools/public_api_guard/core/core.d.ts +++ b/tools/public_api_guard/core/core.d.ts @@ -747,7 +747,7 @@ export declare const ɵɵdefaultStyleSanitizer: StyleSanitizeFn; export declare function ɵɵdefineComponent(componentDefinition: { type: Type; - selectors: CssSelectorList; + selectors?: CssSelectorList; decls: number; vars: number; inputs?: { @@ -777,7 +777,7 @@ export declare function ɵɵdefineComponent(componentDefinition: { export declare const ɵɵdefineDirective: (directiveDefinition: { type: Type; - selectors: (string | SelectorFlags)[][]; + selectors?: (string | SelectorFlags)[][] | undefined; inputs?: { [P in keyof T]?: string | [string, string] | undefined; } | undefined; outputs?: { [P_1 in keyof T]?: string | undefined; } | undefined; features?: DirectiveDefFeature[] | undefined;