From 018d3906c2569df7dda01fd205e1138aec3f1d0c Mon Sep 17 00:00:00 2001
From: ssams <6338356+ssams@users.noreply.github.com>
Date: Mon, 26 Sep 2022 18:04:15 +0200
Subject: [PATCH] feat(eslint-plugin-template): [no-duplicate-attributes] Add
option to ignore properties (#1104)
---
.../docs/rules/no-duplicate-attributes.md | 38 ++++++++++++++++++
.../src/rules/no-duplicate-attributes.ts | 31 ++++++++++++--
.../rules/no-duplicate-attributes/cases.ts | 40 +++++++++++++++++++
3 files changed, 105 insertions(+), 4 deletions(-)
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
+
+ ~~~~~~~~~~~~ ~~~~~~~~~~
+```
+
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..d9bcdc9e7 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?: 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,13 @@ export default createESLintRule({
default: DEFAULT_OPTIONS.allowTwoWayDataBinding,
description: `Whether or not two-way data binding is allowed as an exception to the rule.`,
},
+ ignore: {
+ 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.`,
+ },
},
additionalProperties: false,
},
@@ -44,7 +59,7 @@ export default createESLintRule({
},
},
defaultOptions: [DEFAULT_OPTIONS],
- create(context, [{ allowTwoWayDataBinding }]) {
+ create(context, [{ allowTwoWayDataBinding, ignore }]) {
const parserServices = getTemplateParserServices(context);
return {
@@ -68,7 +83,15 @@ export default createESLintRule({
...duplicateOutputs,
] as const;
- allDuplicates.forEach((duplicate) => {
+ const filteredDuplicates =
+ ignore && ignore.length > 0
+ ? allDuplicates.filter(
+ (duplicate) =>
+ !ignore.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..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
@@ -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' },
+ },
+ ],
+ },
+ ],
+ }),
];