Skip to content

Commit

Permalink
Add vue/no-deprecated-model-definition rule (#2238)
Browse files Browse the repository at this point in the history
  • Loading branch information
FloEdelmann authored Jul 22, 2023
1 parent 52a9966 commit eddf098
Show file tree
Hide file tree
Showing 5 changed files with 395 additions and 0 deletions.
1 change: 1 addition & 0 deletions docs/rules/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,7 @@ For example:
| [vue/next-tick-style](./next-tick-style.md) | enforce Promise or callback style in `nextTick` | :wrench: | :hammer: |
| [vue/no-bare-strings-in-template](./no-bare-strings-in-template.md) | disallow the use of bare strings in `<template>` | | :hammer: |
| [vue/no-boolean-default](./no-boolean-default.md) | disallow boolean defaults | :wrench: | :hammer: |
| [vue/no-deprecated-model-definition](./no-deprecated-model-definition.md) | disallow deprecated `model` definition (in Vue.js 3.0.0+) | :bulb: | :warning: |
| [vue/no-duplicate-attr-inheritance](./no-duplicate-attr-inheritance.md) | enforce `inheritAttrs` to be set to `false` when using `v-bind="$attrs"` | | :hammer: |
| [vue/no-empty-component-block](./no-empty-component-block.md) | disallow the `<template>` `<script>` `<style>` block to be empty | | :hammer: |
| [vue/no-multiple-objects-in-class](./no-multiple-objects-in-class.md) | disallow to pass multiple objects into array to class | | :hammer: |
Expand Down
76 changes: 76 additions & 0 deletions docs/rules/no-deprecated-model-definition.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
---
pageClass: rule-details
sidebarDepth: 0
title: vue/no-deprecated-model-definition
description: disallow deprecated `model` definition (in Vue.js 3.0.0+)
---
# vue/no-deprecated-model-definition

> disallow deprecated `model` definition (in Vue.js 3.0.0+)
- :exclamation: <badge text="This rule has not been released yet." vertical="middle" type="error"> ***This rule has not been released yet.*** </badge>
- :bulb: Some problems reported by this rule are manually fixable by editor [suggestions](https://eslint.org/docs/developer-guide/working-with-rules#providing-suggestions).

## :book: Rule Details

This rule reports use of the component `model` option, which has been deprecated in Vue.js 3.0.0+.

See [Migration Guide – `v-model`](https://v3-migration.vuejs.org/breaking-changes/v-model.html) for more details.

<eslint-code-block :rules="{'vue/no-deprecated-model-definition': ['error']}">

```vue
<script>
export default defineComponent({
model: {
prop: 'my-value',
event: 'input'
}
})
</script>
```

</eslint-code-block>

## :wrench: Options

```json
{
"vue/no-deprecated-model-definition": ["error", {
"allowVue3Compat": true
}]
}
```

### `"allowVue3Compat": true`

Allow `model` definitions with prop/event names that match the Vue.js 3.0.0+ `v-model` syntax, e.g. `fooBar`/`update:fooBar`.

<eslint-code-block :rules="{'vue/no-deprecated-model-definition': ['error', { allowVue3Compat: true }]}">

```vue
<script>
export default defineComponent({
model: {
prop: 'fooBar',
event: 'update:fooBar'
}
})
</script>
```

</eslint-code-block>

## :couple: Related Rules

- [vue/valid-model-definition](./valid-model-definition.md) (for Vue.js 2.x)
- [vue/no-v-model-argument](./no-v-model-argument.md) (for Vue.js 2.x)

## :books: Further Reading

- [Migration Guide – `v-model`](https://v3-migration.vuejs.org/breaking-changes/v-model.html)

## :mag: Implementation

- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/no-deprecated-model-definition.js)
- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/no-deprecated-model-definition.js)
1 change: 1 addition & 0 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ module.exports = {
'no-deprecated-functional-template': require('./rules/no-deprecated-functional-template'),
'no-deprecated-html-element-is': require('./rules/no-deprecated-html-element-is'),
'no-deprecated-inline-template': require('./rules/no-deprecated-inline-template'),
'no-deprecated-model-definition': require('./rules/no-deprecated-model-definition'),
'no-deprecated-props-default-this': require('./rules/no-deprecated-props-default-this'),
'no-deprecated-router-link-tag-prop': require('./rules/no-deprecated-router-link-tag-prop'),
'no-deprecated-scope-attribute': require('./rules/no-deprecated-scope-attribute'),
Expand Down
112 changes: 112 additions & 0 deletions lib/rules/no-deprecated-model-definition.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
/**
* @author Flo Edelmann
* See LICENSE file in root directory for full license.
*/
'use strict'

const utils = require('../utils')

/**
* @param {RuleContext} context
* @param {ASTNode} node
*/
function reportWithoutSuggestion(context, node) {
context.report({
node,
messageId: 'deprecatedModel'
})
}

/**
* @param {ObjectExpression} node
* @param {string} key
* @returns {Literal | 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' ||
property.value.type !== 'Literal'
) {
return undefined
}
return property.value
}

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.',
renameEvent: 'Rename event to `{{expectedEventName}}`.'
}
},
/** @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) {
reportWithoutSuggestion(context, modelProperty)
return
}

const propName = findPropertyValue(modelProperty.value, 'prop')
const eventName = findPropertyValue(modelProperty.value, 'event')

if (!propName || !eventName) {
reportWithoutSuggestion(context, modelProperty)
return
}

const expectedEventName = `update:${propName.value}`
if (eventName.value !== expectedEventName) {
context.report({
node: modelProperty,
messageId: 'deprecatedModel',
suggest: [
{
messageId: 'renameEvent',
data: { expectedEventName },
fix(fixer) {
return fixer.replaceTextRange(
[eventName.range[0] + 1, eventName.range[1] - 1],
expectedEventName
)
}
}
]
})
}
})
}
}
Loading

0 comments on commit eddf098

Please sign in to comment.