no-deprecated-model-definition.js 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. /**
  2. * @author Flo Edelmann
  3. * See LICENSE file in root directory for full license.
  4. */
  5. 'use strict'
  6. const utils = require('../utils')
  7. const allowedPropNames = new Set(['modelValue', 'model-value'])
  8. const allowedEventNames = new Set(['update:modelValue', 'update:model-value'])
  9. /**
  10. * @param {ObjectExpression} node
  11. * @param {string} key
  12. * @returns {Literal | undefined}
  13. */
  14. function findPropertyValue(node, key) {
  15. const property = node.properties.find(
  16. (property) =>
  17. property.type === 'Property' &&
  18. property.key.type === 'Identifier' &&
  19. property.key.name === key
  20. )
  21. if (
  22. !property ||
  23. property.type !== 'Property' ||
  24. property.value.type !== 'Literal'
  25. ) {
  26. return undefined
  27. }
  28. return property.value
  29. }
  30. /**
  31. * @param {RuleFixer} fixer
  32. * @param {Literal} node
  33. * @param {string} text
  34. */
  35. function replaceLiteral(fixer, node, text) {
  36. return fixer.replaceTextRange([node.range[0] + 1, node.range[1] - 1], text)
  37. }
  38. module.exports = {
  39. meta: {
  40. type: 'problem',
  41. docs: {
  42. description: 'disallow deprecated `model` definition (in Vue.js 3.0.0+)',
  43. categories: undefined,
  44. url: 'https://eslint.vuejs.org/rules/no-deprecated-model-definition.html'
  45. },
  46. fixable: null,
  47. hasSuggestions: true,
  48. schema: [
  49. {
  50. type: 'object',
  51. additionalProperties: false,
  52. properties: {
  53. allowVue3Compat: {
  54. type: 'boolean'
  55. }
  56. }
  57. }
  58. ],
  59. messages: {
  60. deprecatedModel: '`model` definition is deprecated.',
  61. vue3Compat:
  62. '`model` definition is deprecated. You may use the Vue 3-compatible `modelValue`/`update:modelValue` though.',
  63. changeToModelValue: 'Change to `modelValue`/`update:modelValue`.',
  64. changeToKebabModelValue: 'Change to `model-value`/`update:model-value`.'
  65. }
  66. },
  67. /** @param {RuleContext} context */
  68. create(context) {
  69. const allowVue3Compat = Boolean(context.options[0]?.allowVue3Compat)
  70. return utils.executeOnVue(context, (obj) => {
  71. const modelProperty = utils.findProperty(obj, 'model')
  72. if (!modelProperty || modelProperty.value.type !== 'ObjectExpression') {
  73. return
  74. }
  75. if (!allowVue3Compat) {
  76. context.report({
  77. node: modelProperty,
  78. messageId: 'deprecatedModel'
  79. })
  80. return
  81. }
  82. const propName = findPropertyValue(modelProperty.value, 'prop')
  83. const eventName = findPropertyValue(modelProperty.value, 'event')
  84. if (
  85. !propName ||
  86. !eventName ||
  87. typeof propName.value !== 'string' ||
  88. typeof eventName.value !== 'string' ||
  89. !allowedPropNames.has(propName.value) ||
  90. !allowedEventNames.has(eventName.value)
  91. ) {
  92. context.report({
  93. node: modelProperty,
  94. messageId: 'vue3Compat',
  95. suggest:
  96. propName && eventName
  97. ? [
  98. {
  99. messageId: 'changeToModelValue',
  100. *fix(fixer) {
  101. const newPropName = 'modelValue'
  102. const newEventName = 'update:modelValue'
  103. yield replaceLiteral(fixer, propName, newPropName)
  104. yield replaceLiteral(fixer, eventName, newEventName)
  105. }
  106. },
  107. {
  108. messageId: 'changeToKebabModelValue',
  109. *fix(fixer) {
  110. const newPropName = 'model-value'
  111. const newEventName = 'update:model-value'
  112. yield replaceLiteral(fixer, propName, newPropName)
  113. yield replaceLiteral(fixer, eventName, newEventName)
  114. }
  115. }
  116. ]
  117. : []
  118. })
  119. }
  120. })
  121. }
  122. }