Skip to content

Commit dfdbe1a

Browse files
authored
feat: add no-deprecated-delete-set rule (#2540)
1 parent d77fbf7 commit dfdbe1a

File tree

5 files changed

+565
-0
lines changed

5 files changed

+565
-0
lines changed

Diff for: docs/rules/index.md

+1
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,7 @@ For example:
229229
| [vue/next-tick-style](./next-tick-style.md) | enforce Promise or callback style in `nextTick` | :wrench: | :hammer: |
230230
| [vue/no-bare-strings-in-template](./no-bare-strings-in-template.md) | disallow the use of bare strings in `<template>` | | :hammer: |
231231
| [vue/no-boolean-default](./no-boolean-default.md) | disallow boolean defaults | | :hammer: |
232+
| [vue/no-deprecated-delete-set](./no-deprecated-delete-set.md) | disallow using deprecated `$delete` and `$set` (in Vue.js 3.0.0+) | | :warning: |
232233
| [vue/no-deprecated-model-definition](./no-deprecated-model-definition.md) | disallow deprecated `model` definition (in Vue.js 3.0.0+) | :bulb: | :warning: |
233234
| [vue/no-duplicate-attr-inheritance](./no-duplicate-attr-inheritance.md) | enforce `inheritAttrs` to be set to `false` when using `v-bind="$attrs"` | | :hammer: |
234235
| [vue/no-empty-component-block](./no-empty-component-block.md) | disallow the `<template>` `<script>` `<style>` block to be empty | | :hammer: |

Diff for: docs/rules/no-deprecated-delete-set.md

+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
---
2+
pageClass: rule-details
3+
sidebarDepth: 0
4+
title: vue/no-deprecated-delete-set
5+
description: disallow using deprecated `$delete` and `$set` (in Vue.js 3.0.0+)
6+
---
7+
8+
# vue/no-deprecated-delete-set
9+
10+
> disallow using deprecated `$delete` and `$set` (in Vue.js 3.0.0+)
11+
12+
- :exclamation: <badge text="This rule has not been released yet." vertical="middle" type="error"> _**This rule has not been released yet.**_ </badge>
13+
14+
## :book: Rule Details
15+
16+
This rule reports use of deprecated `$delete` and `$set`. (in Vue.js 3.0.0+).
17+
18+
<eslint-code-block :rules="{'vue/no-deprecated-delete-set': ['error']}">
19+
20+
```vue
21+
<script>
22+
import { set, del } from 'vue'
23+
export default {
24+
mounted () {
25+
/* ✗ BAD */
26+
this.$set(obj, key, value)
27+
this.$delete(obj, key)
28+
29+
Vue.set(obj, key, value)
30+
Vue.delete(obj, key)
31+
32+
set(obj, key, value)
33+
del(obj, key)
34+
}
35+
}
36+
</script>
37+
```
38+
39+
</eslint-code-block>
40+
41+
## :wrench: Options
42+
43+
Nothing.
44+
45+
## :books: Further Reading
46+
47+
- [Migration Guide - Removed APIs](https://v3-migration.vuejs.org/breaking-changes/#removed-apis)
48+
49+
## :mag: Implementation
50+
51+
- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/no-deprecated-delete-set.js)
52+
- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/no-deprecated-delete-set.js)

Diff for: lib/index.js

+1
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ const plugin = {
9797
'no-constant-condition': require('./rules/no-constant-condition'),
9898
'no-custom-modifiers-on-v-model': require('./rules/no-custom-modifiers-on-v-model'),
9999
'no-deprecated-data-object-declaration': require('./rules/no-deprecated-data-object-declaration'),
100+
'no-deprecated-delete-set': require('./rules/no-deprecated-delete-set'),
100101
'no-deprecated-destroyed-lifecycle': require('./rules/no-deprecated-destroyed-lifecycle'),
101102
'no-deprecated-dollar-listeners-api': require('./rules/no-deprecated-dollar-listeners-api'),
102103
'no-deprecated-dollar-scopedslots-api': require('./rules/no-deprecated-dollar-scopedslots-api'),

Diff for: lib/rules/no-deprecated-delete-set.js

+132
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
/**
2+
* @author Wayne Zhang
3+
* See LICENSE file in root directory for full license.
4+
*/
5+
'use strict'
6+
7+
const utils = require('../utils')
8+
const { ReferenceTracker } = require('@eslint-community/eslint-utils')
9+
10+
/**
11+
* @typedef {import('@eslint-community/eslint-utils').TYPES.TraceMap} TraceMap
12+
*/
13+
14+
/** @type {TraceMap} */
15+
const deletedImportApisMap = {
16+
set: {
17+
[ReferenceTracker.CALL]: true
18+
},
19+
del: {
20+
[ReferenceTracker.CALL]: true
21+
}
22+
}
23+
const deprecatedApis = new Set(['set', 'delete'])
24+
const deprecatedDollarApis = new Set(['$set', '$delete'])
25+
26+
/**
27+
* @param {Expression|Super} node
28+
*/
29+
function isVue(node) {
30+
return node.type === 'Identifier' && node.name === 'Vue'
31+
}
32+
33+
module.exports = {
34+
meta: {
35+
type: 'problem',
36+
docs: {
37+
description:
38+
'disallow using deprecated `$delete` and `$set` (in Vue.js 3.0.0+)',
39+
categories: undefined,
40+
url: 'https://eslint.vuejs.org/rules/no-deprecated-delete-set.html'
41+
},
42+
fixable: null,
43+
schema: [],
44+
messages: {
45+
deprecated: 'The `$delete`, `$set` is deprecated.'
46+
}
47+
},
48+
/** @param {RuleContext} context */
49+
create(context) {
50+
/**
51+
* @param {Identifier} identifier
52+
* @param {RuleContext} context
53+
* @returns {CallExpression|undefined}
54+
*/
55+
function getVueDeprecatedCallExpression(identifier, context) {
56+
// Instance API: this.$set()
57+
if (
58+
deprecatedDollarApis.has(identifier.name) &&
59+
identifier.parent.type === 'MemberExpression' &&
60+
utils.isThis(identifier.parent.object, context) &&
61+
identifier.parent.parent.type === 'CallExpression' &&
62+
identifier.parent.parent.callee === identifier.parent
63+
) {
64+
return identifier.parent.parent
65+
}
66+
67+
// Vue 2 Global API: Vue.set()
68+
if (
69+
deprecatedApis.has(identifier.name) &&
70+
identifier.parent.type === 'MemberExpression' &&
71+
isVue(identifier.parent.object) &&
72+
identifier.parent.parent.type === 'CallExpression' &&
73+
identifier.parent.parent.callee === identifier.parent
74+
) {
75+
return identifier.parent.parent
76+
}
77+
78+
return undefined
79+
}
80+
81+
const nodeVisitor = {
82+
/** @param {Identifier} node */
83+
Identifier(node) {
84+
const callExpression = getVueDeprecatedCallExpression(node, context)
85+
if (!callExpression) {
86+
return
87+
}
88+
89+
context.report({
90+
node,
91+
messageId: 'deprecated'
92+
})
93+
}
94+
}
95+
96+
return utils.compositingVisitors(
97+
utils.defineVueVisitor(context, nodeVisitor),
98+
utils.defineScriptSetupVisitor(context, nodeVisitor),
99+
{
100+
/** @param {Program} node */
101+
Program(node) {
102+
const tracker = new ReferenceTracker(utils.getScope(context, node))
103+
104+
// import { set } from 'vue'; set()
105+
const esmTraceMap = {
106+
vue: {
107+
[ReferenceTracker.ESM]: true,
108+
...deletedImportApisMap
109+
}
110+
}
111+
// const { set } = require('vue'); set()
112+
const cjsTraceMap = {
113+
vue: {
114+
...deletedImportApisMap
115+
}
116+
}
117+
118+
for (const { node } of [
119+
...tracker.iterateEsmReferences(esmTraceMap),
120+
...tracker.iterateCjsReferences(cjsTraceMap)
121+
]) {
122+
const refNode = /** @type {CallExpression} */ (node)
123+
context.report({
124+
node: refNode.callee,
125+
messageId: 'deprecated'
126+
})
127+
}
128+
}
129+
}
130+
)
131+
}
132+
}

0 commit comments

Comments
 (0)