You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

134 lines
3.8 KiB

/**
* @author Flo Edelmann
* See LICENSE file in root directory for full license.
*/
'use strict'
const utils = require('../utils')
const allowedPropNames = new Set(['modelValue', 'model-value'])
const allowedEventNames = new Set(['update:modelValue', 'update:model-value'])
/**
* @param {ObjectExpression} node
* @param {string} key
* @returns {Literal | TemplateLiteral | undefined}
*/
function findPropertyValue(node, key) {
const property = node.properties.find(
(property) =>
property.type === 'Property' &&
property.key.type === 'Identifier' &&
property.key.name === key
)
if (
!property ||
property.type !== 'Property' ||
!utils.isStringLiteral(property.value)
) {
return undefined
}
return property.value
}
/**
* @param {RuleFixer} fixer
* @param {Literal | TemplateLiteral} node
* @param {string} text
*/
function replaceLiteral(fixer, node, text) {
return fixer.replaceTextRange([node.range[0] + 1, node.range[1] - 1], text)
}
module.exports = {
meta: {
type: 'problem',
docs: {
description: 'disallow deprecated `model` definition (in Vue.js 3.0.0+)',
categories: undefined,
url: 'https://eslint.vuejs.org/rules/no-deprecated-model-definition.html'
},
fixable: null,
hasSuggestions: true,
schema: [
{
type: 'object',
additionalProperties: false,
properties: {
allowVue3Compat: {
type: 'boolean'
}
}
}
],
messages: {
deprecatedModel: '`model` definition is deprecated.',
vue3Compat:
'`model` definition is deprecated. You may use the Vue 3-compatible `modelValue`/`update:modelValue` though.',
changeToModelValue: 'Change to `modelValue`/`update:modelValue`.',
changeToKebabModelValue: 'Change to `model-value`/`update:model-value`.'
}
},
/** @param {RuleContext} context */
create(context) {
const allowVue3Compat = Boolean(context.options[0]?.allowVue3Compat)
return utils.executeOnVue(context, (obj) => {
const modelProperty = utils.findProperty(obj, 'model')
if (!modelProperty || modelProperty.value.type !== 'ObjectExpression') {
return
}
if (!allowVue3Compat) {
context.report({
node: modelProperty,
messageId: 'deprecatedModel'
})
return
}
const propName = findPropertyValue(modelProperty.value, 'prop')
const eventName = findPropertyValue(modelProperty.value, 'event')
if (
!propName ||
!eventName ||
!allowedPropNames.has(
utils.getStringLiteralValue(propName, true) ?? ''
) ||
!allowedEventNames.has(
utils.getStringLiteralValue(eventName, true) ?? ''
)
) {
context.report({
node: modelProperty,
messageId: 'vue3Compat',
suggest:
propName && eventName
? [
{
messageId: 'changeToModelValue',
*fix(fixer) {
const newPropName = 'modelValue'
const newEventName = 'update:modelValue'
yield replaceLiteral(fixer, propName, newPropName)
yield replaceLiteral(fixer, eventName, newEventName)
}
},
{
messageId: 'changeToKebabModelValue',
*fix(fixer) {
const newPropName = 'model-value'
const newEventName = 'update:model-value'
yield replaceLiteral(fixer, propName, newPropName)
yield replaceLiteral(fixer, eventName, newEventName)
}
}
]
: []
})
}
})
}
}