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: [