Skip to content

Commit

Permalink
[New] jsx-boolean-value: add assumeUndefinedIsFalse option
Browse files Browse the repository at this point in the history
  • Loading branch information
developer-bandi authored and ljharb committed Jan 9, 2024
1 parent 67e669d commit 3730edb
Show file tree
Hide file tree
Showing 4 changed files with 84 additions and 2 deletions.
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`' },
},
],
},
]),
});

0 comments on commit 3730edb

Please sign in to comment.