Skip to content

Commit 8035aa3

Browse files
authored
Upgrade vue-eslint-parser and add support for v-bind same-name shorthand (#2357)
1 parent ea29fd4 commit 8035aa3

13 files changed

+195
-46
lines changed

Diff for: docs/rules/no-unsupported-features.md

+2
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ 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.4.0+
33+
- `"v-bind-same-name-shorthand"` ... `v-bind` same-name shorthand.
3234
- Vue.js 3.3.0+
3335
- `"define-slots"` ... `defineSlots()` macro.
3436
- `"define-options"` ... `defineOptions()` macro.

Diff for: lib/rules/html-quotes.js

+5
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,11 @@ module.exports = {
5151
return
5252
}
5353

54+
if (utils.isVBindSameNameShorthand(node)) {
55+
// v-bind same-name shorthand (Vue 3.4+)
56+
return
57+
}
58+
5459
const text = sourceCode.getText(node.value)
5560
const firstChar = text[0]
5661

Diff for: lib/rules/no-unsupported-features.js

+7-2
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,9 @@ const FEATURES = {
3535
'v-bind-attr-modifier': require('./syntaxes/v-bind-attr-modifier'),
3636
// Vue.js 3.3.0+
3737
'define-options': require('./syntaxes/define-options'),
38-
'define-slots': require('./syntaxes/define-slots')
38+
'define-slots': require('./syntaxes/define-slots'),
39+
// Vue.js 3.4.0+
40+
'v-bind-same-name-shorthand': require('./syntaxes/v-bind-same-name-shorthand')
3941
}
4042

4143
const SYNTAX_NAMES = /** @type {(keyof FEATURES)[]} */ (Object.keys(FEATURES))
@@ -124,7 +126,10 @@ module.exports = {
124126
forbiddenDefineOptions:
125127
'`defineOptions()` macros are not supported until Vue.js "3.3.0".',
126128
forbiddenDefineSlots:
127-
'`defineSlots()` macros are not supported until Vue.js "3.3.0".'
129+
'`defineSlots()` macros are not supported until Vue.js "3.3.0".',
130+
// Vue.js 3.4.0+
131+
forbiddenVBindSameNameShorthand:
132+
'`v-bind` same-name shorthand is not supported until Vue.js "3.4.0".'
128133
}
129134
},
130135
/** @param {RuleContext} context */

Diff for: lib/rules/syntaxes/v-bind-same-name-shorthand.js

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/**
2+
* @author Yosuke Ota
3+
* See LICENSE file in root directory for full license.
4+
*/
5+
'use strict'
6+
7+
const utils = require('../../utils')
8+
9+
module.exports = {
10+
supported: '>=3.4.0',
11+
/** @param {RuleContext} context @returns {TemplateListener} */
12+
createTemplateBodyVisitor(context) {
13+
/**
14+
* Verify the directive node
15+
* @param {VDirective} node The directive node to check
16+
* @returns {void}
17+
*/
18+
function checkDirective(node) {
19+
if (utils.isVBindSameNameShorthand(node)) {
20+
context.report({
21+
node,
22+
messageId: 'forbiddenVBindSameNameShorthand',
23+
// fix to use `:x="x"` (downgrade)
24+
fix: (fixer) =>
25+
fixer.insertTextAfter(node, `="${node.value.expression.name}"`)
26+
})
27+
}
28+
}
29+
30+
return {
31+
"VAttribute[directive=true][key.name.name='bind']": checkDirective
32+
}
33+
}
34+
}

Diff for: lib/utils/index.js

+17
Original file line numberDiff line numberDiff line change
@@ -828,6 +828,8 @@ module.exports = {
828828
*/
829829
hasDirective,
830830

831+
isVBindSameNameShorthand,
832+
831833
/**
832834
* Returns the list of all registered components
833835
* @param {ObjectExpression} componentObject
@@ -3021,6 +3023,21 @@ function hasDirective(node, name, argument) {
30213023
return Boolean(getDirective(node, name, argument))
30223024
}
30233025

3026+
/**
3027+
* Check whether the given directive node is v-bind same-name shorthand.
3028+
* @param {VAttribute | VDirective} node The directive node to check.
3029+
* @returns {node is VDirective & { value: VExpressionContainer & { expression: Identifier } }} `true` if the directive node is v-bind same-name shorthand.
3030+
*/
3031+
function isVBindSameNameShorthand(node) {
3032+
return (
3033+
node.directive &&
3034+
node.key.name.name === 'bind' &&
3035+
node.value?.expression?.type === 'Identifier' &&
3036+
node.key.range[0] <= node.value.range[0] &&
3037+
node.value.range[1] <= node.key.range[1]
3038+
)
3039+
}
3040+
30243041
/**
30253042
* Checks whether given defineProps call node has withDefaults.
30263043
* @param {CallExpression} node The node of defineProps

Diff for: package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@
5959
"nth-check": "^2.1.1",
6060
"postcss-selector-parser": "^6.0.13",
6161
"semver": "^7.5.4",
62-
"vue-eslint-parser": "^9.3.1",
62+
"vue-eslint-parser": "^9.4.0",
6363
"xml-name-validator": "^4.0.0"
6464
},
6565
"devDependencies": {

Diff for: tests/lib/rules/html-button-has-type.js

-10
Original file line numberDiff line numberDiff line change
@@ -203,16 +203,6 @@ ruleTester.run('html-button-has-type', rule, {
203203
column: 25
204204
}
205205
]
206-
},
207-
{
208-
filename: 'test.vue',
209-
code: `<template><button v-bind:type>Hello World</button></template>`,
210-
errors: [
211-
{
212-
message: 'A value must be set for button type attribute.',
213-
column: 19
214-
}
215-
]
216206
}
217207
]
218208
})

Diff for: tests/lib/rules/html-quotes.js

+9
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,15 @@ tester.run('html-quotes', rule, {
5858
code: '<template><div attr="foo\'bar"></div></template>',
5959
options: ['single', { avoidEscape: true }]
6060
},
61+
// v-bind same-name shorthand (Vue 3.4+)
62+
{
63+
code: '<template><div :foo /></template>',
64+
options: ['double']
65+
},
66+
{
67+
code: '<template><div :foo /></template>',
68+
options: ['single']
69+
},
6170

6271
// Invalid EOF
6372
{

Diff for: tests/lib/rules/no-deprecated-slot-attribute.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -321,7 +321,7 @@ tester.run('no-deprecated-slot-attribute', rule, {
321321
output: `
322322
<template>
323323
<LinkList>
324-
<template v-slot><a /></template>
324+
<template v-slot:[slot]><a /></template>
325325
</LinkList>
326326
</template>`,
327327
errors: [
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
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(
12+
'v-bind-same-name-shorthand',
13+
'^3.3.0'
14+
)
15+
const tester = new RuleTester({
16+
parser: require.resolve('vue-eslint-parser'),
17+
parserOptions: {
18+
ecmaVersion: 2019
19+
}
20+
})
21+
22+
tester.run('no-unsupported-features/v-bind-same-name-shorthand', rule, {
23+
valid: [
24+
{
25+
code: `
26+
<template>
27+
<div :x />
28+
</template>`,
29+
options: buildOptions({ version: '3.4.0' })
30+
},
31+
{
32+
code: `
33+
<template>
34+
<div :x="x" />
35+
</template>`,
36+
options: buildOptions()
37+
}
38+
],
39+
invalid: [
40+
{
41+
code: `
42+
<template>
43+
<div :x />
44+
</template>`,
45+
output: `
46+
<template>
47+
<div :x="x" />
48+
</template>`,
49+
options: buildOptions(),
50+
errors: [
51+
{
52+
message:
53+
'`v-bind` same-name shorthand is not supported until Vue.js "3.4.0".',
54+
line: 3
55+
}
56+
]
57+
},
58+
{
59+
code: `
60+
<template>
61+
<div :x />
62+
</template>`,
63+
output: `
64+
<template>
65+
<div :x="x" />
66+
</template>`,
67+
options: buildOptions({ version: '2.7.0' }),
68+
errors: [
69+
{
70+
message:
71+
'`v-bind` same-name shorthand is not supported until Vue.js "3.4.0".',
72+
line: 3
73+
}
74+
]
75+
},
76+
{
77+
code: `
78+
<template>
79+
<div :data-x />
80+
</template>`,
81+
output: `
82+
<template>
83+
<div :data-x="dataX" />
84+
</template>`,
85+
options: buildOptions(),
86+
errors: [
87+
{
88+
message:
89+
'`v-bind` same-name shorthand is not supported until Vue.js "3.4.0".',
90+
line: 3
91+
}
92+
]
93+
}
94+
]
95+
})

Diff for: tests/lib/rules/no-unused-components.js

-27
Original file line numberDiff line numberDiff line change
@@ -413,13 +413,6 @@ tester.run('no-unused-components', rule, {
413413
<component :is=""></component>
414414
</template>`
415415
},
416-
{
417-
filename: 'test.vue',
418-
code: `
419-
<template>
420-
<component :is></component>
421-
</template>`
422-
},
423416

424417
// computed properties
425418
{
@@ -629,26 +622,6 @@ tester.run('no-unused-components', rule, {
629622
}
630623
]
631624
},
632-
{
633-
filename: 'test.vue',
634-
code: `
635-
<template>
636-
<component :is></component>
637-
</template>
638-
<script>
639-
export default {
640-
components: {
641-
Foo,
642-
},
643-
}
644-
</script>`,
645-
errors: [
646-
{
647-
message: 'The "Foo" component has been registered but not used.',
648-
line: 8
649-
}
650-
]
651-
},
652625

653626
// computed properties
654627
{

Diff for: tests/lib/rules/v-bind-style.js

+15
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,21 @@ tester.run('v-bind-style', rule, {
106106
output: '<template><div v-bind:foo.sync.prop="foo"></div></template>',
107107
options: ['longform'],
108108
errors: ["Expected 'v-bind:' instead of '.'."]
109+
},
110+
// v-bind same-name shorthand (Vue 3.4+)
111+
{
112+
filename: 'test.vue',
113+
code: '<template><div v-bind:foo /></template>',
114+
output: '<template><div :foo /></template>',
115+
options: ['shorthand'],
116+
errors: ["Unexpected 'v-bind' before ':'."]
117+
},
118+
{
119+
filename: 'test.vue',
120+
code: '<template><div :foo /></template>',
121+
output: '<template><div v-bind:foo /></template>',
122+
options: ['longform'],
123+
errors: ["Expected 'v-bind' before ':'."]
109124
}
110125
]
111126
})

Diff for: tests/lib/rules/valid-v-bind.js

+9-5
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,15 @@ tester.run('valid-v-bind', rule, {
7171
filename: 'test.vue',
7272
code: "<template><input v-bind='$attrs' /></template>"
7373
},
74+
// v-bind same-name shorthand (Vue 3.4+)
75+
{
76+
filename: 'test.vue',
77+
code: '<template><div :foo /></template>'
78+
},
79+
{
80+
filename: 'test.vue',
81+
code: '<template><div v-bind:foo /></template>'
82+
},
7483
// parsing error
7584
{
7685
filename: 'parsing-error.vue',
@@ -88,11 +97,6 @@ tester.run('valid-v-bind', rule, {
8897
code: '<template><div v-bind></div></template>',
8998
errors: ["'v-bind' directives require an attribute value."]
9099
},
91-
{
92-
filename: 'test.vue',
93-
code: '<template><div v-bind:aaa></div></template>',
94-
errors: ["'v-bind' directives require an attribute value."]
95-
},
96100
{
97101
filename: 'test.vue',
98102
code: "<template><div :aaa.unknown='bbb'></div></template>",

0 commit comments

Comments
 (0)