Skip to content

Commit

Permalink
Upgrade vue-eslint-parser and add support for v-bind same-name shorth…
Browse files Browse the repository at this point in the history
…and (#2357)
  • Loading branch information
ota-meshi authored Jan 9, 2024
1 parent ea29fd4 commit 8035aa3
Show file tree
Hide file tree
Showing 13 changed files with 195 additions and 46 deletions.
2 changes: 2 additions & 0 deletions docs/rules/no-unsupported-features.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.4.0+
- `"v-bind-same-name-shorthand"` ... `v-bind` same-name shorthand.
- Vue.js 3.3.0+
- `"define-slots"` ... `defineSlots()` macro.
- `"define-options"` ... `defineOptions()` macro.
Expand Down
5 changes: 5 additions & 0 deletions lib/rules/html-quotes.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,11 @@ module.exports = {
return
}

if (utils.isVBindSameNameShorthand(node)) {
// v-bind same-name shorthand (Vue 3.4+)
return
}

const text = sourceCode.getText(node.value)
const firstChar = text[0]

Expand Down
9 changes: 7 additions & 2 deletions lib/rules/no-unsupported-features.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,9 @@ const FEATURES = {
'v-bind-attr-modifier': require('./syntaxes/v-bind-attr-modifier'),
// Vue.js 3.3.0+
'define-options': require('./syntaxes/define-options'),
'define-slots': require('./syntaxes/define-slots')
'define-slots': require('./syntaxes/define-slots'),
// Vue.js 3.4.0+
'v-bind-same-name-shorthand': require('./syntaxes/v-bind-same-name-shorthand')
}

const SYNTAX_NAMES = /** @type {(keyof FEATURES)[]} */ (Object.keys(FEATURES))
Expand Down Expand Up @@ -124,7 +126,10 @@ module.exports = {
forbiddenDefineOptions:
'`defineOptions()` macros are not supported until Vue.js "3.3.0".',
forbiddenDefineSlots:
'`defineSlots()` macros are not supported until Vue.js "3.3.0".'
'`defineSlots()` macros are not supported until Vue.js "3.3.0".',
// Vue.js 3.4.0+
forbiddenVBindSameNameShorthand:
'`v-bind` same-name shorthand is not supported until Vue.js "3.4.0".'
}
},
/** @param {RuleContext} context */
Expand Down
34 changes: 34 additions & 0 deletions lib/rules/syntaxes/v-bind-same-name-shorthand.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/**
* @author Yosuke Ota
* See LICENSE file in root directory for full license.
*/
'use strict'

const utils = require('../../utils')

module.exports = {
supported: '>=3.4.0',
/** @param {RuleContext} context @returns {TemplateListener} */
createTemplateBodyVisitor(context) {
/**
* Verify the directive node
* @param {VDirective} node The directive node to check
* @returns {void}
*/
function checkDirective(node) {
if (utils.isVBindSameNameShorthand(node)) {
context.report({
node,
messageId: 'forbiddenVBindSameNameShorthand',
// fix to use `:x="x"` (downgrade)
fix: (fixer) =>
fixer.insertTextAfter(node, `="${node.value.expression.name}"`)
})
}
}

return {
"VAttribute[directive=true][key.name.name='bind']": checkDirective
}
}
}
17 changes: 17 additions & 0 deletions lib/utils/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -828,6 +828,8 @@ module.exports = {
*/
hasDirective,

isVBindSameNameShorthand,

/**
* Returns the list of all registered components
* @param {ObjectExpression} componentObject
Expand Down Expand Up @@ -3021,6 +3023,21 @@ function hasDirective(node, name, argument) {
return Boolean(getDirective(node, name, argument))
}

/**
* Check whether the given directive node is v-bind same-name shorthand.
* @param {VAttribute | VDirective} node The directive node to check.
* @returns {node is VDirective & { value: VExpressionContainer & { expression: Identifier } }} `true` if the directive node is v-bind same-name shorthand.
*/
function isVBindSameNameShorthand(node) {
return (
node.directive &&
node.key.name.name === 'bind' &&
node.value?.expression?.type === 'Identifier' &&
node.key.range[0] <= node.value.range[0] &&
node.value.range[1] <= node.key.range[1]
)
}

/**
* Checks whether given defineProps call node has withDefaults.
* @param {CallExpression} node The node of defineProps
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@
"nth-check": "^2.1.1",
"postcss-selector-parser": "^6.0.13",
"semver": "^7.5.4",
"vue-eslint-parser": "^9.3.1",
"vue-eslint-parser": "^9.4.0",
"xml-name-validator": "^4.0.0"
},
"devDependencies": {
Expand Down
10 changes: 0 additions & 10 deletions tests/lib/rules/html-button-has-type.js
Original file line number Diff line number Diff line change
Expand Up @@ -203,16 +203,6 @@ ruleTester.run('html-button-has-type', rule, {
column: 25
}
]
},
{
filename: 'test.vue',
code: `<template><button v-bind:type>Hello World</button></template>`,
errors: [
{
message: 'A value must be set for button type attribute.',
column: 19
}
]
}
]
})
9 changes: 9 additions & 0 deletions tests/lib/rules/html-quotes.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,15 @@ tester.run('html-quotes', rule, {
code: '<template><div attr="foo\'bar"></div></template>',
options: ['single', { avoidEscape: true }]
},
// v-bind same-name shorthand (Vue 3.4+)
{
code: '<template><div :foo /></template>',
options: ['double']
},
{
code: '<template><div :foo /></template>',
options: ['single']
},

// Invalid EOF
{
Expand Down
2 changes: 1 addition & 1 deletion tests/lib/rules/no-deprecated-slot-attribute.js
Original file line number Diff line number Diff line change
Expand Up @@ -321,7 +321,7 @@ tester.run('no-deprecated-slot-attribute', rule, {
output: `
<template>
<LinkList>
<template v-slot><a /></template>
<template v-slot:[slot]><a /></template>
</LinkList>
</template>`,
errors: [
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/**
* @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-bind-same-name-shorthand',
'^3.3.0'
)
const tester = new RuleTester({
parser: require.resolve('vue-eslint-parser'),
parserOptions: {
ecmaVersion: 2019
}
})

tester.run('no-unsupported-features/v-bind-same-name-shorthand', rule, {
valid: [
{
code: `
<template>
<div :x />
</template>`,
options: buildOptions({ version: '3.4.0' })
},
{
code: `
<template>
<div :x="x" />
</template>`,
options: buildOptions()
}
],
invalid: [
{
code: `
<template>
<div :x />
</template>`,
output: `
<template>
<div :x="x" />
</template>`,
options: buildOptions(),
errors: [
{
message:
'`v-bind` same-name shorthand is not supported until Vue.js "3.4.0".',
line: 3
}
]
},
{
code: `
<template>
<div :x />
</template>`,
output: `
<template>
<div :x="x" />
</template>`,
options: buildOptions({ version: '2.7.0' }),
errors: [
{
message:
'`v-bind` same-name shorthand is not supported until Vue.js "3.4.0".',
line: 3
}
]
},
{
code: `
<template>
<div :data-x />
</template>`,
output: `
<template>
<div :data-x="dataX" />
</template>`,
options: buildOptions(),
errors: [
{
message:
'`v-bind` same-name shorthand is not supported until Vue.js "3.4.0".',
line: 3
}
]
}
]
})
27 changes: 0 additions & 27 deletions tests/lib/rules/no-unused-components.js
Original file line number Diff line number Diff line change
Expand Up @@ -413,13 +413,6 @@ tester.run('no-unused-components', rule, {
<component :is=""></component>
</template>`
},
{
filename: 'test.vue',
code: `
<template>
<component :is></component>
</template>`
},

// computed properties
{
Expand Down Expand Up @@ -629,26 +622,6 @@ tester.run('no-unused-components', rule, {
}
]
},
{
filename: 'test.vue',
code: `
<template>
<component :is></component>
</template>
<script>
export default {
components: {
Foo,
},
}
</script>`,
errors: [
{
message: 'The "Foo" component has been registered but not used.',
line: 8
}
]
},

// computed properties
{
Expand Down
15 changes: 15 additions & 0 deletions tests/lib/rules/v-bind-style.js
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,21 @@ tester.run('v-bind-style', rule, {
output: '<template><div v-bind:foo.sync.prop="foo"></div></template>',
options: ['longform'],
errors: ["Expected 'v-bind:' instead of '.'."]
},
// v-bind same-name shorthand (Vue 3.4+)
{
filename: 'test.vue',
code: '<template><div v-bind:foo /></template>',
output: '<template><div :foo /></template>',
options: ['shorthand'],
errors: ["Unexpected 'v-bind' before ':'."]
},
{
filename: 'test.vue',
code: '<template><div :foo /></template>',
output: '<template><div v-bind:foo /></template>',
options: ['longform'],
errors: ["Expected 'v-bind' before ':'."]
}
]
})
14 changes: 9 additions & 5 deletions tests/lib/rules/valid-v-bind.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,15 @@ tester.run('valid-v-bind', rule, {
filename: 'test.vue',
code: "<template><input v-bind='$attrs' /></template>"
},
// v-bind same-name shorthand (Vue 3.4+)
{
filename: 'test.vue',
code: '<template><div :foo /></template>'
},
{
filename: 'test.vue',
code: '<template><div v-bind:foo /></template>'
},
// parsing error
{
filename: 'parsing-error.vue',
Expand All @@ -88,11 +97,6 @@ tester.run('valid-v-bind', rule, {
code: '<template><div v-bind></div></template>',
errors: ["'v-bind' directives require an attribute value."]
},
{
filename: 'test.vue',
code: '<template><div v-bind:aaa></div></template>',
errors: ["'v-bind' directives require an attribute value."]
},
{
filename: 'test.vue',
code: "<template><div :aaa.unknown='bbb'></div></template>",
Expand Down

0 comments on commit 8035aa3

Please sign in to comment.