Skip to content

Commit 3261b9a

Browse files
committed
Add scoped varialbles, and more tests
1 parent 9c6a762 commit 3261b9a

File tree

2 files changed

+118
-20
lines changed

2 files changed

+118
-20
lines changed

Diff for: lib/rules/no-mutating-props.js

+46-20
Original file line numberDiff line numberDiff line change
@@ -26,36 +26,48 @@ module.exports = {
2626
create (context) {
2727
let mutatedNodes = []
2828
let props = []
29+
let scope = {
30+
parent: null,
31+
nodes: []
32+
}
2933

3034
function checkForMutations () {
31-
for (const prop of props) {
32-
const propName = utils.getStaticPropertyName(prop.key)
33-
34-
for (const node of mutatedNodes) {
35-
if (propName === node.name) {
36-
context.report({
37-
node: node.node,
38-
message: 'Unexpected mutation of "{{key}}" prop.',
39-
data: { key: node.name }
40-
})
35+
if (mutatedNodes.length > 0) {
36+
for (const prop of props) {
37+
for (const node of mutatedNodes) {
38+
if (prop === node.name) {
39+
context.report({
40+
node: node.node,
41+
message: 'Unexpected mutation of "{{key}}" prop.',
42+
data: {
43+
key: node.name
44+
}
45+
})
46+
}
4147
}
4248
}
4349
}
4450
mutatedNodes = []
4551
}
4652

53+
function isInScope (name) {
54+
return scope.nodes.some(node => node.name === name)
55+
}
56+
4757
function checkTemplateProperty (node) {
4858
if (node.type === 'MemberExpression') {
4959
const expression = utils.parseMemberExpression(node)
50-
mutatedNodes.push({
51-
name: expression[0] === 'this' ? expression[1] : expression[0],
52-
node
53-
})
60+
const name = expression[0] === 'this' ? expression[1] : expression[0]
61+
if (!isInScope(name)) {
62+
mutatedNodes.push({ name, node })
63+
}
5464
} else if (node.type === 'Identifier') {
55-
mutatedNodes.push({
56-
name: node.name,
57-
node
58-
})
65+
if (!isInScope(node.name)) {
66+
mutatedNodes.push({
67+
name: node.name,
68+
node
69+
})
70+
}
5971
}
6072
}
6173

@@ -101,10 +113,26 @@ module.exports = {
101113
utils.executeOnVue(context, (obj) => {
102114
props = utils.getComponentProps(obj)
103115
.filter(cp => cp.key)
116+
.map(cp => utils.getStaticPropertyName(cp.key))
104117
checkForMutations()
105118
}),
106119

107120
utils.defineTemplateBodyVisitor(context, {
121+
VElement (node) {
122+
scope = {
123+
parent: scope,
124+
nodes: scope.nodes.slice() // make copy
125+
}
126+
127+
if (node.variables) {
128+
for (const variable of node.variables) {
129+
scope.nodes.push(variable.id)
130+
}
131+
}
132+
},
133+
'VElement:exit' () {
134+
scope = scope.parent
135+
},
108136
'VExpressionContainer AssignmentExpression' (node) {
109137
checkTemplateProperty(node.left)
110138
},
@@ -125,11 +153,9 @@ module.exports = {
125153
})
126154
}
127155
},
128-
129156
"VAttribute[directive=true][key.name='model'] VExpressionContainer" (node) {
130157
checkTemplateProperty(node.expression)
131158
},
132-
133159
"VElement[name='template']:exit" () {
134160
checkForMutations()
135161
}

Diff for: tests/lib/rules/no-mutating-props.js

+72
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,18 @@ ruleTester.run('no-mutating-props', rule, {
5858
<input v-model="prop2">
5959
<input v-model="this.prop3.text">
6060
<input v-model="this.prop4">
61+
<input :value="prop5.text" @input="$emit('input', $event.target.value)">
62+
<div v-for="prop5 of data">
63+
<input v-model="prop5">
64+
</div>
65+
<div v-for="(prop6, index) of data">
66+
<input v-model="prop6">
67+
</div>
68+
<template v-for="(test, index) of data">
69+
<template v-for="(prop6, index) of data">
70+
<input v-model="prop6">
71+
</template>
72+
</template>
6173
</div>
6274
</template>
6375
<script>
@@ -67,6 +79,40 @@ ruleTester.run('no-mutating-props', rule, {
6779
</script>
6880
`
6981
},
82+
{
83+
filename: 'test.vue',
84+
code: `
85+
<template>
86+
<div>
87+
<input v-model="prop1.text">
88+
<input v-model="prop2">
89+
<input v-model="this.prop3.text">
90+
<input v-model="this.prop4">
91+
</div>
92+
</template>
93+
<script>
94+
export default {
95+
props: ['prop5', 'prop6', 'prop7', 'prop8']
96+
}
97+
</script>
98+
`
99+
},
100+
{
101+
filename: 'test.vue',
102+
code: `
103+
<template>
104+
<div>
105+
<input v-for="i in prop.slice()">
106+
<input v-for="i in prop.foo.slice()">
107+
</div>
108+
</template>
109+
<script>
110+
export default {
111+
props: ['prop']
112+
}
113+
</script>
114+
`
115+
},
70116
{
71117
filename: 'test.vue',
72118
code: `
@@ -226,6 +272,32 @@ ruleTester.run('no-mutating-props', rule, {
226272
line: 18
227273
}
228274
]
275+
},
276+
{
277+
filename: 'test.vue',
278+
code: `
279+
<template>
280+
<div>
281+
<template v-for="(test, index) of data">
282+
<template v-for="(prop, index) of data">
283+
<input v-model="prop">
284+
</template>
285+
<input v-model="prop">
286+
</template>
287+
</div>
288+
</template>
289+
<script>
290+
export default {
291+
props: ['prop']
292+
}
293+
</script>
294+
`,
295+
errors: [
296+
{
297+
message: 'Unexpected mutation of "prop" prop.',
298+
line: 8
299+
}
300+
]
229301
}
230302
]
231303
})

0 commit comments

Comments
 (0)