Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit da4ea71

Browse files
armano2michalsnik
authored andcommitted
⭐️New: Add rule no-template-shadow. (#158)
* Add rule `no-template-shadow`. fixes #101 * fix/merge changes with master * Update tests suite, review suggestions * Add more test cases * Change no-template-shadow category to strongly-recommended
1 parent 341bccf commit da4ea71

File tree

4 files changed

+346
-1
lines changed

4 files changed

+346
-1
lines changed

‎docs/rules/no-template-shadow.md

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# Disallow variable declarations from shadowing variables declared in the outer scope. (no-template-shadow)
2+
3+
`no-template-shadow` should report variable definitions of v-for directives or scope attributes if those shadows the variables in parent scopes.
4+
5+
## :book: Rule Details
6+
7+
This rule aims to eliminate shadowed variable declarations of v-for directives or scope attributes.
8+
9+
:-1: Examples of **incorrect** code for this rule:
10+
11+
```html
12+
<template>
13+
<div>
14+
<div v-for="i in 5">
15+
<div v-for="i in 10"></div>
16+
</div>
17+
</div>
18+
</template>
19+
```
20+
21+
```html
22+
<template>
23+
<div>
24+
<div v-for="i in 5"></div>
25+
</div>
26+
</template>
27+
<script>
28+
export default {
29+
data () {
30+
return {
31+
i: 10
32+
}
33+
}
34+
}
35+
</script>
36+
```
37+
38+
:+1: Examples of **correct** code for this rule:
39+
40+
```html
41+
<template>
42+
<div v-for="i in 5"></div>
43+
<div v-for="i in 5"></div>
44+
</template>
45+
<script>
46+
export default {
47+
computed: {
48+
f () { }
49+
}
50+
}
51+
</script>
52+
```
53+
54+
## :wrench: Options
55+
56+
Nothing.

‎lib/rules/no-template-shadow.js

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
/**
2+
* @fileoverview Disallow variable declarations from shadowing variables declared in the outer scope.
3+
* @author Armano
4+
*/
5+
'use strict'
6+
7+
// ------------------------------------------------------------------------------
8+
// Requirements
9+
// ------------------------------------------------------------------------------
10+
11+
const utils = require('../utils')
12+
13+
// ------------------------------------------------------------------------------
14+
// Rule Definition
15+
// ------------------------------------------------------------------------------
16+
17+
const GROUP_NAMES = ['props', 'computed', 'data', 'methods']
18+
19+
module.exports = {
20+
meta: {
21+
docs: {
22+
description: 'disallow variable declarations from shadowing variables declared in the outer scope',
23+
category: 'strongly-recommended',
24+
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.1/docs/rules/no-template-shadow.md'
25+
},
26+
fixable: null,
27+
schema: []
28+
},
29+
30+
create (context) {
31+
const jsVars = new Set()
32+
let scope = {
33+
parent: null,
34+
nodes: []
35+
}
36+
37+
// ----------------------------------------------------------------------
38+
// Public
39+
// ----------------------------------------------------------------------
40+
41+
return utils.defineTemplateBodyVisitor(context, {
42+
VElement (node) {
43+
scope = {
44+
parent: scope,
45+
nodes: scope.nodes.slice() // make copy
46+
}
47+
if (node.variables) {
48+
for (const variable of node.variables) {
49+
const varNode = variable.id
50+
const name = varNode.name
51+
if (scope.nodes.some(node => node.name === name) || jsVars.has(name)) {
52+
context.report({
53+
node: varNode,
54+
loc: varNode.loc,
55+
message: "Variable '{{name}}' is already declared in the upper scope.",
56+
data: {
57+
name
58+
}
59+
})
60+
} else {
61+
scope.nodes.push(varNode)
62+
}
63+
}
64+
}
65+
},
66+
'VElement:exit' (node) {
67+
scope = scope.parent
68+
}
69+
}, utils.executeOnVue(context, (obj) => {
70+
const properties = Array.from(utils.iterateProperties(obj, new Set(GROUP_NAMES)))
71+
for (const node of properties) {
72+
jsVars.add(node.name)
73+
}
74+
}))
75+
}
76+
}

‎lib/utils/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ module.exports = {
2626
*
2727
* @param {RuleContext} context The rule context to use parser services.
2828
* @param {Object} templateBodyVisitor The visitor to traverse the template body.
29-
* @param {Object} scriptVisitor The visitor to traverse the script.
29+
* @param {Object} [scriptVisitor] The visitor to traverse the script.
3030
* @returns {Object} The merged visitor.
3131
*/
3232
defineTemplateBodyVisitor (context, templateBodyVisitor, scriptVisitor) {

‎tests/lib/rules/no-template-shadow.js

Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
/**
2+
* @fileoverview Disallow variable declarations from shadowing variables declared in the outer scope.
3+
* @author Armano
4+
*/
5+
'use strict'
6+
7+
// ------------------------------------------------------------------------------
8+
// Requirements
9+
// ------------------------------------------------------------------------------
10+
11+
const rule = require('../../../lib/rules/no-template-shadow')
12+
const RuleTester = require('eslint').RuleTester
13+
14+
// ------------------------------------------------------------------------------
15+
// Tests
16+
// ------------------------------------------------------------------------------
17+
18+
const ruleTester = new RuleTester({
19+
parser: 'vue-eslint-parser',
20+
parserOptions: {
21+
ecmaVersion: 2018,
22+
sourceType: 'module'
23+
}
24+
})
25+
26+
ruleTester.run('no-template-shadow', rule, {
27+
28+
valid: [
29+
'',
30+
'<template><div></div></template>',
31+
'<template><div v-for="i in 5"></div></template>',
32+
'<template><div v-for="i in 5"><div v-for="b in 5"></div></div></template>',
33+
'<template><div v-for="i in 5"></div><div v-for="i in 5"></div></template>',
34+
'<template> <ul v-for="i in 5"> <li> <span v-for="j in 10">{{i}},{{j}}</span> </li> </ul> </template>',
35+
{
36+
filename: 'test.vue',
37+
code: `<template>
38+
<div v-for="i in 5"></div>
39+
<div v-for="i in f"></div>
40+
<div v-for="i in 5"></div>
41+
</template>
42+
<script>
43+
export default {
44+
computed: {
45+
f () { }
46+
}
47+
}
48+
</script>`
49+
},
50+
{
51+
filename: 'test.vue',
52+
code: `<template>
53+
<div v-for="i in b" />
54+
<div v-for="b in c" />
55+
<div v-for="d in f" />
56+
</template>
57+
<script>
58+
export default {
59+
...a,
60+
data() {
61+
return {
62+
...b,
63+
c: [1, 2, 3]
64+
}
65+
},
66+
computed: {
67+
...d,
68+
e,
69+
['f']: [1, 2],
70+
}
71+
}
72+
</script>`
73+
}
74+
],
75+
76+
invalid: [
77+
{
78+
filename: 'test.vue',
79+
code: '<template><div v-for="i in 5"><div v-for="i in 5"></div></div></template>',
80+
errors: [{
81+
message: "Variable 'i' is already declared in the upper scope.",
82+
type: 'Identifier'
83+
}]
84+
},
85+
{
86+
filename: 'test.vue',
87+
code: `<template>
88+
<div v-for="i in 5"></div>
89+
</template>
90+
<script>
91+
export default {
92+
data: {
93+
i: 7
94+
}
95+
}
96+
</script>`,
97+
errors: [{
98+
message: "Variable 'i' is already declared in the upper scope.",
99+
type: 'Identifier',
100+
line: 2
101+
}]
102+
},
103+
{
104+
filename: 'test.vue',
105+
code: `<template>
106+
<div v-for="i in 5"></div>
107+
<div v-for="i in 5"></div>
108+
</template>
109+
<script>
110+
export default {
111+
data: {
112+
i: 7
113+
}
114+
}
115+
</script>`,
116+
errors: [{
117+
message: "Variable 'i' is already declared in the upper scope.",
118+
type: 'Identifier',
119+
line: 2
120+
}, {
121+
message: "Variable 'i' is already declared in the upper scope.",
122+
type: 'Identifier',
123+
line: 3
124+
}]
125+
},
126+
{
127+
filename: 'test.vue',
128+
code: `<template>
129+
<div v-for="i in 5">
130+
<div v-for="i in 5"></div>
131+
</div>
132+
</template>
133+
<script>
134+
export default {
135+
data: {
136+
i: 7
137+
}
138+
}
139+
</script>`,
140+
errors: [{
141+
message: "Variable 'i' is already declared in the upper scope.",
142+
type: 'Identifier',
143+
line: 2
144+
}, {
145+
message: "Variable 'i' is already declared in the upper scope.",
146+
type: 'Identifier',
147+
line: 3
148+
}]
149+
},
150+
{
151+
filename: 'test.vue',
152+
code: `<template>
153+
<div v-for="i in 5"></div>
154+
<div v-for="f in 5"></div>
155+
</template>
156+
<script>
157+
export default {
158+
computed: {
159+
i () { }
160+
},
161+
methods: {
162+
f () { }
163+
}
164+
}
165+
</script>`,
166+
errors: [{
167+
message: "Variable 'i' is already declared in the upper scope.",
168+
type: 'Identifier',
169+
line: 2
170+
}, {
171+
message: "Variable 'f' is already declared in the upper scope.",
172+
type: 'Identifier',
173+
line: 3
174+
}]
175+
},
176+
{
177+
filename: 'test.vue',
178+
code: `<template>
179+
<div v-for="i in c" />
180+
<div v-for="a in c" />
181+
<div v-for="b in c" />
182+
<div v-for="d in c" />
183+
<div v-for="e in f" />
184+
<div v-for="f in c" />
185+
</template>
186+
<script>
187+
export default {
188+
...a,
189+
data() {
190+
return {
191+
...b,
192+
c: [1, 2, 3]
193+
}
194+
},
195+
computed: {
196+
...d,
197+
e,
198+
['f']: [1, 2],
199+
}
200+
}
201+
</script>`,
202+
errors: [{
203+
message: "Variable 'e' is already declared in the upper scope.",
204+
type: 'Identifier',
205+
line: 6
206+
}, {
207+
message: "Variable 'f' is already declared in the upper scope.",
208+
type: 'Identifier',
209+
line: 7
210+
}]
211+
}
212+
]
213+
})

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /