Skip to content

Commit 6a49210

Browse files
authored
Update vue/no-unsupported-features rule to support Vue 3.2 syntaxes. (#1599)
1 parent a1a5122 commit 6a49210

9 files changed

+226
-18
lines changed

docs/rules/no-unsupported-features.md

+5-2
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@ This rule reports unsupported Vue.js syntax on the specified version.
2929
- `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.
3030
- `ignores` ... You can use this `ignores` option to ignore the given features.
3131
The `"ignores"` option accepts an array of the following strings.
32+
- Vue.js 3.2.0+
33+
- `"v-memo"` ... [v-memo](https://v3.vuejs.org/api/directives.html#v-memo) directive.
34+
- `"v-bind-prop-modifier-shorthand"` ... `v-bind` with `.prop` modifier shorthand.
35+
- `"v-bind-attr-modifier"` ... `.attr` modifier on `v-bind` directive.
3236
- Vue.js 3.1.0+
3337
- `"is-attribute-with-vue-prefix"` ... [`is` attribute with `vue:` prefix](https://v3.vuejs.org/api/special-attributes.html#is)
3438
- Vue.js 3.0.0+
@@ -42,8 +46,6 @@ The `"ignores"` option accepts an array of the following strings.
4246
- `"v-slot"` ... [v-slot](https://v3.vuejs.org/api/directives.html#v-slot) directive.
4347
- Vue.js 2.5.0+
4448
- `"slot-scope-attribute"` ... [slot-scope](https://vuejs.org/v2/api/#slot-scope-deprecated) attributes.
45-
- Vue.js `">=2.6.0-beta.1 <=2.6.0-beta.3"` or 2.6 custom build
46-
- `"v-bind-prop-modifier-shorthand"` ... `v-bind` with `.prop` modifier shorthand.
4749

4850
### `{"version": "^2.6.0"}`
4951

@@ -97,6 +99,7 @@ The `"ignores"` option accepts an array of the following strings.
9799

98100
## :books: Further Reading
99101

102+
- [API - v-memo](https://v3.vuejs.org/api/directives.html#v-memo)
100103
- [API - v-is](https://v3.vuejs.org/api/directives.html#v-is)
101104
- [API - v-is (Old)](https://github.com/vuejs/docs-next/blob/008613756c3d781128d96b64a2d27f7598f8f548/src/api/directives.md#v-is)
102105
- [Guide - Dynamic Arguments](https://v3.vuejs.org/guide/template-syntax.html#dynamic-arguments)

lib/rules/no-unsupported-features.js

+12-7
Original file line numberDiff line numberDiff line change
@@ -20,16 +20,18 @@ const FEATURES = {
2020
// Vue.js 2.6.0+
2121
'dynamic-directive-arguments': require('./syntaxes/dynamic-directive-arguments'),
2222
'v-slot': require('./syntaxes/v-slot'),
23-
// >=2.6.0-beta.1 <=2.6.0-beta.3
24-
'v-bind-prop-modifier-shorthand': require('./syntaxes/v-bind-prop-modifier-shorthand'),
2523
// Vue.js 3.0.0+
2624
'v-model-argument': require('./syntaxes/v-model-argument'),
2725
'v-model-custom-modifiers': require('./syntaxes/v-model-custom-modifiers'),
2826
'v-is': require('./syntaxes/v-is'),
2927
'script-setup': require('./syntaxes/script-setup'),
3028
'style-css-vars-injection': require('./syntaxes/style-css-vars-injection'),
3129
// Vue.js 3.1.0+
32-
'is-attribute-with-vue-prefix': require('./syntaxes/is-attribute-with-vue-prefix')
30+
'is-attribute-with-vue-prefix': require('./syntaxes/is-attribute-with-vue-prefix'),
31+
// Vue.js 3.2.0+
32+
'v-memo': require('./syntaxes/v-memo'),
33+
'v-bind-prop-modifier-shorthand': require('./syntaxes/v-bind-prop-modifier-shorthand'),
34+
'v-bind-attr-modifier': require('./syntaxes/v-bind-attr-modifier')
3335
}
3436

3537
const SYNTAX_NAMES = /** @type {(keyof FEATURES)[]} */ (Object.keys(FEATURES))
@@ -93,9 +95,6 @@ module.exports = {
9395
forbiddenDynamicDirectiveArguments:
9496
'Dynamic arguments are not supported until Vue.js "2.6.0".',
9597
forbiddenVSlot: '`v-slot` are not supported until Vue.js "2.6.0".',
96-
// >=2.6.0-beta.1 <=2.6.0-beta.3
97-
forbiddenVBindPropModifierShorthand:
98-
'`.prop` shorthand are not supported except Vue.js ">=2.6.0-beta.1 <=2.6.0-beta.3".',
9998
// Vue.js 3.0.0+
10099
forbiddenVModelArgument:
101100
'Argument on `v-model` is not supported until Vue.js "3.0.0".',
@@ -108,7 +107,13 @@ module.exports = {
108107
'SFC CSS variable injection is not supported until Vue.js "3.0.3".',
109108
// Vue.js 3.1.0+
110109
forbiddenIsAttributeWithVuePrefix:
111-
'`is="vue:"` are not supported until Vue.js "3.1.0".'
110+
'`is="vue:"` are not supported until Vue.js "3.1.0".',
111+
// Vue.js 3.2.0+
112+
forbiddenVMemo: '`v-memo` are not supported until Vue.js "3.2.0".',
113+
forbiddenVBindPropModifierShorthand:
114+
'`.prop` shorthand are not supported until Vue.js "3.2.0".',
115+
forbiddenVBindAttrModifier:
116+
'`.attr` modifiers on `v-bind` are not supported until Vue.js "3.2.0".'
112117
}
113118
},
114119
/** @param {RuleContext} context */
+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/**
2+
* @author Yosuke Ota
3+
* See LICENSE file in root directory for full license.
4+
*/
5+
'use strict'
6+
7+
module.exports = {
8+
supported: '>=3.2.0',
9+
/** @param {RuleContext} context @returns {TemplateListener} */
10+
createTemplateBodyVisitor(context) {
11+
/**
12+
* Reports `v-bind.attr` node
13+
* @param { VIdentifier } mod node of `v-bind.attr`
14+
* @returns {void}
15+
*/
16+
function report(mod) {
17+
context.report({
18+
node: mod,
19+
messageId: 'forbiddenVBindAttrModifier'
20+
})
21+
}
22+
23+
return {
24+
"VAttribute[directive=true][key.name.name='bind']"(node) {
25+
const attrMod = node.key.modifiers.find((m) => m.name === 'attr')
26+
if (attrMod) {
27+
report(attrMod)
28+
}
29+
}
30+
}
31+
}
32+
}

lib/rules/syntaxes/v-bind-prop-modifier-shorthand.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
'use strict'
66

77
module.exports = {
8-
supported: '>=2.6.0-beta.1 <=2.6.0-beta.3',
8+
supported: '>=3.2.0 || >=2.6.0-beta.1 <=2.6.0-beta.3',
99
/** @param {RuleContext} context @returns {TemplateListener} */
1010
createTemplateBodyVisitor(context) {
1111
/**

lib/rules/syntaxes/v-is.js

+4-4
Original file line numberDiff line numberDiff line change
@@ -10,18 +10,18 @@ module.exports = {
1010
createTemplateBodyVisitor(context) {
1111
/**
1212
* Reports `v-is` node
13-
* @param {VDirective} vSlotAttr node of `v-is`
13+
* @param {VDirective} vIsAttr node of `v-is`
1414
* @returns {void}
1515
*/
16-
function reportVSlot(vSlotAttr) {
16+
function reportVIs(vIsAttr) {
1717
context.report({
18-
node: vSlotAttr.key,
18+
node: vIsAttr.key,
1919
messageId: 'forbiddenVIs'
2020
})
2121
}
2222

2323
return {
24-
"VAttribute[directive=true][key.name.name='is']": reportVSlot
24+
"VAttribute[directive=true][key.name.name='is']": reportVIs
2525
}
2626
}
2727
}

lib/rules/syntaxes/v-memo.js

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/**
2+
* @author Yosuke Ota
3+
* See LICENSE file in root directory for full license.
4+
*/
5+
'use strict'
6+
module.exports = {
7+
supported: '>=3.2.0',
8+
/** @param {RuleContext} context @returns {TemplateListener} */
9+
createTemplateBodyVisitor(context) {
10+
/**
11+
* Reports `v-is` node
12+
* @param {VDirective} vMemoAttr node of `v-is`
13+
* @returns {void}
14+
*/
15+
function reportVMemo(vMemoAttr) {
16+
context.report({
17+
node: vMemoAttr.key,
18+
messageId: 'forbiddenVMemo'
19+
})
20+
}
21+
22+
return {
23+
"VAttribute[directive=true][key.name.name='memo']": reportVMemo
24+
}
25+
}
26+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/**
2+
* @author Yosuke Ota
3+
* See LICENSE file in root directory for full license.
4+
*/
5+
'use strict'
6+
7+
const RuleTester = require('eslint').RuleTester
8+
const rule = require('../../../../lib/rules/no-unsupported-features')
9+
const utils = require('./utils')
10+
11+
const buildOptions = utils.optionsBuilder('v-bind-attr-modifier', '^3.1.0')
12+
const tester = new RuleTester({
13+
parser: require.resolve('vue-eslint-parser'),
14+
parserOptions: {
15+
ecmaVersion: 2019
16+
}
17+
})
18+
19+
tester.run('no-unsupported-features/v-bind-attr-modifier', rule, {
20+
valid: [
21+
{
22+
code: `
23+
<template>
24+
<div :foo.attr="foo" />
25+
</template>`,
26+
options: buildOptions({ version: '^3.2.0' })
27+
},
28+
{
29+
code: `
30+
<template>
31+
<div v-bind:attr="foo" />
32+
</template>`,
33+
options: buildOptions()
34+
},
35+
{
36+
code: `
37+
<template>
38+
<div :foo.attr="foo" />
39+
</template>`,
40+
options: buildOptions({
41+
version: '^2.5.0',
42+
ignores: ['v-bind-attr-modifier']
43+
})
44+
}
45+
],
46+
invalid: [
47+
{
48+
code: `
49+
<template>
50+
<div :foo.attr="foo" />
51+
</template>`,
52+
options: buildOptions(),
53+
errors: [
54+
{
55+
message:
56+
'`.attr` modifiers on `v-bind` are not supported until Vue.js "3.2.0".',
57+
line: 3
58+
}
59+
]
60+
}
61+
]
62+
})

tests/lib/rules/no-unsupported-features/v-bind-prop-modifier-shorthand.js

+26-4
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,13 @@ tester.run('no-unsupported-features/v-bind-prop-modifier-shorthand', rule, {
2828
</template>`,
2929
options: buildOptions({ version: '2.6.0-beta.1' })
3030
},
31+
{
32+
code: `
33+
<template>
34+
<a .href="'/xxx'" />
35+
</template>`,
36+
options: buildOptions({ version: '3.2.0' })
37+
},
3138
{
3239
code: `
3340
<template>
@@ -63,8 +70,7 @@ tester.run('no-unsupported-features/v-bind-prop-modifier-shorthand', rule, {
6370
</template>`,
6471
errors: [
6572
{
66-
message:
67-
'`.prop` shorthand are not supported except Vue.js ">=2.6.0-beta.1 <=2.6.0-beta.3".',
73+
message: '`.prop` shorthand are not supported until Vue.js "3.2.0".',
6874
line: 3
6975
}
7076
]
@@ -81,8 +87,24 @@ tester.run('no-unsupported-features/v-bind-prop-modifier-shorthand', rule, {
8187
</template>`,
8288
errors: [
8389
{
84-
message:
85-
'`.prop` shorthand are not supported except Vue.js ">=2.6.0-beta.1 <=2.6.0-beta.3".',
90+
message: '`.prop` shorthand are not supported until Vue.js "3.2.0".',
91+
line: 3
92+
}
93+
]
94+
},
95+
{
96+
code: `
97+
<template>
98+
<a .href="'/xxx'" />
99+
</template>`,
100+
options: buildOptions({ version: '3.1.0' }),
101+
output: `
102+
<template>
103+
<a :href.prop="'/xxx'" />
104+
</template>`,
105+
errors: [
106+
{
107+
message: '`.prop` shorthand are not supported until Vue.js "3.2.0".',
86108
line: 3
87109
}
88110
]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/**
2+
* @author Yosuke Ota
3+
* See LICENSE file in root directory for full license.
4+
*/
5+
'use strict'
6+
7+
const RuleTester = require('eslint').RuleTester
8+
const rule = require('../../../../lib/rules/no-unsupported-features')
9+
const utils = require('./utils')
10+
11+
const buildOptions = utils.optionsBuilder('v-memo', '^3.1.0')
12+
const tester = new RuleTester({
13+
parser: require.resolve('vue-eslint-parser'),
14+
parserOptions: {
15+
ecmaVersion: 2019
16+
}
17+
})
18+
19+
tester.run('no-unsupported-features/v-memo', rule, {
20+
valid: [
21+
{
22+
code: `
23+
<template>
24+
<div v-memo="foo" />
25+
</template>`,
26+
options: buildOptions({ version: '^3.2.0' })
27+
},
28+
{
29+
code: `
30+
<template>
31+
<div :memo="foo" />
32+
</template>`,
33+
options: buildOptions()
34+
},
35+
{
36+
code: `
37+
<template>
38+
<div v-memo="foo" />
39+
</template>`,
40+
options: buildOptions({ version: '^2.5.0', ignores: ['v-memo'] })
41+
}
42+
],
43+
invalid: [
44+
{
45+
code: `
46+
<template>
47+
<div v-memo="foo" />
48+
</template>`,
49+
options: buildOptions(),
50+
errors: [
51+
{
52+
message: '`v-memo` are not supported until Vue.js "3.2.0".',
53+
line: 3
54+
}
55+
]
56+
}
57+
]
58+
})

0 commit comments

Comments
 (0)