Skip to content

Commit

Permalink
Vue3 v-model API changes (#1039)
Browse files Browse the repository at this point in the history
* feat(RFC0005): allow v-model argument when v-model is used on custom component

* feat(RFC0005): add no-v-model-argument rule which disallows v-model arguments.

Vue 2 compatibility rule

* feat(RFC0005): add no-deprecated-v-bind-sync rule

* feat(RFC0005): provide autofix fix for deprecated v-bind.prop.sync modifier

* feat(RFC0011): add support for custom modifiers in valid-v-model used on Vue component

* feat(RFC0011): add vue/no-custom-modifiers-on-v-model rule which checks if v-model has not allowed modifiers (Vue 2 backward compatibility)
  • Loading branch information
przemkow committed Mar 14, 2020
1 parent d8e728a commit ef95155
Show file tree
Hide file tree
Showing 16 changed files with 638 additions and 14 deletions.
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-multiple-template-root](./no-multiple-template-root.md) | disallow adding multiple root nodes to the template | |
Expand All @@ -51,6 +52,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 @@ -161,6 +163,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-multiple-template-root': 'error',
Expand All @@ -19,6 +20,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 @@ -64,6 +66,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'),
'padding-line-between-blocks': require('./rules/padding-line-between-blocks'),
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
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}`)
}
})
}
}
})
}
}

0 comments on commit ef95155

Please sign in to comment.