-
-
Notifications
You must be signed in to change notification settings - Fork 673
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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
- Loading branch information
Showing
5 changed files
with
505 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
--- | ||
pageClass: rule-details | ||
sidebarDepth: 0 | ||
title: vue/prefer-true-attribute-shorthand | ||
description: require shorthand form attribute when `v-bind` value is `true` | ||
--- | ||
# vue/prefer-true-attribute-shorthand | ||
|
||
> require shorthand form attribute when `v-bind` value is `true` | ||
- :exclamation: <badge text="This rule has not been released yet." vertical="middle" type="error"> ***This rule has not been released yet.*** </badge> | ||
- :bulb: Some problems reported by this rule are manually fixable by editor [suggestions](https://eslint.org/docs/developer-guide/working-with-rules#providing-suggestions). | ||
|
||
## :book: Rule Details | ||
|
||
`v-bind` attribute with `true` value usually can be written in shorthand form. This can reduce verbosity. | ||
|
||
<eslint-code-block :rules="{'vue/prefer-true-attribute-shorthand': ['error']}"> | ||
|
||
```vue | ||
<template> | ||
<!-- ✗ BAD --> | ||
<MyComponent v-bind:show="true" /> | ||
<MyComponent :show="true" /> | ||
<!-- ✓ GOOD --> | ||
<MyComponent show /> | ||
<MyComponent another-prop="true" /> | ||
</template> | ||
``` | ||
|
||
</eslint-code-block> | ||
|
||
::: warning Warning | ||
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`. | ||
::: | ||
|
||
```vue | ||
<script> | ||
export default { | ||
name: 'MyComponent', | ||
props: { | ||
bool: Boolean, | ||
boolOrString: [Boolean, String], | ||
stringOrBool: [String, Boolean], | ||
} | ||
} | ||
</script> | ||
``` | ||
|
||
**Shorthand form:** | ||
|
||
```vue | ||
<MyComponent bool bool-or-string string-or-bool /> | ||
``` | ||
|
||
``` | ||
bool: true (boolean) | ||
boolOrString: true (boolean) | ||
stringOrBool: "" (string) | ||
``` | ||
|
||
**Longhand form:** | ||
|
||
```vue | ||
<MyComponent :bool="true" :bool-or-string="true" :string-or-bool="true" /> | ||
``` | ||
|
||
``` | ||
bool: true (boolean) | ||
boolOrString: true (boolean) | ||
stringOrBool: true (boolean) | ||
``` | ||
|
||
Those two calls will introduce different render result. See [this demo](https://sfc.vuejs.org/#eyJBcHAudnVlIjoiPHNjcmlwdCBzZXR1cD5cbmltcG9ydCBNeUNvbXBvbmVudCBmcm9tICcuL015Q29tcG9uZW50LnZ1ZSdcbjwvc2NyaXB0PlxuXG48dGVtcGxhdGU+XG4gIFNob3J0aGFuZCBmb3JtOlxuICA8TXlDb21wb25lbnQgYm9vbCBib29sLW9yLXN0cmluZyBzdHJpbmctb3ItYm9vbCAvPlxuICBcbiAgTG9uZ2hhbmQgZm9ybTpcbiAgPE15Q29tcG9uZW50IDpib29sPVwidHJ1ZVwiIDpib29sLW9yLXN0cmluZz1cInRydWVcIiA6c3RyaW5nLW9yLWJvb2w9XCJ0cnVlXCIgLz5cbjwvdGVtcGxhdGU+IiwiaW1wb3J0LW1hcC5qc29uIjoie1xuICBcImltcG9ydHNcIjoge1xuICAgIFwidnVlXCI6IFwiaHR0cHM6Ly9zZmMudnVlanMub3JnL3Z1ZS5ydW50aW1lLmVzbS1icm93c2VyLmpzXCJcbiAgfVxufSIsIk15Q29tcG9uZW50LnZ1ZSI6IjxzY3JpcHQ+XG5leHBvcnQgZGVmYXVsdCB7XG4gIHByb3BzOiB7XG4gICAgYm9vbDogQm9vbGVhbixcbiAgICBib29sT3JTdHJpbmc6IFtCb29sZWFuLCBTdHJpbmddLFxuICAgIHN0cmluZ09yQm9vbDogW1N0cmluZywgQm9vbGVhbl0sXG4gIH1cbn1cbjwvc2NyaXB0PlxuXG48dGVtcGxhdGU+XG4gIDxwcmU+XG5ib29sOiB7e2Jvb2x9fSAoe3sgdHlwZW9mIGJvb2wgfX0pXG5ib29sT3JTdHJpbmc6IHt7Ym9vbE9yU3RyaW5nfX0gKHt7IHR5cGVvZiBib29sT3JTdHJpbmcgfX0pXG5zdHJpbmdPckJvb2w6IHt7c3RyaW5nT3JCb29sfX0gKHt7IHR5cGVvZiBzdHJpbmdPckJvb2wgfX0pXG4gIDwvcHJlPlxuPC90ZW1wbGF0ZT4ifQ==). | ||
|
||
## :wrench: Options | ||
|
||
Default options is `"always"`. | ||
|
||
```json | ||
{ | ||
"vue/prefer-true-attribute-shorthand": ["error", "always" | "never"] | ||
} | ||
``` | ||
|
||
- `"always"` (default) ... requires shorthand form. | ||
- `"never"` ... requires long form. | ||
|
||
### `"never"` | ||
|
||
<eslint-code-block :rules="{'vue/prefer-true-attribute-shorthand': ['error', 'never']}"> | ||
|
||
```vue | ||
<template> | ||
<!-- ✗ BAD --> | ||
<MyComponent show /> | ||
<!-- ✓ GOOD --> | ||
<MyComponent :show="true" /> | ||
<MyComponent v-bind:show="true" /> | ||
</template> | ||
``` | ||
|
||
</eslint-code-block> | ||
|
||
## :mag: Implementation | ||
|
||
- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/prefer-true-attribute-shorthand.js) | ||
- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/prefer-true-attribute-shorthand.js) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
/** | ||
* @author Pig Fang | ||
* See LICENSE file in root directory for full license. | ||
*/ | ||
'use strict' | ||
|
||
// ------------------------------------------------------------------------------ | ||
// Requirements | ||
// ------------------------------------------------------------------------------ | ||
|
||
const utils = require('../utils') | ||
|
||
// ------------------------------------------------------------------------------ | ||
// Rule Definition | ||
// ------------------------------------------------------------------------------ | ||
|
||
module.exports = { | ||
meta: { | ||
type: 'suggestion', | ||
docs: { | ||
description: | ||
'require shorthand form attribute when `v-bind` value is `true`', | ||
categories: undefined, | ||
url: 'https://eslint.vuejs.org/rules/prefer-true-attribute-shorthand.html' | ||
}, | ||
fixable: null, | ||
hasSuggestions: true, | ||
schema: [{ enum: ['always', 'never'] }], | ||
messages: { | ||
expectShort: | ||
"Boolean prop with 'true' value should be written in shorthand form.", | ||
expectLong: | ||
"Boolean prop with 'true' value should be written in long form.", | ||
rewriteIntoShort: 'Rewrite this prop into shorthand form.', | ||
rewriteIntoLongVueProp: | ||
'Rewrite this prop into long-form Vue component prop.', | ||
rewriteIntoLongHtmlAttr: | ||
'Rewrite this prop into long-form HTML attribute.' | ||
} | ||
}, | ||
/** @param {RuleContext} context */ | ||
create(context) { | ||
/** @type {'always' | 'never'} */ | ||
const option = context.options[0] || 'always' | ||
|
||
return utils.defineTemplateBodyVisitor(context, { | ||
VAttribute(node) { | ||
if (!utils.isCustomComponent(node.parent.parent)) { | ||
return | ||
} | ||
|
||
if (option === 'never' && !node.directive && !node.value) { | ||
context.report({ | ||
node, | ||
messageId: 'expectLong', | ||
suggest: [ | ||
{ | ||
messageId: 'rewriteIntoLongVueProp', | ||
fix: (fixer) => | ||
fixer.replaceText(node, `:${node.key.rawName}="true"`) | ||
}, | ||
{ | ||
messageId: 'rewriteIntoLongHtmlAttr', | ||
fix: (fixer) => | ||
fixer.replaceText( | ||
node, | ||
`${node.key.rawName}="${node.key.rawName}"` | ||
) | ||
} | ||
] | ||
}) | ||
return | ||
} | ||
|
||
if (option !== 'always') { | ||
return | ||
} | ||
|
||
if ( | ||
!node.directive || | ||
!node.value || | ||
!node.value.expression || | ||
node.value.expression.type !== 'Literal' || | ||
node.value.expression.value !== true | ||
) { | ||
return | ||
} | ||
|
||
const { argument } = node.key | ||
if (!argument) { | ||
return | ||
} | ||
|
||
context.report({ | ||
node, | ||
messageId: 'expectShort', | ||
suggest: [ | ||
{ | ||
messageId: 'rewriteIntoShort', | ||
fix: (fixer) => { | ||
const sourceCode = context.getSourceCode() | ||
return fixer.replaceText(node, sourceCode.getText(argument)) | ||
} | ||
} | ||
] | ||
}) | ||
} | ||
}) | ||
} | ||
} |
Oops, something went wrong.