Skip to content

Commit 40d9d28

Browse files
committed
Add v-for-delimiter-style rule
1 parent 95cccec commit 40d9d28

File tree

5 files changed

+234
-0
lines changed

5 files changed

+234
-0
lines changed

docs/rules/README.md

+1
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,7 @@ For example:
312312
| [vue/script-indent](./script-indent.md) | enforce consistent indentation in `<script>` | :wrench: |
313313
| [vue/sort-keys](./sort-keys.md) | enforce sort-keys in a manner that is compatible with order-in-components | |
314314
| [vue/static-class-names-order](./static-class-names-order.md) | enforce static class names order | :wrench: |
315+
| [vue/v-for-delimiter-style](./v-for-delimiter-style.md) | enforce `v-for` directive's delimiter style | :wrench: |
315316
| [vue/v-on-function-call](./v-on-function-call.md) | enforce or forbid parentheses after method calls without arguments in `v-on` directives | :wrench: |
316317

317318
### Extension Rules

docs/rules/v-for-delimiter-style.md

+65
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
---
2+
pageClass: rule-details
3+
sidebarDepth: 0
4+
title: vue/v-for-delimiter-style
5+
description: enforce `v-for` directive's delimiter style
6+
---
7+
# vue/v-for-delimiter-style
8+
> enforce `v-for` directive's delimiter style
9+
10+
- :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.
11+
12+
## :book: Rule Details
13+
14+
This rule enforces which delimiter (`in` or `of`) should be used in `v-for` directives.
15+
16+
<eslint-code-block fix :rules="{'vue/v-for-delimiter-style': ['error']}">
17+
18+
```vue
19+
<template>
20+
<!-- ✓ GOOD -->
21+
<div v-for="x in xs" />
22+
23+
<!-- ✗ BAD -->
24+
<div v-for="x of xs" />
25+
</template>
26+
```
27+
28+
</eslint-code-block>
29+
30+
## :wrench: Options
31+
Default is set to `in`.
32+
33+
```json
34+
{
35+
"vue/v-for-delimiter-style": ["error", "in" | "of"]
36+
}
37+
```
38+
39+
- `"in"` (default) ... requires using `in`.
40+
- `"of"` ... requires using `of`.
41+
42+
### `"of"`
43+
44+
<eslint-code-block fix :rules="{'vue/v-for-delimiter-style': ['error', 'of']}">
45+
46+
```vue
47+
<template>
48+
<!-- ✓ GOOD -->
49+
<div v-for="x of xs" />
50+
51+
<!-- ✗ BAD -->
52+
<div v-for="x in xs" />
53+
</template>
54+
```
55+
56+
</eslint-code-block>
57+
58+
## :books: Further Reading
59+
60+
- [Guide - List Rendering](https://v3.vuejs.org/guide/list.html)
61+
62+
## :mag: Implementation
63+
64+
- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/v-for-delimiter-style.js)
65+
- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/v-for-delimiter-style.js)

lib/index.js

+1
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ module.exports = {
146146
'this-in-template': require('./rules/this-in-template'),
147147
'use-v-on-exact': require('./rules/use-v-on-exact'),
148148
'v-bind-style': require('./rules/v-bind-style'),
149+
'v-for-delimiter-style': require('./rules/v-for-delimiter-style'),
149150
'v-on-function-call': require('./rules/v-on-function-call'),
150151
'v-on-style': require('./rules/v-on-style'),
151152
'v-slot-style': require('./rules/v-slot-style'),

lib/rules/v-for-delimiter-style.js

+69
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
/**
2+
* @fileoverview enforce `v-for` directive's delimiter style
3+
* @author Flo Edelmann
4+
* @copyright 2020 Flo Edelmann. All rights reserved.
5+
* See LICENSE file in root directory for full license.
6+
*/
7+
'use strict'
8+
9+
// ------------------------------------------------------------------------------
10+
// Requirements
11+
// ------------------------------------------------------------------------------
12+
13+
const utils = require('../utils')
14+
15+
// ------------------------------------------------------------------------------
16+
// Rule Definition
17+
// ------------------------------------------------------------------------------
18+
19+
module.exports = {
20+
meta: {
21+
type: 'suggestion',
22+
docs: {
23+
description: "enforce `v-for` directive's delimiter style",
24+
categories: undefined,
25+
recommended: false,
26+
url: 'https://eslint.vuejs.org/rules/v-for-delimiter-style.html'
27+
},
28+
fixable: 'code',
29+
schema: [{ enum: ['in', 'of'] }]
30+
},
31+
/** @param {RuleContext} context */
32+
create(context) {
33+
const preferredDelimiter =
34+
/** @type {string|undefined} */ (context.options[0]) || 'in'
35+
36+
return utils.defineTemplateBodyVisitor(context, {
37+
/** @param {VForExpression} node */
38+
VForExpression(node) {
39+
const tokenStore =
40+
context.parserServices.getTemplateBodyTokenStore &&
41+
context.parserServices.getTemplateBodyTokenStore()
42+
43+
const delimiterToken = /** @type {Token} */ (tokenStore.getTokenAfter(
44+
node.left.length
45+
? node.left[node.left.length - 1]
46+
: tokenStore.getFirstToken(node),
47+
(token) => token.type !== 'Punctuator' || token.value !== ')'
48+
))
49+
50+
if (delimiterToken.value === preferredDelimiter) {
51+
return
52+
}
53+
54+
context.report({
55+
node,
56+
loc: node.loc,
57+
message: `Expected '{{preferredDelimiter}}' instead of '{{usedDelimiter}}' in 'v-for'.`,
58+
data: {
59+
preferredDelimiter,
60+
usedDelimiter: delimiterToken.value
61+
},
62+
*fix(fixer) {
63+
yield fixer.replaceText(delimiterToken, preferredDelimiter)
64+
}
65+
})
66+
}
67+
})
68+
}
69+
}
+98
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
/**
2+
* @fileoverview enforce `v-for` directive's delimiter style
3+
* @author Flo Edelmann
4+
* @copyright 2020 Flo Edelmann. All rights reserved.
5+
* See LICENSE file in root directory for full license.
6+
*/
7+
'use strict'
8+
9+
// ------------------------------------------------------------------------------
10+
// Requirements
11+
// ------------------------------------------------------------------------------
12+
13+
const RuleTester = require('eslint').RuleTester
14+
const rule = require('../../../lib/rules/v-for-delimiter-style')
15+
16+
// ------------------------------------------------------------------------------
17+
// Tests
18+
// ------------------------------------------------------------------------------
19+
20+
const tester = new RuleTester({
21+
parser: require.resolve('vue-eslint-parser'),
22+
parserOptions: { ecmaVersion: 2015 }
23+
})
24+
25+
tester.run('v-for-delimiter-style', rule, {
26+
valid: [
27+
{
28+
filename: 'test.vue',
29+
code: ''
30+
},
31+
{
32+
filename: 'test.vue',
33+
code: '<template><div v-for="x in xs"></div></template>'
34+
},
35+
{
36+
filename: 'test.vue',
37+
code: '<template><div v-for="x in xs"></div></template>'
38+
},
39+
{
40+
filename: 'test.vue',
41+
code: '<template><div v-for="x in xs"></div></template>'
42+
},
43+
{
44+
filename: 'test.vue',
45+
code: '<template><div v-for="x in xs"></div></template>'
46+
},
47+
{
48+
filename: 'test.vue',
49+
code: '<template><div v-for="x in xs"></div></template>',
50+
options: ['in']
51+
},
52+
{
53+
filename: 'test.vue',
54+
code: '<template><div v-for="x of xs"></div></template>',
55+
options: ['of']
56+
}
57+
],
58+
invalid: [
59+
{
60+
filename: 'test.vue',
61+
code: '<template><div v-for="x of xs"></div></template>',
62+
output: '<template><div v-for="x in xs"></div></template>',
63+
errors: ["Expected 'in' instead of 'of' in 'v-for'."]
64+
},
65+
{
66+
filename: 'test.vue',
67+
code: '<template><div v-for="x of xs"></div></template>',
68+
output: '<template><div v-for="x in xs"></div></template>',
69+
errors: ["Expected 'in' instead of 'of' in 'v-for'."]
70+
},
71+
{
72+
filename: 'test.vue',
73+
code: '<template><div v-for="x of xs"></div></template>',
74+
output: '<template><div v-for="x in xs"></div></template>',
75+
errors: ["Expected 'in' instead of 'of' in 'v-for'."]
76+
},
77+
{
78+
filename: 'test.vue',
79+
code: '<template><div v-for="x of xs"></div></template>',
80+
output: '<template><div v-for="x in xs"></div></template>',
81+
errors: ["Expected 'in' instead of 'of' in 'v-for'."]
82+
},
83+
{
84+
filename: 'test.vue',
85+
options: ['in'],
86+
code: '<template><div v-for="x of xs"></div></template>',
87+
output: '<template><div v-for="x in xs"></div></template>',
88+
errors: ["Expected 'in' instead of 'of' in 'v-for'."]
89+
},
90+
{
91+
filename: 'test.vue',
92+
options: ['of'],
93+
code: '<template><div v-for="x in xs"></div></template>',
94+
output: '<template><div v-for="x of xs"></div></template>',
95+
errors: ["Expected 'of' instead of 'in' in 'v-for'."]
96+
}
97+
]
98+
})

0 commit comments

Comments
 (0)