Skip to content

Commit 2eafd04

Browse files
authored
Update vue/no-mutating-props rule to support <script setup> (#1531)
1 parent 9a99fe2 commit 2eafd04

File tree

4 files changed

+430
-133
lines changed

4 files changed

+430
-133
lines changed

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

+80-42
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,9 @@ module.exports = {
2626
},
2727
/** @param {RuleContext} context */
2828
create(context) {
29-
/** @type {Map<ObjectExpression, Set<string>>} */
29+
/** @type {Map<ObjectExpression|CallExpression, Set<string>>} */
3030
const propsMap = new Map()
31-
/** @type { { type: 'export' | 'mark' | 'definition', object: ObjectExpression } | null } */
31+
/** @type { { type: 'export' | 'mark' | 'definition', object: ObjectExpression } | { type: 'setup', object: CallExpression } | null } */
3232
let vueObjectData = null
3333

3434
/**
@@ -123,7 +123,7 @@ module.exports = {
123123
* @param {string[]} path
124124
* @returns {Generator<{ node: Identifier, path: string[] }>}
125125
*/
126-
function* iterateParamProperties(param, path) {
126+
function* iteratePatternProperties(param, path) {
127127
if (!param) {
128128
return
129129
}
@@ -133,28 +133,94 @@ module.exports = {
133133
path
134134
}
135135
} else if (param.type === 'RestElement') {
136-
yield* iterateParamProperties(param.argument, path)
136+
yield* iteratePatternProperties(param.argument, path)
137137
} else if (param.type === 'AssignmentPattern') {
138-
yield* iterateParamProperties(param.left, path)
138+
yield* iteratePatternProperties(param.left, path)
139139
} else if (param.type === 'ObjectPattern') {
140140
for (const prop of param.properties) {
141141
if (prop.type === 'Property') {
142142
const name = getPropertyNameText(prop)
143-
yield* iterateParamProperties(prop.value, [...path, name])
143+
yield* iteratePatternProperties(prop.value, [...path, name])
144144
} else if (prop.type === 'RestElement') {
145-
yield* iterateParamProperties(prop.argument, path)
145+
yield* iteratePatternProperties(prop.argument, path)
146146
}
147147
}
148148
} else if (param.type === 'ArrayPattern') {
149149
for (let index = 0; index < param.elements.length; index++) {
150150
const element = param.elements[index]
151-
yield* iterateParamProperties(element, [...path, `${index}`])
151+
yield* iteratePatternProperties(element, [...path, `${index}`])
152152
}
153153
}
154154
}
155155

156-
return Object.assign(
156+
/**
157+
* @param {Identifier} prop
158+
* @param {string[]} path
159+
*/
160+
function verifyPropVariable(prop, path) {
161+
const variable = findVariable(context.getScope(), prop)
162+
if (!variable) {
163+
return
164+
}
165+
166+
for (const reference of variable.references) {
167+
if (!reference.isRead()) {
168+
continue
169+
}
170+
const id = reference.identifier
171+
172+
const invalid = utils.findMutating(id)
173+
if (!invalid) {
174+
continue
175+
}
176+
let name
177+
if (path.length === 0) {
178+
if (invalid.pathNodes.length === 0) {
179+
continue
180+
}
181+
const mem = invalid.pathNodes[0]
182+
name = getPropertyNameText(mem)
183+
} else {
184+
if (invalid.pathNodes.length === 0 && invalid.kind !== 'call') {
185+
continue
186+
}
187+
name = path[0]
188+
}
189+
190+
report(invalid.node, name)
191+
}
192+
}
193+
194+
return utils.compositingVisitors(
157195
{},
196+
utils.defineScriptSetupVisitor(context, {
197+
onDefinePropsEnter(node, props) {
198+
const propsSet = new Set(
199+
props.map((p) => p.propName).filter(utils.isDef)
200+
)
201+
propsMap.set(node, propsSet)
202+
vueObjectData = {
203+
type: 'setup',
204+
object: node
205+
}
206+
207+
if (
208+
!node.parent ||
209+
node.parent.type !== 'VariableDeclarator' ||
210+
node.parent.init !== node
211+
) {
212+
return
213+
}
214+
215+
for (const { node: prop, path } of iteratePatternProperties(
216+
node.parent.id,
217+
[]
218+
)) {
219+
verifyPropVariable(prop, path)
220+
propsSet.add(prop.name)
221+
}
222+
}
223+
}),
158224
utils.defineVueVisitor(context, {
159225
onVueObjectEnter(node) {
160226
propsMap.set(
@@ -169,7 +235,9 @@ module.exports = {
169235
},
170236
onVueObjectExit(node, { type }) {
171237
if (
172-
(!vueObjectData || vueObjectData.type !== 'export') &&
238+
(!vueObjectData ||
239+
(vueObjectData.type !== 'export' &&
240+
vueObjectData.type !== 'setup')) &&
173241
type !== 'instance'
174242
) {
175243
vueObjectData = {
@@ -191,41 +259,11 @@ module.exports = {
191259
// cannot check
192260
return
193261
}
194-
for (const { node: prop, path } of iterateParamProperties(
262+
for (const { node: prop, path } of iteratePatternProperties(
195263
propsParam,
196264
[]
197265
)) {
198-
const variable = findVariable(context.getScope(), prop)
199-
if (!variable) {
200-
continue
201-
}
202-
203-
for (const reference of variable.references) {
204-
if (!reference.isRead()) {
205-
continue
206-
}
207-
const id = reference.identifier
208-
209-
const invalid = utils.findMutating(id)
210-
if (!invalid) {
211-
continue
212-
}
213-
let name
214-
if (path.length === 0) {
215-
if (invalid.pathNodes.length === 0) {
216-
continue
217-
}
218-
const mem = invalid.pathNodes[0]
219-
name = getPropertyNameText(mem)
220-
} else {
221-
if (invalid.pathNodes.length === 0 && invalid.kind !== 'call') {
222-
continue
223-
}
224-
name = path[0]
225-
}
226-
227-
report(invalid.node, name)
228-
}
266+
verifyPropVariable(prop, path)
229267
}
230268
},
231269
/** @param {(Identifier | ThisExpression) & { parent: MemberExpression } } node */

0 commit comments

Comments
 (0)