Skip to content

Commit

Permalink
Change the rules to support "v-is". (#1254)
Browse files Browse the repository at this point in the history
* 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.
  • Loading branch information
ota-meshi committed Jul 19, 2020
1 parent f626498 commit e1c87a1
Show file tree
Hide file tree
Showing 12 changed files with 138 additions and 6 deletions.
2 changes: 1 addition & 1 deletion docs/rules/attributes-order.md
Expand Up @@ -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`
Expand Down
2 changes: 2 additions & 0 deletions docs/rules/no-unsupported-features.md
Expand Up @@ -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.
Expand Down Expand Up @@ -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)
Expand Down
2 changes: 2 additions & 0 deletions lib/rules/attributes-order.js
Expand Up @@ -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
}
Expand Down
2 changes: 1 addition & 1 deletion lib/rules/no-unregistered-components.js
Expand Up @@ -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 (
Expand Down
6 changes: 4 additions & 2 deletions lib/rules/no-unsupported-features.js
Expand Up @@ -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()
Expand Down Expand Up @@ -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 */
Expand Down
2 changes: 1 addition & 1 deletion lib/rules/no-unused-components.js
Expand Up @@ -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 (
Expand Down
26 changes: 26 additions & 0 deletions 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
}
}
}
3 changes: 2 additions & 1 deletion lib/utils/index.js
Expand Up @@ -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')
)
},

Expand Down
11 changes: 11 additions & 0 deletions tests/lib/rules/attributes-order.js
Expand Up @@ -932,6 +932,17 @@ tester.run('attributes-order', rule, {
message: 'Attribute "v-foo.a" should go before "v-foo.b".'
}
]
},

{
filename: 'test.vue',
code: '<template><div v-cloak v-is="foo"></div></template>',
output: '<template><div v-is="foo" v-cloak></div></template>',
errors: [
{
message: 'Attribute "v-is" should go before "v-cloak".'
}
]
}
]
})
15 changes: 15 additions & 0 deletions tests/lib/rules/no-unregistered-components.js
Expand Up @@ -378,6 +378,21 @@ tester.run('no-unregistered-components', rule, {
<Component is />
</template>
`
},
{
filename: 'test.vue',
code: `
<template>
<div v-is="'CustomComponent'" />
</template>
<script>
export default {
components: {
CustomComponent
}
}
</script>
`
}
],
invalid: [
Expand Down
58 changes: 58 additions & 0 deletions 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: `
<template>
<div v-is="foo" />
</template>`,
options: buildOptions({ version: '^3.0.0' })
},
{
code: `
<template>
<div :is="foo" />
</template>`,
options: buildOptions()
},
{
code: `
<template>
<div v-is="foo" />
</template>`,
options: buildOptions({ version: '^2.5.0', ignores: ['v-is'] })
}
],
invalid: [
{
code: `
<template>
<div v-is="foo" />
</template>`,
options: buildOptions(),
errors: [
{
message: '`v-is` are not supported until Vue.js "3.0.0".',
line: 3
}
]
}
]
})
15 changes: 15 additions & 0 deletions tests/lib/rules/no-unused-components.js
Expand Up @@ -471,6 +471,21 @@ tester.run('no-unused-components', rule, {
}
}
</script>`
},
{
filename: 'test.vue',
code: `
<template>
<div v-is="'CustomComponent'" />
</template>
<script>
export default {
components: {
CustomComponent
}
}
</script>
`
}
],
invalid: [
Expand Down

0 comments on commit e1c87a1

Please sign in to comment.