no-deprecated-delete-set.js 3.4 KB

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