From 33c98a543417c5a405b18801042c6486e5de6f71 Mon Sep 17 00:00:00 2001 From: yosuke ota Date: Sat, 7 Aug 2021 17:06:31 +0900 Subject: [PATCH] Update `vue/no-unsupported-features` rule to support Vue 3.2 syntaxes. --- docs/rules/no-unsupported-features.md | 7 ++- lib/rules/no-unsupported-features.js | 19 +++--- lib/rules/syntaxes/v-bind-attr-modifier.js | 32 ++++++++++ .../v-bind-prop-modifier-shorthand.js | 2 +- lib/rules/syntaxes/v-is.js | 8 +-- lib/rules/syntaxes/v-memo.js | 26 ++++++++ .../v-bind-attr-modifier.js | 62 +++++++++++++++++++ .../v-bind-prop-modifier-shorthand.js | 30 +++++++-- .../rules/no-unsupported-features/v-memo.js | 58 +++++++++++++++++ 9 files changed, 226 insertions(+), 18 deletions(-) create mode 100644 lib/rules/syntaxes/v-bind-attr-modifier.js create mode 100644 lib/rules/syntaxes/v-memo.js create mode 100644 tests/lib/rules/no-unsupported-features/v-bind-attr-modifier.js create mode 100644 tests/lib/rules/no-unsupported-features/v-memo.js diff --git a/docs/rules/no-unsupported-features.md b/docs/rules/no-unsupported-features.md index 858a8445d..a70fa2b4e 100644 --- a/docs/rules/no-unsupported-features.md +++ b/docs/rules/no-unsupported-features.md @@ -29,6 +29,10 @@ This rule reports unsupported Vue.js syntax on the specified version. - `version` ... The `version` option accepts [the valid version range of `node-semver`](https://github.com/npm/node-semver#range-grammar). Set the version of Vue.js you are using. This option is required. - `ignores` ... You can use this `ignores` option to ignore the given features. The `"ignores"` option accepts an array of the following strings. + - Vue.js 3.2.0+ + - `"v-memo"` ... [v-memo](https://v3.vuejs.org/api/directives.html#v-memo) directive. + - `"v-bind-prop-modifier-shorthand"` ... `v-bind` with `.prop` modifier shorthand. + - `"v-bind-attr-modifier"` ... `.attr` modifier on `v-bind` directive. - Vue.js 3.1.0+ - `"is-attribute-with-vue-prefix"` ... [`is` attribute with `vue:` prefix](https://v3.vuejs.org/api/special-attributes.html#is) - Vue.js 3.0.0+ @@ -42,8 +46,6 @@ The `"ignores"` option accepts an array of the following strings. - `"v-slot"` ... [v-slot](https://v3.vuejs.org/api/directives.html#v-slot) directive. - Vue.js 2.5.0+ - `"slot-scope-attribute"` ... [slot-scope](https://vuejs.org/v2/api/#slot-scope-deprecated) attributes. - - Vue.js `">=2.6.0-beta.1 <=2.6.0-beta.3"` or 2.6 custom build - - `"v-bind-prop-modifier-shorthand"` ... `v-bind` with `.prop` modifier shorthand. ### `{"version": "^2.6.0"}` @@ -97,6 +99,7 @@ The `"ignores"` option accepts an array of the following strings. ## :books: Further Reading +- [API - v-memo](https://v3.vuejs.org/api/directives.html#v-memo) - [API - v-is](https://v3.vuejs.org/api/directives.html#v-is) - [API - v-is (Old)](https://github.com/vuejs/docs-next/blob/008613756c3d781128d96b64a2d27f7598f8f548/src/api/directives.md#v-is) - [Guide - Dynamic Arguments](https://v3.vuejs.org/guide/template-syntax.html#dynamic-arguments) diff --git a/lib/rules/no-unsupported-features.js b/lib/rules/no-unsupported-features.js index 1ddc69342..def5ca3f6 100644 --- a/lib/rules/no-unsupported-features.js +++ b/lib/rules/no-unsupported-features.js @@ -20,8 +20,6 @@ const FEATURES = { // Vue.js 2.6.0+ 'dynamic-directive-arguments': require('./syntaxes/dynamic-directive-arguments'), 'v-slot': require('./syntaxes/v-slot'), - // >=2.6.0-beta.1 <=2.6.0-beta.3 - 'v-bind-prop-modifier-shorthand': require('./syntaxes/v-bind-prop-modifier-shorthand'), // Vue.js 3.0.0+ 'v-model-argument': require('./syntaxes/v-model-argument'), 'v-model-custom-modifiers': require('./syntaxes/v-model-custom-modifiers'), @@ -29,7 +27,11 @@ const FEATURES = { 'script-setup': require('./syntaxes/script-setup'), 'style-css-vars-injection': require('./syntaxes/style-css-vars-injection'), // Vue.js 3.1.0+ - 'is-attribute-with-vue-prefix': require('./syntaxes/is-attribute-with-vue-prefix') + 'is-attribute-with-vue-prefix': require('./syntaxes/is-attribute-with-vue-prefix'), + // Vue.js 3.2.0+ + 'v-memo': require('./syntaxes/v-memo'), + 'v-bind-prop-modifier-shorthand': require('./syntaxes/v-bind-prop-modifier-shorthand'), + 'v-bind-attr-modifier': require('./syntaxes/v-bind-attr-modifier') } const SYNTAX_NAMES = /** @type {(keyof FEATURES)[]} */ (Object.keys(FEATURES)) @@ -93,9 +95,6 @@ module.exports = { forbiddenDynamicDirectiveArguments: 'Dynamic arguments are not supported until Vue.js "2.6.0".', forbiddenVSlot: '`v-slot` are not supported until Vue.js "2.6.0".', - // >=2.6.0-beta.1 <=2.6.0-beta.3 - forbiddenVBindPropModifierShorthand: - '`.prop` shorthand are not supported except Vue.js ">=2.6.0-beta.1 <=2.6.0-beta.3".', // Vue.js 3.0.0+ forbiddenVModelArgument: 'Argument on `v-model` is not supported until Vue.js "3.0.0".', @@ -108,7 +107,13 @@ module.exports = { 'SFC CSS variable injection is not supported until Vue.js "3.0.3".', // Vue.js 3.1.0+ forbiddenIsAttributeWithVuePrefix: - '`is="vue:"` are not supported until Vue.js "3.1.0".' + '`is="vue:"` are not supported until Vue.js "3.1.0".', + // Vue.js 3.2.0+ + forbiddenVMemo: '`v-memo` are not supported until Vue.js "3.2.0".', + forbiddenVBindPropModifierShorthand: + '`.prop` shorthand are not supported until Vue.js "3.2.0".', + forbiddenVBindAttrModifier: + '`.attr` modifiers on `v-bind` are not supported until Vue.js "3.2.0".' } }, /** @param {RuleContext} context */ diff --git a/lib/rules/syntaxes/v-bind-attr-modifier.js b/lib/rules/syntaxes/v-bind-attr-modifier.js new file mode 100644 index 000000000..82a0922aa --- /dev/null +++ b/lib/rules/syntaxes/v-bind-attr-modifier.js @@ -0,0 +1,32 @@ +/** + * @author Yosuke Ota + * See LICENSE file in root directory for full license. + */ +'use strict' + +module.exports = { + supported: '>=3.2.0', + /** @param {RuleContext} context @returns {TemplateListener} */ + createTemplateBodyVisitor(context) { + /** + * Reports `v-bind.attr` node + * @param { VIdentifier } mod node of `v-bind.attr` + * @returns {void} + */ + function report(mod) { + context.report({ + node: mod, + messageId: 'forbiddenVBindAttrModifier' + }) + } + + return { + "VAttribute[directive=true][key.name.name='bind']"(node) { + const attrMod = node.key.modifiers.find((m) => m.name === 'attr') + if (attrMod) { + report(attrMod) + } + } + } + } +} diff --git a/lib/rules/syntaxes/v-bind-prop-modifier-shorthand.js b/lib/rules/syntaxes/v-bind-prop-modifier-shorthand.js index 5a53a9e3a..645ed375d 100644 --- a/lib/rules/syntaxes/v-bind-prop-modifier-shorthand.js +++ b/lib/rules/syntaxes/v-bind-prop-modifier-shorthand.js @@ -5,7 +5,7 @@ 'use strict' module.exports = { - supported: '>=2.6.0-beta.1 <=2.6.0-beta.3', + supported: '>=3.2.0 || >=2.6.0-beta.1 <=2.6.0-beta.3', /** @param {RuleContext} context @returns {TemplateListener} */ createTemplateBodyVisitor(context) { /** diff --git a/lib/rules/syntaxes/v-is.js b/lib/rules/syntaxes/v-is.js index f5eed36d5..7fb1f862e 100644 --- a/lib/rules/syntaxes/v-is.js +++ b/lib/rules/syntaxes/v-is.js @@ -10,18 +10,18 @@ module.exports = { createTemplateBodyVisitor(context) { /** * Reports `v-is` node - * @param {VDirective} vSlotAttr node of `v-is` + * @param {VDirective} vIsAttr node of `v-is` * @returns {void} */ - function reportVSlot(vSlotAttr) { + function reportVIs(vIsAttr) { context.report({ - node: vSlotAttr.key, + node: vIsAttr.key, messageId: 'forbiddenVIs' }) } return { - "VAttribute[directive=true][key.name.name='is']": reportVSlot + "VAttribute[directive=true][key.name.name='is']": reportVIs } } } diff --git a/lib/rules/syntaxes/v-memo.js b/lib/rules/syntaxes/v-memo.js new file mode 100644 index 000000000..958b51cf3 --- /dev/null +++ b/lib/rules/syntaxes/v-memo.js @@ -0,0 +1,26 @@ +/** + * @author Yosuke Ota + * See LICENSE file in root directory for full license. + */ +'use strict' +module.exports = { + supported: '>=3.2.0', + /** @param {RuleContext} context @returns {TemplateListener} */ + createTemplateBodyVisitor(context) { + /** + * Reports `v-is` node + * @param {VDirective} vMemoAttr node of `v-is` + * @returns {void} + */ + function reportVMemo(vMemoAttr) { + context.report({ + node: vMemoAttr.key, + messageId: 'forbiddenVMemo' + }) + } + + return { + "VAttribute[directive=true][key.name.name='memo']": reportVMemo + } + } +} diff --git a/tests/lib/rules/no-unsupported-features/v-bind-attr-modifier.js b/tests/lib/rules/no-unsupported-features/v-bind-attr-modifier.js new file mode 100644 index 000000000..912c98d9e --- /dev/null +++ b/tests/lib/rules/no-unsupported-features/v-bind-attr-modifier.js @@ -0,0 +1,62 @@ +/** + * @author Yosuke Ota + * See LICENSE file in root directory for full license. + */ +'use strict' + +const RuleTester = require('eslint').RuleTester +const rule = require('../../../../lib/rules/no-unsupported-features') +const utils = require('./utils') + +const buildOptions = utils.optionsBuilder('v-bind-attr-modifier', '^3.1.0') +const tester = new RuleTester({ + parser: require.resolve('vue-eslint-parser'), + parserOptions: { + ecmaVersion: 2019 + } +}) + +tester.run('no-unsupported-features/v-bind-attr-modifier', rule, { + valid: [ + { + code: ` + `, + options: buildOptions({ version: '^3.2.0' }) + }, + { + code: ` + `, + options: buildOptions() + }, + { + code: ` + `, + options: buildOptions({ + version: '^2.5.0', + ignores: ['v-bind-attr-modifier'] + }) + } + ], + invalid: [ + { + code: ` + `, + options: buildOptions(), + errors: [ + { + message: + '`.attr` modifiers on `v-bind` are not supported until Vue.js "3.2.0".', + line: 3 + } + ] + } + ] +}) diff --git a/tests/lib/rules/no-unsupported-features/v-bind-prop-modifier-shorthand.js b/tests/lib/rules/no-unsupported-features/v-bind-prop-modifier-shorthand.js index a49b61e93..1cefc8ff1 100644 --- a/tests/lib/rules/no-unsupported-features/v-bind-prop-modifier-shorthand.js +++ b/tests/lib/rules/no-unsupported-features/v-bind-prop-modifier-shorthand.js @@ -28,6 +28,13 @@ tester.run('no-unsupported-features/v-bind-prop-modifier-shorthand', rule, { `, options: buildOptions({ version: '2.6.0-beta.1' }) }, + { + code: ` + `, + options: buildOptions({ version: '3.2.0' }) + }, { code: ` `, errors: [ { - message: - '`.prop` shorthand are not supported except Vue.js ">=2.6.0-beta.1 <=2.6.0-beta.3".', + message: '`.prop` shorthand are not supported until Vue.js "3.2.0".', line: 3 } ] @@ -81,8 +87,24 @@ tester.run('no-unsupported-features/v-bind-prop-modifier-shorthand', rule, { `, errors: [ { - message: - '`.prop` shorthand are not supported except Vue.js ">=2.6.0-beta.1 <=2.6.0-beta.3".', + message: '`.prop` shorthand are not supported until Vue.js "3.2.0".', + line: 3 + } + ] + }, + { + code: ` + `, + options: buildOptions({ version: '3.1.0' }), + output: ` + `, + errors: [ + { + message: '`.prop` shorthand are not supported until Vue.js "3.2.0".', line: 3 } ] diff --git a/tests/lib/rules/no-unsupported-features/v-memo.js b/tests/lib/rules/no-unsupported-features/v-memo.js new file mode 100644 index 000000000..8c3ea1249 --- /dev/null +++ b/tests/lib/rules/no-unsupported-features/v-memo.js @@ -0,0 +1,58 @@ +/** + * @author Yosuke Ota + * See LICENSE file in root directory for full license. + */ +'use strict' + +const RuleTester = require('eslint').RuleTester +const rule = require('../../../../lib/rules/no-unsupported-features') +const utils = require('./utils') + +const buildOptions = utils.optionsBuilder('v-memo', '^3.1.0') +const tester = new RuleTester({ + parser: require.resolve('vue-eslint-parser'), + parserOptions: { + ecmaVersion: 2019 + } +}) + +tester.run('no-unsupported-features/v-memo', rule, { + valid: [ + { + code: ` + `, + options: buildOptions({ version: '^3.2.0' }) + }, + { + code: ` + `, + options: buildOptions() + }, + { + code: ` + `, + options: buildOptions({ version: '^2.5.0', ignores: ['v-memo'] }) + } + ], + invalid: [ + { + code: ` + `, + options: buildOptions(), + errors: [ + { + message: '`v-memo` are not supported until Vue.js "3.2.0".', + line: 3 + } + ] + } + ] +})