From 2218134c75af97c2124559d5c98ce5b66160a4d3 Mon Sep 17 00:00:00 2001 From: Sebastian Sams <6338356+ssams@users.noreply.github.com> Date: Thu, 4 Aug 2022 12:32:19 +0200 Subject: [PATCH 1/3] feat(eslint-plugin-template): [no-duplicate-attributes] add option to ignore properties --- .../src/rules/no-duplicate-attributes.ts | 37 +++++++++++++++-- .../rules/no-duplicate-attributes/cases.ts | 40 +++++++++++++++++++ 2 files changed, 73 insertions(+), 4 deletions(-) diff --git a/packages/eslint-plugin-template/src/rules/no-duplicate-attributes.ts b/packages/eslint-plugin-template/src/rules/no-duplicate-attributes.ts index 1dad252c9..19a4b7326 100644 --- a/packages/eslint-plugin-template/src/rules/no-duplicate-attributes.ts +++ b/packages/eslint-plugin-template/src/rules/no-duplicate-attributes.ts @@ -10,10 +10,18 @@ import { } from '../utils/create-eslint-rule'; import { getOriginalAttributeName } from '../utils/get-original-attribute-name'; -type Options = [{ readonly allowTwoWayDataBinding?: boolean }]; +type Options = [ + { + readonly allowTwoWayDataBinding?: boolean; + readonly ignore?: string | readonly string[]; + }, +]; export type MessageIds = 'noDuplicateAttributes' | 'suggestRemoveAttribute'; export const RULE_NAME = 'no-duplicate-attributes'; -const DEFAULT_OPTIONS: Options[number] = { allowTwoWayDataBinding: true }; +const DEFAULT_OPTIONS: Options[number] = { + allowTwoWayDataBinding: true, + ignore: [], +}; export default createESLintRule({ name: RULE_NAME, @@ -34,6 +42,11 @@ export default createESLintRule({ default: DEFAULT_OPTIONS.allowTwoWayDataBinding, description: `Whether or not two-way data binding is allowed as an exception to the rule.`, }, + ignore: { + oneOf: [{ type: 'string' }, { type: 'array' }], + default: DEFAULT_OPTIONS.ignore, + description: `Input or output properties for which duplicate presence is allowed as an exception to the rule.`, + }, }, additionalProperties: false, }, @@ -44,7 +57,7 @@ export default createESLintRule({ }, }, defaultOptions: [DEFAULT_OPTIONS], - create(context, [{ allowTwoWayDataBinding }]) { + create(context, [{ allowTwoWayDataBinding, ignore }]) { const parserServices = getTemplateParserServices(context); return { @@ -68,7 +81,23 @@ export default createESLintRule({ ...duplicateOutputs, ] as const; - allDuplicates.forEach((duplicate) => { + const effectiveIgnore = + ignore === undefined + ? [] + : typeof ignore === 'string' + ? [ignore] + : ignore; + const filteredDuplicates = + effectiveIgnore.length > 0 + ? allDuplicates.filter( + (duplicate) => + !effectiveIgnore.includes( + getOriginalAttributeName(duplicate), + ), + ) + : allDuplicates; + + filteredDuplicates.forEach((duplicate) => { const loc = parserServices.convertNodeSourceSpanToLoc( duplicate.sourceSpan, ); diff --git a/packages/eslint-plugin-template/tests/rules/no-duplicate-attributes/cases.ts b/packages/eslint-plugin-template/tests/rules/no-duplicate-attributes/cases.ts index 9656f489a..db47b0316 100644 --- a/packages/eslint-plugin-template/tests/rules/no-duplicate-attributes/cases.ts +++ b/packages/eslint-plugin-template/tests/rules/no-duplicate-attributes/cases.ts @@ -496,4 +496,44 @@ export const invalid = [ }, ], }), + convertAnnotatedSourceToFailureCase({ + description: 'should not report ignored properties', + annotatedSource: ` + + ~~~~~~~~~~~~ ^^^^^^^^^^ + `, + options: [{ ignore: 'class' }], + messages: [ + { + char: '~', + messageId, + data: { attributeName: 'name' }, + suggestions: [ + { + messageId: suggestRemoveAttribute, + output: ` + + + `, + data: { attributeName: 'name' }, + }, + ], + }, + { + char: '^', + messageId, + data: { attributeName: 'name' }, + suggestions: [ + { + messageId: suggestRemoveAttribute, + output: ` + + + `, + data: { attributeName: 'name' }, + }, + ], + }, + ], + }), ]; From 107e43227c63f432d1db45bb53b008fe71fb2412 Mon Sep 17 00:00:00 2001 From: Sebastian Sams <6338356+ssams@users.noreply.github.com> Date: Mon, 19 Sep 2022 08:23:19 +0200 Subject: [PATCH 2/3] feat(eslint-plugin-template): [no-duplicate-attributes] update data type for ignore option --- .../src/rules/no-duplicate-attributes.ts | 18 ++++++------------ .../rules/no-duplicate-attributes/cases.ts | 2 +- 2 files changed, 7 insertions(+), 13 deletions(-) diff --git a/packages/eslint-plugin-template/src/rules/no-duplicate-attributes.ts b/packages/eslint-plugin-template/src/rules/no-duplicate-attributes.ts index 19a4b7326..d9bcdc9e7 100644 --- a/packages/eslint-plugin-template/src/rules/no-duplicate-attributes.ts +++ b/packages/eslint-plugin-template/src/rules/no-duplicate-attributes.ts @@ -13,7 +13,7 @@ import { getOriginalAttributeName } from '../utils/get-original-attribute-name'; type Options = [ { readonly allowTwoWayDataBinding?: boolean; - readonly ignore?: string | readonly string[]; + readonly ignore?: readonly string[]; }, ]; export type MessageIds = 'noDuplicateAttributes' | 'suggestRemoveAttribute'; @@ -43,7 +43,9 @@ export default createESLintRule({ description: `Whether or not two-way data binding is allowed as an exception to the rule.`, }, ignore: { - oneOf: [{ type: 'string' }, { type: 'array' }], + type: 'array', + items: { type: 'string' }, + uniqueItems: true, default: DEFAULT_OPTIONS.ignore, description: `Input or output properties for which duplicate presence is allowed as an exception to the rule.`, }, @@ -81,19 +83,11 @@ export default createESLintRule({ ...duplicateOutputs, ] as const; - const effectiveIgnore = - ignore === undefined - ? [] - : typeof ignore === 'string' - ? [ignore] - : ignore; const filteredDuplicates = - effectiveIgnore.length > 0 + ignore && ignore.length > 0 ? allDuplicates.filter( (duplicate) => - !effectiveIgnore.includes( - getOriginalAttributeName(duplicate), - ), + !ignore.includes(getOriginalAttributeName(duplicate)), ) : allDuplicates; diff --git a/packages/eslint-plugin-template/tests/rules/no-duplicate-attributes/cases.ts b/packages/eslint-plugin-template/tests/rules/no-duplicate-attributes/cases.ts index db47b0316..70f2108a5 100644 --- a/packages/eslint-plugin-template/tests/rules/no-duplicate-attributes/cases.ts +++ b/packages/eslint-plugin-template/tests/rules/no-duplicate-attributes/cases.ts @@ -502,7 +502,7 @@ export const invalid = [ ~~~~~~~~~~~~ ^^^^^^^^^^ `, - options: [{ ignore: 'class' }], + options: [{ ignore: ['class'] }], messages: [ { char: '~', From 27eb7685794caaf1a047b2aa598daf8f6ccf3c1c Mon Sep 17 00:00:00 2001 From: Sebastian Sams <6338356+ssams@users.noreply.github.com> Date: Mon, 26 Sep 2022 17:47:11 +0200 Subject: [PATCH 3/3] feat(eslint-plugin-template): [no-duplicate-attributes] regenerate rule docs for new option --- .../docs/rules/no-duplicate-attributes.md | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/packages/eslint-plugin-template/docs/rules/no-duplicate-attributes.md b/packages/eslint-plugin-template/docs/rules/no-duplicate-attributes.md index a63eb44cc..c840b26f4 100644 --- a/packages/eslint-plugin-template/docs/rules/no-duplicate-attributes.md +++ b/packages/eslint-plugin-template/docs/rules/no-duplicate-attributes.md @@ -35,6 +35,12 @@ interface Options { * Default: `true` */ allowTwoWayDataBinding?: boolean; + /** + * Input or output properties for which duplicate presence is allowed as an exception to the rule. + * + * Default: `[]` + */ + ignore?: string[]; } ``` @@ -346,6 +352,38 @@ interface Options { ~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ``` +
+ +--- + +
+ +#### Custom Config + +```json +{ + "rules": { + "@angular-eslint/template/no-duplicate-attributes": [ + "error", + { + "ignore": [ + "class" + ] + } + ] + } +} +``` + +
+ +#### ❌ Invalid Code + +```html + + ~~~~~~~~~~~~ ~~~~~~~~~~ +``` +