Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit e804ca2

Browse files
erindepewmichalsnik
erindepew
authored andcommitted
attribute order linting
1 parent 789a2fc commit e804ca2

File tree

5 files changed

+391
-0
lines changed

5 files changed

+391
-0
lines changed

‎README.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,7 @@ Enforce all the rules in this category, as well as all higher priority rules, wi
204204

205205
| | Rule ID | Description |
206206
|:---|:--------|:------------|
207+
| | [vue/attributes-order](./docs/rules/attributes-order.md) | enforce order of attributes |
207208
| :wrench: | [vue/html-closing-bracket-newline](./docs/rules/html-closing-bracket-newline.md) | require or disallow a line break before tag's closing brackets |
208209
| :wrench: | [vue/html-closing-bracket-spacing](./docs/rules/html-closing-bracket-spacing.md) | require or disallow a space before tag's closing brackets |
209210
| :wrench: | [vue/script-indent](./docs/rules/script-indent.md) | enforce consistent indentation in `<script>` |

‎docs/rules/attributes-order.md‎

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
# enforce order of attributes (vue/attributes-order)
2+
3+
## :book: Rule Details
4+
5+
This rule aims to enfore ordering of component attributes. The default order is specified in the [Vue styleguide](https://vuejs.org/v2/style-guide/#Element-attribute-order-recommended) and is:
6+
- DEFINITION
7+
ex: 'is'
8+
- LIST_RENDERING
9+
ex: 'v-for item in items'
10+
- CONDITIONALS
11+
ex: 'v-if', 'v-else-if', 'v-else', 'v-show', 'v-cloak'
12+
- RENDER_MODIFIERS
13+
ex: 'v-once', 'v-pre'
14+
- GLOBAL
15+
ex: 'id'
16+
- UNIQUE
17+
ex: 'ref', 'key', 'slot'
18+
- BINDING
19+
ex: 'v-model', 'v-bind'
20+
- OTHER_ATTR
21+
ex: 'customProp="foo"'
22+
- EVENTS
23+
ex: '@click="functionCall"'
24+
- CONTENT
25+
ex: 'v-text', 'v-html'
26+
27+
:+1: Examples of **correct** code`:
28+
29+
```html
30+
<div
31+
is="header"
32+
v-for="item in items"
33+
v-if="!visible"
34+
v-once id="uniqueID"
35+
ref="header"
36+
v-model="headerData"
37+
myProp="prop"
38+
@click="functionCall"
39+
v-text="textContent">
40+
</div>
41+
```
42+
43+
```html
44+
<div
45+
v-for="item in items"
46+
v-if="!visible"
47+
propOne="prop"
48+
propTwo="prop"
49+
propThree="prop"
50+
@click="functionCall"
51+
v-text="textContent">
52+
</div>
53+
```
54+
55+
```html
56+
<div
57+
propOne="prop"
58+
propTwo="prop"
59+
propThree="prop">
60+
</div>
61+
```
62+
63+
:-1: Examples of **incorrect** code`:
64+
65+
```html
66+
<div
67+
ref="header"
68+
v-for="item in items"
69+
v-once id="uniqueID"
70+
v-model="headerData"
71+
myProp="prop"
72+
v-if="!visible"
73+
is="header"
74+
@click="functionCall"
75+
v-text="textContent">
76+
</div>
77+
```
78+
79+
### `order`
80+
81+
Specify custom order of attribute groups
82+
83+
:+1: Examples of **correct** code with custom order`:
84+
85+
```html
86+
<!-- 'vue/attribute-order': [2, { order: ['LIST_RENDERING', 'CONDITIONALS', 'RENDER_MODIFIERS', 'GLOBAL', 'UNIQUE', 'BINDING', 'OTHER_ATTR', 'EVENTS', 'CONTENT', 'DEFINITION'] }] -->
87+
<div
88+
propOne="prop"
89+
propTwo="prop"
90+
is="header">
91+
</div>
92+
```
93+
94+
```html
95+
<!-- 'vue/attribute-order': [2, { order: ['LIST_RENDERING', 'CONDITIONALS', 'RENDER_MODIFIERS', 'GLOBAL', 'UNIQUE', 'BINDING', 'DEFINITION', 'OTHER_ATTR', 'EVENTS', 'CONTENT'] }] -->
96+
<div
97+
ref="header"
98+
is="header"
99+
propOne="prop"
100+
propTwo="prop">
101+
</div>
102+
```
103+
104+
:-1: Examples of **incorrect** code with custom order`:
105+
106+
```html
107+
<!-- 'vue/attribute-order': [2, { order: ['LIST_RENDERING', 'CONDITIONALS', 'RENDER_MODIFIERS', 'GLOBAL', 'UNIQUE', 'BINDING', 'DEFINITION', 'OTHER_ATTR', 'EVENTS', 'CONTENT'] }] -->
108+
<div
109+
ref="header"
110+
propOne="prop"
111+
is="header">
112+
</div>
113+
```

‎lib/index.js‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
module.exports = {
99
rules: {
1010
'attribute-hyphenation': require('./rules/attribute-hyphenation'),
11+
'attributes-order': require('./rules/attributes-order'),
1112
'comment-directive': require('./rules/comment-directive'),
1213
'html-closing-bracket-newline': require('./rules/html-closing-bracket-newline'),
1314
'html-closing-bracket-spacing': require('./rules/html-closing-bracket-spacing'),

‎lib/rules/attributes-order.js‎

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
/**
2+
* @fileoverview enforce ordering of attributes
3+
* @author Erin Depew
4+
*/
5+
'use strict'
6+
const utils = require('../utils')
7+
8+
// ------------------------------------------------------------------------------
9+
// Rule Definition
10+
// ------------------------------------------------------------------------------
11+
12+
function getAttributeType (name, isDirective) {
13+
if (isDirective) {
14+
if (name === 'for') {
15+
return 'LIST_RENDERING'
16+
} else if (name === 'if' || name === 'else-if' || name === 'else' || name === 'show' || name === 'cloak') {
17+
return 'CONDITIONALS'
18+
} else if (name === 'pre' || name === 'once') {
19+
return 'RENDER_MODIFIERS'
20+
} else if (name === 'model' || name === 'bind') {
21+
return 'BINDING'
22+
} else if (name === 'on') {
23+
return 'EVENTS'
24+
} else if (name === 'html' || name === 'text') {
25+
return 'CONTENT'
26+
}
27+
} else {
28+
if (name === 'is') {
29+
return 'DEFINITION'
30+
} else if (name === 'id') {
31+
return 'GLOBAL'
32+
} else if (name === 'ref' || name === 'key' || name === 'slot') {
33+
return 'UNIQUE'
34+
} else {
35+
return 'OTHER_ATTR'
36+
}
37+
}
38+
}
39+
function getPosition (attribute, attributeOrder) {
40+
const attributeType = getAttributeType(attribute.key.name, attribute.directive)
41+
return attributeOrder.indexOf(attributeType)
42+
}
43+
44+
function create (context) {
45+
const sourceCode = context.getSourceCode()
46+
let attributeOrder = ['DEFINITION', 'LIST_RENDERING', 'CONDITIONALS', 'RENDER_MODIFIERS', 'GLOBAL', 'UNIQUE', 'BINDING', 'OTHER_ATTR', 'EVENTS', 'CONTENT']
47+
if (context.options[0] && context.options[0].order) {
48+
attributeOrder = context.options[0].order
49+
}
50+
let currentPosition
51+
let previousNode
52+
53+
function reportIssue (node, previousNode) {
54+
const currentNode = sourceCode.getText(node.key)
55+
const prevNode = sourceCode.getText(previousNode.key)
56+
context.report({
57+
node: node.key,
58+
loc: node.loc,
59+
message: `Attribute "${currentNode}" should go before "${prevNode}".`,
60+
data: {
61+
currentNode
62+
}
63+
})
64+
}
65+
66+
return utils.defineTemplateBodyVisitor(context, {
67+
'VStartTag' () {
68+
currentPosition = -1
69+
previousNode = null
70+
},
71+
'VAttribute' (node) {
72+
if ((currentPosition === -1) || (currentPosition <= getPosition(node, attributeOrder))) {
73+
currentPosition = getPosition(node, attributeOrder)
74+
previousNode = node
75+
} else {
76+
reportIssue(node, previousNode)
77+
}
78+
}
79+
})
80+
}
81+
82+
module.exports = {
83+
meta: {
84+
docs: {
85+
description: 'enforce order of attributes',
86+
category: undefined,
87+
recommended: false
88+
},
89+
fixable: null,
90+
schema: {
91+
type: 'array',
92+
properties: {
93+
order: {
94+
items: {
95+
type: 'string'
96+
},
97+
maxItems: 10,
98+
minItems: 10
99+
}
100+
}
101+
}
102+
},
103+
create
104+
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /