From a9ab7dbec262159dc511a6c65f71490e44a7825b Mon Sep 17 00:00:00 2001 From: YeonJuan Date: Mon, 11 Aug 2025 22:18:38 +0900 Subject: [PATCH 1/4] feat: add pattern support to priority option in sort-attrs --- docs/rules/sort-attrs.md | 32 ++++- .../eslint-plugin/lib/rules/sort-attrs.js | 59 +++++++++- .../tests/rules/sort-attrs.test.js | 111 ++++++++++++++++++ 3 files changed, 194 insertions(+), 8 deletions(-) diff --git a/docs/rules/sort-attrs.md b/docs/rules/sort-attrs.md index 29ff4418..f2fb7ced 100644 --- a/docs/rules/sort-attrs.md +++ b/docs/rules/sort-attrs.md @@ -31,15 +31,20 @@ Examples of **correct** code for this rule: ```ts //... "@html-eslint/sort-attrs": ["error", { - "priority": Array + "priority": Array }] ``` #### priority -This option allows you to set an array of attributes keyć„“. +This option allows you to set an array of attribute names or pattern objects. When `priority` is defined, the specified attributes are sorted to the front with the highest priority. +Priority items can be: + +- Strings: exact attribute name matches +- Objects with `pattern` property: regular expression patterns to match attribute names + The default value of `priority` is `["id", "type", "class", "style"]`. Examples of **incorrect** code for this rule with the default options (`{ "priority": ["id", "type", "class", "style"] }`). @@ -71,3 +76,26 @@ Examples of **correct** code for this rule with the `{ "priority": ["id", "style ```html,correct
``` + +##### Pattern Support + +You can use regular expressions in priority items using the pattern object format: + +Examples of **correct** code for this rule with the `{ "priority": ["id", { "pattern": "data-.*" }, "style"] }` option: + +```html,correct +
+``` + +In this example: + +- `id` has the highest priority (position 0) +- Any attribute matching `data-.*` pattern has the second priority (position 1) +- `style` has the third priority (position 2) +- All other attributes are sorted alphabetically after the priority items + +Examples of **incorrect** code for this rule with the `{ "priority": ["id", { "pattern": "data-.*" }, "style"] }` option: + +```html,incorrect +
+``` diff --git a/packages/eslint-plugin/lib/rules/sort-attrs.js b/packages/eslint-plugin/lib/rules/sort-attrs.js index dde86b4b..01d08c2f 100644 --- a/packages/eslint-plugin/lib/rules/sort-attrs.js +++ b/packages/eslint-plugin/lib/rules/sort-attrs.js @@ -3,7 +3,7 @@ * @import {RuleFixer, RuleModule} from "../types"; * * @typedef {Object} Option - * @property {string[]} [Option.priority] + * @property {Array} [Option.priority] */ const { hasTemplate } = require("./utils/node"); @@ -37,9 +37,23 @@ module.exports = { priority: { type: "array", items: { - type: "string", - uniqueItems: true, + oneOf: [ + { + type: "string", + }, + { + type: "object", + properties: { + pattern: { + type: "string", + }, + }, + required: ["pattern"], + additionalProperties: false, + }, + ], }, + uniqueItems: true, }, }, additionalProperties: false, @@ -55,10 +69,43 @@ module.exports = { priority: ["id", "type", "class", "style"], }; /** - * @type {string[]} + * @type {Array} */ const priority = option.priority || []; + /** + * @param {string} attrName + * @param {string | {pattern: string}} priorityItem + * @returns {boolean} + */ + function matchesPriority(attrName, priorityItem) { + if (typeof priorityItem === "string") { + return attrName === priorityItem; + } + if ( + priorityItem && + typeof priorityItem === "object" && + priorityItem.pattern + ) { + const regex = new RegExp(priorityItem.pattern, "u"); + return regex.test(attrName); + } + return false; + } + + /** + * @param {string} attrName + * @returns {number} + */ + function getPriorityIndex(attrName) { + for (let i = 0; i < priority.length; i++) { + if (matchesPriority(attrName, priority[i])) { + return i; + } + } + return -1; + } + /** * @param {Attribute} attrA * @param {Attribute} attrB @@ -68,8 +115,8 @@ module.exports = { const keyA = attrA.key.value; const keyB = attrB.key.value; - const keyAReservedValue = priority.indexOf(keyA); - const keyBReservedValue = priority.indexOf(keyB); + const keyAReservedValue = getPriorityIndex(keyA); + const keyBReservedValue = getPriorityIndex(keyB); if (keyAReservedValue >= 0 && keyBReservedValue >= 0) { return keyAReservedValue - keyBReservedValue; } else if (keyAReservedValue >= 0) { diff --git a/packages/eslint-plugin/tests/rules/sort-attrs.test.js b/packages/eslint-plugin/tests/rules/sort-attrs.test.js index 7e722109..9c5ddfd1 100644 --- a/packages/eslint-plugin/tests/rules/sort-attrs.test.js +++ b/packages/eslint-plugin/tests/rules/sort-attrs.test.js @@ -40,6 +40,31 @@ ruleTester.run("sort-attrs", rule, { }, }, }, + // Pattern tests + { + code: '
', + options: [ + { + priority: ["id", { pattern: "data-.*" }, "style"], + }, + ], + }, + { + code: '', + options: [ + { + priority: ["id", { pattern: "aria-.*" }, "type"], + }, + ], + }, + { + code: '', + options: [ + { + priority: ["id", { pattern: "ng-.*" }, "class", "type"], + }, + ], + }, { code: ` ', + output: + '', + options: [ + { + priority: ["id", { pattern: "ng-.*" }, "class", "type"], + }, + ], + errors: [{ messageId: "unsorted" }], + }, + { + code: '
', + output: + '
', + options: [ + { + priority: ["id", { pattern: "v-.*" }], + }, + ], + errors: [{ messageId: "unsorted" }], + }, + { + code: '
', + output: + '
', + options: [ + { + priority: ["id", { pattern: "data-.*" }], + }, + ], + errors: [{ messageId: "unsorted" }], + }, ], }); @@ -280,6 +372,14 @@ templateRuleTester.run("[template] sort-attrs", rule, { { code: 'html``', }, + { + code: 'html`
`', + options: [ + { + priority: ["id", { pattern: "data-.*" }, "style"], + }, + ], + }, ], invalid: [ { @@ -346,5 +446,16 @@ templateRuleTester.run("[template] sort-attrs", rule, { }, ], }, + { + code: 'html`
`', + output: + 'html`
`', + options: [ + { + priority: ["id", { pattern: "data-.*" }, "style"], + }, + ], + errors: [{ messageId: "unsorted" }], + }, ], }); From 6de614acd08d66b4c0d9711f03fc51098f9f0acf Mon Sep 17 00:00:00 2001 From: YeonJuan Date: Mon, 11 Aug 2025 22:25:08 +0900 Subject: [PATCH 2/4] Update sort-attrs.test.js --- .../tests/rules/sort-attrs.test.js | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/packages/eslint-plugin/tests/rules/sort-attrs.test.js b/packages/eslint-plugin/tests/rules/sort-attrs.test.js index 9c5ddfd1..f7f8c5e7 100644 --- a/packages/eslint-plugin/tests/rules/sort-attrs.test.js +++ b/packages/eslint-plugin/tests/rules/sort-attrs.test.js @@ -65,6 +65,19 @@ ruleTester.run("sort-attrs", rule, { }, ], }, + { + code: '
', + options: [ + { + priority: [ + "id", + { pattern: "data-.*" }, + { pattern: "aria-.*" }, + "class", + ], + }, + ], + }, { code: `