From e1c87a1f9c22acd0e3a8df04c3d4e821c0e585fd Mon Sep 17 00:00:00 2001 From: Yosuke Ota Date: Sun, 19 Jul 2020 17:48:03 +0900 Subject: [PATCH] Change the rules to support "v-is". (#1254) * Change the rules to support "v-is". - Change the `vue/attributes-order` rule to handle `v-is` as `DEFINITION` category. - Change the `vue/no-unregistered-components` rule to handle `v-is` like `:is`. - Change the `vue/no-unused-components` rule to handle `v-is` like `:is`. * Add `"v-is"` to the syntax checked by the `vue/no-unsupported-features` rule. --- docs/rules/attributes-order.md | 2 +- docs/rules/no-unsupported-features.md | 2 + lib/rules/attributes-order.js | 2 + lib/rules/no-unregistered-components.js | 2 +- lib/rules/no-unsupported-features.js | 6 +- lib/rules/no-unused-components.js | 2 +- lib/rules/syntaxes/v-is.js | 26 +++++++++ lib/utils/index.js | 3 +- tests/lib/rules/attributes-order.js | 11 ++++ tests/lib/rules/no-unregistered-components.js | 15 +++++ .../lib/rules/no-unsupported-features/v-is.js | 58 +++++++++++++++++++ tests/lib/rules/no-unused-components.js | 15 +++++ 12 files changed, 138 insertions(+), 6 deletions(-) create mode 100644 lib/rules/syntaxes/v-is.js create mode 100644 tests/lib/rules/no-unsupported-features/v-is.js diff --git a/docs/rules/attributes-order.md b/docs/rules/attributes-order.md index 2da566bf4..c6391ffa6 100644 --- a/docs/rules/attributes-order.md +++ b/docs/rules/attributes-order.md @@ -15,7 +15,7 @@ description: enforce order of attributes This rule aims to enforce ordering of component attributes. The default order is specified in the [Vue styleguide](https://v3.vuejs.org/style-guide/#element-attribute-order-recommended) and is: - `DEFINITION` - e.g. 'is' + e.g. 'is', 'v-is' - `LIST_RENDERING` e.g. 'v-for item in items' - `CONDITIONALS` diff --git a/docs/rules/no-unsupported-features.md b/docs/rules/no-unsupported-features.md index 4487a4f7e..b706ef884 100644 --- a/docs/rules/no-unsupported-features.md +++ b/docs/rules/no-unsupported-features.md @@ -30,6 +30,7 @@ The `"ignores"` option accepts an array of the following strings. - Vue.js 3.0.0+ - `"v-model-argument"` ... [argument on `v-model`][Vue RFCs - 0005-replace-v-bind-sync-with-v-model-argument] - `"v-model-custom-modifiers"` ... [custom modifiers on `v-model`][Vue RFCs - 0011-v-model-api-change] + - `"v-id"` ... [v-is](https://v3.vuejs.org/api/directives.html#v-is) directive. - Vue.js 2.6.0+ - `"dynamic-directive-arguments"` ... [dynamic directive arguments](https://v3.vuejs.org/guide/template-syntax.html#dynamic-arguments). - `"v-slot"` ... [v-slot](https://v3.vuejs.org/api/directives.html#v-slot) directive. @@ -90,6 +91,7 @@ The `"ignores"` option accepts an array of the following strings. ## :books: Further Reading +- [API - v-is](https://v3.vuejs.org/api/directives.html#v-is) - [Guide - Dynamic Arguments](https://v3.vuejs.org/guide/template-syntax.html#dynamic-arguments) - [API - v-slot](https://v3.vuejs.org/api/directives.html#v-slot) - [API (for v2) - slot-scope](https://vuejs.org/v2/api/#slot-scope-deprecated) diff --git a/lib/rules/attributes-order.js b/lib/rules/attributes-order.js index 794628a5c..34a9d7d32 100644 --- a/lib/rules/attributes-order.js +++ b/lib/rules/attributes-order.js @@ -84,6 +84,8 @@ function getAttributeType(attribute, sourceCode) { return ATTRS.CONTENT } else if (name === 'slot') { return ATTRS.UNIQUE + } else if (name === 'is') { + return ATTRS.DEFINITION } else { return ATTRS.OTHER_DIRECTIVES } diff --git a/lib/rules/no-unregistered-components.js b/lib/rules/no-unregistered-components.js index 205580221..56f03e40c 100644 --- a/lib/rules/no-unregistered-components.js +++ b/lib/rules/no-unregistered-components.js @@ -95,7 +95,7 @@ module.exports = { usedComponentNodes.push({ node, name: node.rawName }) }, /** @param {VDirective} node */ - "VAttribute[directive=true][key.name.name='bind'][key.argument.name='is']"( + "VAttribute[directive=true][key.name.name='bind'][key.argument.name='is'], VAttribute[directive=true][key.name.name='is']"( node ) { if ( diff --git a/lib/rules/no-unsupported-features.js b/lib/rules/no-unsupported-features.js index 468e718ec..6f7a87ab9 100644 --- a/lib/rules/no-unsupported-features.js +++ b/lib/rules/no-unsupported-features.js @@ -24,7 +24,8 @@ const FEATURES = { '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') + 'v-model-custom-modifiers': require('./syntaxes/v-model-custom-modifiers'), + 'v-is': require('./syntaxes/v-is') } const cache = new Map() @@ -93,7 +94,8 @@ module.exports = { forbiddenVModelArgument: 'Argument on `v-model` is not supported until Vue.js "3.0.0".', forbiddenVModelCustomModifiers: - 'Custom modifiers on `v-model` are not supported until Vue.js "3.0.0".' + 'Custom modifiers on `v-model` are not supported until Vue.js "3.0.0".', + forbiddenVIs: '`v-is` are not supported until Vue.js "3.0.0".' } }, /** @param {RuleContext} context */ diff --git a/lib/rules/no-unused-components.js b/lib/rules/no-unused-components.js index b76af7198..e067e3eb0 100644 --- a/lib/rules/no-unused-components.js +++ b/lib/rules/no-unused-components.js @@ -67,7 +67,7 @@ module.exports = { usedComponents.add(node.rawName) }, /** @param {VDirective} node */ - "VAttribute[directive=true][key.name.name='bind'][key.argument.name='is']"( + "VAttribute[directive=true][key.name.name='bind'][key.argument.name='is'], VAttribute[directive=true][key.name.name='is']"( node ) { if ( diff --git a/lib/rules/syntaxes/v-is.js b/lib/rules/syntaxes/v-is.js new file mode 100644 index 000000000..a974b820f --- /dev/null +++ b/lib/rules/syntaxes/v-is.js @@ -0,0 +1,26 @@ +/** + * @author Yosuke Ota + * See LICENSE file in root directory for full license. + */ +'use strict' +module.exports = { + supported: '3.0.0', + /** @param {RuleContext} context @returns {TemplateListener} */ + createTemplateBodyVisitor(context) { + /** + * Reports `v-is` node + * @param {VDirective} vSlotAttr node of `v-is` + * @returns {void} + */ + function reportVSlot(vSlotAttr) { + context.report({ + node: vSlotAttr.key, + messageId: 'forbiddenVIs' + }) + } + + return { + "VAttribute[directive=true][key.name.name='is']": reportVSlot + } + } +} diff --git a/lib/utils/index.js b/lib/utils/index.js index 130530526..06775eafa 100644 --- a/lib/utils/index.js +++ b/lib/utils/index.js @@ -578,7 +578,8 @@ module.exports = { (this.isHtmlElementNode(node) && !this.isHtmlWellKnownElementName(node.rawName)) || this.hasAttribute(node, 'is') || - this.hasDirective(node, 'bind', 'is') + this.hasDirective(node, 'bind', 'is') || + this.hasDirective(node, 'is') ) }, diff --git a/tests/lib/rules/attributes-order.js b/tests/lib/rules/attributes-order.js index a0b930241..87e72b56e 100644 --- a/tests/lib/rules/attributes-order.js +++ b/tests/lib/rules/attributes-order.js @@ -932,6 +932,17 @@ tester.run('attributes-order', rule, { message: 'Attribute "v-foo.a" should go before "v-foo.b".' } ] + }, + + { + filename: 'test.vue', + code: '', + output: '', + errors: [ + { + message: 'Attribute "v-is" should go before "v-cloak".' + } + ] } ] }) diff --git a/tests/lib/rules/no-unregistered-components.js b/tests/lib/rules/no-unregistered-components.js index e7ae51983..da9e37ee2 100644 --- a/tests/lib/rules/no-unregistered-components.js +++ b/tests/lib/rules/no-unregistered-components.js @@ -378,6 +378,21 @@ tester.run('no-unregistered-components', rule, { ` + }, + { + filename: 'test.vue', + code: ` + + + ` } ], invalid: [ diff --git a/tests/lib/rules/no-unsupported-features/v-is.js b/tests/lib/rules/no-unsupported-features/v-is.js new file mode 100644 index 000000000..3212a7578 --- /dev/null +++ b/tests/lib/rules/no-unsupported-features/v-is.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-is', '^2.6.0') +const tester = new RuleTester({ + parser: require.resolve('vue-eslint-parser'), + parserOptions: { + ecmaVersion: 2019 + } +}) + +tester.run('no-unsupported-features/v-is', rule, { + valid: [ + { + code: ` + `, + options: buildOptions({ version: '^3.0.0' }) + }, + { + code: ` + `, + options: buildOptions() + }, + { + code: ` + `, + options: buildOptions({ version: '^2.5.0', ignores: ['v-is'] }) + } + ], + invalid: [ + { + code: ` + `, + options: buildOptions(), + errors: [ + { + message: '`v-is` are not supported until Vue.js "3.0.0".', + line: 3 + } + ] + } + ] +}) diff --git a/tests/lib/rules/no-unused-components.js b/tests/lib/rules/no-unused-components.js index 181fbd6a5..02cb1a3cf 100644 --- a/tests/lib/rules/no-unused-components.js +++ b/tests/lib/rules/no-unused-components.js @@ -471,6 +471,21 @@ tester.run('no-unused-components', rule, { } } ` + }, + { + filename: 'test.vue', + code: ` + + + ` } ], invalid: [