Skip to content

Commit

Permalink
Update vue/no-mutating-props rule to support <script setup> (#1531)
Browse files Browse the repository at this point in the history
  • Loading branch information
ota-meshi committed Jul 2, 2021
1 parent 9a99fe2 commit 2eafd04
Show file tree
Hide file tree
Showing 4 changed files with 430 additions and 133 deletions.
122 changes: 80 additions & 42 deletions lib/rules/no-mutating-props.js
Expand Up @@ -26,9 +26,9 @@ module.exports = {
},
/** @param {RuleContext} context */
create(context) {
/** @type {Map<ObjectExpression, Set<string>>} */
/** @type {Map<ObjectExpression|CallExpression, Set<string>>} */
const propsMap = new Map()
/** @type { { type: 'export' | 'mark' | 'definition', object: ObjectExpression } | null } */
/** @type { { type: 'export' | 'mark' | 'definition', object: ObjectExpression } | { type: 'setup', object: CallExpression } | null } */
let vueObjectData = null

/**
Expand Down Expand Up @@ -123,7 +123,7 @@ module.exports = {
* @param {string[]} path
* @returns {Generator<{ node: Identifier, path: string[] }>}
*/
function* iterateParamProperties(param, path) {
function* iteratePatternProperties(param, path) {
if (!param) {
return
}
Expand All @@ -133,28 +133,94 @@ module.exports = {
path
}
} else if (param.type === 'RestElement') {
yield* iterateParamProperties(param.argument, path)
yield* iteratePatternProperties(param.argument, path)
} else if (param.type === 'AssignmentPattern') {
yield* iterateParamProperties(param.left, path)
yield* iteratePatternProperties(param.left, path)
} else if (param.type === 'ObjectPattern') {
for (const prop of param.properties) {
if (prop.type === 'Property') {
const name = getPropertyNameText(prop)
yield* iterateParamProperties(prop.value, [...path, name])
yield* iteratePatternProperties(prop.value, [...path, name])
} else if (prop.type === 'RestElement') {
yield* iterateParamProperties(prop.argument, path)
yield* iteratePatternProperties(prop.argument, path)
}
}
} else if (param.type === 'ArrayPattern') {
for (let index = 0; index < param.elements.length; index++) {
const element = param.elements[index]
yield* iterateParamProperties(element, [...path, `${index}`])
yield* iteratePatternProperties(element, [...path, `${index}`])
}
}
}

return Object.assign(
/**
* @param {Identifier} prop
* @param {string[]} path
*/
function verifyPropVariable(prop, path) {
const variable = findVariable(context.getScope(), prop)
if (!variable) {
return
}

for (const reference of variable.references) {
if (!reference.isRead()) {
continue
}
const id = reference.identifier

const invalid = utils.findMutating(id)
if (!invalid) {
continue
}
let name
if (path.length === 0) {
if (invalid.pathNodes.length === 0) {
continue
}
const mem = invalid.pathNodes[0]
name = getPropertyNameText(mem)
} else {
if (invalid.pathNodes.length === 0 && invalid.kind !== 'call') {
continue
}
name = path[0]
}

report(invalid.node, name)
}
}

return utils.compositingVisitors(
{},
utils.defineScriptSetupVisitor(context, {
onDefinePropsEnter(node, props) {
const propsSet = new Set(
props.map((p) => p.propName).filter(utils.isDef)
)
propsMap.set(node, propsSet)
vueObjectData = {
type: 'setup',
object: node
}

if (
!node.parent ||
node.parent.type !== 'VariableDeclarator' ||
node.parent.init !== node
) {
return
}

for (const { node: prop, path } of iteratePatternProperties(
node.parent.id,
[]
)) {
verifyPropVariable(prop, path)
propsSet.add(prop.name)
}
}
}),
utils.defineVueVisitor(context, {
onVueObjectEnter(node) {
propsMap.set(
Expand All @@ -169,7 +235,9 @@ module.exports = {
},
onVueObjectExit(node, { type }) {
if (
(!vueObjectData || vueObjectData.type !== 'export') &&
(!vueObjectData ||
(vueObjectData.type !== 'export' &&
vueObjectData.type !== 'setup')) &&
type !== 'instance'
) {
vueObjectData = {
Expand All @@ -191,41 +259,11 @@ module.exports = {
// cannot check
return
}
for (const { node: prop, path } of iterateParamProperties(
for (const { node: prop, path } of iteratePatternProperties(
propsParam,
[]
)) {
const variable = findVariable(context.getScope(), prop)
if (!variable) {
continue
}

for (const reference of variable.references) {
if (!reference.isRead()) {
continue
}
const id = reference.identifier

const invalid = utils.findMutating(id)
if (!invalid) {
continue
}
let name
if (path.length === 0) {
if (invalid.pathNodes.length === 0) {
continue
}
const mem = invalid.pathNodes[0]
name = getPropertyNameText(mem)
} else {
if (invalid.pathNodes.length === 0 && invalid.kind !== 'call') {
continue
}
name = path[0]
}

report(invalid.node, name)
}
verifyPropVariable(prop, path)
}
},
/** @param {(Identifier | ThisExpression) & { parent: MemberExpression } } node */
Expand Down

0 comments on commit 2eafd04

Please sign in to comment.