Skip to content

Commit 83a230a

Browse files
committed
Add rule no-template-shadow.
fixes #101
1 parent df84d9b commit 83a230a

File tree

3 files changed

+251
-0
lines changed

3 files changed

+251
-0
lines changed

Diff for: docs/rules/no-template-shadow.md

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# Disallow variable declarations from shadowing variables declared in the outer scope. (no-template-shadow)
2+
3+
`no-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 5"></div>
16+
</div>
17+
</div>
18+
</template>
19+
<script>
20+
export default {
21+
data: {
22+
i: 7
23+
}
24+
}
25+
</script>
26+
```
27+
28+
:+1: Examples of **correct** code for this rule:
29+
30+
```html
31+
<template>
32+
<div v-for="i in 5"></div>
33+
<div v-for="i in 5"></div>
34+
</template>
35+
<script>
36+
export default {
37+
computed: {
38+
f () { }
39+
}
40+
}
41+
</script>
42+
```
43+
44+
## :wrench: Options
45+
46+
Nothing.

Diff for: lib/rules/no-template-shadow.js

+78
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
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: 'Possible Errors',
24+
recommended: false
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+
utils.registerTemplateBodyVisitor(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+
})
70+
71+
return utils.executeOnVue(context, (obj) => {
72+
const properties = Array.from(utils.iterateProperties(obj, new Set(GROUP_NAMES)))
73+
for (const node of properties) {
74+
jsVars.add(node.name)
75+
}
76+
})
77+
}
78+
}

Diff for: tests/lib/rules/no-template-shadow.js

+127
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
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: 2015,
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+
{
35+
filename: 'test.vue',
36+
code: `<template>
37+
<div v-for="i in 5"></div>
38+
<div v-for="i in 5"></div>
39+
</template>
40+
<script>
41+
export default {
42+
computed: {
43+
f () { }
44+
}
45+
}
46+
</script>`
47+
}
48+
],
49+
50+
invalid: [
51+
{
52+
filename: 'test.vue',
53+
code: '<template><div v-for="i in 5"><div v-for="i in 5"></div></div></template>',
54+
errors: [{
55+
message: "Variable 'i' is already declared in the upper scope.",
56+
type: 'Identifier'
57+
}]
58+
},
59+
{
60+
filename: 'test.vue',
61+
code: `<template>
62+
<div v-for="i in 5">
63+
<div v-for="i in 5"></div>
64+
</div>
65+
</template>
66+
<script>
67+
export default {
68+
data: {
69+
i: 7
70+
}
71+
}
72+
</script>`,
73+
errors: [{
74+
message: "Variable 'i' is already declared in the upper scope.",
75+
type: 'Identifier'
76+
}, {
77+
message: "Variable 'i' is already declared in the upper scope.",
78+
type: 'Identifier'
79+
}]
80+
},
81+
{
82+
filename: 'test.vue',
83+
code: `<template>
84+
<div v-for="i in 5"></div>
85+
<div v-for="i in 5"></div>
86+
</template>
87+
<script>
88+
export default {
89+
data: {
90+
i: 7
91+
}
92+
}
93+
</script>`,
94+
errors: [{
95+
message: "Variable 'i' is already declared in the upper scope.",
96+
type: 'Identifier'
97+
}, {
98+
message: "Variable 'i' is already declared in the upper scope.",
99+
type: 'Identifier'
100+
}]
101+
},
102+
{
103+
filename: 'test.vue',
104+
code: `<template>
105+
<div v-for="i in 5"></div>
106+
<div v-for="f in 5"></div>
107+
</template>
108+
<script>
109+
export default {
110+
computed: {
111+
i () { }
112+
},
113+
methods: {
114+
f () { }
115+
}
116+
}
117+
</script>`,
118+
errors: [{
119+
message: "Variable 'i' is already declared in the upper scope.",
120+
type: 'Identifier'
121+
}, {
122+
message: "Variable 'f' is already declared in the upper scope.",
123+
type: 'Identifier'
124+
}]
125+
}
126+
]
127+
})

0 commit comments

Comments
 (0)