diff --git a/docs/rules/attributes-order.md b/docs/rules/attributes-order.md index 8c61d3688..879ec1d57 100644 --- a/docs/rules/attributes-order.md +++ b/docs/rules/attributes-order.md @@ -105,11 +105,57 @@ This rule aims to enforce ordering of component attributes. The default order is "OTHER_ATTR", "EVENTS", "CONTENT" - ] + ], + "alphabetical": true }] } ``` +### Alphabetical order + + +```vue + +``` + + + ### Custom orders #### `['LIST_RENDERING', 'CONDITIONALS', 'RENDER_MODIFIERS', 'GLOBAL', 'UNIQUE', 'TWO_WAY_BINDING', 'DEFINITION', 'OTHER_DIRECTIVES', 'OTHER_ATTR', 'EVENTS', 'CONTENT']` diff --git a/lib/rules/attributes-order.js b/lib/rules/attributes-order.js index afa0b3c5a..ac8afe283 100644 --- a/lib/rules/attributes-order.js +++ b/lib/rules/attributes-order.js @@ -22,6 +22,14 @@ const ATTRS = { CONTENT: 'CONTENT' } +function getAttributeName (attribute, sourceCode) { + const isBind = attribute.directive && attribute.key.name.name === 'bind' + debugger + return isBind + ? (attribute.key.argument ? sourceCode.getText(attribute.key.argument) : '') + : (attribute.directive ? sourceCode.getText(attribute.key.argument) : attribute.key.name) +} + function getAttributeType (attribute, sourceCode) { const isBind = attribute.directive && attribute.key.name.name === 'bind' const name = isBind @@ -64,6 +72,14 @@ function getPosition (attribute, attributePosition, sourceCode) { return attributePosition.hasOwnProperty(attributeType) ? attributePosition[attributeType] : -1 } +function isAlphabetical (prevNode, currNode, sourceCode) { + const isSameType = getAttributeType(prevNode, sourceCode) === getAttributeType(currNode, sourceCode) + if (isSameType) { + return getAttributeName(prevNode, sourceCode) < getAttributeName(currNode, sourceCode) + } + return true +} + function create (context) { const sourceCode = context.getSourceCode() let attributeOrder = [ATTRS.DEFINITION, ATTRS.LIST_RENDERING, ATTRS.CONDITIONALS, ATTRS.RENDER_MODIFIERS, ATTRS.GLOBAL, ATTRS.UNIQUE, ATTRS.TWO_WAY_BINDING, ATTRS.OTHER_DIRECTIVES, ATTRS.OTHER_ATTR, ATTRS.EVENTS, ATTRS.CONTENT] @@ -110,7 +126,11 @@ function create (context) { previousNode = null }, 'VAttribute' (node) { - if ((currentPosition === -1) || (currentPosition <= getPosition(node, attributePosition, sourceCode))) { + let inAlphaOrder = true + if (currentPosition !== -1 && (context.options[0] && context.options[0].alphabetical)) { + inAlphaOrder = isAlphabetical(previousNode, node, sourceCode) + } + if ((currentPosition === -1) || ((currentPosition <= getPosition(node, attributePosition, sourceCode)) && inAlphaOrder)) { currentPosition = getPosition(node, attributePosition, sourceCode) previousNode = node } else { diff --git a/tests/lib/rules/attributes-order.js b/tests/lib/rules/attributes-order.js index 7ac9aaf59..481148f9d 100644 --- a/tests/lib/rules/attributes-order.js +++ b/tests/lib/rules/attributes-order.js @@ -293,6 +293,25 @@ tester.run('attributes-order', rule, { v-text="textContent"> ` + }, + { + filename: 'test.vue', + code: + ``, + options: [{ alphabetical: true }] } ], @@ -318,23 +337,23 @@ tester.run('attributes-order', rule, { { filename: 'test.vue', code: - ``, + ``, output: - ``, + ``, errors: [{ message: 'Attribute "v-model" should go before "model".', type: 'VDirectiveKey' @@ -347,25 +366,25 @@ tester.run('attributes-order', rule, { { filename: 'test.vue', code: - ``, + ``, output: - ``, + ``, errors: [{ message: 'Attribute "v-model" should go before "v-on".', type: 'VDirectiveKey' @@ -389,17 +408,17 @@ tester.run('attributes-order', rule, { code: '', options: [ { order: - ['LIST_RENDERING', - 'CONDITIONALS', - 'RENDER_MODIFIERS', - 'GLOBAL', - 'UNIQUE', - 'TWO_WAY_BINDING', - 'DEFINITION', - 'OTHER_DIRECTIVES', - 'OTHER_ATTR', - 'EVENTS', - 'CONTENT'] + ['LIST_RENDERING', + 'CONDITIONALS', + 'RENDER_MODIFIERS', + 'GLOBAL', + 'UNIQUE', + 'TWO_WAY_BINDING', + 'DEFINITION', + 'OTHER_DIRECTIVES', + 'OTHER_ATTR', + 'EVENTS', + 'CONTENT'] }], output: '', errors: [{ @@ -410,17 +429,17 @@ tester.run('attributes-order', rule, { { filename: 'test.vue', code: - ``, + ``, output: - ``, + ``, errors: [{ message: 'Attribute "is" should go before "v-cloak".', type: 'VIdentifier' @@ -429,37 +448,37 @@ tester.run('attributes-order', rule, { { filename: 'test.vue', code: - ``, + ``, output: - ``, + ``, errors: [ { message: 'Attribute "v-for" should go before "v-if".', @@ -490,53 +509,53 @@ tester.run('attributes-order', rule, { { filename: 'test.vue', code: - ``, + ``, options: [ { order: - [ - 'EVENTS', - 'TWO_WAY_BINDING', - 'UNIQUE', - 'DEFINITION', - 'CONDITIONALS', - 'LIST_RENDERING', - 'RENDER_MODIFIERS', - 'GLOBAL', - 'OTHER_ATTR', - 'OTHER_DIRECTIVES', - 'CONTENT' - ] + [ + 'EVENTS', + 'TWO_WAY_BINDING', + 'UNIQUE', + 'DEFINITION', + 'CONDITIONALS', + 'LIST_RENDERING', + 'RENDER_MODIFIERS', + 'GLOBAL', + 'OTHER_ATTR', + 'OTHER_DIRECTIVES', + 'CONTENT' + ] }], output: - ``, + ``, errors: [ { message: 'Attribute "is" should go before "v-once".', @@ -562,39 +581,39 @@ tester.run('attributes-order', rule, { }, { code: - ``, + ``, options: [ { order: - [ - 'CONDITIONALS', - 'LIST_RENDERING', - 'RENDER_MODIFIERS', - 'DEFINITION', - 'EVENTS', - 'UNIQUE', - ['BINDING', 'OTHER_ATTR'], - 'CONTENT', - 'GLOBAL' - ] + [ + 'CONDITIONALS', + 'LIST_RENDERING', + 'RENDER_MODIFIERS', + 'DEFINITION', + 'EVENTS', + 'UNIQUE', + ['BINDING', 'OTHER_ATTR'], + 'CONTENT', + 'GLOBAL' + ] }], output: - ``, + ``, errors: [ { message: 'Attribute "v-if" should go before "class".', @@ -604,29 +623,161 @@ tester.run('attributes-order', rule, { }, { code: - ``, + ``, output: - ``, + ``, errors: [ { message: 'Attribute "v-slot" should go before "v-model".', nodeType: 'VIdentifier' } ] + }, + { + filename: 'test.vue', + code: + ``, + options: [{ alphabetical: true }], + output: + ``, + errors: [{ + message: 'Attribute "a-prop" should go before "z-prop".', + type: 'VIdentifier' + }] + }, + { + filename: 'test.vue', + code: + ``, + options: [{ alphabetical: true }], + output: + ``, + errors: [{ + message: 'Attribute ":a-prop" should go before ":z-prop".', + type: 'VDirectiveKey' + }] + }, + { + filename: 'test.vue', + code: + ``, + options: [{ alphabetical: true }], + output: + ``, + errors: [{ + message: 'Attribute "@change" should go before "@input".', + type: 'VDirectiveKey' + }] + }, + { + filename: 'test.vue', + code: + ``, + options: [{ alphabetical: true }], + output: + ``, + errors: [{ + message: 'Attribute "boolean-prop" should go before "z-prop".', + type: 'VIdentifier' + }] + }, + { + filename: 'test.vue', + code: + ``, + options: [{ alphabetical: true }], + output: + ``, + errors: [{ + message: 'Attribute "v-on:[c]" should go before "v-on:click".', + type: 'VDirectiveKey' + }] + }, + { + filename: 'test.vue', + code: + ``, + options: [{ alphabetical: true }], + output: + ``, + errors: [{ + message: 'Attribute "v-on:click" should go before "v-text".', + type: 'VDirectiveKey' + }] } ] })