Skip to content

Commit 8f09420

Browse files
lsdsjyIWANABETHATGUYota-meshi
authored
Auto-fix for vue/component-tags-order (#1811)
* feat: 🎸 finish sort order * Auto-fix for vue/component-tags-order * use fix:true and output for test case Apply suggestions from code review Co-authored-by: Yosuke Ota <[email protected]> * fix lint Co-authored-by: IWANABETHATGUY <[email protected]> Co-authored-by: Yosuke Ota <[email protected]>
1 parent 508ea0e commit 8f09420

File tree

4 files changed

+151
-41
lines changed

4 files changed

+151
-41
lines changed

Diff for: docs/rules/README.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ Enforce all the rules in this category, as well as all higher priority rules, wi
167167
| Rule ID | Description | |
168168
|:--------|:------------|:---|
169169
| [vue/attributes-order](./attributes-order.md) | enforce order of attributes | :wrench: |
170-
| [vue/component-tags-order](./component-tags-order.md) | enforce order of component top-level elements | |
170+
| [vue/component-tags-order](./component-tags-order.md) | enforce order of component top-level elements | :wrench: |
171171
| [vue/no-lone-template](./no-lone-template.md) | disallow unnecessary `<template>` | |
172172
| [vue/no-multiple-slot-args](./no-multiple-slot-args.md) | disallow to pass multiple arguments to scoped slots | |
173173
| [vue/no-v-html](./no-v-html.md) | disallow use of v-html to prevent XSS attack | |
@@ -283,7 +283,7 @@ Enforce all the rules in this category, as well as all higher priority rules, wi
283283
| Rule ID | Description | |
284284
|:--------|:------------|:---|
285285
| [vue/attributes-order](./attributes-order.md) | enforce order of attributes | :wrench: |
286-
| [vue/component-tags-order](./component-tags-order.md) | enforce order of component top-level elements | |
286+
| [vue/component-tags-order](./component-tags-order.md) | enforce order of component top-level elements | :wrench: |
287287
| [vue/no-lone-template](./no-lone-template.md) | disallow unnecessary `<template>` | |
288288
| [vue/no-multiple-slot-args](./no-multiple-slot-args.md) | disallow to pass multiple arguments to scoped slots | |
289289
| [vue/no-v-html](./no-v-html.md) | disallow use of v-html to prevent XSS attack | |

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

+8-7
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ since: v6.1.0
1010
> enforce order of component top-level elements
1111
1212
- :gear: This rule is included in `"plugin:vue/vue3-recommended"` and `"plugin:vue/recommended"`.
13+
- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fixing-problems) can automatically fix some of the problems reported by this rule.
1314

1415
## :book: Rule Details
1516

@@ -29,7 +30,7 @@ This rule warns about the order of the `<script>`, `<template>` & `<style>` tags
2930

3031
### `{ "order": [ [ "script", "template" ], "style" ] }` (default)
3132

32-
<eslint-code-block :rules="{'vue/component-tags-order': ['error']}">
33+
<eslint-code-block fix :rules="{'vue/component-tags-order': ['error']}">
3334

3435
```vue
3536
<!-- ✓ GOOD -->
@@ -40,7 +41,7 @@ This rule warns about the order of the `<script>`, `<template>` & `<style>` tags
4041

4142
</eslint-code-block>
4243

43-
<eslint-code-block :rules="{'vue/component-tags-order': ['error']}">
44+
<eslint-code-block fix :rules="{'vue/component-tags-order': ['error']}">
4445

4546
```vue
4647
<!-- ✓ GOOD -->
@@ -51,7 +52,7 @@ This rule warns about the order of the `<script>`, `<template>` & `<style>` tags
5152

5253
</eslint-code-block>
5354

54-
<eslint-code-block :rules="{'vue/component-tags-order': ['error']}">
55+
<eslint-code-block fix :rules="{'vue/component-tags-order': ['error']}">
5556

5657
```vue
5758
<!-- ✗ BAD -->
@@ -64,7 +65,7 @@ This rule warns about the order of the `<script>`, `<template>` & `<style>` tags
6465

6566
### `{ "order": ["template", "script", "style"] }`
6667

67-
<eslint-code-block :rules="{'vue/component-tags-order': ['error', { 'order': ['template', 'script', 'style'] }]}">
68+
<eslint-code-block fix :rules="{'vue/component-tags-order': ['error', { 'order': ['template', 'script', 'style'] }]}">
6869

6970
```vue
7071
<!-- ✓ GOOD -->
@@ -75,7 +76,7 @@ This rule warns about the order of the `<script>`, `<template>` & `<style>` tags
7576

7677
</eslint-code-block>
7778

78-
<eslint-code-block :rules="{'vue/component-tags-order': ['error', { 'order': ['template', 'script', 'style'] }]}">
79+
<eslint-code-block fix :rules="{'vue/component-tags-order': ['error', { 'order': ['template', 'script', 'style'] }]}">
7980

8081
```vue
8182
<!-- ✗ BAD -->
@@ -88,7 +89,7 @@ This rule warns about the order of the `<script>`, `<template>` & `<style>` tags
8889

8990
### `{ "order": ["docs", "template", "script", "style"] }`
9091

91-
<eslint-code-block :rules="{'vue/component-tags-order': ['error', { 'order': ['docs', 'template', 'script', 'style'] }]}">
92+
<eslint-code-block fix :rules="{'vue/component-tags-order': ['error', { 'order': ['docs', 'template', 'script', 'style'] }]}">
9293

9394
```vue
9495
<!-- ✓ GOOD -->
@@ -100,7 +101,7 @@ This rule warns about the order of the `<script>`, `<template>` & `<style>` tags
100101

101102
</eslint-code-block>
102103

103-
<eslint-code-block :rules="{'vue/component-tags-order': ['error', { 'order': ['docs', 'template', 'script', 'style'] }]}">
104+
<eslint-code-block fix :rules="{'vue/component-tags-order': ['error', { 'order': ['docs', 'template', 'script', 'style'] }]}">
104105

105106
```vue
106107
<!-- ✗ BAD -->

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

+31-20
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ module.exports = {
2424
categories: ['vue3-recommended', 'recommended'],
2525
url: 'https://eslint.vuejs.org/rules/component-tags-order.html'
2626
},
27-
fixable: null,
27+
fixable: 'code',
2828
schema: [
2929
{
3030
type: 'object',
@@ -87,30 +87,13 @@ module.exports = {
8787
return []
8888
}
8989

90-
/**
91-
* @param {VElement} element
92-
* @param {VElement} firstUnorderedElement
93-
*/
94-
function report(element, firstUnorderedElement) {
95-
context.report({
96-
node: element,
97-
loc: element.loc,
98-
messageId: 'unexpected',
99-
data: {
100-
name: element.name,
101-
firstUnorderedName: firstUnorderedElement.name,
102-
line: firstUnorderedElement.loc.start.line
103-
}
104-
})
105-
}
106-
10790
return {
10891
Program(node) {
10992
if (utils.hasInvalidEOF(node)) {
11093
return
11194
}
11295
const elements = getTopLevelHTMLElements()
113-
96+
const sourceCode = context.getSourceCode()
11497
elements.forEach((element, index) => {
11598
const expectedIndex = getOrderPosition(element.name)
11699
if (expectedIndex < 0) {
@@ -123,7 +106,35 @@ module.exports = {
123106
(e1, e2) => getOrderPosition(e1.name) - getOrderPosition(e2.name)
124107
)[0]
125108
if (firstUnordered) {
126-
report(element, firstUnordered)
109+
context.report({
110+
node: element,
111+
loc: element.loc,
112+
messageId: 'unexpected',
113+
data: {
114+
name: element.name,
115+
firstUnorderedName: firstUnordered.name,
116+
line: firstUnordered.loc.start.line
117+
},
118+
*fix(fixer) {
119+
// insert element before firstUnordered
120+
const fixedElements = elements.flatMap((it) => {
121+
if (it === firstUnordered) {
122+
return [element, it]
123+
} else if (it === element) {
124+
return []
125+
}
126+
return [it]
127+
})
128+
for (let i = elements.length - 1; i >= 0; i--) {
129+
if (elements[i] !== fixedElements[i]) {
130+
yield fixer.replaceTextRange(
131+
elements[i].range,
132+
sourceCode.text.slice(...fixedElements[i].range)
133+
)
134+
}
135+
}
136+
}
137+
})
127138
}
128139
})
129140
}

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

+110-12
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,26 @@
99

1010
const rule = require('../../../lib/rules/component-tags-order')
1111
const RuleTester = require('eslint').RuleTester
12+
const assert = require('assert')
13+
const { ESLint } = require('../../eslint-compat')
14+
15+
// Initialize linter.
16+
const eslint = new ESLint({
17+
overrideConfig: {
18+
parser: require.resolve('vue-eslint-parser'),
19+
parserOptions: {
20+
ecmaVersion: 2015
21+
},
22+
plugins: ['vue'],
23+
rules: {
24+
'vue/comment-directive': 'error',
25+
'vue/component-tags-order': 'error'
26+
}
27+
},
28+
useEslintrc: false,
29+
plugins: { vue: require('../../../lib/index') },
30+
fix: true
31+
})
1232

1333
// ------------------------------------------------------------------------------
1434
// Tests
@@ -108,7 +128,8 @@ tester.run('component-tags-order', rule, {
108128
line: 1,
109129
column: 37
110130
}
111-
]
131+
],
132+
output: '<template></template><style></style><script></script>'
112133
},
113134
{
114135
code: '<template></template><script></script><style></style>',
@@ -119,7 +140,8 @@ tester.run('component-tags-order', rule, {
119140
line: 1,
120141
column: 22
121142
}
122-
]
143+
],
144+
output: '<script></script><template></template><style></style>'
123145
},
124146
{
125147
code: `
@@ -133,7 +155,14 @@ tester.run('component-tags-order', rule, {
133155
message: 'The <script> should be above the <style> on line 4.',
134156
line: 6
135157
}
136-
]
158+
],
159+
output:
160+
'\n' +
161+
' <template></template>\n' +
162+
'\n' +
163+
' <script></script>\n' +
164+
'\n' +
165+
' <style></style>'
137166
},
138167
{
139168
code: `
@@ -147,7 +176,13 @@ tester.run('component-tags-order', rule, {
147176
message: 'The <script> should be above the <template> on line 2.',
148177
line: 3
149178
}
150-
]
179+
],
180+
output:
181+
'\n' +
182+
' <script></script>\n' +
183+
' <template></template>\n' +
184+
' <style></style>\n' +
185+
' '
151186
},
152187
{
153188
code: `
@@ -161,7 +196,13 @@ tester.run('component-tags-order', rule, {
161196
message: 'The <template> should be above the <script> on line 2.',
162197
line: 3
163198
}
164-
]
199+
],
200+
output:
201+
'\n' +
202+
' <template></template>\n' +
203+
' <script></script>\n' +
204+
' <style></style>\n' +
205+
' '
165206
},
166207
{
167208
code: `
@@ -176,7 +217,14 @@ tester.run('component-tags-order', rule, {
176217
message: 'The <docs> should be above the <template> on line 2.',
177218
line: 3
178219
}
179-
]
220+
],
221+
output:
222+
'\n' +
223+
' <docs></docs>\n' +
224+
' <template></template>\n' +
225+
' <script></script>\n' +
226+
' <style></style>\n' +
227+
' '
180228
},
181229
{
182230
code: `
@@ -191,7 +239,14 @@ tester.run('component-tags-order', rule, {
191239
message: 'The <script> should be above the <template> on line 2.',
192240
line: 4
193241
}
194-
]
242+
],
243+
output:
244+
'\n' +
245+
' <script></script>\n' +
246+
' <template></template>\n' +
247+
' <docs></docs>\n' +
248+
' <style></style>\n' +
249+
' '
195250
},
196251
{
197252
code: `
@@ -207,7 +262,15 @@ tester.run('component-tags-order', rule, {
207262
message: 'The <script> should be above the <template> on line 2.',
208263
line: 5
209264
}
210-
]
265+
],
266+
output:
267+
'\n' +
268+
' <script></script>\n' +
269+
' <template></template>\n' +
270+
' <docs>\n' +
271+
' </docs>\n' +
272+
' <style></style>\n' +
273+
' '
211274
},
212275
{
213276
code: `
@@ -220,7 +283,9 @@ tester.run('component-tags-order', rule, {
220283
message: 'The <template> should be above the <script> on line 2.',
221284
line: 3
222285
}
223-
]
286+
],
287+
output:
288+
'\n <template></template>\n <script></script>\n '
224289
},
225290
{
226291
code: `
@@ -237,7 +302,13 @@ tester.run('component-tags-order', rule, {
237302
message: 'The <script> should be above the <style> on line 2.',
238303
line: 4
239304
}
240-
]
305+
],
306+
output:
307+
'\n' +
308+
' <template></template>\n' +
309+
' <style></style>\n' +
310+
' <script></script>\n' +
311+
' '
241312
},
242313
{
243314
code: `
@@ -255,7 +326,14 @@ tester.run('component-tags-order', rule, {
255326
message: 'The <script> should be above the <style> on line 2.',
256327
line: 5
257328
}
258-
]
329+
],
330+
output:
331+
'\n' +
332+
' <template></template>\n' +
333+
' <style></style>\n' +
334+
' <docs></docs>\n' +
335+
' <script></script>\n' +
336+
' '
259337
},
260338
// no <template>
261339
{
@@ -268,7 +346,27 @@ tester.run('component-tags-order', rule, {
268346
message: 'The <script> should be above the <style> on line 2.',
269347
line: 3
270348
}
271-
]
349+
],
350+
output: '\n <script></script>\n <style></style>\n '
272351
}
273352
]
274353
})
354+
355+
describe('suppress reporting with eslint-disable-next-line', () => {
356+
it('do not report if <!-- eslint-disable-next-line -->', async () => {
357+
const code = `<style></style><template></template>
358+
<!-- eslint-disable-next-line vue/component-tags-order -->
359+
<script></script>`
360+
const [{ messages, output }] = await eslint.lintText(code, {
361+
filePath: 'test.vue'
362+
})
363+
assert.strictEqual(messages.length, 0)
364+
// should not fix <script>
365+
assert.strictEqual(
366+
output,
367+
`<template></template><style></style>
368+
<!-- eslint-disable-next-line vue/component-tags-order -->
369+
<script></script>`
370+
)
371+
})
372+
})

0 commit comments

Comments
 (0)