diff --git a/packages/eslint-plugin/docs/rules/no-inputs-metadata-property.md b/packages/eslint-plugin/docs/rules/no-inputs-metadata-property.md index 7d86b54a9..69759ff75 100644 --- a/packages/eslint-plugin/docs/rules/no-inputs-metadata-property.md +++ b/packages/eslint-plugin/docs/rules/no-inputs-metadata-property.md @@ -489,6 +489,105 @@ class Test {} class Test {} ``` +
+ +--- + +
+ +#### Default Config + +```json +{ + "rules": { + "@angular-eslint/no-inputs-metadata-property": [ + "error" + ] + } +} +``` + +
+ +#### ✅ Valid Code + +```ts +@Component({ + selector: 'qx-menuitem', + hostDirectives: [{ + directive: CdkMenuItem, + inputs: ['cdkMenuItemDisabled: disabled'], + }] +}) +class Test {} +``` + +
+ +--- + +
+ +#### Default Config + +```json +{ + "rules": { + "@angular-eslint/no-inputs-metadata-property": [ + "error" + ] + } +} +``` + +
+ +#### ✅ Valid Code + +```ts +@Component({ + selector: 'qx-menuitem', + 'hostDirectives': [{ + directive: CdkMenuItem, + inputs: ['cdkMenuItemDisabled: disabled'], + }] +}) +class Test {} +``` + +
+ +--- + +
+ +#### Default Config + +```json +{ + "rules": { + "@angular-eslint/no-inputs-metadata-property": [ + "error" + ] + } +} +``` + +
+ +#### ✅ Valid Code + +```ts +@Component({ + selector: 'qx-menuitem', + ['hostDirectives']: [{ + directive: CdkMenuItem, + inputs: ['cdkMenuItemDisabled: disabled'], + }] +}) +class Test {} +``` +
diff --git a/packages/eslint-plugin/src/rules/no-inputs-metadata-property.ts b/packages/eslint-plugin/src/rules/no-inputs-metadata-property.ts index e48624cb1..2a91c0eaa 100644 --- a/packages/eslint-plugin/src/rules/no-inputs-metadata-property.ts +++ b/packages/eslint-plugin/src/rules/no-inputs-metadata-property.ts @@ -1,5 +1,6 @@ -import { Selectors } from '@angular-eslint/utils'; +import { ASTUtils, Selectors } from '@angular-eslint/utils'; import type { TSESTree } from '@typescript-eslint/utils'; +import { ASTUtils as TSESLintASTUtils } from '@typescript-eslint/utils'; import { createESLintRule } from '../utils/create-eslint-rule'; type Options = []; @@ -29,6 +30,30 @@ export default createESLintRule({ } ${Selectors.metadataProperty(METADATA_PROPERTY_NAME)}`]( node: TSESTree.Property, ) { + /** + * Angular v15 introduced the directive composition API: https://angular.io/guide/directive-composition-api + * Using host directive inputs using this API is not a bad practice and should not be reported + */ + const ancestorMayBeHostDirectiveAPI = node.parent?.parent?.parent; + + if ( + ancestorMayBeHostDirectiveAPI && + ASTUtils.isProperty(ancestorMayBeHostDirectiveAPI) + ) { + const hostDirectiveAPIPropertyName = 'hostDirectives'; + + if ( + (ASTUtils.isLiteral(ancestorMayBeHostDirectiveAPI.key) && + ancestorMayBeHostDirectiveAPI.key.value === + hostDirectiveAPIPropertyName) || + (TSESLintASTUtils.isIdentifier(ancestorMayBeHostDirectiveAPI.key) && + ancestorMayBeHostDirectiveAPI.key.name === + hostDirectiveAPIPropertyName) + ) { + return; + } + } + context.report({ node, messageId: 'noInputsMetadataProperty', diff --git a/packages/eslint-plugin/tests/rules/no-inputs-metadata-property/cases.ts b/packages/eslint-plugin/tests/rules/no-inputs-metadata-property/cases.ts index 1ce7075ab..2edb3f9b3 100644 --- a/packages/eslint-plugin/tests/rules/no-inputs-metadata-property/cases.ts +++ b/packages/eslint-plugin/tests/rules/no-inputs-metadata-property/cases.ts @@ -45,6 +45,41 @@ export const valid = [ }) class Test {} `, + /** + * Using inputs when using the directive composition API is not a bad practice + * https://angular.io/guide/directive-composition-api + * https://www.youtube.com/watch?v=EJJwyyjsRGs + */ + ` + @Component({ + selector: 'qx-menuitem', + hostDirectives: [{ + directive: CdkMenuItem, + inputs: ['cdkMenuItemDisabled: disabled'], + }] + }) + class Test {} + `, + ` + @Component({ + selector: 'qx-menuitem', + 'hostDirectives': [{ + directive: CdkMenuItem, + inputs: ['cdkMenuItemDisabled: disabled'], + }] + }) + class Test {} + `, + ` + @Component({ + selector: 'qx-menuitem', + ['hostDirectives']: [{ + directive: CdkMenuItem, + inputs: ['cdkMenuItemDisabled: disabled'], + }] + }) + class Test {} + `, ]; export const invalid = [