diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6f468f9264..8bd7d07507 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -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)
@@ -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
diff --git a/docs/rules/jsx-boolean-value.md b/docs/rules/jsx-boolean-value.md
index d96285b9cf..1a0c2bde17 100644
--- a/docs/rules/jsx-boolean-value.md
+++ b/docs/rules/jsx-boolean-value.md
@@ -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"] }`:
@@ -40,6 +44,18 @@ Examples of **correct** code for this rule, when configured with `"always"`, or
var Hello = ;
```
+Examples of **incorrect** code for this rule, when configured with `"never", { "assumeUndefinedIsFalse": true }`, or with `"always", { "never": ["personal"], "assumeUndefinedIsFalse": true }`:
+
+```jsx
+var Hello = ;
+```
+
+Examples of **correct** code for this rule, when configured with `"never", { "assumeUndefinedIsFalse": true }`, or with `"always", { "never": ["personal"], "assumeUndefinedIsFalse": true }`:
+
+```jsx
+var Hello = ;
+```
+
## When Not To Use It
If you do not want to enforce any style for boolean attributes, then you can disable this rule.
diff --git a/lib/rules/jsx-boolean-value.js b/lib/rules/jsx-boolean-value.js
index 43395e2490..ae6dda0da7 100644
--- a/lib/rules/jsx-boolean-value.js
+++ b/lib/rules/jsx-boolean-value.js
@@ -39,7 +39,7 @@ function getErrorData(exceptions) {
* @param {Set} exceptions
* @param {string} propName
* @returns {boolean} propName
-*/
+ */
function isAlways(configuration, exceptions, propName) {
const isException = exceptions.has(propName);
if (configuration === ALWAYS) {
@@ -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 = {
@@ -94,6 +96,9 @@ module.exports = {
additionalProperties: false,
properties: {
[NEVER]: exceptionsSchema,
+ assumeUndefinedIsFalse: {
+ type: 'boolean',
+ },
},
}],
additionalItems: false,
@@ -106,6 +111,9 @@ module.exports = {
additionalProperties: false,
properties: {
[ALWAYS]: exceptionsSchema,
+ assumeUndefinedIsFalse: {
+ type: 'boolean',
+ },
},
}],
additionalItems: false,
@@ -139,6 +147,7 @@ module.exports = {
}
if (
isNever(configuration, exceptions, propName)
+ && !configObject.assumeUndefinedIsFalse
&& value
&& value.type === 'JSXExpressionContainer'
&& value.expression.value === true
@@ -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]]);
+ },
+ });
+ }
},
};
},
diff --git a/tests/lib/rules/jsx-boolean-value.js b/tests/lib/rules/jsx-boolean-value.js
index de7e9f710d..678b492740 100644
--- a/tests/lib/rules/jsx-boolean-value.js
+++ b/tests/lib/rules/jsx-boolean-value.js
@@ -48,6 +48,14 @@ ruleTester.run('jsx-boolean-value', rule, {
code: ';',
options: ['never', { always: ['foo'] }],
},
+ {
+ code: ';',
+ options: ['never', { assumeUndefinedIsFalse: true }],
+ },
+ {
+ code: ';',
+ options: ['never', { assumeUndefinedIsFalse: true, always: ['foo'] }],
+ },
]),
invalid: parsers.all([
{
@@ -110,5 +118,32 @@ ruleTester.run('jsx-boolean-value', rule, {
},
],
},
+ {
+ code: ';',
+ output: ';',
+ options: ['never', { assumeUndefinedIsFalse: true }],
+ errors: [
+ { messageId: 'omitPropAndBoolean_noMessage' },
+ { messageId: 'omitPropAndBoolean_noMessage' },
+ ],
+ },
+ {
+ code: ';',
+ output: ';',
+ 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`' },
+ },
+ ],
+ },
]),
});