/** * @author Wayne Zhang * See LICENSE file in root directory for full license. */ 'use strict' const utils = require('../utils') const { ReferenceTracker } = require('@eslint-community/eslint-utils') /** * @typedef {import('@eslint-community/eslint-utils').TYPES.TraceMap} TraceMap */ /** @type {TraceMap} */ const deletedImportApisMap = { set: { [ReferenceTracker.CALL]: true }, del: { [ReferenceTracker.CALL]: true } } const deprecatedApis = new Set(['set', 'delete']) const deprecatedDollarApis = new Set(['$set', '$delete']) /** * @param {Expression|Super} node */ function isVue(node) { return node.type === 'Identifier' && node.name === 'Vue' } module.exports = { meta: { type: 'problem', docs: { description: 'disallow using deprecated `$delete` and `$set` (in Vue.js 3.0.0+)', categories: undefined, url: 'https://eslint.vuejs.org/rules/no-deprecated-delete-set.html' }, fixable: null, schema: [], messages: { deprecated: 'The `$delete`, `$set` is deprecated.' } }, /** @param {RuleContext} context */ create(context) { /** * @param {Identifier} identifier * @param {RuleContext} context * @returns {CallExpression|undefined} */ function getVueDeprecatedCallExpression(identifier, context) { // Instance API: this.$set() if ( deprecatedDollarApis.has(identifier.name) && identifier.parent.type === 'MemberExpression' && utils.isThis(identifier.parent.object, context) && identifier.parent.parent.type === 'CallExpression' && identifier.parent.parent.callee === identifier.parent ) { return identifier.parent.parent } // Vue 2 Global API: Vue.set() if ( deprecatedApis.has(identifier.name) && identifier.parent.type === 'MemberExpression' && isVue(identifier.parent.object) && identifier.parent.parent.type === 'CallExpression' && identifier.parent.parent.callee === identifier.parent ) { return identifier.parent.parent } return undefined } const nodeVisitor = { /** @param {Identifier} node */ Identifier(node) { const callExpression = getVueDeprecatedCallExpression(node, context) if (!callExpression) { return } context.report({ node, messageId: 'deprecated' }) } } return utils.compositingVisitors( utils.defineVueVisitor(context, nodeVisitor), utils.defineScriptSetupVisitor(context, nodeVisitor), { /** @param {Program} node */ Program(node) { const tracker = new ReferenceTracker(utils.getScope(context, node)) // import { set } from 'vue'; set() const esmTraceMap = { vue: { [ReferenceTracker.ESM]: true, ...deletedImportApisMap } } // const { set } = require('vue'); set() const cjsTraceMap = { vue: { ...deletedImportApisMap } } for (const { node } of [ ...tracker.iterateEsmReferences(esmTraceMap), ...tracker.iterateCjsReferences(cjsTraceMap) ]) { const refNode = /** @type {CallExpression} */ (node) context.report({ node: refNode.callee, messageId: 'deprecated' }) } } } ) } }