Skip to content

Commit d868611

Browse files
committed
⭐️New: Add vue/component-tags-order rule
1 parent 9c49dcc commit d868611

File tree

5 files changed

+398
-0
lines changed

5 files changed

+398
-0
lines changed

Diff for: docs/rules/README.md

+1
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ For example:
141141
|:--------|:------------|:---|
142142
| [vue/array-bracket-spacing](./array-bracket-spacing.md) | enforce consistent spacing inside array brackets | :wrench: |
143143
| [vue/component-name-in-template-casing](./component-name-in-template-casing.md) | enforce specific casing for the component naming style in template | :wrench: |
144+
| [vue/component-tags-order](./component-tags-order.md) | enforce order of component top-level elements | |
144145
| [vue/eqeqeq](./eqeqeq.md) | require the use of `===` and `!==` | :wrench: |
145146
| [vue/key-spacing](./key-spacing.md) | enforce consistent spacing between keys and values in object literal properties | :wrench: |
146147
| [vue/match-component-file-name](./match-component-file-name.md) | require component name property to match its file name | |

Diff for: docs/rules/component-tags-order.md

+96
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
---
2+
pageClass: rule-details
3+
sidebarDepth: 0
4+
title: vue/component-tags-order
5+
description: enforce order of component top-level elements
6+
---
7+
# vue/component-tags-order
8+
> enforce order of component top-level elements
9+
10+
## :book: Rule Details
11+
12+
This rule warns about the order of the `<script>`, `<template>` & `<style>` tags.
13+
14+
## :wrench: Options
15+
16+
```json
17+
{
18+
"vue/component-tags-order": ["error", {
19+
"order": ["script", "template", "style"]
20+
}]
21+
}
22+
```
23+
24+
- `order` (`string[]`) ... The order of top-level element names. default `["script", "template", "style"]`.
25+
26+
### `{ "order": ["script", "template", "style"] }` (default)
27+
28+
<eslint-code-block :rules="{'vue/component-tags-order': ['error']}">
29+
30+
```vue
31+
<!-- ✓ GOOD -->
32+
<script>/* ... */</script>
33+
<template>...</template>
34+
<style>/* ... */</style>
35+
```
36+
37+
</eslint-code-block>
38+
39+
<eslint-code-block :rules="{'vue/component-tags-order': ['error']}">
40+
41+
```vue
42+
<!-- ✗ BAD -->
43+
<style>/* ... */</style>
44+
<script>/* ... */</script>
45+
<template>...</template>
46+
```
47+
48+
</eslint-code-block>
49+
50+
### `{ "order": ["template", "script", "style"] }`
51+
52+
<eslint-code-block :rules="{'vue/component-tags-order': ['error', { 'order': ['template', 'script', 'style'] }]}">
53+
54+
```vue
55+
<!-- ✓ GOOD -->
56+
<template>...</template>
57+
<script>/* ... */</script>
58+
<style>/* ... */</style>
59+
```
60+
61+
</eslint-code-block>
62+
63+
### `{ "order": ["docs", "template", "script", "style"] }`
64+
65+
<eslint-code-block :rules="{'vue/component-tags-order': ['error', { 'order': ['docs', 'template', 'script', 'style'] }]}">
66+
67+
```vue
68+
<!-- ✓ GOOD -->
69+
<docs> documents </docs>
70+
<template>...</template>
71+
<script>/* ... */</script>
72+
<style>/* ... */</style>
73+
```
74+
75+
</eslint-code-block>
76+
77+
<eslint-code-block :rules="{'vue/component-tags-order': ['error', { 'order': ['docs', 'template', 'script', 'style'] }]}">
78+
79+
```vue
80+
<!-- ✗ BAD -->
81+
<template>...</template>
82+
<script>/* ... */</script>
83+
<docs> documents </docs>
84+
<style>/* ... */</style>
85+
```
86+
87+
</eslint-code-block>
88+
89+
## :books: Further reading
90+
91+
- [Style guide - Single-file component top-level element order](https://vuejs.org/v2/style-guide/#Single-file-component-top-level-element-order-recommended)
92+
93+
## :mag: Implementation
94+
95+
- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/component-tags-order.js)
96+
- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/component-tags-order.js)

Diff for: lib/index.js

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ module.exports = {
1212
'attributes-order': require('./rules/attributes-order'),
1313
'comment-directive': require('./rules/comment-directive'),
1414
'component-name-in-template-casing': require('./rules/component-name-in-template-casing'),
15+
'component-tags-order': require('./rules/component-tags-order'),
1516
'eqeqeq': require('./rules/eqeqeq'),
1617
'html-closing-bracket-newline': require('./rules/html-closing-bracket-newline'),
1718
'html-closing-bracket-spacing': require('./rules/html-closing-bracket-spacing'),

Diff for: lib/rules/component-tags-order.js

+94
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
/**
2+
* @author Yosuke Ota
3+
* issue https://github.com/vuejs/eslint-plugin-vue/issues/140
4+
*/
5+
'use strict'
6+
7+
// ------------------------------------------------------------------------------
8+
// Requirements
9+
// ------------------------------------------------------------------------------
10+
11+
const utils = require('../utils')
12+
13+
const DEFAULT_ORDER = Object.freeze(['script', 'template', 'style'])
14+
15+
// ------------------------------------------------------------------------------
16+
// Rule Definition
17+
// ------------------------------------------------------------------------------
18+
19+
module.exports = {
20+
meta: {
21+
type: 'suggestion',
22+
docs: {
23+
description: 'enforce order of component top-level elements',
24+
category: undefined,
25+
url: 'https://eslint.vuejs.org/rules/component-tags-order.html'
26+
},
27+
fixable: null,
28+
schema: {
29+
type: 'array',
30+
properties: {
31+
order: {
32+
type: 'array'
33+
}
34+
}
35+
},
36+
messages: {
37+
unexpected: 'The <{{name}}> should be above the <{{firstUnorderedName}}> on line {{line}}.'
38+
}
39+
},
40+
create (context) {
41+
const order = (context.options[0] && context.options[0].order) || DEFAULT_ORDER
42+
43+
function getTopLevelHTMLElements (node) {
44+
const templateBody = node.templateBody
45+
if (templateBody) {
46+
const document = templateBody.parent
47+
return document.children
48+
}
49+
return []
50+
}
51+
52+
function report (element, firstUnorderedElement) {
53+
context.report({
54+
node: element,
55+
loc: element.loc,
56+
messageId: 'unexpected',
57+
data: {
58+
name: element.name,
59+
firstUnorderedName: firstUnorderedElement.name,
60+
line: firstUnorderedElement.loc.start.line
61+
}
62+
})
63+
}
64+
65+
return utils.defineTemplateBodyVisitor(
66+
context,
67+
{},
68+
{
69+
Program (node) {
70+
if (utils.hasInvalidEOF(node)) {
71+
return
72+
}
73+
const elements = getTopLevelHTMLElements(node)
74+
75+
elements.forEach((element, index) => {
76+
const expectedIndex = order.indexOf(element.name)
77+
if (expectedIndex < 0) {
78+
return
79+
}
80+
const firstUnordered = elements
81+
.slice(0, index)
82+
.filter(e => expectedIndex < order.indexOf(e.name))
83+
.sort(
84+
(e1, e2) => order.indexOf(e1.name) - order.indexOf(e2.name)
85+
)[0]
86+
if (firstUnordered) {
87+
report(element, firstUnordered)
88+
}
89+
})
90+
}
91+
}
92+
)
93+
}
94+
}

0 commit comments

Comments
 (0)