Skip to content

Commit d45a4cd

Browse files
committed
feat: Object.assign detection to mutation rules
1 parent 10d645e commit d45a4cd

File tree

3 files changed

+119
-0
lines changed

3 files changed

+119
-0
lines changed

lib/utils/index.js

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2068,6 +2068,27 @@ module.exports = {
20682068
return false
20692069
},
20702070

2071+
/**
2072+
* Check if a call expression is Object.assign with the given node as first argument
2073+
* @param {CallExpression} callExpr
2074+
* @param {ASTNode} targetNode
2075+
* @returns {boolean}
2076+
*/
2077+
isObjectAssignCall(callExpr, targetNode) {
2078+
const { callee, arguments: args } = callExpr
2079+
2080+
return (
2081+
callExpr.type === 'CallExpression' &&
2082+
args.length > 0 &&
2083+
args[0] === targetNode &&
2084+
callee?.type === 'MemberExpression' &&
2085+
callee.object?.type === 'Identifier' &&
2086+
callee.object.name === 'Object' &&
2087+
callee.property?.type === 'Identifier' &&
2088+
callee.property.name === 'assign'
2089+
)
2090+
},
2091+
20712092
/**
20722093
* @param {MemberExpression|Identifier} props
20732094
* @returns { { kind: 'assignment' | 'update' | 'call' , node: ESNode, pathNodes: MemberExpression[] } | null }
@@ -2128,6 +2149,14 @@ module.exports = {
21282149
}
21292150
}
21302151
}
2152+
if (this.isObjectAssignCall(target, node)) {
2153+
// Object.assign(xxx, {})
2154+
return {
2155+
kind: 'call',
2156+
node: target,
2157+
pathNodes
2158+
}
2159+
}
21312160
break
21322161
}
21332162
case 'MemberExpression': {

tests/lib/rules/no-mutating-props.js

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -413,6 +413,22 @@ ruleTester.run('no-mutating-props', rule, {
413413
const foo = ref('')
414414
</script>
415415
`
416+
},
417+
{
418+
// Object.assign not mutating the prop
419+
filename: 'test.vue',
420+
code: `
421+
<script>
422+
export default {
423+
props: ['data'],
424+
methods: {
425+
update() {
426+
return Object.assign({}, this.data, { extra: 'value' })
427+
}
428+
}
429+
}
430+
</script>
431+
`
416432
}
417433
],
418434

@@ -1420,6 +1436,31 @@ ruleTester.run('no-mutating-props', rule, {
14201436
endColumn: 21
14211437
}
14221438
]
1439+
},
1440+
{
1441+
// Object.assign mutating the prop as first argument
1442+
filename: 'test.vue',
1443+
code: `
1444+
<script>
1445+
export default {
1446+
props: ['data'],
1447+
methods: {
1448+
update() {
1449+
return Object.assign(this.data, { extra: 'value' })
1450+
}
1451+
}
1452+
}
1453+
</script>
1454+
`,
1455+
errors: [
1456+
{
1457+
message: 'Unexpected mutation of "data" prop.',
1458+
line: 7,
1459+
column: 24,
1460+
endLine: 7,
1461+
endColumn: 68
1462+
}
1463+
]
14231464
}
14241465
]
14251466
})

tests/lib/rules/no-side-effects-in-computed-properties.js

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,7 @@ ruleTester.run('no-side-effects-in-computed-properties', rule, {
259259
})
260260
const test18 = computed(() => (console.log('a'), true))
261261
const test19 = computed(() => utils.reverse(foo.array))
262+
const test20 = computed(() => Object.assign({}, foo.data, { extra: 'value' }))
262263
}
263264
}
264265
</script>`
@@ -889,6 +890,54 @@ ruleTester.run('no-side-effects-in-computed-properties', rule, {
889890
endColumn: 59
890891
}
891892
]
893+
},
894+
{
895+
// Object.assign mutating the prop as first argument in computed properties
896+
filename: 'test.vue',
897+
code: `
898+
<script>
899+
import {ref, computed} from 'vue'
900+
export default {
901+
setup() {
902+
const foo = useFoo()
903+
904+
const test1 = computed(() => Object.assign(foo.data, { extra: 'value' }))
905+
const test2 = computed(() => {
906+
return Object.assign(foo.user, foo.updates)
907+
})
908+
const test3 = computed({
909+
get() {
910+
Object.assign(foo.settings, { theme: 'dark' })
911+
return foo.settings
912+
}
913+
})
914+
}
915+
}
916+
</script>
917+
`,
918+
errors: [
919+
{
920+
message: 'Unexpected side effect in computed function.',
921+
line: 8,
922+
column: 40,
923+
endLine: 8,
924+
endColumn: 83
925+
},
926+
{
927+
message: 'Unexpected side effect in computed function.',
928+
line: 10,
929+
column: 20,
930+
endLine: 10,
931+
endColumn: 56
932+
},
933+
{
934+
message: 'Unexpected side effect in computed function.',
935+
line: 14,
936+
column: 15,
937+
endLine: 14,
938+
endColumn: 61
939+
}
940+
]
892941
}
893942
]
894943
})

0 commit comments

Comments
 (0)