Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 1d1be85

Browse files
authored
Add prefer-true-attribute-shorthand rule (#1796)
* Add `prefer-true-attribute-shorthand` rule * fix typo * accept suggestions * accept option `"always"` or `"never"` * ignore native HTML elements * provide suggestions instead of auto fix * meta update
1 parent 7ebbd85 commit 1d1be85

File tree

5 files changed

+505
-0
lines changed

5 files changed

+505
-0
lines changed

‎docs/rules/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,7 @@ For example:
353353
| [vue/no-v-text](./no-v-text.md) | disallow use of v-text | |
354354
| [vue/padding-line-between-blocks](./padding-line-between-blocks.md) | require or disallow padding lines between blocks | :wrench: |
355355
| [vue/prefer-separate-static-class](./prefer-separate-static-class.md) | require static class names in template to be in a separate `class` attribute | :wrench: |
356+
| [vue/prefer-true-attribute-shorthand](./prefer-true-attribute-shorthand.md) | require shorthand form attribute when `v-bind` value is `true` | :bulb: |
356357
| [vue/require-direct-export](./require-direct-export.md) | require the component to be directly exported | |
357358
| [vue/require-emit-validator](./require-emit-validator.md) | require type definitions in emits | :bulb: |
358359
| [vue/require-expose](./require-expose.md) | require declare public properties using `expose` | :bulb: |
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
---
2+
pageClass: rule-details
3+
sidebarDepth: 0
4+
title: vue/prefer-true-attribute-shorthand
5+
description: require shorthand form attribute when `v-bind` value is `true`
6+
---
7+
# vue/prefer-true-attribute-shorthand
8+
9+
> require shorthand form attribute when `v-bind` value is `true`
10+
11+
- :exclamation: <badge text="This rule has not been released yet." vertical="middle" type="error"> ***This rule has not been released yet.*** </badge>
12+
- :bulb: Some problems reported by this rule are manually fixable by editor [suggestions](https://eslint.org/docs/developer-guide/working-with-rules#providing-suggestions).
13+
14+
## :book: Rule Details
15+
16+
`v-bind` attribute with `true` value usually can be written in shorthand form. This can reduce verbosity.
17+
18+
<eslint-code-block :rules="{'vue/prefer-true-attribute-shorthand': ['error']}">
19+
20+
```vue
21+
<template>
22+
<!-- ✗ BAD -->
23+
<MyComponent v-bind:show="true" />
24+
<MyComponent :show="true" />
25+
26+
<!-- ✓ GOOD -->
27+
<MyComponent show />
28+
<MyComponent another-prop="true" />
29+
</template>
30+
```
31+
32+
</eslint-code-block>
33+
34+
::: warning Warning
35+
The shorthand form is not always equivalent! If a prop accepts multiple types, but Boolean is not the first one, a shorthand prop won't pass `true`.
36+
:::
37+
38+
```vue
39+
<script>
40+
export default {
41+
name: 'MyComponent',
42+
props: {
43+
bool: Boolean,
44+
boolOrString: [Boolean, String],
45+
stringOrBool: [String, Boolean],
46+
}
47+
}
48+
</script>
49+
```
50+
51+
**Shorthand form:**
52+
53+
```vue
54+
<MyComponent bool bool-or-string string-or-bool />
55+
```
56+
57+
```
58+
bool: true (boolean)
59+
boolOrString: true (boolean)
60+
stringOrBool: "" (string)
61+
```
62+
63+
**Longhand form:**
64+
65+
```vue
66+
<MyComponent :bool="true" :bool-or-string="true" :string-or-bool="true" />
67+
```
68+
69+
```
70+
bool: true (boolean)
71+
boolOrString: true (boolean)
72+
stringOrBool: true (boolean)
73+
```
74+
75+
Those two calls will introduce different render result. See [this demo](https://sfc.vuejs.org/#eyJBcHAudnVlIjoiPHNjcmlwdCBzZXR1cD5cbmltcG9ydCBNeUNvbXBvbmVudCBmcm9tICcuL015Q29tcG9uZW50LnZ1ZSdcbjwvc2NyaXB0PlxuXG48dGVtcGxhdGU+XG4gIFNob3J0aGFuZCBmb3JtOlxuICA8TXlDb21wb25lbnQgYm9vbCBib29sLW9yLXN0cmluZyBzdHJpbmctb3ItYm9vbCAvPlxuICBcbiAgTG9uZ2hhbmQgZm9ybTpcbiAgPE15Q29tcG9uZW50IDpib29sPVwidHJ1ZVwiIDpib29sLW9yLXN0cmluZz1cInRydWVcIiA6c3RyaW5nLW9yLWJvb2w9XCJ0cnVlXCIgLz5cbjwvdGVtcGxhdGU+IiwiaW1wb3J0LW1hcC5qc29uIjoie1xuICBcImltcG9ydHNcIjoge1xuICAgIFwidnVlXCI6IFwiaHR0cHM6Ly9zZmMudnVlanMub3JnL3Z1ZS5ydW50aW1lLmVzbS1icm93c2VyLmpzXCJcbiAgfVxufSIsIk15Q29tcG9uZW50LnZ1ZSI6IjxzY3JpcHQ+XG5leHBvcnQgZGVmYXVsdCB7XG4gIHByb3BzOiB7XG4gICAgYm9vbDogQm9vbGVhbixcbiAgICBib29sT3JTdHJpbmc6IFtCb29sZWFuLCBTdHJpbmddLFxuICAgIHN0cmluZ09yQm9vbDogW1N0cmluZywgQm9vbGVhbl0sXG4gIH1cbn1cbjwvc2NyaXB0PlxuXG48dGVtcGxhdGU+XG4gIDxwcmU+XG5ib29sOiB7e2Jvb2x9fSAoe3sgdHlwZW9mIGJvb2wgfX0pXG5ib29sT3JTdHJpbmc6IHt7Ym9vbE9yU3RyaW5nfX0gKHt7IHR5cGVvZiBib29sT3JTdHJpbmcgfX0pXG5zdHJpbmdPckJvb2w6IHt7c3RyaW5nT3JCb29sfX0gKHt7IHR5cGVvZiBzdHJpbmdPckJvb2wgfX0pXG4gIDwvcHJlPlxuPC90ZW1wbGF0ZT4ifQ==).
76+
77+
## :wrench: Options
78+
79+
Default options is `"always"`.
80+
81+
```json
82+
{
83+
"vue/prefer-true-attribute-shorthand": ["error", "always" | "never"]
84+
}
85+
```
86+
87+
- `"always"` (default) ... requires shorthand form.
88+
- `"never"` ... requires long form.
89+
90+
### `"never"`
91+
92+
<eslint-code-block :rules="{'vue/prefer-true-attribute-shorthand': ['error', 'never']}">
93+
94+
```vue
95+
<template>
96+
<!-- ✗ BAD -->
97+
<MyComponent show />
98+
99+
<!-- ✓ GOOD -->
100+
<MyComponent :show="true" />
101+
<MyComponent v-bind:show="true" />
102+
</template>
103+
```
104+
105+
</eslint-code-block>
106+
107+
## :mag: Implementation
108+
109+
- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/prefer-true-attribute-shorthand.js)
110+
- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/prefer-true-attribute-shorthand.js)

‎lib/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@ module.exports = {
159159
'padding-line-between-blocks': require('./rules/padding-line-between-blocks'),
160160
'prefer-separate-static-class': require('./rules/prefer-separate-static-class'),
161161
'prefer-template': require('./rules/prefer-template'),
162+
'prefer-true-attribute-shorthand': require('./rules/prefer-true-attribute-shorthand'),
162163
'prop-name-casing': require('./rules/prop-name-casing'),
163164
'quote-props': require('./rules/quote-props'),
164165
'require-component-is': require('./rules/require-component-is'),
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
/**
2+
* @author Pig Fang
3+
* See LICENSE file in root directory for full license.
4+
*/
5+
'use strict'
6+
7+
// ------------------------------------------------------------------------------
8+
// Requirements
9+
// ------------------------------------------------------------------------------
10+
11+
const utils = require('../utils')
12+
13+
// ------------------------------------------------------------------------------
14+
// Rule Definition
15+
// ------------------------------------------------------------------------------
16+
17+
module.exports = {
18+
meta: {
19+
type: 'suggestion',
20+
docs: {
21+
description:
22+
'require shorthand form attribute when `v-bind` value is `true`',
23+
categories: undefined,
24+
url: 'https://eslint.vuejs.org/rules/prefer-true-attribute-shorthand.html'
25+
},
26+
fixable: null,
27+
hasSuggestions: true,
28+
schema: [{ enum: ['always', 'never'] }],
29+
messages: {
30+
expectShort:
31+
"Boolean prop with 'true' value should be written in shorthand form.",
32+
expectLong:
33+
"Boolean prop with 'true' value should be written in long form.",
34+
rewriteIntoShort: 'Rewrite this prop into shorthand form.',
35+
rewriteIntoLongVueProp:
36+
'Rewrite this prop into long-form Vue component prop.',
37+
rewriteIntoLongHtmlAttr:
38+
'Rewrite this prop into long-form HTML attribute.'
39+
}
40+
},
41+
/** @param {RuleContext} context */
42+
create(context) {
43+
/** @type {'always' | 'never'} */
44+
const option = context.options[0] || 'always'
45+
46+
return utils.defineTemplateBodyVisitor(context, {
47+
VAttribute(node) {
48+
if (!utils.isCustomComponent(node.parent.parent)) {
49+
return
50+
}
51+
52+
if (option === 'never' && !node.directive && !node.value) {
53+
context.report({
54+
node,
55+
messageId: 'expectLong',
56+
suggest: [
57+
{
58+
messageId: 'rewriteIntoLongVueProp',
59+
fix: (fixer) =>
60+
fixer.replaceText(node, `:${node.key.rawName}="true"`)
61+
},
62+
{
63+
messageId: 'rewriteIntoLongHtmlAttr',
64+
fix: (fixer) =>
65+
fixer.replaceText(
66+
node,
67+
`${node.key.rawName}="${node.key.rawName}"`
68+
)
69+
}
70+
]
71+
})
72+
return
73+
}
74+
75+
if (option !== 'always') {
76+
return
77+
}
78+
79+
if (
80+
!node.directive ||
81+
!node.value ||
82+
!node.value.expression ||
83+
node.value.expression.type !== 'Literal' ||
84+
node.value.expression.value !== true
85+
) {
86+
return
87+
}
88+
89+
const { argument } = node.key
90+
if (!argument) {
91+
return
92+
}
93+
94+
context.report({
95+
node,
96+
messageId: 'expectShort',
97+
suggest: [
98+
{
99+
messageId: 'rewriteIntoShort',
100+
fix: (fixer) => {
101+
const sourceCode = context.getSourceCode()
102+
return fixer.replaceText(node, sourceCode.getText(argument))
103+
}
104+
}
105+
]
106+
})
107+
}
108+
})
109+
}
110+
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /