Skip to content

Commit 7dc3e5b

Browse files
author
erindepew
committed
adding in attribute-order
1 parent 4f394c8 commit 7dc3e5b

File tree

5 files changed

+273
-1
lines changed

5 files changed

+273
-1
lines changed

Diff for: README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ The `--fix` option on the command line automatically fixes problems reported by
147147
| :wrench: | [no-multi-spaces](./docs/rules/no-multi-spaces.md) | disallow multiple spaces |
148148
| :wrench: | [v-bind-style](./docs/rules/v-bind-style.md) | enforce `v-bind` directive style |
149149
| :wrench: | [v-on-style](./docs/rules/v-on-style.md) | enforce `v-on` directive style |
150-
150+
| :wrench: | [attribute-order](./docs/rules/attribute-order.md) | enforce alphabetical ordering of properties and prioritizing vue-specific attributes |
151151

152152
### Variables
153153

Diff for: docs/rules/attribute-order.md

+77
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
# enforce alphabetical ordering of properties and prioritizing vue-specific attributes (attribute-order)
2+
3+
## Rule Details
4+
5+
:+1: Examples of **correct** code`:
6+
7+
```html
8+
9+
<div
10+
@click="handleClick"
11+
:class="$style.myStyle"
12+
v-for="(item, index) in items"
13+
myProp="title"
14+
ref="myComponent">
15+
</div>
16+
17+
```
18+
19+
```html
20+
21+
<div
22+
@click="handleClick"
23+
:class="$style.myStyle"
24+
v-if="itemExists"
25+
:myProp="title"
26+
ref="myComponent">
27+
</div>
28+
29+
```
30+
31+
```html
32+
33+
<div
34+
@click="handleClick"
35+
:amount="34"
36+
:class="$style.myStyle"
37+
ref="myComponent">
38+
</div>
39+
40+
```
41+
42+
:-1: Examples of **incorrect** code`:
43+
44+
```html
45+
46+
<div
47+
@click="handleClick"
48+
v-for="(item, index) in items"
49+
:class="$style.myStyle"
50+
myProp="title"
51+
ref="myComponent">
52+
</div>
53+
54+
```
55+
56+
```html
57+
58+
<div
59+
@click="handleClick"
60+
v-if="itemExists"
61+
:class="$style.myStyle"
62+
myProp="title"
63+
ref="myComponent">
64+
</div>
65+
66+
```
67+
68+
```html
69+
70+
<div
71+
:amount="34"
72+
@click="handleClick"
73+
:class="$style.myStyle"
74+
ref="myComponent">
75+
</div>
76+
77+
```

Diff for: lib/recommended-rules.js

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
*/
66
module.exports = {
77
"vue/attribute-hyphenation": "off",
8+
"vue/attribute-order": "off",
89
"vue/html-end-tags": "off",
910
"vue/html-no-self-closing": "off",
1011
"vue/html-quotes": "off",

Diff for: lib/rules/attribute-order.js

+76
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
/**
2+
* @fileoverview enforce alphabetical ordering of properties and prioritizing vue-specific attributes
3+
* @author Erin Depew
4+
*/
5+
'use strict'
6+
const utils = require('../utils')
7+
8+
// ------------------------------------------------------------------------------
9+
// Rule Definition
10+
// ------------------------------------------------------------------------------
11+
12+
function getName (attribute) {
13+
if (!attribute.directive) {
14+
return attribute.key.name
15+
}
16+
if (attribute.key.name === 'bind') {
17+
return attribute.key.argument || null
18+
}
19+
if (attribute.directive) {
20+
return '@' + attribute.key.argument
21+
}
22+
return null
23+
}
24+
25+
function create (context) {
26+
const sourceCode = context.getSourceCode()
27+
let attributeList
28+
let previousNode
29+
30+
function reportIssue (node, previousNode, name) {
31+
const currentNode = sourceCode.getText(node.key)
32+
const prevNode = sourceCode.getText(previousNode.key)
33+
34+
context.report({
35+
node: node.key,
36+
loc: node.loc,
37+
message: `Attribute ${currentNode} must go before ${prevNode}.`,
38+
data: {
39+
currentNode
40+
}
41+
})
42+
}
43+
44+
return utils.defineTemplateBodyVisitor(context, {
45+
'VStartTag' () {
46+
attributeList = []
47+
},
48+
'VAttribute' (node) {
49+
const name = getName(node)
50+
51+
if (attributeList.length && attributeList[attributeList.length - 1] < name && name) {
52+
attributeList.push(name)
53+
previousNode = node
54+
} else if (attributeList.length === 0 && name) {
55+
attributeList.push(name)
56+
previousNode = node
57+
} else {
58+
reportIssue(node, previousNode, name)
59+
}
60+
}
61+
})
62+
}
63+
64+
module.exports = {
65+
meta: {
66+
docs: {
67+
description: 'enforce alphabetical ordering of properties and prioritizing vue-specific attributes',
68+
category: 'Fill me in',
69+
recommended: false
70+
},
71+
fixable: null,
72+
schema: []
73+
},
74+
75+
create
76+
}

Diff for: tests/lib/rules/attribute-order.js

+118
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
/**
2+
* @fileoverview enforce alphabetical ordering of properties and prioritizing vue-specific attributes
3+
* @author Erin Depew
4+
*/
5+
'use strict'
6+
7+
// ------------------------------------------------------------------------------
8+
// Requirements
9+
// ------------------------------------------------------------------------------
10+
11+
var rule = require('../../../lib/rules/attribute-order')
12+
13+
var RuleTester = require('eslint').RuleTester
14+
15+
// ------------------------------------------------------------------------------
16+
// Tests
17+
// ------------------------------------------------------------------------------
18+
19+
var tester = new RuleTester({
20+
parser: 'vue-eslint-parser',
21+
parserOptions: {ecmaVersion: 2015}
22+
})
23+
tester.run('attribute-order', rule, {
24+
25+
valid: [
26+
{
27+
filename: 'test.vue',
28+
code: '<template><div></div></template>'
29+
},
30+
{
31+
filename: 'test.vue',
32+
code: '<template><div><custom aria-test="bar" data-id="foo" myProp="prop"></custom></div></template>'
33+
},
34+
{
35+
filename: 'test.vue',
36+
code: '<template><div><custom v-if="bar" data-id="foo" myProp="prop"></custom></div></template>'
37+
},
38+
{
39+
filename: 'test.vue',
40+
code: '<template><div><custom v-if="bar" data-id="foo" :myProp="prop"></custom></div></template>'
41+
},
42+
{
43+
filename: 'test.vue',
44+
code: '<template><div><custom aria-test="bar" :class="$style.foo" myProp="prop"></custom></div></template>'
45+
},
46+
{
47+
filename: 'test.vue',
48+
code: '<template><div><custom @click="bar" :class="$style.foo" myProp="prop"></custom></div></template>'
49+
},
50+
{
51+
filename: 'test.vue',
52+
code: '<template><div><custom @click="bar" :class="$style.foo" myProp="prop" ref="myComponent"></custom></div></template>'
53+
},
54+
{
55+
filename: 'test.vue',
56+
code: '<template><div><custom @click="bar" v-for="(item, index) in items" :class="$style.foo" myProp="prop" ref="myComponent"></custom></div></template>'
57+
}
58+
],
59+
60+
invalid: [
61+
{
62+
filename: 'test.vue',
63+
code: '<template><div><custom data-id="foo" aria-test="bar" myProp="prop"></custom></div></template>',
64+
errors: [{
65+
message: 'Attribute aria-test must go before data-id.',
66+
type: 'VIdentifier'
67+
}]
68+
},
69+
{
70+
filename: 'test.vue',
71+
code: '<template><div><custom data-id="foo" myProp="prop" v-if="bar" ></custom></div></template>',
72+
errors: [{
73+
message: 'Attribute v-if must go before myProp.',
74+
type: 'VDirectiveKey'
75+
}]
76+
},
77+
{
78+
filename: 'test.vue',
79+
code: '<template><div><custom data-id="foo" :myProp="prop" v-if="bar"></custom></div></template>',
80+
errors: [{
81+
message: 'Attribute v-if must go before :myProp.',
82+
type: 'VDirectiveKey'
83+
}]
84+
},
85+
{
86+
filename: 'test.vue',
87+
code: '<template><div><custom :class="$style.foo" aria-test="bar" myProp="prop"></custom></div></template>',
88+
errors: [{
89+
message: 'Attribute aria-test must go before :class.',
90+
type: 'VIdentifier'
91+
}]
92+
},
93+
{
94+
filename: 'test.vue',
95+
code: '<template><div><custom @click="bar" myProp="prop" :class="$style.foo"></custom></div></template>',
96+
errors: [{
97+
message: 'Attribute :class must go before myProp.',
98+
type: 'VDirectiveKey'
99+
}]
100+
},
101+
{
102+
filename: 'test.vue',
103+
code: '<template><div><custom :class="$style.foo" @click="bar" myProp="prop" ref="myComponent"></custom></div></template>',
104+
errors: [{
105+
message: 'Attribute @click must go before :class.',
106+
type: 'VDirectiveKey'
107+
}]
108+
},
109+
{
110+
filename: 'test.vue',
111+
code: '<template><div><custom v-for="(item, index) in items" @click="bar" :class="$style.foo" myProp="prop" ref="myComponent"></custom></div></template>',
112+
errors: [{
113+
message: 'Attribute @click must go before v-for.',
114+
type: 'VDirectiveKey'
115+
}]
116+
}
117+
]
118+
})

0 commit comments

Comments
 (0)