From be4b47338126d0561a195501901adf598028a2be Mon Sep 17 00:00:00 2001 From: GreatGui Date: Fri, 30 Dec 2022 20:16:42 -0300 Subject: [PATCH 01/12] add new attributes order for static and dynamic props --- lib/rules/attributes-order.js | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/lib/rules/attributes-order.js b/lib/rules/attributes-order.js index d47001adf..6213af835 100644 --- a/lib/rules/attributes-order.js +++ b/lib/rules/attributes-order.js @@ -20,6 +20,8 @@ const ATTRS = { TWO_WAY_BINDING: 'TWO_WAY_BINDING', OTHER_DIRECTIVES: 'OTHER_DIRECTIVES', OTHER_ATTR: 'OTHER_ATTR', + OTHER_ATTR_STATIC: 'OTHER_ATTR_STATIC', + OTHER_ATTR_DYNAMIC: 'OTHER_ATTR_DYNAMIC', EVENTS: 'EVENTS', CONTENT: 'CONTENT' } @@ -153,7 +155,10 @@ function getAttributeType(attribute) { case 'slot-scope': return ATTRS.SLOT default: - return ATTRS.OTHER_ATTR + if (attribute.directive) { + return ATTRS.OTHER_ATTR_DYNAMIC + } + return ATTRS.OTHER_ATTR_STATIC } } @@ -191,6 +196,7 @@ function isAlphabetical(prevNode, currNode, sourceCode) { */ function create(context) { const sourceCode = context.getSourceCode() + const otherAttrs = [ATTRS.OTHER_ATTR_DYNAMIC, ATTRS.OTHER_ATTR_STATIC] let attributeOrder = [ ATTRS.DEFINITION, ATTRS.LIST_RENDERING, @@ -200,17 +206,27 @@ function create(context) { [ATTRS.UNIQUE, ATTRS.SLOT], ATTRS.TWO_WAY_BINDING, ATTRS.OTHER_DIRECTIVES, - ATTRS.OTHER_ATTR, + [ATTRS.OTHER_ATTR_DYNAMIC, ATTRS.OTHER_ATTR_STATIC], ATTRS.EVENTS, ATTRS.CONTENT ] if (context.options[0] && context.options[0].order) { - attributeOrder = context.options[0].order + attributeOrder = [...context.options[0].order] } const alphabetical = Boolean( context.options[0] && context.options[0].alphabetical ) + for (const [index, item] of attributeOrder.entries()) { + if (Array.isArray(item) && item.includes(ATTRS.OTHER_ATTR)) { + const attributes = item.filter((i) => i !== ATTRS.OTHER_ATTR) + attributes.push(...otherAttrs) + attributeOrder[index] = attributes + } else if (item === ATTRS.OTHER_ATTR) { + attributeOrder[index] = otherAttrs + } + } + /** @type { { [key: string]: number } } */ const attributePosition = {} for (const [i, item] of attributeOrder.entries()) { From f29b1e472d8e379930ea9c772f59bcca81996007 Mon Sep 17 00:00:00 2001 From: GreatGui Date: Fri, 30 Dec 2022 20:22:41 -0300 Subject: [PATCH 02/12] separate OTHER_ATTR into OTHER_ATTR_DYNAMIC and OTHER_ATTR_STATIC --- docs/rules/attributes-order.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/rules/attributes-order.md b/docs/rules/attributes-order.md index 5cf2a4be1..8d1dafbc1 100644 --- a/docs/rules/attributes-order.md +++ b/docs/rules/attributes-order.md @@ -35,7 +35,11 @@ This rule aims to enforce ordering of component attributes. The default order is - `OTHER_DIRECTIVES` e.g. 'v-custom-directive' - `OTHER_ATTR` - e.g. 'custom-prop="foo"', 'v-bind:prop="foo"', ':prop="foo"' + alias for '[OTHER_ATTR_DYNAMIC, OTHER_ATTR_STATIC]' +- `OTHER_ATTR_DYNAMIC` + e.g. 'v-bind:prop="foo"', ':prop="foo"' +- `OTHER_ATTR_STATIC` + e.g. 'prop="foo"', 'custom-prop="foo"' - `EVENTS` e.g. '@click="functionCall"', 'v-on="event"' - `CONTENT` From 46565365f7b0a417cb17943457fc92e9ff8f497a Mon Sep 17 00:00:00 2001 From: GreatGui Date: Fri, 30 Dec 2022 20:25:54 -0300 Subject: [PATCH 03/12] add tests to cover new attributes rule --- tests/lib/rules/attributes-order.js | 197 ++++++++++++++++++++++++++++ 1 file changed, 197 insertions(+) diff --git a/tests/lib/rules/attributes-order.js b/tests/lib/rules/attributes-order.js index 47099273d..ad7fe60e7 100644 --- a/tests/lib/rules/attributes-order.js +++ b/tests/lib/rules/attributes-order.js @@ -483,6 +483,104 @@ tester.run('attributes-order', rule, { `, options: [{ order: ['LIST_RENDERING', 'CONDITIONALS'] }] + }, + + // https://github.com/vuejs/eslint-plugin-vue/issues/1728 + { + filename: 'test.vue', + code: ` + `, + options: [ + { + order: ['OTHER_ATTR_DYNAMIC', 'OTHER_ATTR_STATIC'], + alphabetical: false + } + ] + }, + { + filename: 'test.vue', + code: ` + `, + options: [ + { + order: ['TWO_WAY_BINDING', 'OTHER_ATTR_DYNAMIC', 'OTHER_ATTR_STATIC'], + alphabetical: false + } + ] + }, + { + filename: 'test.vue', + code: ` + `, + options: [ + { + order: [ + 'DEFINITION', + 'LIST_RENDERING', + 'CONDITIONALS', + 'RENDER_MODIFIERS', + 'GLOBAL', + ['UNIQUE', 'SLOT'], + 'TWO_WAY_BINDING', + 'OTHER_DIRECTIVES', + 'OTHER_ATTR_STATIC', + 'OTHER_ATTR_DYNAMIC', + 'EVENTS', + 'CONTENT' + ], + alphabetical: false + } + ] + }, + { + filename: 'test.vue', + code: ` + `, + options: [ + { + order: [ + 'DEFINITION', + 'LIST_RENDERING', + 'CONDITIONALS', + 'RENDER_MODIFIERS', + 'GLOBAL', + ['UNIQUE', 'SLOT'], + 'TWO_WAY_BINDING', + 'OTHER_DIRECTIVES', + ['OTHER_ATTR_STATIC', 'OTHER_ATTR_DYNAMIC'], + 'EVENTS', + 'CONTENT' + ], + alphabetical: false + } + ] } ], @@ -1528,6 +1626,105 @@ tester.run('attributes-order', rule, { attr="foo"/> `, errors: ['Attribute "@click" should go before "v-bind".'] + }, + + { + filename: 'test.vue', + code: ` + `, + options: [{ order: ['OTHER_ATTR_STATIC', 'OTHER_ATTR_DYNAMIC'] }], + output: ` + `, + errors: ['Attribute "prop-two" should go before "v-bind:prop-one".'] + }, + + { + filename: 'test.vue', + code: ` + `, + options: [ + { + order: [ + 'LIST_RENDERING', + 'CONDITIONALS', + 'RENDER_MODIFIERS', + 'TWO_WAY_BINDING', + 'OTHER_DIRECTIVES', + 'OTHER_ATTR_DYNAMIC', + 'OTHER_ATTR_STATIC', + 'EVENTS' + ] + } + ], + output: ` + `, + errors: [ + 'Attribute "v-model" should go before ":prop-one".', + 'Attribute ":prop-three" should go before "prop-two".' + ] + }, + + { + filename: 'test.vue', + code: ` + `, + options: [ + { + order: [ + 'UNIQUE', + 'LIST_RENDERING', + 'CONDITIONALS', + 'RENDER_MODIFIERS', + 'GLOBAL', + 'TWO_WAY_BINDING', + 'OTHER_DIRECTIVES', + ['OTHER_ATTR_STATIC', 'OTHER_ATTR_DYNAMIC'], + 'EVENTS', + 'CONTENT', + 'DEFINITION', + 'SLOT' + ] + } + ], + output: ` + `, + errors: ['Attribute "v-model" should go before ":prop-one".'] } ] }) From 391ac1877ac98143a312dc36334bbb0766746be4 Mon Sep 17 00:00:00 2001 From: GreatGui Date: Tue, 3 Jan 2023 12:18:42 -0300 Subject: [PATCH 04/12] refactor: change if condition to be clearer and more specific --- lib/rules/attributes-order.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rules/attributes-order.js b/lib/rules/attributes-order.js index 6213af835..dcf20a849 100644 --- a/lib/rules/attributes-order.js +++ b/lib/rules/attributes-order.js @@ -155,7 +155,7 @@ function getAttributeType(attribute) { case 'slot-scope': return ATTRS.SLOT default: - if (attribute.directive) { + if (isVBind(attribute)) { return ATTRS.OTHER_ATTR_DYNAMIC } return ATTRS.OTHER_ATTR_STATIC From 61278e7036617ab7d9fbb399081867fb14e7ff67 Mon Sep 17 00:00:00 2001 From: GreatGui Date: Tue, 3 Jan 2023 13:39:15 -0300 Subject: [PATCH 05/12] refactor: change OTHER_ATTR validation check just when is necessary --- lib/rules/attributes-order.js | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/lib/rules/attributes-order.js b/lib/rules/attributes-order.js index dcf20a849..3db5f1851 100644 --- a/lib/rules/attributes-order.js +++ b/lib/rules/attributes-order.js @@ -211,22 +211,26 @@ function create(context) { ATTRS.CONTENT ] if (context.options[0] && context.options[0].order) { - attributeOrder = [...context.options[0].order] + const contextAttributeOrder = [...context.options[0].order] + + // expand `OTHER_ATTR` alias + for (const [index, item] of contextAttributeOrder.entries()) { + if (item === ATTRS.OTHER_ATTR) { + contextAttributeOrder[index] = otherAttrs + } else if (Array.isArray(item)) { + const itemIndex = item.indexOf(ATTRS.OTHER_ATTR) + if (itemIndex > -1) { + contextAttributeOrder[index].splice(itemIndex, 1, ...otherAttrs) + } + } + } + + attributeOrder = contextAttributeOrder } const alphabetical = Boolean( context.options[0] && context.options[0].alphabetical ) - for (const [index, item] of attributeOrder.entries()) { - if (Array.isArray(item) && item.includes(ATTRS.OTHER_ATTR)) { - const attributes = item.filter((i) => i !== ATTRS.OTHER_ATTR) - attributes.push(...otherAttrs) - attributeOrder[index] = attributes - } else if (item === ATTRS.OTHER_ATTR) { - attributeOrder[index] = otherAttrs - } - } - /** @type { { [key: string]: number } } */ const attributePosition = {} for (const [i, item] of attributeOrder.entries()) { From 82973f296ef05631e52d38fdbee5d19a793ed568 Mon Sep 17 00:00:00 2001 From: GreatGui Date: Tue, 3 Jan 2023 14:03:03 -0300 Subject: [PATCH 06/12] fix: add OTHER_ATTR_DYNAMIC and OTHER_ATTR_STATIC inside OTHER_ATTR --- docs/rules/attributes-order.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/rules/attributes-order.md b/docs/rules/attributes-order.md index 8d1dafbc1..2397b754d 100644 --- a/docs/rules/attributes-order.md +++ b/docs/rules/attributes-order.md @@ -35,11 +35,11 @@ This rule aims to enforce ordering of component attributes. The default order is - `OTHER_DIRECTIVES` e.g. 'v-custom-directive' - `OTHER_ATTR` - alias for '[OTHER_ATTR_DYNAMIC, OTHER_ATTR_STATIC]' -- `OTHER_ATTR_DYNAMIC` - e.g. 'v-bind:prop="foo"', ':prop="foo"' -- `OTHER_ATTR_STATIC` - e.g. 'prop="foo"', 'custom-prop="foo"' + alias for `[OTHER_ATTR_DYNAMIC, OTHER_ATTR_STATIC]`: + - `OTHER_ATTR_DYNAMIC` + e.g. 'v-bind:prop="foo"', ':prop="foo"' + - `OTHER_ATTR_STATIC` + e.g. 'prop="foo"', 'custom-prop="foo"' - `EVENTS` e.g. '@click="functionCall"', 'v-on="event"' - `CONTENT` From 4b599f5bf9ae964aa8db094d2cb5180d5b2a4a00 Mon Sep 17 00:00:00 2001 From: GreatGui Date: Tue, 3 Jan 2023 14:32:41 -0300 Subject: [PATCH 07/12] refactor: remove OTHER_ prefix of attribute dynamic and static --- docs/rules/attributes-order.md | 6 +++--- lib/rules/attributes-order.js | 12 ++++++------ tests/lib/rules/attributes-order.js | 18 +++++++++--------- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/docs/rules/attributes-order.md b/docs/rules/attributes-order.md index 2397b754d..fec83a458 100644 --- a/docs/rules/attributes-order.md +++ b/docs/rules/attributes-order.md @@ -35,10 +35,10 @@ This rule aims to enforce ordering of component attributes. The default order is - `OTHER_DIRECTIVES` e.g. 'v-custom-directive' - `OTHER_ATTR` - alias for `[OTHER_ATTR_DYNAMIC, OTHER_ATTR_STATIC]`: - - `OTHER_ATTR_DYNAMIC` + alias for `[ATTR_DYNAMIC, ATTR_STATIC]`: + - `ATTR_DYNAMIC` e.g. 'v-bind:prop="foo"', ':prop="foo"' - - `OTHER_ATTR_STATIC` + - `ATTR_STATIC` e.g. 'prop="foo"', 'custom-prop="foo"' - `EVENTS` e.g. '@click="functionCall"', 'v-on="event"' diff --git a/lib/rules/attributes-order.js b/lib/rules/attributes-order.js index 3db5f1851..17a8676b7 100644 --- a/lib/rules/attributes-order.js +++ b/lib/rules/attributes-order.js @@ -20,8 +20,8 @@ const ATTRS = { TWO_WAY_BINDING: 'TWO_WAY_BINDING', OTHER_DIRECTIVES: 'OTHER_DIRECTIVES', OTHER_ATTR: 'OTHER_ATTR', - OTHER_ATTR_STATIC: 'OTHER_ATTR_STATIC', - OTHER_ATTR_DYNAMIC: 'OTHER_ATTR_DYNAMIC', + ATTR_STATIC: 'ATTR_STATIC', + ATTR_DYNAMIC: 'ATTR_DYNAMIC', EVENTS: 'EVENTS', CONTENT: 'CONTENT' } @@ -156,9 +156,9 @@ function getAttributeType(attribute) { return ATTRS.SLOT default: if (isVBind(attribute)) { - return ATTRS.OTHER_ATTR_DYNAMIC + return ATTRS.ATTR_DYNAMIC } - return ATTRS.OTHER_ATTR_STATIC + return ATTRS.ATTR_STATIC } } @@ -196,7 +196,7 @@ function isAlphabetical(prevNode, currNode, sourceCode) { */ function create(context) { const sourceCode = context.getSourceCode() - const otherAttrs = [ATTRS.OTHER_ATTR_DYNAMIC, ATTRS.OTHER_ATTR_STATIC] + const otherAttrs = [ATTRS.ATTR_DYNAMIC, ATTRS.ATTR_STATIC] let attributeOrder = [ ATTRS.DEFINITION, ATTRS.LIST_RENDERING, @@ -206,7 +206,7 @@ function create(context) { [ATTRS.UNIQUE, ATTRS.SLOT], ATTRS.TWO_WAY_BINDING, ATTRS.OTHER_DIRECTIVES, - [ATTRS.OTHER_ATTR_DYNAMIC, ATTRS.OTHER_ATTR_STATIC], + [ATTRS.ATTR_DYNAMIC, ATTRS.ATTR_STATIC], ATTRS.EVENTS, ATTRS.CONTENT ] diff --git a/tests/lib/rules/attributes-order.js b/tests/lib/rules/attributes-order.js index ad7fe60e7..ef883d356 100644 --- a/tests/lib/rules/attributes-order.js +++ b/tests/lib/rules/attributes-order.js @@ -498,7 +498,7 @@ tester.run('attributes-order', rule, { `, options: [ { - order: ['OTHER_ATTR_DYNAMIC', 'OTHER_ATTR_STATIC'], + order: ['ATTR_DYNAMIC', 'ATTR_STATIC'], alphabetical: false } ] @@ -518,7 +518,7 @@ tester.run('attributes-order', rule, { `, options: [ { - order: ['TWO_WAY_BINDING', 'OTHER_ATTR_DYNAMIC', 'OTHER_ATTR_STATIC'], + order: ['TWO_WAY_BINDING', 'ATTR_DYNAMIC', 'ATTR_STATIC'], alphabetical: false } ] @@ -544,8 +544,8 @@ tester.run('attributes-order', rule, { ['UNIQUE', 'SLOT'], 'TWO_WAY_BINDING', 'OTHER_DIRECTIVES', - 'OTHER_ATTR_STATIC', - 'OTHER_ATTR_DYNAMIC', + 'ATTR_STATIC', + 'ATTR_DYNAMIC', 'EVENTS', 'CONTENT' ], @@ -574,7 +574,7 @@ tester.run('attributes-order', rule, { ['UNIQUE', 'SLOT'], 'TWO_WAY_BINDING', 'OTHER_DIRECTIVES', - ['OTHER_ATTR_STATIC', 'OTHER_ATTR_DYNAMIC'], + ['ATTR_STATIC', 'ATTR_DYNAMIC'], 'EVENTS', 'CONTENT' ], @@ -1637,7 +1637,7 @@ tester.run('attributes-order', rule, { prop-two="b" :prop-three="c"/> `, - options: [{ order: ['OTHER_ATTR_STATIC', 'OTHER_ATTR_DYNAMIC'] }], + options: [{ order: ['ATTR_STATIC', 'ATTR_DYNAMIC'] }], output: ` `, options: [ { @@ -1669,6 +1704,7 @@ tester.run('attributes-order', rule, { 'OTHER_DIRECTIVES', 'ATTR_DYNAMIC', 'ATTR_STATIC', + 'ATTR_SHORTHAND_BOOL', 'EVENTS' ] } @@ -1680,7 +1716,8 @@ tester.run('attributes-order', rule, { :prop-one="a" :prop-three="c" prop-two="b" - class="class"/> + class="class" + boolean-prop/> `, errors: [ 'Attribute "v-model" should go before ":prop-one".', @@ -1695,6 +1732,7 @@ tester.run('attributes-order', rule, {
`, @@ -1708,7 +1746,7 @@ tester.run('attributes-order', rule, { 'GLOBAL', 'TWO_WAY_BINDING', 'OTHER_DIRECTIVES', - ['ATTR_STATIC', 'ATTR_DYNAMIC'], + ['ATTR_STATIC', 'ATTR_DYNAMIC', 'ATTR_SHORTHAND_BOOL'], 'EVENTS', 'CONTENT', 'DEFINITION', @@ -1721,6 +1759,7 @@ tester.run('attributes-order', rule, {
`, From c86b2daaf738e96f4509b906d47fc02c6deda1a2 Mon Sep 17 00:00:00 2001 From: GreatGui Date: Tue, 3 Jan 2023 17:40:51 -0300 Subject: [PATCH 10/12] add ATTR_SHORTHAND_BOOL to OTHER_ATTR --- lib/rules/attributes-order.js | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/lib/rules/attributes-order.js b/lib/rules/attributes-order.js index 17a8676b7..6eceb2c58 100644 --- a/lib/rules/attributes-order.js +++ b/lib/rules/attributes-order.js @@ -22,6 +22,7 @@ const ATTRS = { OTHER_ATTR: 'OTHER_ATTR', ATTR_STATIC: 'ATTR_STATIC', ATTR_DYNAMIC: 'ATTR_DYNAMIC', + ATTR_SHORTHAND_BOOL: 'ATTR_SHORTHAND_BOOL', EVENTS: 'EVENTS', CONTENT: 'CONTENT' } @@ -68,6 +69,15 @@ function isVBindObject(node) { return isVBind(node) && node.key.argument == null } +/** + * Check whether the given attribute is `v-bind="..."` directive. + * @param {VAttribute | VDirective | undefined | null} node + * @returns { node is VAttribute } + */ +function isVShorthandBoolean(node) { + return isVAttribute(node) && !node.value +} + /** * @param {VAttribute | VDirective} attribute * @param {SourceCode} sourceCode @@ -157,6 +167,8 @@ function getAttributeType(attribute) { default: if (isVBind(attribute)) { return ATTRS.ATTR_DYNAMIC + } else if (isVShorthandBoolean(attribute)) { + return ATTRS.ATTR_SHORTHAND_BOOL } return ATTRS.ATTR_STATIC } @@ -196,7 +208,11 @@ function isAlphabetical(prevNode, currNode, sourceCode) { */ function create(context) { const sourceCode = context.getSourceCode() - const otherAttrs = [ATTRS.ATTR_DYNAMIC, ATTRS.ATTR_STATIC] + const otherAttrs = [ + ATTRS.ATTR_DYNAMIC, + ATTRS.ATTR_STATIC, + ATTRS.ATTR_SHORTHAND_BOOL + ] let attributeOrder = [ ATTRS.DEFINITION, ATTRS.LIST_RENDERING, @@ -206,26 +222,26 @@ function create(context) { [ATTRS.UNIQUE, ATTRS.SLOT], ATTRS.TWO_WAY_BINDING, ATTRS.OTHER_DIRECTIVES, - [ATTRS.ATTR_DYNAMIC, ATTRS.ATTR_STATIC], + otherAttrs, ATTRS.EVENTS, ATTRS.CONTENT ] if (context.options[0] && context.options[0].order) { - const contextAttributeOrder = [...context.options[0].order] + attributeOrder = [...context.options[0].order] // expand `OTHER_ATTR` alias - for (const [index, item] of contextAttributeOrder.entries()) { + for (const [index, item] of attributeOrder.entries()) { if (item === ATTRS.OTHER_ATTR) { - contextAttributeOrder[index] = otherAttrs + attributeOrder[index] = otherAttrs } else if (Array.isArray(item)) { const itemIndex = item.indexOf(ATTRS.OTHER_ATTR) if (itemIndex > -1) { - contextAttributeOrder[index].splice(itemIndex, 1, ...otherAttrs) + const attributes = item.filter((i) => i !== ATTRS.OTHER_ATTR) + attributes.push(...otherAttrs) + attributeOrder[index] = attributes } } } - - attributeOrder = contextAttributeOrder } const alphabetical = Boolean( context.options[0] && context.options[0].alphabetical From 827ff2d05df8130c9c9a1c790d87f142cfea8052 Mon Sep 17 00:00:00 2001 From: GreatGui Date: Wed, 11 Jan 2023 14:05:29 -0300 Subject: [PATCH 11/12] fix: correct comment and break line --- lib/rules/attributes-order.js | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/lib/rules/attributes-order.js b/lib/rules/attributes-order.js index 6eceb2c58..8771e8c82 100644 --- a/lib/rules/attributes-order.js +++ b/lib/rules/attributes-order.js @@ -70,7 +70,7 @@ function isVBindObject(node) { } /** - * Check whether the given attribute is `v-bind="..."` directive. + * Check whether the given attribute is a shorthand boolean like `selected`. * @param {VAttribute | VDirective | undefined | null} node * @returns { node is VAttribute } */ @@ -167,7 +167,8 @@ function getAttributeType(attribute) { default: if (isVBind(attribute)) { return ATTRS.ATTR_DYNAMIC - } else if (isVShorthandBoolean(attribute)) { + } + if (isVShorthandBoolean(attribute)) { return ATTRS.ATTR_SHORTHAND_BOOL } return ATTRS.ATTR_STATIC @@ -233,13 +234,10 @@ function create(context) { for (const [index, item] of attributeOrder.entries()) { if (item === ATTRS.OTHER_ATTR) { attributeOrder[index] = otherAttrs - } else if (Array.isArray(item)) { - const itemIndex = item.indexOf(ATTRS.OTHER_ATTR) - if (itemIndex > -1) { - const attributes = item.filter((i) => i !== ATTRS.OTHER_ATTR) - attributes.push(...otherAttrs) - attributeOrder[index] = attributes - } + } else if (Array.isArray(item) && item.includes(ATTRS.OTHER_ATTR)) { + const attributes = item.filter((i) => i !== ATTRS.OTHER_ATTR) + attributes.push(...otherAttrs) + attributeOrder[index] = attributes } } } From e4c109a43d560706ed5a66eac6b2f3b459f00c39 Mon Sep 17 00:00:00 2001 From: GreatGui Date: Wed, 11 Jan 2023 14:06:27 -0300 Subject: [PATCH 12/12] feat: check if `OTHER_ATTR` is valid --- lib/rules/attributes-order.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/lib/rules/attributes-order.js b/lib/rules/attributes-order.js index 8771e8c82..e6915aebf 100644 --- a/lib/rules/attributes-order.js +++ b/lib/rules/attributes-order.js @@ -230,6 +230,19 @@ function create(context) { if (context.options[0] && context.options[0].order) { attributeOrder = [...context.options[0].order] + // check if `OTHER_ATTR` is valid + for (const item of attributeOrder.flat()) { + if (item === ATTRS.OTHER_ATTR) { + for (const attribute of attributeOrder.flat()) { + if (otherAttrs.includes(attribute)) { + throw new Error( + `Value "${ATTRS.OTHER_ATTR}" is not allowed with "${attribute}".` + ) + } + } + } + } + // expand `OTHER_ATTR` alias for (const [index, item] of attributeOrder.entries()) { if (item === ATTRS.OTHER_ATTR) {