Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Vue3 v-model API changes #1039

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions docs/rules/README.md
Expand Up @@ -39,6 +39,7 @@ Enforce all the rules in this category, as well as all higher priority rules, wi
| Rule ID | Description | |
|:--------|:------------|:---|
| [vue/no-async-in-computed-properties](./no-async-in-computed-properties.md) | disallow asynchronous actions in computed properties | |
| [vue/no-custom-modifiers-on-v-model](./no-custom-modifiers-on-v-model.md) | disallow custom modifiers on v-model used on the component | |
| [vue/no-dupe-keys](./no-dupe-keys.md) | disallow duplication of field names | |
| [vue/no-duplicate-attributes](./no-duplicate-attributes.md) | disallow duplication of attributes | |
| [vue/no-parsing-error](./no-parsing-error.md) | disallow parsing errors in `<template>` | |
Expand All @@ -50,6 +51,7 @@ Enforce all the rules in this category, as well as all higher priority rules, wi
| [vue/no-unused-components](./no-unused-components.md) | disallow registering components that are not used inside templates | |
| [vue/no-unused-vars](./no-unused-vars.md) | disallow unused variable definitions of v-for directives or scope attributes | |
| [vue/no-use-v-if-with-v-for](./no-use-v-if-with-v-for.md) | disallow use v-if on the same element as v-for | |
| [vue/no-v-model-argument](./no-v-model-argument.md) | disallow adding an argument to `v-model` used in custom component | |
| [vue/require-component-is](./require-component-is.md) | require `v-bind:is` of `<component>` elements | |
| [vue/require-prop-type-constructor](./require-prop-type-constructor.md) | require prop type to be a constructor | :wrench: |
| [vue/require-render-return](./require-render-return.md) | enforce render function to always return value | |
Expand Down Expand Up @@ -158,6 +160,7 @@ For example:
| [vue/no-deprecated-scope-attribute](./no-deprecated-scope-attribute.md) | disallow deprecated `scope` attribute (in Vue.js 2.5.0+) | :wrench: |
| [vue/no-deprecated-slot-attribute](./no-deprecated-slot-attribute.md) | disallow deprecated `slot` attribute (in Vue.js 2.6.0+) | :wrench: |
| [vue/no-deprecated-slot-scope-attribute](./no-deprecated-slot-scope-attribute.md) | disallow deprecated `slot-scope` attribute (in Vue.js 2.6.0+) | :wrench: |
| [vue/no-deprecated-v-bind-sync](./no-deprecated-v-bind-sync.md) | disallow use of deprecated `.sync` modifier on `v-bind` directive (in Vue.js 3.0.0+) | :wrench: |
| [vue/no-empty-pattern](./no-empty-pattern.md) | disallow empty destructuring patterns | |
| [vue/no-irregular-whitespace](./no-irregular-whitespace.md) | disallow irregular whitespace | |
| [vue/no-reserved-component-names](./no-reserved-component-names.md) | disallow the use of reserved names in component definitions | |
Expand Down
53 changes: 53 additions & 0 deletions docs/rules/no-custom-modifiers-on-v-model.md
@@ -0,0 +1,53 @@
---
pageClass: rule-details
sidebarDepth: 0
title: vue/no-custom-modifiers-on-v-model
description: disallow custom modifiers on v-model used on the component
---
# vue/no-custom-modifiers-on-v-model
> disallow custom modifiers on v-model used on the component

- :gear: This rule is included in all of `"plugin:vue/essential"`, `"plugin:vue/strongly-recommended"` and `"plugin:vue/recommended"`.

This rule checks whether `v-model `used on the component do not have custom modifiers.

## Rule Details

This rule reports `v-model` directives in the following cases:

- The directive used on the component has custom modifiers. E.g. `<MyComponent v-model.aaa="foo" />`

<eslint-code-block :rules="{'vue/no-custom-modifiers-on-v-model': ['error']}">

```vue
<template>
<!-- ✓ GOOD -->
<MyComponent v-model="foo" />
<MyComponent v-model.trim="foo" />
<MyComponent v-model.lazy="foo" />
<MyComponent v-model.number="foo" />


<!-- ✗ BAD -->
<MyComponent v-model.aaa="foo" />
<MyComponent v-model.aaa.bbb="foo" />

</template>
```

</eslint-code-block>

### Options

Nothing.

## :couple: Related rules

- [valid-v-model]

[valid-v-model]: valid-v-model.md

## :mag: Implementation

- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/no-custom-modifiers-on-v-model.js)
- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/no-custom-modifiers-on-v-model.js)
52 changes: 52 additions & 0 deletions docs/rules/no-deprecated-v-bind-sync.md
@@ -0,0 +1,52 @@
---
pageClass: rule-details
sidebarDepth: 0
title: vue/no-deprecated-v-bind-sync
description: disallow use of deprecated `.sync` modifier on `v-bind` directive (in Vue.js 3.0.0+)
---
# vue/no-deprecated-v-bind-sync
> disallow use of deprecated `.sync` modifier on `v-bind` directive (in Vue.js 3.0.0+)

- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fixing-problems) can automatically fix some of the problems reported by this rule.

## :book: Rule Details

This rule reports use of deprecated `.sync` modifier on `v-bind` directive (in Vue.js 3.0.0+)

<eslint-code-block fix :rules="{'vue/no-deprecated-v-bind-sync': ['error']}">

```vue
<template>
<!-- ✓ GOOD -->
<MyComponent v-bind:propName="foo"/>
<MyComponent :propName="foo"/>


<!-- ✗ BAD -->
<MyComponent v-bind:propName.sync="foo"/>
<MyComponent v-bind:[dynamiArg].sync="foo"/>
<MyComponent v-bind.sync="foo"/>
<MyComponent :propName.sync="foo"/>
</template>
```

</eslint-code-block>

## :wrench: Options

Nothing.

## :couple: Related rules

- [valid-v-bind]

[valid-v-bind]: valid-v-bind.md

## :books: Further reading

- [RFC: Replace v-bind.sync with v-model argument](https://github.com/vuejs/rfcs/blob/master/active-rfcs/0005-replace-v-bind-sync-with-v-model-argument.md)

## :mag: Implementation

- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/no-deprecated-v-bind-sync.js)
- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/no-deprecated-v-bind-sync.js)
49 changes: 49 additions & 0 deletions docs/rules/no-v-model-argument.md
@@ -0,0 +1,49 @@
---
pageClass: rule-details
sidebarDepth: 0
title: vue/no-v-model-argument
description: disallow adding an argument to `v-model` used in custom component
---
# vue/no-v-model-argument
> disallow adding an argument to `v-model` used in custom component

- :gear: This rule is included in all of `"plugin:vue/essential"`, `"plugin:vue/strongly-recommended"` and `"plugin:vue/recommended"`.

This rule checks whether `v-model` used on custom component do not have an argument.

## :book: Rule Details

This rule reports `v-model` directives in the following cases:

- The directive used on component has an argument. E.g. `<MyComponent v-model:aaa="foo" />`

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

```vue
<template>
<!-- ✓ GOOD -->
<MyComponent v-model="foo" />


<!-- ✗ BAD -->
<MyComponent v-model:aaa="foo" />
</template>
```

</eslint-code-block>


## :wrench: Options

Nothing.

## :couple: Related rules

- [valid-v-model]

[valid-v-model]: valid-v-model.md

## :mag: Implementation

- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/no-v-model-argument.js)
- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/no-v-model-argument.js)
8 changes: 8 additions & 0 deletions docs/rules/valid-v-bind.md
Expand Up @@ -54,6 +54,14 @@ Nothing.

[no-parsing-error]: no-parsing-error.md

- [no-deprecated-v-bind-sync]

[no-deprecated-v-bind-sync]: no-deprecated-v-bind-sync.md

- [valid-v-bind-sync]

[valid-v-bind-sync]: valid-v-bind-sync.md

## :mag: Implementation

- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/valid-v-bind.js)
Expand Down
7 changes: 5 additions & 2 deletions docs/rules/valid-v-model.md
Expand Up @@ -15,8 +15,8 @@ This rule checks whether every `v-model` directive is valid.

This rule reports `v-model` directives in the following cases:

- The directive has that argument. E.g. `<input v-model:aaa="foo">`
- The directive has the modifiers which are not supported. E.g. `<input v-model.bbb="foo">`
- The directive used on HTMLElement has an argument. E.g. `<input v-model:aaa="foo">`
- The directive used on HTMLElement has modifiers which are not supported. E.g. `<input v-model.bbb="foo">`
- The directive does not have that attribute value. E.g. `<input v-model>`
- The directive does not have the attribute value which is valid as LHS. E.g. `<input v-model="foo() + bar()">`
- The directive is on unsupported elements. E.g. `<div v-model="foo"></div>`
Expand All @@ -32,6 +32,9 @@ This rule reports `v-model` directives in the following cases:
<input v-model.lazy="foo">
<textarea v-model="foo"/>
<MyComponent v-model="foo"/>
<MyComponent v-model:propName="foo"/>
<MyComponent v-model.modifier="foo"/>
<MyComponent v-model:propName.modifier="foo"/>
<div v-for="todo in todos">
<input v-model="todo.name">
</div>
Expand Down
2 changes: 2 additions & 0 deletions lib/configs/essential.js
Expand Up @@ -7,6 +7,7 @@ module.exports = {
extends: require.resolve('./base'),
rules: {
'vue/no-async-in-computed-properties': 'error',
'vue/no-custom-modifiers-on-v-model': 'error',
'vue/no-dupe-keys': 'error',
'vue/no-duplicate-attributes': 'error',
'vue/no-parsing-error': 'error',
Expand All @@ -18,6 +19,7 @@ module.exports = {
'vue/no-unused-components': 'error',
'vue/no-unused-vars': 'error',
'vue/no-use-v-if-with-v-for': 'error',
'vue/no-v-model-argument': 'error',
'vue/require-component-is': 'error',
'vue/require-prop-type-constructor': 'error',
'vue/require-render-return': 'error',
Expand Down
3 changes: 3 additions & 0 deletions lib/index.js
Expand Up @@ -39,9 +39,11 @@ module.exports = {
'no-async-in-computed-properties': require('./rules/no-async-in-computed-properties'),
'no-boolean-default': require('./rules/no-boolean-default'),
'no-confusing-v-for-v-if': require('./rules/no-confusing-v-for-v-if'),
'no-custom-modifiers-on-v-model': require('./rules/no-custom-modifiers-on-v-model'),
'no-deprecated-scope-attribute': require('./rules/no-deprecated-scope-attribute'),
'no-deprecated-slot-attribute': require('./rules/no-deprecated-slot-attribute'),
'no-deprecated-slot-scope-attribute': require('./rules/no-deprecated-slot-scope-attribute'),
'no-deprecated-v-bind-sync': require('./rules/no-deprecated-v-bind-sync'),
'no-dupe-keys': require('./rules/no-dupe-keys'),
'no-duplicate-attributes': require('./rules/no-duplicate-attributes'),
'no-empty-pattern': require('./rules/no-empty-pattern'),
Expand All @@ -63,6 +65,7 @@ module.exports = {
'no-unused-vars': require('./rules/no-unused-vars'),
'no-use-v-if-with-v-for': require('./rules/no-use-v-if-with-v-for'),
'no-v-html': require('./rules/no-v-html'),
'no-v-model-argument': require('./rules/no-v-model-argument'),
'object-curly-spacing': require('./rules/object-curly-spacing'),
'order-in-components': require('./rules/order-in-components'),
'prop-name-casing': require('./rules/prop-name-casing'),
Expand Down
57 changes: 57 additions & 0 deletions lib/rules/no-custom-modifiers-on-v-model.js
@@ -0,0 +1,57 @@
/**
* @author Przemyslaw Falowski (@przemkow)
* @fileoverview This rule checks whether v-model used on the component do not have custom modifiers
*/
'use strict'

// ------------------------------------------------------------------------------
// Requirements
// ------------------------------------------------------------------------------

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

// ------------------------------------------------------------------------------
// Helpers
// ------------------------------------------------------------------------------

const VALID_MODIFIERS = new Set(['lazy', 'number', 'trim'])

// ------------------------------------------------------------------------------
// Rule Definition
// ------------------------------------------------------------------------------

module.exports = {
meta: {
type: 'problem',
docs: {
description: 'disallow custom modifiers on v-model used on the component',
category: 'essential',
url: 'https://eslint.vuejs.org/rules/no-custom-modifiers-on-v-model.html'
},
fixable: null,
schema: [],
messages: {
notSupportedModifier: "'v-model' directives don't support the modifier '{{name}}'."
}
},
create (context) {
return utils.defineTemplateBodyVisitor(context, {
"VAttribute[directive=true][key.name.name='model']" (node) {
const element = node.parent.parent

if (utils.isCustomComponent(element)) {
for (const modifier of node.key.modifiers) {
if (!VALID_MODIFIERS.has(modifier.name)) {
context.report({
node,
loc: node.loc,
messageId: 'notSupportedModifier',
data: { name: modifier.name }
})
}
}
}
}
})
}
}
54 changes: 54 additions & 0 deletions lib/rules/no-deprecated-v-bind-sync.js
@@ -0,0 +1,54 @@
/**
* @author Przemyslaw Falowski (@przemkow)
* @fileoverview Disallow use of deprecated `.sync` modifier on `v-bind` directive (in Vue.js 3.0.0+)
*/
'use strict'

// ------------------------------------------------------------------------------
// Requirements
// ------------------------------------------------------------------------------

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

// ------------------------------------------------------------------------------
// Rule Definition
// ------------------------------------------------------------------------------

module.exports = {
meta: {
type: 'problem',
docs: {
description: 'disallow use of deprecated `.sync` modifier on `v-bind` directive (in Vue.js 3.0.0+)',
category: undefined,
url: 'https://eslint.vuejs.org/rules/no-deprecated-v-bind-sync.html'
},
fixable: 'code',
schema: [],
messages: {
syncModifierIsDeprecated: "'.sync' modifier on 'v-bind' directive is deprecated. Use 'v-model:propName' instead."
}
},
create (context) {
return utils.defineTemplateBodyVisitor(context, {
"VAttribute[directive=true][key.name.name='bind']" (node) {
if (node.key.modifiers.map(mod => mod.name).includes('sync')) {
context.report({
node,
loc: node.loc,
messageId: 'syncModifierIsDeprecated',
fix: (fixer) => {
const isUsingSpreadSyntax = node.key.argument == null
przemkow marked this conversation as resolved.
Show resolved Hide resolved
const hasMultipleModifiers = node.key.modifiers.length > 1
if (isUsingSpreadSyntax || hasMultipleModifiers) {
return
}

const bindArgument = context.getSourceCode().getText(node.key.argument)
return fixer.replaceText(node.key, `v-model:${bindArgument}`)
}
})
}
}
})
}
}