Skip to content

Commit d1479f2

Browse files
committed
feat: add no-deprecated-delete-set rule
1 parent 0fbe35e commit d1479f2

File tree

7 files changed

+502
-0
lines changed

7 files changed

+502
-0
lines changed

docs/rules/index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ Rules in this category are enabled for all presets provided by eslint-plugin-vue
4848
| [vue/no-computed-properties-in-data](./no-computed-properties-in-data.md) | disallow accessing computed properties in `data` | | :three::two::warning: |
4949
| [vue/no-custom-modifiers-on-v-model](./no-custom-modifiers-on-v-model.md) | disallow custom modifiers on v-model used on the component | | :two::warning: |
5050
| [vue/no-deprecated-data-object-declaration](./no-deprecated-data-object-declaration.md) | disallow using deprecated object declaration on data (in Vue.js 3.0.0+) | :wrench: | :three::warning: |
51+
| [vue/no-deprecated-delete-set](./no-deprecated-delete-set.md) | disallow using deprecated `$delete` and `$set` (in Vue.js 3.0.0+) | | :three::warning: |
5152
| [vue/no-deprecated-destroyed-lifecycle](./no-deprecated-destroyed-lifecycle.md) | disallow using deprecated `destroyed` and `beforeDestroy` lifecycle hooks (in Vue.js 3.0.0+) | :wrench: | :three::warning: |
5253
| [vue/no-deprecated-dollar-listeners-api](./no-deprecated-dollar-listeners-api.md) | disallow using deprecated `$listeners` (in Vue.js 3.0.0+) | | :three::warning: |
5354
| [vue/no-deprecated-dollar-scopedslots-api](./no-deprecated-dollar-scopedslots-api.md) | disallow using deprecated `$scopedSlots` (in Vue.js 3.0.0+) | :wrench: | :three::warning: |
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
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+
- :gear: This rule is included in all of `"plugin:vue/vue3-essential"`, `*.configs["flat/essential"]`, `"plugin:vue/vue3-strongly-recommended"`, `*.configs["flat/strongly-recommended"]`, `"plugin:vue/vue3-recommended"` and `*.configs["flat/recommended"]`.
14+
15+
## :book: Rule Details
16+
17+
This rule reports use of deprecated `$delete` and `$set`. (in Vue.js 3.0.0+).
18+
19+
<eslint-code-block :rules="{'vue/no-deprecated-delete-set': ['error']}">
20+
21+
```vue
22+
<script>
23+
import { set as st, delete as del } from 'vue'
24+
export default {
25+
mounted () {
26+
/* ✗ BAD */
27+
this.$set(obj, key, value)
28+
this.$delete(obj, key)
29+
30+
Vue.set(obj, key, value)
31+
Vue.delete(obj, key)
32+
33+
st(obj, key, value)
34+
del(obj, key)
35+
}
36+
}
37+
</script>
38+
```
39+
40+
</eslint-code-block>
41+
42+
## :wrench: Options
43+
44+
Nothing.
45+
46+
## :mag: Implementation
47+
48+
- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/no-deprecated-delete-set.js)
49+
- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/no-deprecated-delete-set.js)

lib/configs/flat/vue3-essential.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ module.exports = [
1717
'vue/no-child-content': 'error',
1818
'vue/no-computed-properties-in-data': 'error',
1919
'vue/no-deprecated-data-object-declaration': 'error',
20+
'vue/no-deprecated-delete-set': 'error',
2021
'vue/no-deprecated-destroyed-lifecycle': 'error',
2122
'vue/no-deprecated-dollar-listeners-api': 'error',
2223
'vue/no-deprecated-dollar-scopedslots-api': 'error',

lib/configs/vue3-essential.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ module.exports = {
1212
'vue/no-child-content': 'error',
1313
'vue/no-computed-properties-in-data': 'error',
1414
'vue/no-deprecated-data-object-declaration': 'error',
15+
'vue/no-deprecated-delete-set': 'error',
1516
'vue/no-deprecated-destroyed-lifecycle': 'error',
1617
'vue/no-deprecated-dollar-listeners-api': 'error',
1718
'vue/no-deprecated-dollar-scopedslots-api': 'error',

lib/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ const plugin = {
9696
'no-constant-condition': require('./rules/no-constant-condition'),
9797
'no-custom-modifiers-on-v-model': require('./rules/no-custom-modifiers-on-v-model'),
9898
'no-deprecated-data-object-declaration': require('./rules/no-deprecated-data-object-declaration'),
99+
'no-deprecated-delete-set': require('./rules/no-deprecated-delete-set'),
99100
'no-deprecated-destroyed-lifecycle': require('./rules/no-deprecated-destroyed-lifecycle'),
100101
'no-deprecated-dollar-listeners-api': require('./rules/no-deprecated-dollar-listeners-api'),
101102
'no-deprecated-dollar-scopedslots-api': require('./rules/no-deprecated-dollar-scopedslots-api'),

lib/rules/no-deprecated-delete-set.js

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
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 { findVariable } = require('@eslint-community/eslint-utils')
9+
10+
const DeprecatedApis = ['set', 'delete']
11+
const DeprecatedDollarApis = new Set(DeprecatedApis.map((item) => `$${item}`))
12+
13+
/**
14+
* @param {Expression|Super} node
15+
*/
16+
function isVue(node) {
17+
return node.type === 'Identifier' && node.name === 'Vue'
18+
}
19+
20+
module.exports = {
21+
meta: {
22+
type: 'problem',
23+
docs: {
24+
description:
25+
'disallow using deprecated `$delete` and `$set` (in Vue.js 3.0.0+)',
26+
categories: ['vue3-essential'],
27+
url: 'https://eslint.vuejs.org/rules/no-deprecated-delete-set.html'
28+
},
29+
fixable: null,
30+
schema: [],
31+
messages: {
32+
deprecated: 'The `$delete`, `$set` is deprecated.'
33+
}
34+
},
35+
/** @param {RuleContext} context */
36+
create(context) {
37+
/**
38+
* @param {Identifier} identifier
39+
* @param {RuleContext} context
40+
* @returns {CallExpression|undefined}
41+
*/
42+
function getVueDeprecatedCallExpression(identifier, context) {
43+
// Instance API: this.$set()
44+
if (
45+
DeprecatedDollarApis.has(identifier.name) &&
46+
identifier.parent.type === 'MemberExpression' &&
47+
utils.isThis(identifier.parent.object, context) &&
48+
identifier.parent.parent.type === 'CallExpression' &&
49+
identifier.parent.parent.callee === identifier.parent
50+
) {
51+
return identifier.parent.parent
52+
}
53+
54+
// Vue 2 Global API: Vue.set()
55+
if (
56+
DeprecatedApis.includes(identifier.name) &&
57+
identifier.parent.type === 'MemberExpression' &&
58+
isVue(identifier.parent.object) &&
59+
identifier.parent.parent.type === 'CallExpression' &&
60+
identifier.parent.parent.callee === identifier.parent
61+
) {
62+
return identifier.parent.parent
63+
}
64+
65+
// Vue 3 Global API
66+
if (
67+
identifier.parent.type === 'CallExpression' &&
68+
identifier.parent.callee === identifier
69+
) {
70+
const variable = findVariable(
71+
utils.getScope(context, identifier),
72+
identifier
73+
)
74+
75+
if (variable != null && variable.defs.length === 1) {
76+
const def = variable.defs[0]
77+
78+
// import { set as st } from 'vue'; st()
79+
if (
80+
def.type === 'ImportBinding' &&
81+
def.node.type === 'ImportSpecifier' &&
82+
def.node.imported.type === 'Identifier' &&
83+
DeprecatedApis.includes(def.node.imported.name) &&
84+
def.node.parent.type === 'ImportDeclaration' &&
85+
def.node.parent.source.value === 'vue'
86+
) {
87+
return identifier.parent
88+
}
89+
90+
// const { set, delete } = require('vue'); set()
91+
if (
92+
def.type === 'Variable' &&
93+
def.node.type === 'VariableDeclarator' &&
94+
def.node.id.type === 'ObjectPattern' &&
95+
def.node.init?.type === 'CallExpression' &&
96+
def.node.init.callee.type === 'Identifier' &&
97+
def.node.init.callee.name === 'require' &&
98+
def.node.init.arguments.length === 1 &&
99+
def.node.init.arguments[0].type === 'Literal' &&
100+
def.node.init.arguments[0].value === 'vue'
101+
) {
102+
const properties = def.node.id.properties
103+
for (const prop of properties) {
104+
if (
105+
prop.type === 'Property' &&
106+
prop.key.type === 'Identifier' &&
107+
DeprecatedApis.includes(prop.key.name) &&
108+
prop.value.type === 'Identifier' &&
109+
prop.value.name === identifier.name
110+
) {
111+
return identifier.parent
112+
}
113+
}
114+
}
115+
}
116+
}
117+
118+
return undefined
119+
}
120+
121+
const nodeVisitor = {
122+
/** @param {Identifier} node */
123+
Identifier(node) {
124+
const callExpression = getVueDeprecatedCallExpression(node, context)
125+
if (!callExpression) {
126+
return
127+
}
128+
129+
context.report({
130+
node,
131+
messageId: 'deprecated'
132+
})
133+
}
134+
}
135+
136+
return utils.compositingVisitors(
137+
utils.defineVueVisitor(context, nodeVisitor),
138+
utils.defineScriptSetupVisitor(context, nodeVisitor)
139+
)
140+
}
141+
}

0 commit comments

Comments
 (0)