Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[New] jsx-boolean-value: add assumeUndefinedIsFalse option #3675

Merged
merged 1 commit into from Jan 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Expand Up @@ -14,6 +14,7 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange
* [`no-unknown-property`]: support `onResize` on audio/video tags ([#3662][] @caesar1030)
* [`jsx-wrap-multilines`]: add `never` option to prohibit wrapping parens on multiline JSX ([#3668][] @reedws)
* [`jsx-filename-extension`]: add `ignoreFilesWithoutCode` option to allow empty files ([#3674][] @burtek)
* [`jsx-boolean-value`]: add `assumeUndefinedIsFalse` option ([#3675][] @developer-bandi)

### Fixed
* [`jsx-no-leaked-render`]: preserve RHS parens for multiline jsx elements while fixing ([#3623][] @akulsr0)
Expand All @@ -29,6 +30,7 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange
* [Docs] [`jsx-key`]: fix correct example ([#3656][] @developer-bandi)
* [Tests] `jsx-wrap-multilines`: passing tests ([#3545][] @burtek)

[#3675]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3675
[#3674]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3674
[#3668]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3668
[#3666]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3666
Expand Down
18 changes: 17 additions & 1 deletion docs/rules/jsx-boolean-value.md
Expand Up @@ -14,7 +14,11 @@ This rule will enforce one or the other to keep consistency in your code.

This rule takes two arguments. If the first argument is `"always"` then it warns whenever an attribute is missing its value. If `"never"` then it warns if an attribute has a `true` value. The default value of this option is `"never"`.

The second argument is optional: if provided, it must be an object with a `"never"` property (if the first argument is `"always"`), or an `"always"` property (if the first argument is `"never"`). This property’s value must be an array of strings representing prop names.
The second argument is optional. If provided, it must be an object. These properties are supported:

First, the `"never"` and `"always"` properties are one set. The two properties cannot be set together. `"never"` must be used when the first argument is `"always"` and `"always"` must be used when the first argument is `"never"`. This property’s value must be an array of strings representing prop names.

When the first argument is `"never"`, a boolean `"assumeUndefinedIsFalse"` may be provided, which defaults to `false`. When `true`, an absent boolean prop will be treated as if it were explicitly set to `false`.

Examples of **incorrect** code for this rule, when configured with `"never"`, or with `"always", { "never": ["personal"] }`:

Expand All @@ -40,6 +44,18 @@ Examples of **correct** code for this rule, when configured with `"always"`, or
var Hello = <Hello personal={true} />;
```

Examples of **incorrect** code for this rule, when configured with `"never", { "assumeUndefinedIsFalse": true }`, or with `"always", { "never": ["personal"], "assumeUndefinedIsFalse": true }`:

```jsx
var Hello = <Hello personal={false} />;
```

Examples of **correct** code for this rule, when configured with `"never", { "assumeUndefinedIsFalse": true }`, or with `"always", { "never": ["personal"], "assumeUndefinedIsFalse": true }`:

```jsx
var Hello = <Hello />;
```

## When Not To Use It

If you do not want to enforce any style for boolean attributes, then you can disable this rule.
31 changes: 30 additions & 1 deletion lib/rules/jsx-boolean-value.js
Expand Up @@ -39,7 +39,7 @@ function getErrorData(exceptions) {
* @param {Set<string>} exceptions
* @param {string} propName
* @returns {boolean} propName
*/
*/
function isAlways(configuration, exceptions, propName) {
const isException = exceptions.has(propName);
if (configuration === ALWAYS) {
Expand All @@ -66,6 +66,8 @@ const messages = {
omitBoolean_noMessage: 'Value must be omitted for boolean attributes',
setBoolean: 'Value must be set for boolean attributes{{exceptionsMessage}}',
setBoolean_noMessage: 'Value must be set for boolean attributes',
omitPropAndBoolean: 'Value and Prop must be omitted for false attributes{{exceptionsMessage}}',
omitPropAndBoolean_noMessage: 'Value and Prop must be omitted for false attributes',
};

module.exports = {
Expand Down Expand Up @@ -94,6 +96,9 @@ module.exports = {
additionalProperties: false,
properties: {
[NEVER]: exceptionsSchema,
assumeUndefinedIsFalse: {
type: 'boolean',
},
},
}],
additionalItems: false,
Expand All @@ -106,6 +111,9 @@ module.exports = {
additionalProperties: false,
properties: {
[ALWAYS]: exceptionsSchema,
assumeUndefinedIsFalse: {
type: 'boolean',
},
},
}],
additionalItems: false,
Expand Down Expand Up @@ -139,6 +147,7 @@ module.exports = {
}
if (
isNever(configuration, exceptions, propName)
&& !configObject.assumeUndefinedIsFalse
&& value
&& value.type === 'JSXExpressionContainer'
&& value.expression.value === true
Expand All @@ -153,6 +162,26 @@ module.exports = {
},
});
}
if (
isNever(configuration, exceptions, propName)
&& configObject.assumeUndefinedIsFalse
&& value
&& value.type === 'JSXExpressionContainer'
&& value.expression.value === false
) {
const data = getErrorData(exceptions);
const messageId = data.exceptionsMessage
? 'omitPropAndBoolean'
: 'omitPropAndBoolean_noMessage';

report(context, messages[messageId], messageId, {
node,
data,
fix(fixer) {
return fixer.removeRange([node.name.range[0], value.range[1]]);
},
});
}
},
};
},
Expand Down
35 changes: 35 additions & 0 deletions tests/lib/rules/jsx-boolean-value.js
Expand Up @@ -48,6 +48,14 @@ ruleTester.run('jsx-boolean-value', rule, {
code: '<App foo={true} bar />;',
options: ['never', { always: ['foo'] }],
},
{
code: '<App />;',
options: ['never', { assumeUndefinedIsFalse: true }],
},
{
code: '<App foo={false} />;',
options: ['never', { assumeUndefinedIsFalse: true, always: ['foo'] }],
},
]),
invalid: parsers.all([
{
Expand Down Expand Up @@ -110,5 +118,32 @@ ruleTester.run('jsx-boolean-value', rule, {
},
],
},
{
code: '<App foo={false} bak={false} />;',
output: '<App />;',
options: ['never', { assumeUndefinedIsFalse: true }],
errors: [
{ messageId: 'omitPropAndBoolean_noMessage' },
{ messageId: 'omitPropAndBoolean_noMessage' },
],
},
{
code: '<App foo={true} bar={false} baz={false} bak={false} />;',
output: '<App foo={true} bar={false} />;',
options: [
'always',
{ assumeUndefinedIsFalse: true, never: ['baz', 'bak'] },
],
errors: [
{
messageId: 'omitPropAndBoolean',
data: { exceptionsMessage: ' for the following props: `baz`, `bak`' },
},
{
messageId: 'omitPropAndBoolean',
data: { exceptionsMessage: ' for the following props: `baz`, `bak`' },
},
],
},
]),
});