From 607a3ecc57e4b4e2fe6e85ff1b1dfa5c27eced08 Mon Sep 17 00:00:00 2001 From: yosuke ota Date: Tue, 8 Jun 2021 12:07:46 +0900 Subject: [PATCH 1/3] Add support `is="vue:"` (Vue 3.1) --- lib/rules/no-deprecated-html-element-is.js | 32 ++++++++--- lib/rules/no-unregistered-components.js | 9 ++- lib/rules/no-unused-components.js | 5 +- .../rules/no-deprecated-html-element-is.js | 6 ++ tests/lib/rules/no-unregistered-components.js | 55 +++++++++++++++++++ tests/lib/rules/no-unused-components.js | 15 +++++ 6 files changed, 111 insertions(+), 11 deletions(-) diff --git a/lib/rules/no-deprecated-html-element-is.js b/lib/rules/no-deprecated-html-element-is.js index e07f493a5..8382bb5c0 100644 --- a/lib/rules/no-deprecated-html-element-is.js +++ b/lib/rules/no-deprecated-html-element-is.js @@ -31,16 +31,34 @@ module.exports = { }, /** @param {RuleContext} context */ create(context) { + /** @param {VElement} node */ + function isValidElement(node) { + return ( + !utils.isHtmlWellKnownElementName(node.rawName) && + !utils.isSvgWellKnownElementName(node.rawName) + ) + } return utils.defineTemplateBodyVisitor(context, { - /** @param {VDirective | VAttribute} node */ - "VAttribute[directive=true][key.name.name='bind'][key.argument.name='is'], VAttribute[directive=false][key.name='is']"( + /** @param {VDirective} node */ + "VAttribute[directive=true][key.name.name='bind'][key.argument.name='is']"( node ) { - const element = node.parent.parent - if ( - !utils.isHtmlWellKnownElementName(element.rawName) && - !utils.isSvgWellKnownElementName(element.rawName) - ) { + if (isValidElement(node.parent.parent)) { + return + } + context.report({ + node, + loc: node.loc, + messageId: 'unexpected' + }) + }, + /** @param {VAttribute} node */ + "VAttribute[directive=false][key.name='is']"(node) { + if (isValidElement(node.parent.parent)) { + return + } + if (node.value && node.value.value.startsWith('vue:')) { + // Usage on native elements 3.1+ return } context.report({ diff --git a/lib/rules/no-unregistered-components.js b/lib/rules/no-unregistered-components.js index 6e6cb2ada..07d9e800a 100644 --- a/lib/rules/no-unregistered-components.js +++ b/lib/rules/no-unregistered-components.js @@ -119,11 +119,14 @@ module.exports = { /** @param {VAttribute} node */ "VAttribute[directive=false][key.name='is']"(node) { if ( - !node.value || // `` - utils.isHtmlWellKnownElementName(node.value.value) + !node.value // `` ) return - usedComponentNodes.push({ node, name: node.value.value }) + const value = node.value.value.startsWith('vue:') // Usage on native elements 3.1+ + ? node.value.value.slice(4) + : node.value.value + if (utils.isHtmlWellKnownElementName(value)) return + usedComponentNodes.push({ node, name: value }) }, /** @param {VElement} node */ "VElement[name='template'][parent.type='VDocumentFragment']:exit"() { diff --git a/lib/rules/no-unused-components.js b/lib/rules/no-unused-components.js index e067e3eb0..8497dd961 100644 --- a/lib/rules/no-unused-components.js +++ b/lib/rules/no-unused-components.js @@ -88,7 +88,10 @@ module.exports = { if (!node.value) { return } - usedComponents.add(node.value.value) + const value = node.value.value.startsWith('vue:') // Usage on native elements 3.1+ + ? node.value.value.slice(4) + : node.value.value + usedComponents.add(value) }, /** @param {VElement} node */ "VElement[name='template']"(node) { diff --git a/tests/lib/rules/no-deprecated-html-element-is.js b/tests/lib/rules/no-deprecated-html-element-is.js index 961644946..9b7cfdb23 100644 --- a/tests/lib/rules/no-deprecated-html-element-is.js +++ b/tests/lib/rules/no-deprecated-html-element-is.js @@ -37,6 +37,12 @@ ruleTester.run('no-deprecated-html-element-is', rule, { { filename: 'test.vue', code: '' + }, + + // is="vue:xxx" + { + filename: 'test.vue', + code: '' } ], diff --git a/tests/lib/rules/no-unregistered-components.js b/tests/lib/rules/no-unregistered-components.js index 27739b78a..6a51bf494 100644 --- a/tests/lib/rules/no-unregistered-components.js +++ b/tests/lib/rules/no-unregistered-components.js @@ -446,6 +446,19 @@ tester.run('no-unregistered-components', rule, { ` }, + { + filename: 'test.vue', + code: ` + + + ` + }, { filename: 'test.vue', code: ` @@ -706,6 +719,48 @@ tester.run('no-unregistered-components', rule, { line: 3 } ] + }, + { + filename: 'test.vue', + code: ` + + + `, + errors: [ + { + message: 'The "foo" component has been used but not registered.', + line: 3 + } + ] + }, + { + filename: 'test.vue', + code: ` + + + `, + errors: [ + { + message: 'The "foo" component has been used but not registered.', + line: 3 + } + ] } ] }) diff --git a/tests/lib/rules/no-unused-components.js b/tests/lib/rules/no-unused-components.js index 02cb1a3cf..f63b75f13 100644 --- a/tests/lib/rules/no-unused-components.js +++ b/tests/lib/rules/no-unused-components.js @@ -486,6 +486,21 @@ tester.run('no-unused-components', rule, { } ` + }, + { + filename: 'test.vue', + code: ` + + + ` } ], invalid: [ From 5d153f244e3fd24d5d43ee60788bdd185ab24a98 Mon Sep 17 00:00:00 2001 From: yosuke ota Date: Tue, 8 Jun 2021 12:28:29 +0900 Subject: [PATCH 2/3] update --- docs/rules/no-unsupported-features.md | 2 + lib/rules/no-unsupported-features.js | 8 ++- .../is-attribute-with-prefixed-vue.js | 25 ++++++++ .../is-attribute-with-prefixed-vue.js | 64 +++++++++++++++++++ 4 files changed, 97 insertions(+), 2 deletions(-) create mode 100644 lib/rules/syntaxes/is-attribute-with-prefixed-vue.js create mode 100644 tests/lib/rules/no-unsupported-features/is-attribute-with-prefixed-vue.js diff --git a/docs/rules/no-unsupported-features.md b/docs/rules/no-unsupported-features.md index dfb73e851..dd3939de1 100644 --- a/docs/rules/no-unsupported-features.md +++ b/docs/rules/no-unsupported-features.md @@ -29,6 +29,8 @@ 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.1.0+ + - `"is-attribute-with-prefixed-vue"` ... [`is` attribute with the prefixed `vue:`](https://v3.vuejs.org/api/special-attributes.html#is) - 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] diff --git a/lib/rules/no-unsupported-features.js b/lib/rules/no-unsupported-features.js index 9eb00be0e..ecd5ba39e 100644 --- a/lib/rules/no-unsupported-features.js +++ b/lib/rules/no-unsupported-features.js @@ -25,7 +25,9 @@ const FEATURES = { // Vue.js 3.0.0+ 'v-model-argument': require('./syntaxes/v-model-argument'), 'v-model-custom-modifiers': require('./syntaxes/v-model-custom-modifiers'), - 'v-is': require('./syntaxes/v-is') + 'v-is': require('./syntaxes/v-is'), + // Vue.js 3.1.0+ + 'is-attribute-with-prefixed-vue': require('./syntaxes/is-attribute-with-prefixed-vue') } const SYNTAX_NAMES = /** @type {(keyof FEATURES)[]} */ (Object.keys(FEATURES)) @@ -97,7 +99,9 @@ module.exports = { '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".', - forbiddenVIs: '`v-is` are not supported until Vue.js "3.0.0".' + forbiddenVIs: '`v-is` are not supported until Vue.js "3.0.0".', + forbiddenIsAttributeWithPrefixedVue: + '`is="vue:"` are not supported until Vue.js "3.1.0".' } }, /** @param {RuleContext} context */ diff --git a/lib/rules/syntaxes/is-attribute-with-prefixed-vue.js b/lib/rules/syntaxes/is-attribute-with-prefixed-vue.js new file mode 100644 index 000000000..b30ca70aa --- /dev/null +++ b/lib/rules/syntaxes/is-attribute-with-prefixed-vue.js @@ -0,0 +1,25 @@ +/** + * @author Yosuke Ota + * See LICENSE file in root directory for full license. + */ +'use strict' +module.exports = { + supported: '>=3.1.0', + /** @param {RuleContext} context @returns {TemplateListener} */ + createTemplateBodyVisitor(context) { + return { + /** @param {VAttribute} node */ + "VAttribute[directive=false][key.name='is']"(node) { + if (!node.value) { + return + } + if (node.value.value.startsWith('vue:')) { + context.report({ + node: node.value, + messageId: 'forbiddenIsAttributeWithPrefixedVue' + }) + } + } + } + } +} diff --git a/tests/lib/rules/no-unsupported-features/is-attribute-with-prefixed-vue.js b/tests/lib/rules/no-unsupported-features/is-attribute-with-prefixed-vue.js new file mode 100644 index 000000000..aff4b4f19 --- /dev/null +++ b/tests/lib/rules/no-unsupported-features/is-attribute-with-prefixed-vue.js @@ -0,0 +1,64 @@ +/** + * @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( + 'is-attribute-with-prefixed-vue', + '^3.1.0' +) +const tester = new RuleTester({ + parser: require.resolve('vue-eslint-parser'), + parserOptions: { + ecmaVersion: 2019 + } +}) + +tester.run('no-unsupported-features/is-attribute-with-prefixed-vue', rule, { + valid: [ + { + code: ` + `, + options: buildOptions() + }, + { + code: ` + `, + options: buildOptions({ version: '^2.5.0' }) + }, + { + code: ` + `, + options: buildOptions({ + version: '^2.5.0', + ignores: ['is-attribute-with-prefixed-vue'] + }) + } + ], + invalid: [ + { + code: ` + `, + options: buildOptions({ version: '^3.0.0' }), + errors: [ + { + message: '`is="vue:"` are not supported until Vue.js "3.1.0".', + line: 3 + } + ] + } + ] +}) From 01ca087a834620940d2f73df4b86e08ba12a2d26 Mon Sep 17 00:00:00 2001 From: yosuke ota Date: Tue, 8 Jun 2021 12:55:51 +0900 Subject: [PATCH 3/3] update --- docs/rules/no-unsupported-features.md | 2 +- lib/rules/no-unsupported-features.js | 4 ++-- ...with-prefixed-vue.js => is-attribute-with-vue-prefix.js} | 2 +- lib/rules/syntaxes/v-is.js | 1 + ...with-prefixed-vue.js => is-attribute-with-vue-prefix.js} | 6 +++--- 5 files changed, 8 insertions(+), 7 deletions(-) rename lib/rules/syntaxes/{is-attribute-with-prefixed-vue.js => is-attribute-with-vue-prefix.js} (90%) rename tests/lib/rules/no-unsupported-features/{is-attribute-with-prefixed-vue.js => is-attribute-with-vue-prefix.js} (87%) diff --git a/docs/rules/no-unsupported-features.md b/docs/rules/no-unsupported-features.md index dd3939de1..8665e9b83 100644 --- a/docs/rules/no-unsupported-features.md +++ b/docs/rules/no-unsupported-features.md @@ -30,7 +30,7 @@ This rule reports unsupported Vue.js syntax on the specified version. - `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.1.0+ - - `"is-attribute-with-prefixed-vue"` ... [`is` attribute with the prefixed `vue:`](https://v3.vuejs.org/api/special-attributes.html#is) + - `"is-attribute-with-vue-prefix"` ... [`is` attribute with `vue:` prefix](https://v3.vuejs.org/api/special-attributes.html#is) - 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] diff --git a/lib/rules/no-unsupported-features.js b/lib/rules/no-unsupported-features.js index ecd5ba39e..2d4caf3e2 100644 --- a/lib/rules/no-unsupported-features.js +++ b/lib/rules/no-unsupported-features.js @@ -27,7 +27,7 @@ const FEATURES = { 'v-model-custom-modifiers': require('./syntaxes/v-model-custom-modifiers'), 'v-is': require('./syntaxes/v-is'), // Vue.js 3.1.0+ - 'is-attribute-with-prefixed-vue': require('./syntaxes/is-attribute-with-prefixed-vue') + 'is-attribute-with-vue-prefix': require('./syntaxes/is-attribute-with-vue-prefix') } const SYNTAX_NAMES = /** @type {(keyof FEATURES)[]} */ (Object.keys(FEATURES)) @@ -100,7 +100,7 @@ module.exports = { forbiddenVModelCustomModifiers: '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".', - forbiddenIsAttributeWithPrefixedVue: + forbiddenIsAttributeWithVuePrefix: '`is="vue:"` are not supported until Vue.js "3.1.0".' } }, diff --git a/lib/rules/syntaxes/is-attribute-with-prefixed-vue.js b/lib/rules/syntaxes/is-attribute-with-vue-prefix.js similarity index 90% rename from lib/rules/syntaxes/is-attribute-with-prefixed-vue.js rename to lib/rules/syntaxes/is-attribute-with-vue-prefix.js index b30ca70aa..9fd2afdd0 100644 --- a/lib/rules/syntaxes/is-attribute-with-prefixed-vue.js +++ b/lib/rules/syntaxes/is-attribute-with-vue-prefix.js @@ -16,7 +16,7 @@ module.exports = { if (node.value.value.startsWith('vue:')) { context.report({ node: node.value, - messageId: 'forbiddenIsAttributeWithPrefixedVue' + messageId: 'forbiddenIsAttributeWithVuePrefix' }) } } diff --git a/lib/rules/syntaxes/v-is.js b/lib/rules/syntaxes/v-is.js index 9aeeb8d7e..f5eed36d5 100644 --- a/lib/rules/syntaxes/v-is.js +++ b/lib/rules/syntaxes/v-is.js @@ -4,6 +4,7 @@ */ 'use strict' module.exports = { + deprecated: '3.1.0', supported: '>=3.0.0', /** @param {RuleContext} context @returns {TemplateListener} */ createTemplateBodyVisitor(context) { diff --git a/tests/lib/rules/no-unsupported-features/is-attribute-with-prefixed-vue.js b/tests/lib/rules/no-unsupported-features/is-attribute-with-vue-prefix.js similarity index 87% rename from tests/lib/rules/no-unsupported-features/is-attribute-with-prefixed-vue.js rename to tests/lib/rules/no-unsupported-features/is-attribute-with-vue-prefix.js index aff4b4f19..adcc64835 100644 --- a/tests/lib/rules/no-unsupported-features/is-attribute-with-prefixed-vue.js +++ b/tests/lib/rules/no-unsupported-features/is-attribute-with-vue-prefix.js @@ -9,7 +9,7 @@ const rule = require('../../../../lib/rules/no-unsupported-features') const utils = require('./utils') const buildOptions = utils.optionsBuilder( - 'is-attribute-with-prefixed-vue', + 'is-attribute-with-vue-prefix', '^3.1.0' ) const tester = new RuleTester({ @@ -19,7 +19,7 @@ const tester = new RuleTester({ } }) -tester.run('no-unsupported-features/is-attribute-with-prefixed-vue', rule, { +tester.run('no-unsupported-features/is-attribute-with-vue-prefix', rule, { valid: [ { code: ` @@ -42,7 +42,7 @@ tester.run('no-unsupported-features/is-attribute-with-prefixed-vue', rule, { `, options: buildOptions({ version: '^2.5.0', - ignores: ['is-attribute-with-prefixed-vue'] + ignores: ['is-attribute-with-vue-prefix'] }) } ],