diff --git a/.changeset/hot-beers-help.md b/.changeset/hot-beers-help.md
new file mode 100644
index 000000000..b32ab0ad2
--- /dev/null
+++ b/.changeset/hot-beers-help.md
@@ -0,0 +1,5 @@
+---
+'eslint-plugin-vue': minor
+---
+
+Changed `vue/no-mutating-props` and `vue/no-side-effects-in-computed-properties` rules to detect `Object.assign` mutations
diff --git a/lib/utils/index.js b/lib/utils/index.js
index e47dc4acf..dabdc2ba3 100644
--- a/lib/utils/index.js
+++ b/lib/utils/index.js
@@ -2068,6 +2068,26 @@ module.exports = {
return false
},
+ /**
+ * Check if a call expression is Object.assign with the given node as first argument
+ * @param {CallExpression} callExpr
+ * @param {ASTNode} targetNode
+ * @returns {boolean}
+ */
+ isObjectAssignCall(callExpr, targetNode) {
+ const { callee, arguments: args } = callExpr
+
+ return (
+ args.length > 0 &&
+ args[0] === targetNode &&
+ callee?.type === 'MemberExpression' &&
+ callee.object?.type === 'Identifier' &&
+ callee.object.name === 'Object' &&
+ callee.property?.type === 'Identifier' &&
+ callee.property.name === 'assign'
+ )
+ },
+
/**
* @param {MemberExpression|Identifier} props
* @returns { { kind: 'assignment' | 'update' | 'call' , node: ESNode, pathNodes: MemberExpression[] } | null }
@@ -2128,6 +2148,14 @@ module.exports = {
}
}
}
+ if (this.isObjectAssignCall(target, node)) {
+ // Object.assign(xxx, {})
+ return {
+ kind: 'call',
+ node: target,
+ pathNodes
+ }
+ }
break
}
case 'MemberExpression': {
diff --git a/tests/lib/rules/no-mutating-props.js b/tests/lib/rules/no-mutating-props.js
index d4584b57c..1448ef22a 100644
--- a/tests/lib/rules/no-mutating-props.js
+++ b/tests/lib/rules/no-mutating-props.js
@@ -413,6 +413,22 @@ ruleTester.run('no-mutating-props', rule, {
const foo = ref('')
`
+ },
+ {
+ // Object.assign not mutating the prop
+ filename: 'test.vue',
+ code: `
+
+ `
}
],
@@ -1420,6 +1436,31 @@ ruleTester.run('no-mutating-props', rule, {
endColumn: 21
}
]
+ },
+ {
+ // Object.assign mutating the prop as first argument
+ filename: 'test.vue',
+ code: `
+
+ `,
+ errors: [
+ {
+ message: 'Unexpected mutation of "data" prop.',
+ line: 7,
+ column: 24,
+ endLine: 7,
+ endColumn: 68
+ }
+ ]
}
]
})
diff --git a/tests/lib/rules/no-side-effects-in-computed-properties.js b/tests/lib/rules/no-side-effects-in-computed-properties.js
index 2d0a124a4..7ba23f875 100644
--- a/tests/lib/rules/no-side-effects-in-computed-properties.js
+++ b/tests/lib/rules/no-side-effects-in-computed-properties.js
@@ -259,6 +259,7 @@ ruleTester.run('no-side-effects-in-computed-properties', rule, {
})
const test18 = computed(() => (console.log('a'), true))
const test19 = computed(() => utils.reverse(foo.array))
+ const test20 = computed(() => Object.assign({}, foo.data, { extra: 'value' }))
}
}
`
@@ -889,6 +890,54 @@ ruleTester.run('no-side-effects-in-computed-properties', rule, {
endColumn: 59
}
]
+ },
+ {
+ // Object.assign mutating the prop as first argument in computed properties
+ filename: 'test.vue',
+ code: `
+
+ `,
+ errors: [
+ {
+ message: 'Unexpected side effect in computed function.',
+ line: 8,
+ column: 40,
+ endLine: 8,
+ endColumn: 83
+ },
+ {
+ message: 'Unexpected side effect in computed function.',
+ line: 10,
+ column: 20,
+ endLine: 10,
+ endColumn: 56
+ },
+ {
+ message: 'Unexpected side effect in computed function.',
+ line: 14,
+ column: 15,
+ endLine: 14,
+ endColumn: 61
+ }
+ ]
}
]
})