Skip to content

Commit

Permalink
Add v-for-delimiter-style rule (#1267)
Browse files Browse the repository at this point in the history
* Add `v-for-delimiter-style` rule

* Change rule type to "layout"

* Include error columns in tests
  • Loading branch information
FloEdelmann committed Jul 31, 2020
1 parent 3a4aa1a commit 61c62e9
Show file tree
Hide file tree
Showing 6 changed files with 266 additions and 1 deletion.
1 change: 1 addition & 0 deletions docs/rules/README.md
Expand Up @@ -312,6 +312,7 @@ For example:
| [vue/script-indent](./script-indent.md) | enforce consistent indentation in `<script>` | :wrench: |
| [vue/sort-keys](./sort-keys.md) | enforce sort-keys in a manner that is compatible with order-in-components | |
| [vue/static-class-names-order](./static-class-names-order.md) | enforce static class names order | :wrench: |
| [vue/v-for-delimiter-style](./v-for-delimiter-style.md) | enforce `v-for` directive's delimiter style | :wrench: |
| [vue/v-on-function-call](./v-on-function-call.md) | enforce or forbid parentheses after method calls without arguments in `v-on` directives | :wrench: |

### Extension Rules
Expand Down
65 changes: 65 additions & 0 deletions docs/rules/v-for-delimiter-style.md
@@ -0,0 +1,65 @@
---
pageClass: rule-details
sidebarDepth: 0
title: vue/v-for-delimiter-style
description: enforce `v-for` directive's delimiter style
---
# vue/v-for-delimiter-style
> enforce `v-for` directive's delimiter style
- :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

This rule enforces which delimiter (`in` or `of`) should be used in `v-for` directives.

<eslint-code-block fix :rules="{'vue/v-for-delimiter-style': ['error']}">

```vue
<template>
<!-- ✓ GOOD -->
<div v-for="x in xs" />
<!-- ✗ BAD -->
<div v-for="x of xs" />
</template>
```

</eslint-code-block>

## :wrench: Options
Default is set to `in`.

```json
{
"vue/v-for-delimiter-style": ["error", "in" | "of"]
}
```

- `"in"` (default) ... requires using `in`.
- `"of"` ... requires using `of`.

### `"of"`

<eslint-code-block fix :rules="{'vue/v-for-delimiter-style': ['error', 'of']}">

```vue
<template>
<!-- ✓ GOOD -->
<div v-for="x of xs" />
<!-- ✗ BAD -->
<div v-for="x in xs" />
</template>
```

</eslint-code-block>

## :books: Further Reading

- [Guide - List Rendering](https://v3.vuejs.org/guide/list.html)

## :mag: Implementation

- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/v-for-delimiter-style.js)
- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/v-for-delimiter-style.js)
3 changes: 2 additions & 1 deletion lib/configs/no-layout-rules.js
Expand Up @@ -41,6 +41,7 @@ module.exports = {
'vue/space-in-parens': 'off',
'vue/space-infix-ops': 'off',
'vue/space-unary-ops': 'off',
'vue/template-curly-spacing': 'off'
'vue/template-curly-spacing': 'off',
'vue/v-for-delimiter-style': 'off'
}
}
1 change: 1 addition & 0 deletions lib/index.js
Expand Up @@ -146,6 +146,7 @@ module.exports = {
'this-in-template': require('./rules/this-in-template'),
'use-v-on-exact': require('./rules/use-v-on-exact'),
'v-bind-style': require('./rules/v-bind-style'),
'v-for-delimiter-style': require('./rules/v-for-delimiter-style'),
'v-on-function-call': require('./rules/v-on-function-call'),
'v-on-style': require('./rules/v-on-style'),
'v-slot-style': require('./rules/v-slot-style'),
Expand Down
69 changes: 69 additions & 0 deletions lib/rules/v-for-delimiter-style.js
@@ -0,0 +1,69 @@
/**
* @fileoverview enforce `v-for` directive's delimiter style
* @author Flo Edelmann
* @copyright 2020 Flo Edelmann. All rights reserved.
* See LICENSE file in root directory for full license.
*/
'use strict'

// ------------------------------------------------------------------------------
// Requirements
// ------------------------------------------------------------------------------

const utils = require('../utils')

// ------------------------------------------------------------------------------
// Rule Definition
// ------------------------------------------------------------------------------

module.exports = {
meta: {
type: 'layout',
docs: {
description: "enforce `v-for` directive's delimiter style",
categories: undefined,
recommended: false,
url: 'https://eslint.vuejs.org/rules/v-for-delimiter-style.html'
},
fixable: 'code',
schema: [{ enum: ['in', 'of'] }]
},
/** @param {RuleContext} context */
create(context) {
const preferredDelimiter =
/** @type {string|undefined} */ (context.options[0]) || 'in'

return utils.defineTemplateBodyVisitor(context, {
/** @param {VForExpression} node */
VForExpression(node) {
const tokenStore =
context.parserServices.getTemplateBodyTokenStore &&
context.parserServices.getTemplateBodyTokenStore()

const delimiterToken = /** @type {Token} */ (tokenStore.getTokenAfter(
node.left.length
? node.left[node.left.length - 1]
: tokenStore.getFirstToken(node),
(token) => token.type !== 'Punctuator' || token.value !== ')'
))

if (delimiterToken.value === preferredDelimiter) {
return
}

context.report({
node,
loc: node.loc,
message: `Expected '{{preferredDelimiter}}' instead of '{{usedDelimiter}}' in 'v-for'.`,
data: {
preferredDelimiter,
usedDelimiter: delimiterToken.value
},
*fix(fixer) {
yield fixer.replaceText(delimiterToken, preferredDelimiter)
}
})
}
})
}
}
128 changes: 128 additions & 0 deletions tests/lib/rules/v-for-delimiter-style.js
@@ -0,0 +1,128 @@
/**
* @fileoverview enforce `v-for` directive's delimiter style
* @author Flo Edelmann
* @copyright 2020 Flo Edelmann. All rights reserved.
* See LICENSE file in root directory for full license.
*/
'use strict'

// ------------------------------------------------------------------------------
// Requirements
// ------------------------------------------------------------------------------

const RuleTester = require('eslint').RuleTester
const rule = require('../../../lib/rules/v-for-delimiter-style')

// ------------------------------------------------------------------------------
// Tests
// ------------------------------------------------------------------------------

const tester = new RuleTester({
parser: require.resolve('vue-eslint-parser'),
parserOptions: { ecmaVersion: 2015 }
})

tester.run('v-for-delimiter-style', rule, {
valid: [
{
filename: 'test.vue',
code: ''
},
{
filename: 'test.vue',
code: '<template><div v-for="x in xs"></div></template>'
},
{
filename: 'test.vue',
code: '<template><div v-for="x in xs"></div></template>'
},
{
filename: 'test.vue',
code: '<template><div v-for="x in xs"></div></template>'
},
{
filename: 'test.vue',
code: '<template><div v-for="x in xs"></div></template>'
},
{
filename: 'test.vue',
code: '<template><div v-for="x in xs"></div></template>',
options: ['in']
},
{
filename: 'test.vue',
code: '<template><div v-for="x of xs"></div></template>',
options: ['of']
}
],
invalid: [
{
filename: 'test.vue',
code: '<template><div v-for="x of xs"></div></template>',
output: '<template><div v-for="x in xs"></div></template>',
errors: [
{
message: "Expected 'in' instead of 'of' in 'v-for'.",
column: 23
}
]
},
{
filename: 'test.vue',
code: '<template><div v-for="x of xs"></div></template>',
output: '<template><div v-for="x in xs"></div></template>',
errors: [
{
message: "Expected 'in' instead of 'of' in 'v-for'.",
column: 23
}
]
},
{
filename: 'test.vue',
code: '<template><div v-for="x of xs"></div></template>',
output: '<template><div v-for="x in xs"></div></template>',
errors: [
{
message: "Expected 'in' instead of 'of' in 'v-for'.",
column: 23
}
]
},
{
filename: 'test.vue',
code: '<template><div v-for="x of xs"></div></template>',
output: '<template><div v-for="x in xs"></div></template>',
errors: [
{
message: "Expected 'in' instead of 'of' in 'v-for'.",
column: 23
}
]
},
{
filename: 'test.vue',
options: ['in'],
code: '<template><div v-for="x of xs"></div></template>',
output: '<template><div v-for="x in xs"></div></template>',
errors: [
{
message: "Expected 'in' instead of 'of' in 'v-for'.",
column: 23
}
]
},
{
filename: 'test.vue',
options: ['of'],
code: '<template><div v-for="x in xs"></div></template>',
output: '<template><div v-for="x of xs"></div></template>',
errors: [
{
message: "Expected 'of' instead of 'in' in 'v-for'.",
column: 23
}
]
}
]
})

0 comments on commit 61c62e9

Please sign in to comment.