Skip to content

Commit

Permalink
Auto-fix for vue/component-tags-order (#1811)
Browse files Browse the repository at this point in the history
* 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 <otameshiyo23@gmail.com>

* fix lint

Co-authored-by: IWANABETHATGUY <974153916@qq.com>
Co-authored-by: Yosuke Ota <otameshiyo23@gmail.com>
  • Loading branch information
3 people committed Mar 6, 2022
1 parent 508ea0e commit 8f09420
Show file tree
Hide file tree
Showing 4 changed files with 151 additions and 41 deletions.
4 changes: 2 additions & 2 deletions docs/rules/README.md
Expand Up @@ -167,7 +167,7 @@ Enforce all the rules in this category, as well as all higher priority rules, wi
| Rule ID | Description | |
|:--------|:------------|:---|
| [vue/attributes-order](./attributes-order.md) | enforce order of attributes | :wrench: |
| [vue/component-tags-order](./component-tags-order.md) | enforce order of component top-level elements | |
| [vue/component-tags-order](./component-tags-order.md) | enforce order of component top-level elements | :wrench: |
| [vue/no-lone-template](./no-lone-template.md) | disallow unnecessary `<template>` | |
| [vue/no-multiple-slot-args](./no-multiple-slot-args.md) | disallow to pass multiple arguments to scoped slots | |
| [vue/no-v-html](./no-v-html.md) | disallow use of v-html to prevent XSS attack | |
Expand Down Expand Up @@ -283,7 +283,7 @@ Enforce all the rules in this category, as well as all higher priority rules, wi
| Rule ID | Description | |
|:--------|:------------|:---|
| [vue/attributes-order](./attributes-order.md) | enforce order of attributes | :wrench: |
| [vue/component-tags-order](./component-tags-order.md) | enforce order of component top-level elements | |
| [vue/component-tags-order](./component-tags-order.md) | enforce order of component top-level elements | :wrench: |
| [vue/no-lone-template](./no-lone-template.md) | disallow unnecessary `<template>` | |
| [vue/no-multiple-slot-args](./no-multiple-slot-args.md) | disallow to pass multiple arguments to scoped slots | |
| [vue/no-v-html](./no-v-html.md) | disallow use of v-html to prevent XSS attack | |
Expand Down
15 changes: 8 additions & 7 deletions docs/rules/component-tags-order.md
Expand Up @@ -10,6 +10,7 @@ since: v6.1.0
> enforce order of component top-level elements
- :gear: This rule is included in `"plugin:vue/vue3-recommended"` and `"plugin:vue/recommended"`.
- :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.

## :book: Rule Details

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

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

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

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

</eslint-code-block>

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

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

</eslint-code-block>

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

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

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

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

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

</eslint-code-block>

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

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

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

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

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

</eslint-code-block>

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

```vue
<!-- ✗ BAD -->
Expand Down
51 changes: 31 additions & 20 deletions lib/rules/component-tags-order.js
Expand Up @@ -24,7 +24,7 @@ module.exports = {
categories: ['vue3-recommended', 'recommended'],
url: 'https://eslint.vuejs.org/rules/component-tags-order.html'
},
fixable: null,
fixable: 'code',
schema: [
{
type: 'object',
Expand Down Expand Up @@ -87,30 +87,13 @@ module.exports = {
return []
}

/**
* @param {VElement} element
* @param {VElement} firstUnorderedElement
*/
function report(element, firstUnorderedElement) {
context.report({
node: element,
loc: element.loc,
messageId: 'unexpected',
data: {
name: element.name,
firstUnorderedName: firstUnorderedElement.name,
line: firstUnorderedElement.loc.start.line
}
})
}

return {
Program(node) {
if (utils.hasInvalidEOF(node)) {
return
}
const elements = getTopLevelHTMLElements()

const sourceCode = context.getSourceCode()
elements.forEach((element, index) => {
const expectedIndex = getOrderPosition(element.name)
if (expectedIndex < 0) {
Expand All @@ -123,7 +106,35 @@ module.exports = {
(e1, e2) => getOrderPosition(e1.name) - getOrderPosition(e2.name)
)[0]
if (firstUnordered) {
report(element, firstUnordered)
context.report({
node: element,
loc: element.loc,
messageId: 'unexpected',
data: {
name: element.name,
firstUnorderedName: firstUnordered.name,
line: firstUnordered.loc.start.line
},
*fix(fixer) {
// insert element before firstUnordered
const fixedElements = elements.flatMap((it) => {
if (it === firstUnordered) {
return [element, it]
} else if (it === element) {
return []
}
return [it]
})
for (let i = elements.length - 1; i >= 0; i--) {
if (elements[i] !== fixedElements[i]) {
yield fixer.replaceTextRange(
elements[i].range,
sourceCode.text.slice(...fixedElements[i].range)
)
}
}
}
})
}
})
}
Expand Down
122 changes: 110 additions & 12 deletions tests/lib/rules/component-tags-order.js
Expand Up @@ -9,6 +9,26 @@

const rule = require('../../../lib/rules/component-tags-order')
const RuleTester = require('eslint').RuleTester
const assert = require('assert')
const { ESLint } = require('../../eslint-compat')

// Initialize linter.
const eslint = new ESLint({
overrideConfig: {
parser: require.resolve('vue-eslint-parser'),
parserOptions: {
ecmaVersion: 2015
},
plugins: ['vue'],
rules: {
'vue/comment-directive': 'error',
'vue/component-tags-order': 'error'
}
},
useEslintrc: false,
plugins: { vue: require('../../../lib/index') },
fix: true
})

// ------------------------------------------------------------------------------
// Tests
Expand Down Expand Up @@ -108,7 +128,8 @@ tester.run('component-tags-order', rule, {
line: 1,
column: 37
}
]
],
output: '<template></template><style></style><script></script>'
},
{
code: '<template></template><script></script><style></style>',
Expand All @@ -119,7 +140,8 @@ tester.run('component-tags-order', rule, {
line: 1,
column: 22
}
]
],
output: '<script></script><template></template><style></style>'
},
{
code: `
Expand All @@ -133,7 +155,14 @@ tester.run('component-tags-order', rule, {
message: 'The <script> should be above the <style> on line 4.',
line: 6
}
]
],
output:
'\n' +
' <template></template>\n' +
'\n' +
' <script></script>\n' +
'\n' +
' <style></style>'
},
{
code: `
Expand All @@ -147,7 +176,13 @@ tester.run('component-tags-order', rule, {
message: 'The <script> should be above the <template> on line 2.',
line: 3
}
]
],
output:
'\n' +
' <script></script>\n' +
' <template></template>\n' +
' <style></style>\n' +
' '
},
{
code: `
Expand All @@ -161,7 +196,13 @@ tester.run('component-tags-order', rule, {
message: 'The <template> should be above the <script> on line 2.',
line: 3
}
]
],
output:
'\n' +
' <template></template>\n' +
' <script></script>\n' +
' <style></style>\n' +
' '
},
{
code: `
Expand All @@ -176,7 +217,14 @@ tester.run('component-tags-order', rule, {
message: 'The <docs> should be above the <template> on line 2.',
line: 3
}
]
],
output:
'\n' +
' <docs></docs>\n' +
' <template></template>\n' +
' <script></script>\n' +
' <style></style>\n' +
' '
},
{
code: `
Expand All @@ -191,7 +239,14 @@ tester.run('component-tags-order', rule, {
message: 'The <script> should be above the <template> on line 2.',
line: 4
}
]
],
output:
'\n' +
' <script></script>\n' +
' <template></template>\n' +
' <docs></docs>\n' +
' <style></style>\n' +
' '
},
{
code: `
Expand All @@ -207,7 +262,15 @@ tester.run('component-tags-order', rule, {
message: 'The <script> should be above the <template> on line 2.',
line: 5
}
]
],
output:
'\n' +
' <script></script>\n' +
' <template></template>\n' +
' <docs>\n' +
' </docs>\n' +
' <style></style>\n' +
' '
},
{
code: `
Expand All @@ -220,7 +283,9 @@ tester.run('component-tags-order', rule, {
message: 'The <template> should be above the <script> on line 2.',
line: 3
}
]
],
output:
'\n <template></template>\n <script></script>\n '
},
{
code: `
Expand All @@ -237,7 +302,13 @@ tester.run('component-tags-order', rule, {
message: 'The <script> should be above the <style> on line 2.',
line: 4
}
]
],
output:
'\n' +
' <template></template>\n' +
' <style></style>\n' +
' <script></script>\n' +
' '
},
{
code: `
Expand All @@ -255,7 +326,14 @@ tester.run('component-tags-order', rule, {
message: 'The <script> should be above the <style> on line 2.',
line: 5
}
]
],
output:
'\n' +
' <template></template>\n' +
' <style></style>\n' +
' <docs></docs>\n' +
' <script></script>\n' +
' '
},
// no <template>
{
Expand All @@ -268,7 +346,27 @@ tester.run('component-tags-order', rule, {
message: 'The <script> should be above the <style> on line 2.',
line: 3
}
]
],
output: '\n <script></script>\n <style></style>\n '
}
]
})

describe('suppress reporting with eslint-disable-next-line', () => {
it('do not report if <!-- eslint-disable-next-line -->', async () => {
const code = `<style></style><template></template>
<!-- eslint-disable-next-line vue/component-tags-order -->
<script></script>`
const [{ messages, output }] = await eslint.lintText(code, {
filePath: 'test.vue'
})
assert.strictEqual(messages.length, 0)
// should not fix <script>
assert.strictEqual(
output,
`<template></template><style></style>
<!-- eslint-disable-next-line vue/component-tags-order -->
<script></script>`
)
})
})

0 comments on commit 8f09420

Please sign in to comment.