From bd6f6ccec2b9c24b39af0bb31ebb93dfdc40da90 Mon Sep 17 00:00:00 2001 From: Axetroy Date: Wed, 21 Aug 2024 20:44:59 +0800 Subject: [PATCH 01/26] Add `prefer-math-min-max` --- docs/rules/prefer-math-min-max.md | 56 ++++ readme.md | 1 + rules/prefer-math-min-max.js | 96 +++++++ test/package.mjs | 1 + test/prefer-math-min-max.mjs | 35 +++ test/snapshots/prefer-math-min-max.mjs.md | 299 ++++++++++++++++++++ test/snapshots/prefer-math-min-max.mjs.snap | Bin 0 -> 623 bytes 7 files changed, 488 insertions(+) create mode 100644 docs/rules/prefer-math-min-max.md create mode 100644 rules/prefer-math-min-max.js create mode 100644 test/prefer-math-min-max.mjs create mode 100644 test/snapshots/prefer-math-min-max.mjs.md create mode 100644 test/snapshots/prefer-math-min-max.mjs.snap diff --git a/docs/rules/prefer-math-min-max.md b/docs/rules/prefer-math-min-max.md new file mode 100644 index 0000000000..7e8f6577dc --- /dev/null +++ b/docs/rules/prefer-math-min-max.md @@ -0,0 +1,56 @@ +# Prefer `Math.min()` and `Math.max()` over ternary expressions for simple comparisons + +πŸ’Ό This rule is enabled in the βœ… `recommended` [config](https://github.com/sindresorhus/eslint-plugin-unicorn#preset-configs-eslintconfigjs). + +πŸ”§ This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix). + + + + +Enforce the use of `Math.min()` and `Math.max()` instead of ternary expressions for simple comparisons. This makes the code more readable. + +## Examples + + + +```js +height > 50 ? 50 : height; // ❌ +Math.min(height, 50); // βœ… +``` + +```js +height >= 50 ? 50 : height; // ❌ +Math.min(height, 50); // βœ… +``` + +```js +height < 50 ? height : 50; // ❌ +Math.min(height, 50); // βœ… +``` + +```js +height <= 50 ? height : 50; // ❌ +Math.min(height, 50); // βœ… +``` + + + +```js +height > 50 ? height : 50; // ❌ +Math.max(height, 50); // βœ… +``` + +```js +height >= 50 ? height : 50; // ❌ +Math.max(height, 50); // βœ… +``` + +```js +height < 50 ? 50 : height; // ❌ +Math.max(height, 50); // βœ… +``` + +```js +height <= 50 ? 50 : height; // ❌ +Math.max(height, 50); // βœ… +``` diff --git a/readme.md b/readme.md index df459ba791..14e00e6300 100644 --- a/readme.md +++ b/readme.md @@ -193,6 +193,7 @@ If you don't use the preset, ensure you use the same `env` and `parserOptions` c | [prefer-json-parse-buffer](docs/rules/prefer-json-parse-buffer.md) | Prefer reading a JSON file as a buffer. | | πŸ”§ | | | [prefer-keyboard-event-key](docs/rules/prefer-keyboard-event-key.md) | Prefer `KeyboardEvent#key` over `KeyboardEvent#keyCode`. | βœ… | πŸ”§ | | | [prefer-logical-operator-over-ternary](docs/rules/prefer-logical-operator-over-ternary.md) | Prefer using a logical operator over a ternary. | βœ… | | πŸ’‘ | +| [prefer-math-min-max](docs/rules/prefer-math-min-max.md) | Prefer `Math.min()` and `Math.max()` over ternary expressions for simple comparisons. | βœ… | πŸ”§ | | | [prefer-math-trunc](docs/rules/prefer-math-trunc.md) | Enforce the use of `Math.trunc` instead of bitwise operators. | βœ… | πŸ”§ | πŸ’‘ | | [prefer-modern-dom-apis](docs/rules/prefer-modern-dom-apis.md) | Prefer `.before()` over `.insertBefore()`, `.replaceWith()` over `.replaceChild()`, prefer one of `.before()`, `.after()`, `.append()` or `.prepend()` over `insertAdjacentText()` and `insertAdjacentElement()`. | βœ… | πŸ”§ | | | [prefer-modern-math-apis](docs/rules/prefer-modern-math-apis.md) | Prefer modern `Math` APIs over legacy patterns. | βœ… | πŸ”§ | | diff --git a/rules/prefer-math-min-max.js b/rules/prefer-math-min-max.js new file mode 100644 index 0000000000..48fa111132 --- /dev/null +++ b/rules/prefer-math-min-max.js @@ -0,0 +1,96 @@ +'use strict'; + +const MESSAGE_ID = 'prefer-math-min-max'; +const messages = { + [MESSAGE_ID]: 'Prefer `{{replacement}}` to simplify ternary expressions.', +}; + +/** + +@param {import('eslint').Rule.RuleContext} context +@param {import('estree').ConditionalExpression} node +@param {string} method +*/ +function reportPreferMathMinOrMax(context, node, left, right, method) { + const {sourceCode} = context; + + context.report({ + node, + messageId: MESSAGE_ID, + data: { + replacement: `${method}()`, + value: sourceCode.getText(node), + }, + fix: fixer => fixer.replaceText(node, `${method}(${sourceCode.getText(left)}, ${sourceCode.getText(right)})`), + }); +} + +/** @param {import('eslint').Rule.RuleContext} context */ +const create = context => ({ + /** @param {import('estree').ConditionalExpression} node */ + ConditionalExpression(node) { + const {test, consequent, alternate} = node; + + if (test.type !== 'BinaryExpression') { + return; + } + + const {sourceCode} = context; + const {operator, left, right} = test; + + const leftCode = sourceCode.getText(left); + const rightCode = sourceCode.getText(right); + const alternateCode = sourceCode.getText(alternate); + const consequentCode = sourceCode.getText(consequent); + + switch (operator) { + case '>': + case '>=': { + if (leftCode === alternateCode && rightCode === consequentCode) { + // Example `height > 50 ? 50 : height` + // Prefer `Math.min()` + reportPreferMathMinOrMax(context, node, left, right, 'Math.min'); + } else if (leftCode === consequentCode && rightCode === alternateCode) { + // Example `height > 50 ? height : 50` + // Prefer `Math.max()` + reportPreferMathMinOrMax(context, node, left, right, 'Math.max'); + } + + break; + } + + case '<': + case '<=': { + if (leftCode === consequentCode && rightCode === alternateCode) { + // Example `height < 50 ? height : 50` + // Prefer `Math.min()` + reportPreferMathMinOrMax(context, node, left, right, 'Math.min'); + } else if (leftCode === alternateCode && rightCode === consequentCode) { + // Example `height < 50 ? 50 : height` + // Prefer `Math.max()` + reportPreferMathMinOrMax(context, node, left, right, 'Math.max'); + } + + break; + } + + default: { + break; + } + } + }, +}); + +/** @type {import('eslint').Rule.RuleModule} */ +module.exports = { + create, + meta: { + type: 'problem', + docs: { + description: 'Prefer `Math.min()` and `Math.max()` over ternary expressions for simple comparisons.', + recommended: true, + }, + fixable: 'code', + messages, + }, +}; diff --git a/test/package.mjs b/test/package.mjs index 96bfcb1351..7434537ca7 100644 --- a/test/package.mjs +++ b/test/package.mjs @@ -30,6 +30,7 @@ const RULES_WITHOUT_PASS_FAIL_SECTIONS = new Set([ 'filename-case', // Intended to not use `pass`/`fail` section in this rule. 'prefer-modern-math-apis', + 'prefer-math-min-max', ]); test('Every rule is defined in index file in alphabetical order', t => { diff --git a/test/prefer-math-min-max.mjs b/test/prefer-math-min-max.mjs new file mode 100644 index 0000000000..a907bb5c96 --- /dev/null +++ b/test/prefer-math-min-max.mjs @@ -0,0 +1,35 @@ +import {getTester} from './utils/test.mjs'; + +const {test} = getTester(import.meta); + +test.snapshot({ + valid: [ + 'height > 10 ? height : 20', + 'height > 50 ? Math.min(50, height) : height', + ], + invalid: [ + // Prefer `Math.min()` + 'height > 50 ? 50 : height', + 'height >= 50 ? 50 : height', + 'height < 50 ? height : 50', + 'height <= 50 ? height : 50', + + // Prefer `Math.min()` + 'height > maxHeight ? maxHeight : height', + 'height < maxHeight ? height : maxHeight', + + // Prefer `Math.min()` + 'window.height > 50 ? 50 : window.height', + 'window.height < 50 ? window.height : 50', + + // Prefer `Math.max()` + 'height > 50 ? height : 50', + 'height >= 50 ? height : 50', + 'height < 50 ? 50 : height', + 'height <= 50 ? 50 : height', + + // Prefer `Math.max()` + 'height > maxHeight ? height : maxHeight', + 'height < maxHeight ? maxHeight : height', + ], +}); diff --git a/test/snapshots/prefer-math-min-max.mjs.md b/test/snapshots/prefer-math-min-max.mjs.md new file mode 100644 index 0000000000..0ff8b033ee --- /dev/null +++ b/test/snapshots/prefer-math-min-max.mjs.md @@ -0,0 +1,299 @@ +# Snapshot report for `test/prefer-math-min-max.mjs` + +The actual snapshot is saved in `prefer-math-min-max.mjs.snap`. + +Generated by [AVA](https://avajs.dev). + +## invalid(1): height > 50 ? 50 : height + +> Input + + `␊ + 1 | height > 50 ? 50 : height␊ + ` + +> Output + + `␊ + 1 | Math.min(height, 50)␊ + ` + +> Error 1/1 + + `␊ + > 1 | height > 50 ? 50 : height␊ + | ^^^^^^^^^^^^^^^^^^^^^^^^^ Prefer \`Math.min()\` to simplify ternary expressions.␊ + ` + +## invalid(2): height >= 50 ? 50 : height + +> Input + + `␊ + 1 | height >= 50 ? 50 : height␊ + ` + +> Output + + `␊ + 1 | Math.min(height, 50)␊ + ` + +> Error 1/1 + + `␊ + > 1 | height >= 50 ? 50 : height␊ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ Prefer \`Math.min()\` to simplify ternary expressions.␊ + ` + +## invalid(3): height < 50 ? height : 50 + +> Input + + `␊ + 1 | height < 50 ? height : 50␊ + ` + +> Output + + `␊ + 1 | Math.min(height, 50)␊ + ` + +> Error 1/1 + + `␊ + > 1 | height < 50 ? height : 50␊ + | ^^^^^^^^^^^^^^^^^^^^^^^^^ Prefer \`Math.min()\` to simplify ternary expressions.␊ + ` + +## invalid(4): height <= 50 ? height : 50 + +> Input + + `␊ + 1 | height <= 50 ? height : 50␊ + ` + +> Output + + `␊ + 1 | Math.min(height, 50)␊ + ` + +> Error 1/1 + + `␊ + > 1 | height <= 50 ? height : 50␊ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ Prefer \`Math.min()\` to simplify ternary expressions.␊ + ` + +## invalid(5): height > maxHeight ? maxHeight : height + +> Input + + `␊ + 1 | height > maxHeight ? maxHeight : height␊ + ` + +> Output + + `␊ + 1 | Math.min(height, maxHeight)␊ + ` + +> Error 1/1 + + `␊ + > 1 | height > maxHeight ? maxHeight : height␊ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Prefer \`Math.min()\` to simplify ternary expressions.␊ + ` + +## invalid(6): height < maxHeight ? height : maxHeight + +> Input + + `␊ + 1 | height < maxHeight ? height : maxHeight␊ + ` + +> Output + + `␊ + 1 | Math.min(height, maxHeight)␊ + ` + +> Error 1/1 + + `␊ + > 1 | height < maxHeight ? height : maxHeight␊ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Prefer \`Math.min()\` to simplify ternary expressions.␊ + ` + +## invalid(7): window.height > 50 ? 50 : window.height + +> Input + + `␊ + 1 | window.height > 50 ? 50 : window.height␊ + ` + +> Output + + `␊ + 1 | Math.min(window.height, 50)␊ + ` + +> Error 1/1 + + `␊ + > 1 | window.height > 50 ? 50 : window.height␊ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Prefer \`Math.min()\` to simplify ternary expressions.␊ + ` + +## invalid(8): window.height < 50 ? window.height : 50 + +> Input + + `␊ + 1 | window.height < 50 ? window.height : 50␊ + ` + +> Output + + `␊ + 1 | Math.min(window.height, 50)␊ + ` + +> Error 1/1 + + `␊ + > 1 | window.height < 50 ? window.height : 50␊ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Prefer \`Math.min()\` to simplify ternary expressions.␊ + ` + +## invalid(9): height > 50 ? height : 50 + +> Input + + `␊ + 1 | height > 50 ? height : 50␊ + ` + +> Output + + `␊ + 1 | Math.max(height, 50)␊ + ` + +> Error 1/1 + + `␊ + > 1 | height > 50 ? height : 50␊ + | ^^^^^^^^^^^^^^^^^^^^^^^^^ Prefer \`Math.max()\` to simplify ternary expressions.␊ + ` + +## invalid(10): height >= 50 ? height : 50 + +> Input + + `␊ + 1 | height >= 50 ? height : 50␊ + ` + +> Output + + `␊ + 1 | Math.max(height, 50)␊ + ` + +> Error 1/1 + + `␊ + > 1 | height >= 50 ? height : 50␊ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ Prefer \`Math.max()\` to simplify ternary expressions.␊ + ` + +## invalid(11): height < 50 ? 50 : height + +> Input + + `␊ + 1 | height < 50 ? 50 : height␊ + ` + +> Output + + `␊ + 1 | Math.max(height, 50)␊ + ` + +> Error 1/1 + + `␊ + > 1 | height < 50 ? 50 : height␊ + | ^^^^^^^^^^^^^^^^^^^^^^^^^ Prefer \`Math.max()\` to simplify ternary expressions.␊ + ` + +## invalid(12): height <= 50 ? 50 : height + +> Input + + `␊ + 1 | height <= 50 ? 50 : height␊ + ` + +> Output + + `␊ + 1 | Math.max(height, 50)␊ + ` + +> Error 1/1 + + `␊ + > 1 | height <= 50 ? 50 : height␊ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ Prefer \`Math.max()\` to simplify ternary expressions.␊ + ` + +## invalid(13): height > maxHeight ? height : maxHeight + +> Input + + `␊ + 1 | height > maxHeight ? height : maxHeight␊ + ` + +> Output + + `␊ + 1 | Math.max(height, maxHeight)␊ + ` + +> Error 1/1 + + `␊ + > 1 | height > maxHeight ? height : maxHeight␊ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Prefer \`Math.max()\` to simplify ternary expressions.␊ + ` + +## invalid(14): height < maxHeight ? maxHeight : height + +> Input + + `␊ + 1 | height < maxHeight ? maxHeight : height␊ + ` + +> Output + + `␊ + 1 | Math.max(height, maxHeight)␊ + ` + +> Error 1/1 + + `␊ + > 1 | height < maxHeight ? maxHeight : height␊ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Prefer \`Math.max()\` to simplify ternary expressions.␊ + ` diff --git a/test/snapshots/prefer-math-min-max.mjs.snap b/test/snapshots/prefer-math-min-max.mjs.snap new file mode 100644 index 0000000000000000000000000000000000000000..f765ee17430ba5f75ce46b1647ffa900cffd4b4b GIT binary patch literal 623 zcmV-#0+9VdRzV zP3U2A+oRBih5?7)KjT-#v(dooIn;ap>~s)C7uC7KLgD+GnJ+BPEEfSxcv1rtv4yU& zg-`6kkOmN&9K=!paf)cK(kFIlv~UX=MmbpBJsZ-W#ztySR&8 zxMBp#S3n)`NPl3F?jum_+Q@wb>j<|D*|MlSP6rC5?c?++KQm- zuZfi)WSv+Ug=`aB8%x^=&)7clk4N{1p?mdYXmKA|ot(J8!^uQ9Ilxtw=nPD*{YMyO z_PsDUhc&!(PGn)rPI!=n`Lf8cms8rJgUibnEwBGz%Wk;|55(>`4;9I-&ixbBmWXgM zY~j|cCzZmMsJ#AzEqj73!PdpUly4SMY*E7HY>S##j<&Eiv@i7c_-#l8n0C`fek%Pv zej7r5B3U=svB(e$eEd2jLHGk@BNg}rbts{>tZAC2(BR{rIYof1Gb>?`Q)ait3LpQ_ zDM83OvoZ?VX130>t*JVE; Date: Wed, 21 Aug 2024 20:34:34 +0700 Subject: [PATCH 02/26] Update prefer-math-min-max.js --- rules/prefer-math-min-max.js | 1 - 1 file changed, 1 deletion(-) diff --git a/rules/prefer-math-min-max.js b/rules/prefer-math-min-max.js index 48fa111132..16162b897a 100644 --- a/rules/prefer-math-min-max.js +++ b/rules/prefer-math-min-max.js @@ -6,7 +6,6 @@ const messages = { }; /** - @param {import('eslint').Rule.RuleContext} context @param {import('estree').ConditionalExpression} node @param {string} method From dc5aedcb0e35ad6345541fd7ccbb7f19d173bb97 Mon Sep 17 00:00:00 2001 From: Axetroy Date: Wed, 21 Aug 2024 23:06:09 +0800 Subject: [PATCH 03/26] simplify the switch case --- rules/prefer-math-min-max.js | 50 +++++++++++++----------------------- 1 file changed, 18 insertions(+), 32 deletions(-) diff --git a/rules/prefer-math-min-max.js b/rules/prefer-math-min-max.js index 16162b897a..5318b3dc3b 100644 --- a/rules/prefer-math-min-max.js +++ b/rules/prefer-math-min-max.js @@ -42,39 +42,25 @@ const create = context => ({ const alternateCode = sourceCode.getText(alternate); const consequentCode = sourceCode.getText(consequent); - switch (operator) { - case '>': - case '>=': { - if (leftCode === alternateCode && rightCode === consequentCode) { - // Example `height > 50 ? 50 : height` - // Prefer `Math.min()` - reportPreferMathMinOrMax(context, node, left, right, 'Math.min'); - } else if (leftCode === consequentCode && rightCode === alternateCode) { - // Example `height > 50 ? height : 50` - // Prefer `Math.max()` - reportPreferMathMinOrMax(context, node, left, right, 'Math.max'); - } - - break; + if (['>', '>='].includes(operator)) { + if (leftCode === alternateCode && rightCode === consequentCode) { + // Example `height > 50 ? 50 : height` + // Prefer `Math.min()` + reportPreferMathMinOrMax(context, node, left, right, 'Math.min'); + } else if (leftCode === consequentCode && rightCode === alternateCode) { + // Example `height > 50 ? height : 50` + // Prefer `Math.max()` + reportPreferMathMinOrMax(context, node, left, right, 'Math.max'); } - - case '<': - case '<=': { - if (leftCode === consequentCode && rightCode === alternateCode) { - // Example `height < 50 ? height : 50` - // Prefer `Math.min()` - reportPreferMathMinOrMax(context, node, left, right, 'Math.min'); - } else if (leftCode === alternateCode && rightCode === consequentCode) { - // Example `height < 50 ? 50 : height` - // Prefer `Math.max()` - reportPreferMathMinOrMax(context, node, left, right, 'Math.max'); - } - - break; - } - - default: { - break; + } else if (['<', '<='].includes(operator)) { + if (leftCode === consequentCode && rightCode === alternateCode) { + // Example `height < 50 ? height : 50` + // Prefer `Math.min()` + reportPreferMathMinOrMax(context, node, left, right, 'Math.min'); + } else if (leftCode === alternateCode && rightCode === consequentCode) { + // Example `height < 50 ? 50 : height` + // Prefer `Math.max()` + reportPreferMathMinOrMax(context, node, left, right, 'Math.max'); } } }, From 7948804fc2d6d905ce92427ea8f2a3983d70ea1a Mon Sep 17 00:00:00 2001 From: Axetroy Date: Wed, 21 Aug 2024 23:06:59 +0800 Subject: [PATCH 04/26] Remove unnecessary placeholder --- rules/prefer-math-min-max.js | 1 - 1 file changed, 1 deletion(-) diff --git a/rules/prefer-math-min-max.js b/rules/prefer-math-min-max.js index 5318b3dc3b..2f5e2ac764 100644 --- a/rules/prefer-math-min-max.js +++ b/rules/prefer-math-min-max.js @@ -18,7 +18,6 @@ function reportPreferMathMinOrMax(context, node, left, right, method) { messageId: MESSAGE_ID, data: { replacement: `${method}()`, - value: sourceCode.getText(node), }, fix: fixer => fixer.replaceText(node, `${method}(${sourceCode.getText(left)}, ${sourceCode.getText(right)})`), }); From 92babf1c1c7a841d503bdcfae28a964e6cb3703f Mon Sep 17 00:00:00 2001 From: Axetroy Date: Wed, 21 Aug 2024 23:07:38 +0800 Subject: [PATCH 05/26] update description --- docs/rules/prefer-math-min-max.md | 2 +- readme.md | 2 +- rules/prefer-math-min-max.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/rules/prefer-math-min-max.md b/docs/rules/prefer-math-min-max.md index 7e8f6577dc..25bfe15192 100644 --- a/docs/rules/prefer-math-min-max.md +++ b/docs/rules/prefer-math-min-max.md @@ -1,4 +1,4 @@ -# Prefer `Math.min()` and `Math.max()` over ternary expressions for simple comparisons +# Prefer `Math.min()` and `Math.max()` over ternaries for simple comparisons πŸ’Ό This rule is enabled in the βœ… `recommended` [config](https://github.com/sindresorhus/eslint-plugin-unicorn#preset-configs-eslintconfigjs). diff --git a/readme.md b/readme.md index 14e00e6300..33619961eb 100644 --- a/readme.md +++ b/readme.md @@ -193,7 +193,7 @@ If you don't use the preset, ensure you use the same `env` and `parserOptions` c | [prefer-json-parse-buffer](docs/rules/prefer-json-parse-buffer.md) | Prefer reading a JSON file as a buffer. | | πŸ”§ | | | [prefer-keyboard-event-key](docs/rules/prefer-keyboard-event-key.md) | Prefer `KeyboardEvent#key` over `KeyboardEvent#keyCode`. | βœ… | πŸ”§ | | | [prefer-logical-operator-over-ternary](docs/rules/prefer-logical-operator-over-ternary.md) | Prefer using a logical operator over a ternary. | βœ… | | πŸ’‘ | -| [prefer-math-min-max](docs/rules/prefer-math-min-max.md) | Prefer `Math.min()` and `Math.max()` over ternary expressions for simple comparisons. | βœ… | πŸ”§ | | +| [prefer-math-min-max](docs/rules/prefer-math-min-max.md) | Prefer `Math.min()` and `Math.max()` over ternaries for simple comparisons. | βœ… | πŸ”§ | | | [prefer-math-trunc](docs/rules/prefer-math-trunc.md) | Enforce the use of `Math.trunc` instead of bitwise operators. | βœ… | πŸ”§ | πŸ’‘ | | [prefer-modern-dom-apis](docs/rules/prefer-modern-dom-apis.md) | Prefer `.before()` over `.insertBefore()`, `.replaceWith()` over `.replaceChild()`, prefer one of `.before()`, `.after()`, `.append()` or `.prepend()` over `insertAdjacentText()` and `insertAdjacentElement()`. | βœ… | πŸ”§ | | | [prefer-modern-math-apis](docs/rules/prefer-modern-math-apis.md) | Prefer modern `Math` APIs over legacy patterns. | βœ… | πŸ”§ | | diff --git a/rules/prefer-math-min-max.js b/rules/prefer-math-min-max.js index 2f5e2ac764..de83e9bffb 100644 --- a/rules/prefer-math-min-max.js +++ b/rules/prefer-math-min-max.js @@ -71,7 +71,7 @@ module.exports = { meta: { type: 'problem', docs: { - description: 'Prefer `Math.min()` and `Math.max()` over ternary expressions for simple comparisons.', + description: 'Prefer `Math.min()` and `Math.max()` over ternaries for simple comparisons.', recommended: true, }, fixable: 'code', From 87815e94088d0667e8faa1bf7d655a4d5baa8188 Mon Sep 17 00:00:00 2001 From: Axetroy Date: Wed, 21 Aug 2024 23:10:54 +0800 Subject: [PATCH 06/26] fix edge case --- rules/prefer-math-min-max.js | 6 ++++++ test/prefer-math-min-max.mjs | 1 + 2 files changed, 7 insertions(+) diff --git a/rules/prefer-math-min-max.js b/rules/prefer-math-min-max.js index de83e9bffb..059a246fe5 100644 --- a/rules/prefer-math-min-max.js +++ b/rules/prefer-math-min-max.js @@ -36,6 +36,12 @@ const create = context => ({ const {sourceCode} = context; const {operator, left, right} = test; + const checkTypes = new Set(['Literal', 'Identifier', 'MemberExpression', 'CallExpression']); + + if ([left, right, alternate, consequent].some(n => !checkTypes.has(n.type))) { + return; + } + const leftCode = sourceCode.getText(left); const rightCode = sourceCode.getText(right); const alternateCode = sourceCode.getText(alternate); diff --git a/test/prefer-math-min-max.mjs b/test/prefer-math-min-max.mjs index a907bb5c96..c3db318cd1 100644 --- a/test/prefer-math-min-max.mjs +++ b/test/prefer-math-min-max.mjs @@ -6,6 +6,7 @@ test.snapshot({ valid: [ 'height > 10 ? height : 20', 'height > 50 ? Math.min(50, height) : height', + '(0,foo) > 10 ? 10 : (0,foo)', ], invalid: [ // Prefer `Math.min()` From 48de8a4ce8815b353b0f8fdf6b42886702d98937 Mon Sep 17 00:00:00 2001 From: Axetroy Date: Wed, 21 Aug 2024 23:24:22 +0800 Subject: [PATCH 07/26] Fix edge case in `prefer-math-min-max` rule --- rules/prefer-math-min-max.js | 18 ++++++- test/prefer-math-min-max.mjs | 13 +++++ test/snapshots/prefer-math-min-max.mjs.md | 54 ++++++++++++++++++++ test/snapshots/prefer-math-min-max.mjs.snap | Bin 623 -> 769 bytes 4 files changed, 83 insertions(+), 2 deletions(-) diff --git a/rules/prefer-math-min-max.js b/rules/prefer-math-min-max.js index 059a246fe5..e2bd0d2694 100644 --- a/rules/prefer-math-min-max.js +++ b/rules/prefer-math-min-max.js @@ -19,7 +19,21 @@ function reportPreferMathMinOrMax(context, node, left, right, method) { data: { replacement: `${method}()`, }, - fix: fixer => fixer.replaceText(node, `${method}(${sourceCode.getText(left)}, ${sourceCode.getText(right)})`), + * fix(fixer) { + /** + * ```js + * function a() { + * return+foo > 10 ? 10 : +foo + * } + * ``` + */ + if (node.parent.type === 'ReturnStatement' && node.parent.argument === node && node.parent.start + 'return'.length === node.start) { + // If there is no space between ReturnStatement and ConditionalExpression, add a space. + yield fixer.insertTextBefore(node, ' '); + } + + yield fixer.replaceText(node, `${method}(${sourceCode.getText(left)}, ${sourceCode.getText(right)})`); + }, }); } @@ -36,7 +50,7 @@ const create = context => ({ const {sourceCode} = context; const {operator, left, right} = test; - const checkTypes = new Set(['Literal', 'Identifier', 'MemberExpression', 'CallExpression']); + const checkTypes = new Set(['Literal', 'Identifier', 'MemberExpression', 'CallExpression', 'UnaryExpression']); if ([left, right, alternate, consequent].some(n => !checkTypes.has(n.type))) { return; diff --git a/test/prefer-math-min-max.mjs b/test/prefer-math-min-max.mjs index c3db318cd1..5f5b6b59d3 100644 --- a/test/prefer-math-min-max.mjs +++ b/test/prefer-math-min-max.mjs @@ -1,3 +1,4 @@ +import {outdent} from 'outdent'; import {getTester} from './utils/test.mjs'; const {test} = getTester(import.meta); @@ -32,5 +33,17 @@ test.snapshot({ // Prefer `Math.max()` 'height > maxHeight ? height : maxHeight', 'height < maxHeight ? maxHeight : height', + + // Edge test when there is no space between ReturnStatement and ConditionalExpression + outdent` + function a() { + return +foo > 10 ? 10 : +foo + } + `, + outdent` + function a() { + return+foo > 10 ? 10 : +foo + } + `, ], }); diff --git a/test/snapshots/prefer-math-min-max.mjs.md b/test/snapshots/prefer-math-min-max.mjs.md index 0ff8b033ee..03fa42dd24 100644 --- a/test/snapshots/prefer-math-min-max.mjs.md +++ b/test/snapshots/prefer-math-min-max.mjs.md @@ -297,3 +297,57 @@ Generated by [AVA](https://avajs.dev). > 1 | height < maxHeight ? maxHeight : height␊ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Prefer \`Math.max()\` to simplify ternary expressions.␊ ` + +## invalid(15): function a() { return +foo > 10 ? 10 : +foo } + +> Input + + `␊ + 1 | function a() {␊ + 2 | return +foo > 10 ? 10 : +foo␊ + 3 | }␊ + ` + +> Output + + `␊ + 1 | function a() {␊ + 2 | return Math.min(+foo, 10)␊ + 3 | }␊ + ` + +> Error 1/1 + + `␊ + 1 | function a() {␊ + > 2 | return +foo > 10 ? 10 : +foo␊ + | ^^^^^^^^^^^^^^^^^^^^^ Prefer \`Math.min()\` to simplify ternary expressions.␊ + 3 | }␊ + ` + +## invalid(16): function a() { return+foo > 10 ? 10 : +foo } + +> Input + + `␊ + 1 | function a() {␊ + 2 | return+foo > 10 ? 10 : +foo␊ + 3 | }␊ + ` + +> Output + + `␊ + 1 | function a() {␊ + 2 | return Math.min(+foo, 10)␊ + 3 | }␊ + ` + +> Error 1/1 + + `␊ + 1 | function a() {␊ + > 2 | return+foo > 10 ? 10 : +foo␊ + | ^^^^^^^^^^^^^^^^^^^^^ Prefer \`Math.min()\` to simplify ternary expressions.␊ + 3 | }␊ + ` diff --git a/test/snapshots/prefer-math-min-max.mjs.snap b/test/snapshots/prefer-math-min-max.mjs.snap index f765ee17430ba5f75ce46b1647ffa900cffd4b4b..fc4d52b9741d409e444faa05fabe20efda5d9c72 100644 GIT binary patch literal 769 zcmV+c1OEI$RzV3|P4F>Xk}?MYpa^!17YS5^r7nL-}T*gGDJ}d#vE7| g9pcz-Ny`5``OZRx7}-5QkRumL*Ng5)yp&Gp8t! zch5>HD5z)aVua6r=#(hr-LtY1^6l9s^R}VN@GFPKG`VHBzl7^+%G63+!xG2jdMn2voHp4*-WU9h5mwiW%e|3A18=^S!q zzKPA`Mauwnn85ZT?J__ECU7AGw$IJc)0u zP3U2A+oRBih5?7)KjT-#v(dooIn;ap>~s)C7uC7KLgD+GnJ+BPEEfSxcv1rtv4yU& zg-`6kkOmN&9K=!paf)cK(kFIlv~UX=MmbpBJsZ-W#ztySR&8 zxMBp#S3n)`NPl3F?jum_+Q@wb>j<|D*|MlSP6rC5?c?++KQm- zuZfi)WSv+Ug=`aB8%x^=&)7clk4N{1p?mdYXmKA|ot(J8!^uQ9Ilxtw=nPD*{YMyO z_PsDUhc&!(PGn)rPI!=n`Lf8cms8rJgUibnEwBGz%Wk;|55(>`4;9I-&ixbBmWXgM zY~j|cCzZmMsJ#AzEqj73!PdpUly4SMY*E7HY>S##j<&Eiv@i7c_-#l8n0C`fek%Pv zej7r5B3U=svB(e$eEd2jLHGk@BNg}rbts{>tZAC2(BR{rIYof1Gb>?`Q)ait3LpQ_ zDM83OvoZ?VX130>t*JVE; Date: Wed, 21 Aug 2024 23:27:20 +0800 Subject: [PATCH 08/26] update docs --- docs/rules/prefer-math-min-max.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/rules/prefer-math-min-max.md b/docs/rules/prefer-math-min-max.md index 25bfe15192..ad044454dd 100644 --- a/docs/rules/prefer-math-min-max.md +++ b/docs/rules/prefer-math-min-max.md @@ -7,7 +7,9 @@ -Enforce the use of `Math.min()` and `Math.max()` instead of ternary expressions for simple comparisons. This makes the code more readable. +This rule enforces the use of `Math.min()` and `Math.max()` functions instead of ternary expressions when performing simple comparisons, such as selecting the minimum or maximum value between two or more options. + +By replacing ternary expressions with these functions, the code becomes more concise, easier to understand, and less prone to errors. It also enhances consistency across the codebase, ensuring that the same approach is used for similar operations, ultimately improving the overall readability and maintainability of the code. ## Examples From 7cd3d39d86adde4186f5a2ae2a14fc222fbec36e Mon Sep 17 00:00:00 2001 From: Axetroy Date: Wed, 21 Aug 2024 23:29:25 +0800 Subject: [PATCH 09/26] remove context.report() with return statement --- rules/prefer-math-min-max.js | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/rules/prefer-math-min-max.js b/rules/prefer-math-min-max.js index e2bd0d2694..b36eab6adc 100644 --- a/rules/prefer-math-min-max.js +++ b/rules/prefer-math-min-max.js @@ -13,7 +13,7 @@ const messages = { function reportPreferMathMinOrMax(context, node, left, right, method) { const {sourceCode} = context; - context.report({ + return { node, messageId: MESSAGE_ID, data: { @@ -34,7 +34,7 @@ function reportPreferMathMinOrMax(context, node, left, right, method) { yield fixer.replaceText(node, `${method}(${sourceCode.getText(left)}, ${sourceCode.getText(right)})`); }, - }); + }; } /** @param {import('eslint').Rule.RuleContext} context */ @@ -65,21 +65,25 @@ const create = context => ({ if (leftCode === alternateCode && rightCode === consequentCode) { // Example `height > 50 ? 50 : height` // Prefer `Math.min()` - reportPreferMathMinOrMax(context, node, left, right, 'Math.min'); - } else if (leftCode === consequentCode && rightCode === alternateCode) { + return reportPreferMathMinOrMax(context, node, left, right, 'Math.min'); + } + + if (leftCode === consequentCode && rightCode === alternateCode) { // Example `height > 50 ? height : 50` // Prefer `Math.max()` - reportPreferMathMinOrMax(context, node, left, right, 'Math.max'); + return reportPreferMathMinOrMax(context, node, left, right, 'Math.max'); } } else if (['<', '<='].includes(operator)) { if (leftCode === consequentCode && rightCode === alternateCode) { // Example `height < 50 ? height : 50` // Prefer `Math.min()` - reportPreferMathMinOrMax(context, node, left, right, 'Math.min'); - } else if (leftCode === alternateCode && rightCode === consequentCode) { + return reportPreferMathMinOrMax(context, node, left, right, 'Math.min'); + } + + if (leftCode === alternateCode && rightCode === consequentCode) { // Example `height < 50 ? 50 : height` // Prefer `Math.max()` - reportPreferMathMinOrMax(context, node, left, right, 'Math.max'); + return reportPreferMathMinOrMax(context, node, left, right, 'Math.max'); } } }, From 616e3d00634ac38c8878e1114134e5b3d10e20d0 Mon Sep 17 00:00:00 2001 From: Axetroy Date: Wed, 21 Aug 2024 23:35:00 +0800 Subject: [PATCH 10/26] update docs --- docs/rules/prefer-math-min-max.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/rules/prefer-math-min-max.md b/docs/rules/prefer-math-min-max.md index ad044454dd..c5de13f45e 100644 --- a/docs/rules/prefer-math-min-max.md +++ b/docs/rules/prefer-math-min-max.md @@ -7,7 +7,7 @@ -This rule enforces the use of `Math.min()` and `Math.max()` functions instead of ternary expressions when performing simple comparisons, such as selecting the minimum or maximum value between two or more options. +This rule enforces the use of `Math.min()` and `Math.max()` functions instead of ternary expressions when performing simple comparisons, such as selecting the minimum or maximum value between two or more options. By replacing ternary expressions with these functions, the code becomes more concise, easier to understand, and less prone to errors. It also enhances consistency across the codebase, ensuring that the same approach is used for similar operations, ultimately improving the overall readability and maintainability of the code. From f0cf4bc6fab3f7125487f6c46d63e2db3f178b34 Mon Sep 17 00:00:00 2001 From: Axetroy Date: Wed, 21 Aug 2024 23:38:22 +0800 Subject: [PATCH 11/26] refactor code to improve readability and performance --- rules/prefer-math-min-max.js | 84 ++++++++++++++++++------------------ 1 file changed, 43 insertions(+), 41 deletions(-) diff --git a/rules/prefer-math-min-max.js b/rules/prefer-math-min-max.js index b36eab6adc..4f70a55477 100644 --- a/rules/prefer-math-min-max.js +++ b/rules/prefer-math-min-max.js @@ -38,56 +38,58 @@ function reportPreferMathMinOrMax(context, node, left, right, method) { } /** @param {import('eslint').Rule.RuleContext} context */ -const create = context => ({ - /** @param {import('estree').ConditionalExpression} node */ - ConditionalExpression(node) { - const {test, consequent, alternate} = node; +const create = context => { + const allowNodeTypes = new Set(['Literal', 'Identifier', 'MemberExpression', 'CallExpression', 'UnaryExpression']); - if (test.type !== 'BinaryExpression') { - return; - } + return { + /** @param {import('estree').ConditionalExpression} node */ + ConditionalExpression(node) { + const {test, consequent, alternate} = node; - const {sourceCode} = context; - const {operator, left, right} = test; + if (test.type !== 'BinaryExpression') { + return; + } - const checkTypes = new Set(['Literal', 'Identifier', 'MemberExpression', 'CallExpression', 'UnaryExpression']); + const {sourceCode} = context; + const {operator, left, right} = test; - if ([left, right, alternate, consequent].some(n => !checkTypes.has(n.type))) { - return; - } + if ([left, right, alternate, consequent].some(n => !allowNodeTypes.has(n.type))) { + return; + } - const leftCode = sourceCode.getText(left); - const rightCode = sourceCode.getText(right); - const alternateCode = sourceCode.getText(alternate); - const consequentCode = sourceCode.getText(consequent); + const leftCode = sourceCode.getText(left); + const rightCode = sourceCode.getText(right); + const alternateCode = sourceCode.getText(alternate); + const consequentCode = sourceCode.getText(consequent); - if (['>', '>='].includes(operator)) { - if (leftCode === alternateCode && rightCode === consequentCode) { - // Example `height > 50 ? 50 : height` - // Prefer `Math.min()` - return reportPreferMathMinOrMax(context, node, left, right, 'Math.min'); - } + if (['>', '>='].includes(operator)) { + if (leftCode === alternateCode && rightCode === consequentCode) { + // Example `height > 50 ? 50 : height` + // Prefer `Math.min()` + return reportPreferMathMinOrMax(context, node, left, right, 'Math.min'); + } - if (leftCode === consequentCode && rightCode === alternateCode) { - // Example `height > 50 ? height : 50` - // Prefer `Math.max()` - return reportPreferMathMinOrMax(context, node, left, right, 'Math.max'); - } - } else if (['<', '<='].includes(operator)) { - if (leftCode === consequentCode && rightCode === alternateCode) { - // Example `height < 50 ? height : 50` - // Prefer `Math.min()` - return reportPreferMathMinOrMax(context, node, left, right, 'Math.min'); - } + if (leftCode === consequentCode && rightCode === alternateCode) { + // Example `height > 50 ? height : 50` + // Prefer `Math.max()` + return reportPreferMathMinOrMax(context, node, left, right, 'Math.max'); + } + } else if (['<', '<='].includes(operator)) { + if (leftCode === consequentCode && rightCode === alternateCode) { + // Example `height < 50 ? height : 50` + // Prefer `Math.min()` + return reportPreferMathMinOrMax(context, node, left, right, 'Math.min'); + } - if (leftCode === alternateCode && rightCode === consequentCode) { - // Example `height < 50 ? 50 : height` - // Prefer `Math.max()` - return reportPreferMathMinOrMax(context, node, left, right, 'Math.max'); + if (leftCode === alternateCode && rightCode === consequentCode) { + // Example `height < 50 ? 50 : height` + // Prefer `Math.max()` + return reportPreferMathMinOrMax(context, node, left, right, 'Math.max'); + } } - } - }, -}); + }, + }; +}; /** @type {import('eslint').Rule.RuleModule} */ module.exports = { From 9b27968b875aa0fcdaba274f11ce2b3bbcddf8ae Mon Sep 17 00:00:00 2001 From: Axetroy Date: Wed, 21 Aug 2024 23:48:07 +0800 Subject: [PATCH 12/26] support edge case `(0,foo) > 10 ? 10 : (0,foo)` --- rules/prefer-math-min-max.js | 95 ++++++++++---------- test/prefer-math-min-max.mjs | 3 +- test/snapshots/prefer-math-min-max.mjs.md | 21 +++++ test/snapshots/prefer-math-min-max.mjs.snap | Bin 769 -> 821 bytes 4 files changed, 73 insertions(+), 46 deletions(-) diff --git a/rules/prefer-math-min-max.js b/rules/prefer-math-min-max.js index 4f70a55477..528efa41ca 100644 --- a/rules/prefer-math-min-max.js +++ b/rules/prefer-math-min-max.js @@ -8,6 +8,8 @@ const messages = { /** @param {import('eslint').Rule.RuleContext} context @param {import('estree').ConditionalExpression} node +@param {import('estree').Node} left +@param {import('estree').Node} right @param {string} method */ function reportPreferMathMinOrMax(context, node, left, right, method) { @@ -32,64 +34,67 @@ function reportPreferMathMinOrMax(context, node, left, right, method) { yield fixer.insertTextBefore(node, ' '); } - yield fixer.replaceText(node, `${method}(${sourceCode.getText(left)}, ${sourceCode.getText(right)})`); + let leftText = sourceCode.getText(left); + let rightText = sourceCode.getText(right); + + if (left.type === 'SequenceExpression') { + leftText = `(${leftText})`; + } + + if (right.type === 'SequenceExpression') { + rightText = `(${rightText})`; + } + + yield fixer.replaceText(node, `${method}(${leftText}, ${rightText})`); }, }; } /** @param {import('eslint').Rule.RuleContext} context */ -const create = context => { - const allowNodeTypes = new Set(['Literal', 'Identifier', 'MemberExpression', 'CallExpression', 'UnaryExpression']); +const create = context => ({ + /** @param {import('estree').ConditionalExpression} node */ + ConditionalExpression(node) { + const {test, consequent, alternate} = node; - return { - /** @param {import('estree').ConditionalExpression} node */ - ConditionalExpression(node) { - const {test, consequent, alternate} = node; + if (test.type !== 'BinaryExpression') { + return; + } - if (test.type !== 'BinaryExpression') { - return; - } + const {sourceCode} = context; + const {operator, left, right} = test; - const {sourceCode} = context; - const {operator, left, right} = test; + const leftCode = sourceCode.getText(left); + const rightCode = sourceCode.getText(right); + const alternateCode = sourceCode.getText(alternate); + const consequentCode = sourceCode.getText(consequent); - if ([left, right, alternate, consequent].some(n => !allowNodeTypes.has(n.type))) { - return; + if (['>', '>='].includes(operator)) { + if (leftCode === alternateCode && rightCode === consequentCode) { + // Example `height > 50 ? 50 : height` + // Prefer `Math.min()` + return reportPreferMathMinOrMax(context, node, left, right, 'Math.min'); } - const leftCode = sourceCode.getText(left); - const rightCode = sourceCode.getText(right); - const alternateCode = sourceCode.getText(alternate); - const consequentCode = sourceCode.getText(consequent); - - if (['>', '>='].includes(operator)) { - if (leftCode === alternateCode && rightCode === consequentCode) { - // Example `height > 50 ? 50 : height` - // Prefer `Math.min()` - return reportPreferMathMinOrMax(context, node, left, right, 'Math.min'); - } - - if (leftCode === consequentCode && rightCode === alternateCode) { - // Example `height > 50 ? height : 50` - // Prefer `Math.max()` - return reportPreferMathMinOrMax(context, node, left, right, 'Math.max'); - } - } else if (['<', '<='].includes(operator)) { - if (leftCode === consequentCode && rightCode === alternateCode) { - // Example `height < 50 ? height : 50` - // Prefer `Math.min()` - return reportPreferMathMinOrMax(context, node, left, right, 'Math.min'); - } + if (leftCode === consequentCode && rightCode === alternateCode) { + // Example `height > 50 ? height : 50` + // Prefer `Math.max()` + return reportPreferMathMinOrMax(context, node, left, right, 'Math.max'); + } + } else if (['<', '<='].includes(operator)) { + if (leftCode === consequentCode && rightCode === alternateCode) { + // Example `height < 50 ? height : 50` + // Prefer `Math.min()` + return reportPreferMathMinOrMax(context, node, left, right, 'Math.min'); + } - if (leftCode === alternateCode && rightCode === consequentCode) { - // Example `height < 50 ? 50 : height` - // Prefer `Math.max()` - return reportPreferMathMinOrMax(context, node, left, right, 'Math.max'); - } + if (leftCode === alternateCode && rightCode === consequentCode) { + // Example `height < 50 ? 50 : height` + // Prefer `Math.max()` + return reportPreferMathMinOrMax(context, node, left, right, 'Math.max'); } - }, - }; -}; + } + }, +}); /** @type {import('eslint').Rule.RuleModule} */ module.exports = { diff --git a/test/prefer-math-min-max.mjs b/test/prefer-math-min-max.mjs index 5f5b6b59d3..45deb95150 100644 --- a/test/prefer-math-min-max.mjs +++ b/test/prefer-math-min-max.mjs @@ -7,7 +7,6 @@ test.snapshot({ valid: [ 'height > 10 ? height : 20', 'height > 50 ? Math.min(50, height) : height', - '(0,foo) > 10 ? 10 : (0,foo)', ], invalid: [ // Prefer `Math.min()` @@ -45,5 +44,7 @@ test.snapshot({ return+foo > 10 ? 10 : +foo } `, + + '(0,foo) > 10 ? 10 : (0,foo)', ], }); diff --git a/test/snapshots/prefer-math-min-max.mjs.md b/test/snapshots/prefer-math-min-max.mjs.md index 03fa42dd24..7533b13711 100644 --- a/test/snapshots/prefer-math-min-max.mjs.md +++ b/test/snapshots/prefer-math-min-max.mjs.md @@ -351,3 +351,24 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^^^^^^^^^^^^^^^ Prefer \`Math.min()\` to simplify ternary expressions.␊ 3 | }␊ ` + +## invalid(17): (0,foo) > 10 ? 10 : (0,foo) + +> Input + + `␊ + 1 | (0,foo) > 10 ? 10 : (0,foo)␊ + ` + +> Output + + `␊ + 1 | Math.min((0,foo), 10)␊ + ` + +> Error 1/1 + + `␊ + > 1 | (0,foo) > 10 ? 10 : (0,foo)␊ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ Prefer \`Math.min()\` to simplify ternary expressions.␊ + ` diff --git a/test/snapshots/prefer-math-min-max.mjs.snap b/test/snapshots/prefer-math-min-max.mjs.snap index fc4d52b9741d409e444faa05fabe20efda5d9c72..28813576e1f591720901bb4edf012cd9b4d73d77 100644 GIT binary patch literal 821 zcmV-51IqkCRzV3_;U9|#00000000Bcm(6d}Fc`)=5JKyXJ4i1epkY;;^kcAUZ3Pk%9H#Ba2`yV4 zYLO;Y;%?dKP9Ve;Ziv%%Lj08|H1*=xY1}kRnnRVk@{>2u@2&m#(l|2h<4^97D?~6c zk@o~!C%TCT6;0{D5aEwQ0zGJ}(Dy&)L)f+Su{*Sh`{imd(24%Oe!FzH5=cB}B<=?idpa4`M%b#%CRU-XD#2`He?qROe`ThhlzRyk z002zkL)--i4lw9A`Qe6MKIdR=M@F6kUfC>PjM!2bS&dLK3c*whSFy5=)X?QCJe|S_dpIln$I? zYha(&1iL!(i!2sNxfEEK`SB}N3>MkC)&a{)4ok3gv4i5xLIz8$T)|l4>nacnO+!0e zeovo@+)HE)^r$2M@ z0(tkWpn`&Ww!ugE^oLG?Lf$XnymxtDvb$3|P4F>Xk}?MYpa^!17YS5^r7nL-}T*gGDJ}d#vE7| g9pcz-Ny`5``OZRx7}-5QkRumL*Ng5)yp&Gp8t! zch5>HD5z)aVua6r=#(hr-LtY1^6l9s^R}VN@GFPKG`VHBzl7^+%G63+!xG2jdMn2voHp4*-WU9h5mwiW%e|3A18=^S!q zzKPA`Mauwnn85ZT?J__ECU7AGw$IJc)0u Date: Wed, 21 Aug 2024 23:51:07 +0800 Subject: [PATCH 13/26] refactor code to improve readability and performance --- rules/prefer-math-min-max.js | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/rules/prefer-math-min-max.js b/rules/prefer-math-min-max.js index 528efa41ca..6a9b066ea6 100644 --- a/rules/prefer-math-min-max.js +++ b/rules/prefer-math-min-max.js @@ -34,16 +34,14 @@ function reportPreferMathMinOrMax(context, node, left, right, method) { yield fixer.insertTextBefore(node, ' '); } - let leftText = sourceCode.getText(left); - let rightText = sourceCode.getText(right); - - if (left.type === 'SequenceExpression') { - leftText = `(${leftText})`; - } - - if (right.type === 'SequenceExpression') { - rightText = `(${rightText})`; - } + /** + * Fix edge case: + * ```js + * (0,foo) > 10 ? 10 : (0,foo) + * ``` + */ + const leftText = left.type === 'SequenceExpression' ? `(${sourceCode.getText(left)})` : sourceCode.getText(left); + const rightText = right.type === 'SequenceExpression' ? `(${sourceCode.getText(right)})` : sourceCode.getText(right); yield fixer.replaceText(node, `${method}(${leftText}, ${rightText})`); }, From 6b7c587cf29ad75148f224db66b6fc6d07326f3f Mon Sep 17 00:00:00 2001 From: Axetroy Date: Wed, 21 Aug 2024 23:58:07 +0800 Subject: [PATCH 14/26] add more test case --- test/prefer-math-min-max.mjs | 9 +++ test/snapshots/prefer-math-min-max.mjs.md | 69 ++++++++++++++++++++ test/snapshots/prefer-math-min-max.mjs.snap | Bin 821 -> 1023 bytes 3 files changed, 78 insertions(+) diff --git a/test/prefer-math-min-max.mjs b/test/prefer-math-min-max.mjs index 45deb95150..6624851a7c 100644 --- a/test/prefer-math-min-max.mjs +++ b/test/prefer-math-min-max.mjs @@ -46,5 +46,14 @@ test.snapshot({ `, '(0,foo) > 10 ? 10 : (0,foo)', + + 'foo.bar() > 10 ? 10 : foo.bar()', + outdent` + async function foo() { + return await foo.bar() > 10 ? 10 : await foo.bar() + } + `, + + 'foo.length > bar.length ? bar.length : foo.length', ], }); diff --git a/test/snapshots/prefer-math-min-max.mjs.md b/test/snapshots/prefer-math-min-max.mjs.md index 7533b13711..677bb9f487 100644 --- a/test/snapshots/prefer-math-min-max.mjs.md +++ b/test/snapshots/prefer-math-min-max.mjs.md @@ -372,3 +372,72 @@ Generated by [AVA](https://avajs.dev). > 1 | (0,foo) > 10 ? 10 : (0,foo)␊ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ Prefer \`Math.min()\` to simplify ternary expressions.␊ ` + +## invalid(18): foo.bar() > 10 ? 10 : foo.bar() + +> Input + + `␊ + 1 | foo.bar() > 10 ? 10 : foo.bar()␊ + ` + +> Output + + `␊ + 1 | Math.min(foo.bar(), 10)␊ + ` + +> Error 1/1 + + `␊ + > 1 | foo.bar() > 10 ? 10 : foo.bar()␊ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Prefer \`Math.min()\` to simplify ternary expressions.␊ + ` + +## invalid(19): async function foo() { return await foo.bar() > 10 ? 10 : await foo.bar() } + +> Input + + `␊ + 1 | async function foo() {␊ + 2 | return await foo.bar() > 10 ? 10 : await foo.bar()␊ + 3 | }␊ + ` + +> Output + + `␊ + 1 | async function foo() {␊ + 2 | return Math.min(await foo.bar(), 10)␊ + 3 | }␊ + ` + +> Error 1/1 + + `␊ + 1 | async function foo() {␊ + > 2 | return await foo.bar() > 10 ? 10 : await foo.bar()␊ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Prefer \`Math.min()\` to simplify ternary expressions.␊ + 3 | }␊ + ` + +## invalid(20): foo.length > bar.length ? bar.length : foo.length + +> Input + + `␊ + 1 | foo.length > bar.length ? bar.length : foo.length␊ + ` + +> Output + + `␊ + 1 | Math.min(foo.length, bar.length)␊ + ` + +> Error 1/1 + + `␊ + > 1 | foo.length > bar.length ? bar.length : foo.length␊ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Prefer \`Math.min()\` to simplify ternary expressions.␊ + ` diff --git a/test/snapshots/prefer-math-min-max.mjs.snap b/test/snapshots/prefer-math-min-max.mjs.snap index 28813576e1f591720901bb4edf012cd9b4d73d77..372b2043227f6fb4ee66b3fb643aae1bf2ed7be0 100644 GIT binary patch literal 1023 zcmV2$2yJcD8Qpra<2m|fjQ ziRDr3YafdU00000000BcS;218KoCtq2sJnEAO;ZBp&_-KrqGBc0to?!suic6P&SR- zSarRT?X)SR7eI(BhzsJ>9uVTrckl;%3o>!G9(!%C6W6H^RlMoEef!?*?5uL4wGFzn zXMa6a9o;e1!Cl=vAco$Nlu{kKs=nKGpaC@*n(m+RkZzl#XLqS%zd!ACh(orQE*GvA z+-L4e;o97-B7g$JXn>+>khW^5FU?-x@gVLai0dB2264KTechBsfn})4B`;dr>^o=S zA5i!~v5~+40AL8OqaN6@R86&@#R!yI;83vj{hpy~N8qTINvtDK2R%!*ZJnBS#UrLa zvn*KNKiIoX<5cr&!l}kBIUi+ zp(dxy*f2oe8OvcXn~YuY$+|9}Yi!1H5c1Ag9))}}wmO!!=C85)Wbk72x#{~HT^ZKV zBA0>{Hy(~Bx`F`jM2XJ8_}ZWNQ7(Nc1nY1DuOuh(uw~U>Nc?9GOuna?(kHankRAC6mJi|8798HlqSpVi?mqC$BfeEmg zzmS>V3;Zb>Ld$S{orlpQ;-+AeiRp)3U1P4UM!70%>LwEWiGbi2(9@sD({Cc43Rs$> zqY`t}y=_Z!S);Us=y_vd1NkK`27+hNA_BP)yuNvnBO`bQd_wVWyaE|VBC#lDdp|2P zf!%wvMrox@ED2L%qhXP2XnhOg{ECO+3H<;8-3$uNVxy)0jB@?m_%KT1B6J94tNAw5 z+`SYN`^elm+mafk{zHfy65WYdMDROK6*G~nEl*bdlz~!{V&=Fa*Fp?lrD4Ft4osI^ z_x_?>_*00VwK2uI6HmG<6Dp}%HrTIvvbUKH)!cQu(113v#xG6(i~rxk>GK?!x8~d4 tsepNTv5KH2-i99qmwl-Go&OuT{lmZW)6q=TkY-&S{sFzLn{4|Y007i<`gH&R literal 821 zcmV-51IqkCRzV3_;U9|#00000000Bcm(6d}Fc`)=5JKyXJ4i1epkY;;^kcAUZ3Pk%9H#Ba2`yV4 zYLO;Y;%?dKP9Ve;Ziv%%Lj08|H1*=xY1}kRnnRVk@{>2u@2&m#(l|2h<4^97D?~6c zk@o~!C%TCT6;0{D5aEwQ0zGJ}(Dy&)L)f+Su{*Sh`{imd(24%Oe!FzH5=cB}B<=?idpa4`M%b#%CRU-XD#2`He?qROe`ThhlzRyk z002zkL)--i4lw9A`Qe6MKIdR=M@F6kUfC>PjM!2bS&dLK3c*whSFy5=)X?QCJe|S_dpIln$I? zYha(&1iL!(i!2sNxfEEK`SB}N3>MkC)&a{)4ok3gv4i5xLIz8$T)|l4>nacnO+!0e zeovo@+)HE)^r$2M@ z0(tkWpn`&Ww!ugE^oLG?Lf$XnymxtDvb$ Date: Thu, 22 Aug 2024 08:39:33 +0800 Subject: [PATCH 15/26] add more edge test case --- rules/prefer-math-min-max.js | 11 +++- test/prefer-math-min-max.mjs | 10 ++++ test/snapshots/prefer-math-min-max.mjs.md | 56 +++++++++++++++++++- test/snapshots/prefer-math-min-max.mjs.snap | Bin 1023 -> 1143 bytes 4 files changed, 75 insertions(+), 2 deletions(-) diff --git a/rules/prefer-math-min-max.js b/rules/prefer-math-min-max.js index 6a9b066ea6..b28338a944 100644 --- a/rules/prefer-math-min-max.js +++ b/rules/prefer-math-min-max.js @@ -22,14 +22,23 @@ function reportPreferMathMinOrMax(context, node, left, right, method) { replacement: `${method}()`, }, * fix(fixer) { + /** @type {{parent: import('estree'.Node)}} */ + const {parent} = node; /** * ```js * function a() { * return+foo > 10 ? 10 : +foo * } + * + * function* foo() { + * yield+foo > 10 ? 10 : foo + * } * ``` */ - if (node.parent.type === 'ReturnStatement' && node.parent.argument === node && node.parent.start + 'return'.length === node.start) { + if ( + (parent.type === 'ReturnStatement' && parent.argument === node && parent.start + 'return'.length === node.start) + || (parent.type === 'YieldExpression' && parent.argument === node && parent.start + 'yield'.length === node.start) + ) { // If there is no space between ReturnStatement and ConditionalExpression, add a space. yield fixer.insertTextBefore(node, ' '); } diff --git a/test/prefer-math-min-max.mjs b/test/prefer-math-min-max.mjs index 6624851a7c..74d11c238a 100644 --- a/test/prefer-math-min-max.mjs +++ b/test/prefer-math-min-max.mjs @@ -53,6 +53,16 @@ test.snapshot({ return await foo.bar() > 10 ? 10 : await foo.bar() } `, + outdent` + function foo() { + return(foo.bar() > 10) ? 10 : foo.bar() + } + `, + outdent` + function* foo() { + yield+foo > 10 ? 10 : +foo + } + `, 'foo.length > bar.length ? bar.length : foo.length', ], diff --git a/test/snapshots/prefer-math-min-max.mjs.md b/test/snapshots/prefer-math-min-max.mjs.md index 677bb9f487..22628ef301 100644 --- a/test/snapshots/prefer-math-min-max.mjs.md +++ b/test/snapshots/prefer-math-min-max.mjs.md @@ -421,7 +421,61 @@ Generated by [AVA](https://avajs.dev). 3 | }␊ ` -## invalid(20): foo.length > bar.length ? bar.length : foo.length +## invalid(20): function foo() { return(foo.bar() > 10) ? 10 : foo.bar() } + +> Input + + `␊ + 1 | function foo() {␊ + 2 | return(foo.bar() > 10) ? 10 : foo.bar()␊ + 3 | }␊ + ` + +> Output + + `␊ + 1 | function foo() {␊ + 2 | return Math.min(foo.bar(), 10)␊ + 3 | }␊ + ` + +> Error 1/1 + + `␊ + 1 | function foo() {␊ + > 2 | return(foo.bar() > 10) ? 10 : foo.bar()␊ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Prefer \`Math.min()\` to simplify ternary expressions.␊ + 3 | }␊ + ` + +## invalid(21): function* foo() { yield+foo > 10 ? 10 : +foo } + +> Input + + `␊ + 1 | function* foo() {␊ + 2 | yield+foo > 10 ? 10 : +foo␊ + 3 | }␊ + ` + +> Output + + `␊ + 1 | function* foo() {␊ + 2 | yield Math.min(+foo, 10)␊ + 3 | }␊ + ` + +> Error 1/1 + + `␊ + 1 | function* foo() {␊ + > 2 | yield+foo > 10 ? 10 : +foo␊ + | ^^^^^^^^^^^^^^^^^^^^^ Prefer \`Math.min()\` to simplify ternary expressions.␊ + 3 | }␊ + ` + +## invalid(22): foo.length > bar.length ? bar.length : foo.length > Input diff --git a/test/snapshots/prefer-math-min-max.mjs.snap b/test/snapshots/prefer-math-min-max.mjs.snap index 372b2043227f6fb4ee66b3fb643aae1bf2ed7be0..ffb747f9ef10c2eb6373cdb13bbdf6205f05bab9 100644 GIT binary patch literal 1143 zcmV--1c>`VRzVX4M0^ zq3T=D&5wtYrC6%ey`pHlqN+4Gfv-SIQnp(bRH0min*C$=NHI0BZMJmFe0|t#ik7%J ze=>7AW4~rjWzNoA%mN6|4;IKus@RZJ>7mx{SdK&vNt|;e)<(^^a1_<-Q(AKL%TT3omR~URPqElV+`07_U0%PBz zZw-QS-c_JnjG3{1fwVK062WvbcF|?)3WKkq8B0+}J7Z}fq?@q|Lu3@hl6^WKUZE=Mz6K!TTpOebP=?+@H6Cq87nbvPofC=zL4S#lQ=cf2HJ$itXe zqU5H9B{Ht#faN-0fjvcQ>U#xBS4W?)VqugU0}EOoT&V)Eu*P*9u-x!raatFCNN*M_ zSOVpyj3qd(DPkeh(6VoSAAcJ13&d^OVmFcbef()i>`r9sEV&jH{R8~?;}AvR2%Kda z;3pV|CiIp}nu6e);K#pn`UTR?SxN*`%Gr{CgdhLXNl{2UXK5j%o3o3=+l8PRe&S&< zN-l2ubGe@244?3@m>8GEpdo(ZyH%9jw75jZHAOBJ!o|Mx${lUXQgjVOj)#|ENLI(t zU{2O`sDfaBvjzK`{r|zfpUws{^NwRC7R>^bpbuxlw6g#U(1(2*ur;(tjb#rqzuDS( z5Q@BK0}S&Ag83yw-=fNN44dm27`#|$G7gzId_UD?LUk#KDl^n|WcY0c!;c};Z;0t9 z7N!hHQzXg}qV{c@E9PZg=TUghGe1Cji3^dSA(}%X=e^fAXXO9{4Zu6J{)JN@!-9w` zipk#3^2A{K-Yn~Sp&=R^4vjqZn_Nccn;qs?xD3tcYe?vV*J!df*y{Hv*Ix~rQB*8U zO(PV=H}2PpOHqb6An9qX-_y ziGY~x*)F>7{YAO(ClkJFb%Jw8cDf`JDymwplK<)z-Te=z6o-$-`@RyiJbH>Ku6z3k zm0#pWVQKROZFrw)!@zP8mMXtds{Hh|NNSR>94)G09(_98B}V^2^J5-9zJ!^H;FqGeMiJUdx__~$U{#uqMRbwDgj$^nzkxw+g1yz&=8H` zo0|KL`_KGrSCQ?@t~MS^m_~F7NprNuK3JT0snR?AF9hzZyTiu|GuFj(QkY(d{{UF> JOIYI~002uMBL4sY literal 1023 zcmV2$2yJcD8Qpra<2m|fjQ ziRDr3YafdU00000000BcS;218KoCtq2sJnEAO;ZBp&_-KrqGBc0to?!suic6P&SR- zSarRT?X)SR7eI(BhzsJ>9uVTrckl;%3o>!G9(!%C6W6H^RlMoEef!?*?5uL4wGFzn zXMa6a9o;e1!Cl=vAco$Nlu{kKs=nKGpaC@*n(m+RkZzl#XLqS%zd!ACh(orQE*GvA z+-L4e;o97-B7g$JXn>+>khW^5FU?-x@gVLai0dB2264KTechBsfn})4B`;dr>^o=S zA5i!~v5~+40AL8OqaN6@R86&@#R!yI;83vj{hpy~N8qTINvtDK2R%!*ZJnBS#UrLa zvn*KNKiIoX<5cr&!l}kBIUi+ zp(dxy*f2oe8OvcXn~YuY$+|9}Yi!1H5c1Ag9))}}wmO!!=C85)Wbk72x#{~HT^ZKV zBA0>{Hy(~Bx`F`jM2XJ8_}ZWNQ7(Nc1nY1DuOuh(uw~U>Nc?9GOuna?(kHankRAC6mJi|8798HlqSpVi?mqC$BfeEmg zzmS>V3;Zb>Ld$S{orlpQ;-+AeiRp)3U1P4UM!70%>LwEWiGbi2(9@sD({Cc43Rs$> zqY`t}y=_Z!S);Us=y_vd1NkK`27+hNA_BP)yuNvnBO`bQd_wVWyaE|VBC#lDdp|2P zf!%wvMrox@ED2L%qhXP2XnhOg{ECO+3H<;8-3$uNVxy)0jB@?m_%KT1B6J94tNAw5 z+`SYN`^elm+mafk{zHfy65WYdMDROK6*G~nEl*bdlz~!{V&=Fa*Fp?lrD4Ft4osI^ z_x_?>_*00VwK2uI6HmG<6Dp}%HrTIvvbUKH)!cQu(113v#xG6(i~rxk>GK?!x8~d4 tsepNTv5KH2-i99qmwl-Go&OuT{lmZW)6q=TkY-&S{sFzLn{4|Y007i<`gH&R From 63b9d31728b78c4b032eea0f060a51b2d08cd178 Mon Sep 17 00:00:00 2001 From: Axetroy Date: Thu, 22 Aug 2024 09:12:07 +0800 Subject: [PATCH 16/26] add more edge case --- rules/prefer-math-min-max.js | 13 ++------ test/prefer-math-min-max.mjs | 5 +++ test/snapshots/prefer-math-min-max.mjs.md | 33 ++++++++++++++++++-- test/snapshots/prefer-math-min-max.mjs.snap | Bin 1143 -> 1193 bytes 4 files changed, 37 insertions(+), 14 deletions(-) diff --git a/rules/prefer-math-min-max.js b/rules/prefer-math-min-max.js index b28338a944..f8555f9dd9 100644 --- a/rules/prefer-math-min-max.js +++ b/rules/prefer-math-min-max.js @@ -24,19 +24,10 @@ function reportPreferMathMinOrMax(context, node, left, right, method) { * fix(fixer) { /** @type {{parent: import('estree'.Node)}} */ const {parent} = node; - /** - * ```js - * function a() { - * return+foo > 10 ? 10 : +foo - * } - * - * function* foo() { - * yield+foo > 10 ? 10 : foo - * } - * ``` - */ if ( + // Edge case: `return+foo > 10 ? 10 : +foo` (parent.type === 'ReturnStatement' && parent.argument === node && parent.start + 'return'.length === node.start) + // Edge case: `yield+foo > 10 ? 10 : foo` || (parent.type === 'YieldExpression' && parent.argument === node && parent.start + 'yield'.length === node.start) ) { // If there is no space between ReturnStatement and ConditionalExpression, add a space. diff --git a/test/prefer-math-min-max.mjs b/test/prefer-math-min-max.mjs index 74d11c238a..9d289ef3e7 100644 --- a/test/prefer-math-min-max.mjs +++ b/test/prefer-math-min-max.mjs @@ -53,6 +53,11 @@ test.snapshot({ return await foo.bar() > 10 ? 10 : await foo.bar() } `, + outdent` + async function foo() { + await(+foo > 10 ? 10 : +foo) + } + `, outdent` function foo() { return(foo.bar() > 10) ? 10 : foo.bar() diff --git a/test/snapshots/prefer-math-min-max.mjs.md b/test/snapshots/prefer-math-min-max.mjs.md index 22628ef301..6ca752b3d6 100644 --- a/test/snapshots/prefer-math-min-max.mjs.md +++ b/test/snapshots/prefer-math-min-max.mjs.md @@ -421,7 +421,34 @@ Generated by [AVA](https://avajs.dev). 3 | }␊ ` -## invalid(20): function foo() { return(foo.bar() > 10) ? 10 : foo.bar() } +## invalid(20): async function foo() { await(+foo > 10 ? 10 : +foo) } + +> Input + + `␊ + 1 | async function foo() {␊ + 2 | await(+foo > 10 ? 10 : +foo)␊ + 3 | }␊ + ` + +> Output + + `␊ + 1 | async function foo() {␊ + 2 | await(Math.min(+foo, 10))␊ + 3 | }␊ + ` + +> Error 1/1 + + `␊ + 1 | async function foo() {␊ + > 2 | await(+foo > 10 ? 10 : +foo)␊ + | ^^^^^^^^^^^^^^^^^^^^^ Prefer \`Math.min()\` to simplify ternary expressions.␊ + 3 | }␊ + ` + +## invalid(21): function foo() { return(foo.bar() > 10) ? 10 : foo.bar() } > Input @@ -448,7 +475,7 @@ Generated by [AVA](https://avajs.dev). 3 | }␊ ` -## invalid(21): function* foo() { yield+foo > 10 ? 10 : +foo } +## invalid(22): function* foo() { yield+foo > 10 ? 10 : +foo } > Input @@ -475,7 +502,7 @@ Generated by [AVA](https://avajs.dev). 3 | }␊ ` -## invalid(22): foo.length > bar.length ? bar.length : foo.length +## invalid(23): foo.length > bar.length ? bar.length : foo.length > Input diff --git a/test/snapshots/prefer-math-min-max.mjs.snap b/test/snapshots/prefer-math-min-max.mjs.snap index ffb747f9ef10c2eb6373cdb13bbdf6205f05bab9..36a8b6d62b496dba371fabf86dae24e9593ca329 100644 GIT binary patch literal 1193 zcmV;a1XlY&RzVBbx@V<;#iViYwYpwWaE4TZI= zakNX@!O-!d#`vl)Mty<@qrMsA7w`-C4g4&oth?*!z3si-M#mlqE8PD%_y0RP_gvW5 z)@$a*bNl0g>gY~QZC=)mZKbAHBsnt+RaM`tI#7mE0p{H=okzNDD0RDPI`->>N=0#$ z)rpg-(<%2gaw>IZ%j zt(%6O_Y%|ZSe9vlJRx^%z0$FD(r4?OfUgC#tBV0&X@w&&_AOf83>eEuf#d9ettAy( z>{@%Xos{=(7D|Pf6-x^wU9pS|hEuUqK3nGme2uJFhCLz|okcHC$=mgzDQycA|S5o3G5?^ikTArlE5i z8kPa$nrT8AWcQOTyPxdC2YWP~6=dduXC@X+1582-Mx(UT07YoQ9t+qKiZL$|gUoNP zb`Io1CJ+F_{DEM8NwGteg|^{x9fkHwgr?w-iPNX4&Je28VN`{oE+fNl2^fA1nSMh| zKZ!6EK$;*?i4b*f+fpH?nPvv1=RKnVvRhn)1PjqP5;+&VzIiL#AXot2q4h7k1{swE zw-j42+`SYl_O7wf{YYx2=|6;&9YuFSHWB<8ql+2H)-}&ocE~`hi4i%f$*rLb z9>vLkm>mem?|c7HE&NSIM=kfUZ|+Gq$b#Z^%LVdZy+U|l9qvJ5ws~d=ts;OAgaAgx zh$@yRDAr<(SoFadT_HbFg&c@g2xnWvIU*McblE@j;65VZdd^UNkVXC}qOlwt9+)3m zZlEaqk5Rz8LIFe1h$#Ah5dB}&T*T}QB3t8?z|oh(4N?x1zH-Qtdf3&~T19vYeu#+N z4)~M}A_*6d!X7^zVIiD(ftox0?ef5uZ@zRX-*M@#!1|j|j)E82d H*dzb|n5ab@ literal 1143 zcmV--1c>`VRzVX4M0^ zq3T=D&5wtYrC6%ey`pHlqN+4Gfv-SIQnp(bRH0min*C$=NHI0BZMJmFe0|t#ik7%J ze=>7AW4~rjWzNoA%mN6|4;IKus@RZJ>7mx{SdK&vNt|;e)<(^^a1_<-Q(AKL%TT3omR~URPqElV+`07_U0%PBz zZw-QS-c_JnjG3{1fwVK062WvbcF|?)3WKkq8B0+}J7Z}fq?@q|Lu3@hl6^WKUZE=Mz6K!TTpOebP=?+@H6Cq87nbvPofC=zL4S#lQ=cf2HJ$itXe zqU5H9B{Ht#faN-0fjvcQ>U#xBS4W?)VqugU0}EOoT&V)Eu*P*9u-x!raatFCNN*M_ zSOVpyj3qd(DPkeh(6VoSAAcJ13&d^OVmFcbef()i>`r9sEV&jH{R8~?;}AvR2%Kda z;3pV|CiIp}nu6e);K#pn`UTR?SxN*`%Gr{CgdhLXNl{2UXK5j%o3o3=+l8PRe&S&< zN-l2ubGe@244?3@m>8GEpdo(ZyH%9jw75jZHAOBJ!o|Mx${lUXQgjVOj)#|ENLI(t zU{2O`sDfaBvjzK`{r|zfpUws{^NwRC7R>^bpbuxlw6g#U(1(2*ur;(tjb#rqzuDS( z5Q@BK0}S&Ag83yw-=fNN44dm27`#|$G7gzId_UD?LUk#KDl^n|WcY0c!;c};Z;0t9 z7N!hHQzXg}qV{c@E9PZg=TUghGe1Cji3^dSA(}%X=e^fAXXO9{4Zu6J{)JN@!-9w` zipk#3^2A{K-Yn~Sp&=R^4vjqZn_Nccn;qs?xD3tcYe?vV*J!df*y{Hv*Ix~rQB*8U zO(PV=H}2PpOHqb6An9qX-_y ziGY~x*)F>7{YAO(ClkJFb%Jw8cDf`JDymwplK<)z-Te=z6o-$-`@RyiJbH>Ku6z3k zm0#pWVQKROZFrw)!@zP8mMXtds{Hh|NNSR>94)G09(_98B}V^2^J5-9zJ!^H;FqGeMiJUdx__~$U{#uqMRbwDgj$^nzkxw+g1yz&=8H` zo0|KL`_KGrSCQ?@t~MS^m_~F7NprNuK3JT0snR?AF9hzZyTiu|GuFj(QkY(d{{UF> JOIYI~002uMBL4sY From f5afe1e12a478305043c09b3e6fcb473aeec070e Mon Sep 17 00:00:00 2001 From: Axetroy Date: Thu, 22 Aug 2024 09:15:13 +0800 Subject: [PATCH 17/26] refactor code to use fixSpaceAroundKeyword in prefer-math-min-max rule --- rules/prefer-math-min-max.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/rules/prefer-math-min-max.js b/rules/prefer-math-min-max.js index f8555f9dd9..cb4a143829 100644 --- a/rules/prefer-math-min-max.js +++ b/rules/prefer-math-min-max.js @@ -1,4 +1,5 @@ 'use strict'; +const {fixSpaceAroundKeyword} = require('./fix/index.js'); const MESSAGE_ID = 'prefer-math-min-max'; const messages = { @@ -31,7 +32,7 @@ function reportPreferMathMinOrMax(context, node, left, right, method) { || (parent.type === 'YieldExpression' && parent.argument === node && parent.start + 'yield'.length === node.start) ) { // If there is no space between ReturnStatement and ConditionalExpression, add a space. - yield fixer.insertTextBefore(node, ' '); + yield * fixSpaceAroundKeyword(fixer, node, sourceCode); } /** From f88e2266f3853be44149e816422eb16bb6730d03 Mon Sep 17 00:00:00 2001 From: Axetroy Date: Thu, 22 Aug 2024 10:02:06 +0800 Subject: [PATCH 18/26] improve codecov --- test/prefer-math-min-max.mjs | 1 + 1 file changed, 1 insertion(+) diff --git a/test/prefer-math-min-max.mjs b/test/prefer-math-min-max.mjs index 9d289ef3e7..28e6b0daf4 100644 --- a/test/prefer-math-min-max.mjs +++ b/test/prefer-math-min-max.mjs @@ -7,6 +7,7 @@ test.snapshot({ valid: [ 'height > 10 ? height : 20', 'height > 50 ? Math.min(50, height) : height', + 'foo ? foo : bar', ], invalid: [ // Prefer `Math.min()` From 49d6e0e8316bb29ba4075b578efb90a30b9d7046 Mon Sep 17 00:00:00 2001 From: Axetroy Date: Thu, 22 Aug 2024 10:42:18 +0800 Subject: [PATCH 19/26] Simplify the code --- rules/prefer-math-min-max.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/rules/prefer-math-min-max.js b/rules/prefer-math-min-max.js index cb4a143829..f8f28f31dd 100644 --- a/rules/prefer-math-min-max.js +++ b/rules/prefer-math-min-max.js @@ -61,11 +61,7 @@ const create = context => ({ const {sourceCode} = context; const {operator, left, right} = test; - - const leftCode = sourceCode.getText(left); - const rightCode = sourceCode.getText(right); - const alternateCode = sourceCode.getText(alternate); - const consequentCode = sourceCode.getText(consequent); + const [leftCode, rightCode, alternateCode, consequentCode] = [left, right, alternate, consequent].map(n => sourceCode.getText(n)); if (['>', '>='].includes(operator)) { if (leftCode === alternateCode && rightCode === consequentCode) { From 2d8b9aa49c8c0491c105bb003e3e800238ba705b Mon Sep 17 00:00:00 2001 From: Axetroy Date: Thu, 22 Aug 2024 11:18:38 +0800 Subject: [PATCH 20/26] rename `reportPreferMathMinOrMax` to `getProblem` --- rules/prefer-math-min-max.js | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/rules/prefer-math-min-max.js b/rules/prefer-math-min-max.js index f8f28f31dd..a242f8088c 100644 --- a/rules/prefer-math-min-max.js +++ b/rules/prefer-math-min-max.js @@ -13,7 +13,7 @@ const messages = { @param {import('estree').Node} right @param {string} method */ -function reportPreferMathMinOrMax(context, node, left, right, method) { +function getProblem(context, node, left, right, method) { const {sourceCode} = context; return { @@ -22,6 +22,7 @@ function reportPreferMathMinOrMax(context, node, left, right, method) { data: { replacement: `${method}()`, }, + /** @param {import('eslint').Rule.RuleFixer} fixer */ * fix(fixer) { /** @type {{parent: import('estree'.Node)}} */ const {parent} = node; @@ -67,25 +68,25 @@ const create = context => ({ if (leftCode === alternateCode && rightCode === consequentCode) { // Example `height > 50 ? 50 : height` // Prefer `Math.min()` - return reportPreferMathMinOrMax(context, node, left, right, 'Math.min'); + return getProblem(context, node, left, right, 'Math.min'); } if (leftCode === consequentCode && rightCode === alternateCode) { // Example `height > 50 ? height : 50` // Prefer `Math.max()` - return reportPreferMathMinOrMax(context, node, left, right, 'Math.max'); + return getProblem(context, node, left, right, 'Math.max'); } } else if (['<', '<='].includes(operator)) { if (leftCode === consequentCode && rightCode === alternateCode) { // Example `height < 50 ? height : 50` // Prefer `Math.min()` - return reportPreferMathMinOrMax(context, node, left, right, 'Math.min'); + return getProblem(context, node, left, right, 'Math.min'); } if (leftCode === alternateCode && rightCode === consequentCode) { // Example `height < 50 ? 50 : height` // Prefer `Math.max()` - return reportPreferMathMinOrMax(context, node, left, right, 'Math.max'); + return getProblem(context, node, left, right, 'Math.max'); } } }, From 9feae753f587b3c3717c722041746fd606bd65c3 Mon Sep 17 00:00:00 2001 From: Axetroy Date: Thu, 22 Aug 2024 14:42:42 +0800 Subject: [PATCH 21/26] refactor prefer-math-min-max rule to improve readability and performance --- rules/prefer-math-min-max.js | 26 ++++++++------------------ 1 file changed, 8 insertions(+), 18 deletions(-) diff --git a/rules/prefer-math-min-max.js b/rules/prefer-math-min-max.js index a242f8088c..caa84f1835 100644 --- a/rules/prefer-math-min-max.js +++ b/rules/prefer-math-min-max.js @@ -16,36 +16,27 @@ const messages = { function getProblem(context, node, left, right, method) { const {sourceCode} = context; + // Catch edge case: `(0,foo) > 10 ? 10 : (0,foo)` + const getText = n => n.type === 'SequenceExpression' ? `(${sourceCode.getText(n)})` : sourceCode.getText(n); + return { node, messageId: MESSAGE_ID, - data: { - replacement: `${method}()`, - }, + data: {replacement: `${method}()`}, /** @param {import('eslint').Rule.RuleFixer} fixer */ * fix(fixer) { /** @type {{parent: import('estree'.Node)}} */ const {parent} = node; if ( - // Edge case: `return+foo > 10 ? 10 : +foo` + // Catch edge case: `return+foo > 10 ? 10 : +foo` (parent.type === 'ReturnStatement' && parent.argument === node && parent.start + 'return'.length === node.start) - // Edge case: `yield+foo > 10 ? 10 : foo` + // Catch edge case: `yield+foo > 10 ? 10 : foo` || (parent.type === 'YieldExpression' && parent.argument === node && parent.start + 'yield'.length === node.start) ) { - // If there is no space between ReturnStatement and ConditionalExpression, add a space. yield * fixSpaceAroundKeyword(fixer, node, sourceCode); } - /** - * Fix edge case: - * ```js - * (0,foo) > 10 ? 10 : (0,foo) - * ``` - */ - const leftText = left.type === 'SequenceExpression' ? `(${sourceCode.getText(left)})` : sourceCode.getText(left); - const rightText = right.type === 'SequenceExpression' ? `(${sourceCode.getText(right)})` : sourceCode.getText(right); - - yield fixer.replaceText(node, `${method}(${leftText}, ${rightText})`); + yield fixer.replaceText(node, `${method}(${getText(left)}, ${getText(right)})`); }, }; } @@ -60,9 +51,8 @@ const create = context => ({ return; } - const {sourceCode} = context; const {operator, left, right} = test; - const [leftCode, rightCode, alternateCode, consequentCode] = [left, right, alternate, consequent].map(n => sourceCode.getText(n)); + const [leftCode, rightCode, alternateCode, consequentCode] = [left, right, alternate, consequent].map(n => context.sourceCode.getText(n)); if (['>', '>='].includes(operator)) { if (leftCode === alternateCode && rightCode === consequentCode) { From 9bdef777c5997c9bedf92e38fe139434adf9b57d Mon Sep 17 00:00:00 2001 From: Axetroy Date: Thu, 22 Aug 2024 14:46:43 +0800 Subject: [PATCH 22/26] refactor prefer-math-min-max rule for improved readability and performance --- rules/prefer-math-min-max.js | 37 +++++++++++++++--------------------- 1 file changed, 15 insertions(+), 22 deletions(-) diff --git a/rules/prefer-math-min-max.js b/rules/prefer-math-min-max.js index caa84f1835..c9967c7360 100644 --- a/rules/prefer-math-min-max.js +++ b/rules/prefer-math-min-max.js @@ -54,30 +54,23 @@ const create = context => ({ const {operator, left, right} = test; const [leftCode, rightCode, alternateCode, consequentCode] = [left, right, alternate, consequent].map(n => context.sourceCode.getText(n)); - if (['>', '>='].includes(operator)) { - if (leftCode === alternateCode && rightCode === consequentCode) { - // Example `height > 50 ? 50 : height` - // Prefer `Math.min()` - return getProblem(context, node, left, right, 'Math.min'); - } + const isGreaterOrEqual = ['>', '>='].includes(operator); + const isLessOrEqual = ['<', '<='].includes(operator); - if (leftCode === consequentCode && rightCode === alternateCode) { - // Example `height > 50 ? height : 50` - // Prefer `Math.max()` - return getProblem(context, node, left, right, 'Math.max'); - } - } else if (['<', '<='].includes(operator)) { - if (leftCode === consequentCode && rightCode === alternateCode) { - // Example `height < 50 ? height : 50` - // Prefer `Math.min()` - return getProblem(context, node, left, right, 'Math.min'); - } + // Prefer `Math.min()` + if ( + (isGreaterOrEqual && leftCode === alternateCode && rightCode === consequentCode) // Example `height > 50 ? 50 : height` + || (isLessOrEqual && leftCode === consequentCode && rightCode === alternateCode) // Example `height < 50 ? height : 50` + ) { + return getProblem(context, node, left, right, 'Math.min'); + } - if (leftCode === alternateCode && rightCode === consequentCode) { - // Example `height < 50 ? 50 : height` - // Prefer `Math.max()` - return getProblem(context, node, left, right, 'Math.max'); - } + // Prefer `Math.max()` + if ( + (isGreaterOrEqual && leftCode === consequentCode && rightCode === alternateCode) // Example `height > 50 ? height : 50` + || (isLessOrEqual && leftCode === alternateCode && rightCode === consequentCode) // Example `height < 50 ? 50 : height` + ) { + return getProblem(context, node, left, right, 'Math.max'); } }, }); From 9544a1d02ee6a9d8a58dd4de35bac952246c2fa9 Mon Sep 17 00:00:00 2001 From: Axetroy Date: Thu, 22 Aug 2024 14:48:57 +0800 Subject: [PATCH 23/26] update format --- rules/prefer-math-min-max.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rules/prefer-math-min-max.js b/rules/prefer-math-min-max.js index c9967c7360..ca66fef150 100644 --- a/rules/prefer-math-min-max.js +++ b/rules/prefer-math-min-max.js @@ -28,7 +28,7 @@ function getProblem(context, node, left, right, method) { /** @type {{parent: import('estree'.Node)}} */ const {parent} = node; if ( - // Catch edge case: `return+foo > 10 ? 10 : +foo` + // Catch edge case: `return+foo > 10 ? 10 : +foo` (parent.type === 'ReturnStatement' && parent.argument === node && parent.start + 'return'.length === node.start) // Catch edge case: `yield+foo > 10 ? 10 : foo` || (parent.type === 'YieldExpression' && parent.argument === node && parent.start + 'yield'.length === node.start) From 6ae06c646f486a7ba3ccd7afd15b8bb28e42c853 Mon Sep 17 00:00:00 2001 From: fisker Date: Fri, 23 Aug 2024 14:22:45 +0800 Subject: [PATCH 24/26] Add test --- test/prefer-math-min-max.mjs | 1 + test/snapshots/prefer-math-min-max.mjs.md | 10 +++++++++- test/snapshots/prefer-math-min-max.mjs.snap | Bin 1193 -> 1231 bytes 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/test/prefer-math-min-max.mjs b/test/prefer-math-min-max.mjs index 28e6b0daf4..6da574dfbd 100644 --- a/test/prefer-math-min-max.mjs +++ b/test/prefer-math-min-max.mjs @@ -69,6 +69,7 @@ test.snapshot({ yield+foo > 10 ? 10 : +foo } `, + 'export default+foo > 10 ? 10 : +foo', 'foo.length > bar.length ? bar.length : foo.length', ], diff --git a/test/snapshots/prefer-math-min-max.mjs.md b/test/snapshots/prefer-math-min-max.mjs.md index 6ca752b3d6..63458b697f 100644 --- a/test/snapshots/prefer-math-min-max.mjs.md +++ b/test/snapshots/prefer-math-min-max.mjs.md @@ -502,7 +502,15 @@ Generated by [AVA](https://avajs.dev). 3 | }␊ ` -## invalid(23): foo.length > bar.length ? bar.length : foo.length +## invalid(23): export default+foo > 10 ? 10 : +foo + +> Input + + `␊ + 1 | export default+foo > 10 ? 10 : +foo␊ + ` + +## invalid(24): foo.length > bar.length ? bar.length : foo.length > Input diff --git a/test/snapshots/prefer-math-min-max.mjs.snap b/test/snapshots/prefer-math-min-max.mjs.snap index 36a8b6d62b496dba371fabf86dae24e9593ca329..ed0e2ac92e0554053deac1c294a02750385350b0 100644 GIT binary patch literal 1231 zcmV;=1TgzSRzVbb{d*_^!J7?O@ z#){>vKJwll-qwB7xAfL2(|$}Wv!-aJS*YvgTHS{#R8*J`zH}a$o=qBF-SNGb+qD|; z$1i#wPf>YZG@9^j5>w zJ1sWI^kbCsl$hvVH9aSQ;j>fNV;mWoUJOSRI^-sb9 znbw4GC=T|1R;C08_h!R!##V@{;MV92ZcN4xfWVN0^XDo zFs(+KS?;4+3mIy$2V=TJejtZz%Q-}_t>GMz3siJ18hY>?QLvv&%pBw?{{X467!MEJ z53R6Jn*8@r!D~_l6VHe=^?xJve=@mL7JA& zZM3WA^N@rYMD%FLfdmv*Eo`Vt8=4BEqovzxejTc?Lfp=q`RJSIud!xZK<&*$!el03 tp2emSw8D#*?ZxE?DnDobLQeV;&6(NS%=G7ve%%#!)j#40W7%pY0092gRjvR4 literal 1193 zcmV;a1XlY&RzVBbx@V<;#iViYwYpwWaE4TZI= zakNX@!O-!d#`vl)Mty<@qrMsA7w`-C4g4&oth?*!z3si-M#mlqE8PD%_y0RP_gvW5 z)@$a*bNl0g>gY~QZC=)mZKbAHBsnt+RaM`tI#7mE0p{H=okzNDD0RDPI`->>N=0#$ z)rpg-(<%2gaw>IZ%j zt(%6O_Y%|ZSe9vlJRx^%z0$FD(r4?OfUgC#tBV0&X@w&&_AOf83>eEuf#d9ettAy( z>{@%Xos{=(7D|Pf6-x^wU9pS|hEuUqK3nGme2uJFhCLz|okcHC$=mgzDQycA|S5o3G5?^ikTArlE5i z8kPa$nrT8AWcQOTyPxdC2YWP~6=dduXC@X+1582-Mx(UT07YoQ9t+qKiZL$|gUoNP zb`Io1CJ+F_{DEM8NwGteg|^{x9fkHwgr?w-iPNX4&Je28VN`{oE+fNl2^fA1nSMh| zKZ!6EK$;*?i4b*f+fpH?nPvv1=RKnVvRhn)1PjqP5;+&VzIiL#AXot2q4h7k1{swE zw-j42+`SYl_O7wf{YYx2=|6;&9YuFSHWB<8ql+2H)-}&ocE~`hi4i%f$*rLb z9>vLkm>mem?|c7HE&NSIM=kfUZ|+Gq$b#Z^%LVdZy+U|l9qvJ5ws~d=ts;OAgaAgx zh$@yRDAr<(SoFadT_HbFg&c@g2xnWvIU*McblE@j;65VZdd^UNkVXC}qOlwt9+)3m zZlEaqk5Rz8LIFe1h$#Ah5dB}&T*T}QB3t8?z|oh(4N?x1zH-Qtdf3&~T19vYeu#+N z4)~M}A_*6d!X7^zVIiD(ftox0?ef5uZ@zRX-*M@#!1|j|j)E82d H*dzb|n5ab@ From 8d08770a85b060e114c7fc50bb1e78ed790aaf4a Mon Sep 17 00:00:00 2001 From: fisker Date: Fri, 23 Aug 2024 14:31:00 +0800 Subject: [PATCH 25/26] Fix tests / Simplify rule --- rules/prefer-math-min-max.js | 78 ++++++++++---------- test/snapshots/prefer-math-min-max.mjs.md | 15 +++- test/snapshots/prefer-math-min-max.mjs.snap | Bin 1231 -> 1256 bytes 3 files changed, 52 insertions(+), 41 deletions(-) diff --git a/rules/prefer-math-min-max.js b/rules/prefer-math-min-max.js index ca66fef150..5043e3db6b 100644 --- a/rules/prefer-math-min-max.js +++ b/rules/prefer-math-min-max.js @@ -3,7 +3,7 @@ const {fixSpaceAroundKeyword} = require('./fix/index.js'); const MESSAGE_ID = 'prefer-math-min-max'; const messages = { - [MESSAGE_ID]: 'Prefer `{{replacement}}` to simplify ternary expressions.', + [MESSAGE_ID]: 'Prefer `Math.{{method}}()` to simplify ternary expressions.', }; /** @@ -14,64 +14,62 @@ const messages = { @param {string} method */ function getProblem(context, node, left, right, method) { - const {sourceCode} = context; - - // Catch edge case: `(0,foo) > 10 ? 10 : (0,foo)` - const getText = n => n.type === 'SequenceExpression' ? `(${sourceCode.getText(n)})` : sourceCode.getText(n); - - return { - node, - messageId: MESSAGE_ID, - data: {replacement: `${method}()`}, - /** @param {import('eslint').Rule.RuleFixer} fixer */ - * fix(fixer) { - /** @type {{parent: import('estree'.Node)}} */ - const {parent} = node; - if ( - // Catch edge case: `return+foo > 10 ? 10 : +foo` - (parent.type === 'ReturnStatement' && parent.argument === node && parent.start + 'return'.length === node.start) - // Catch edge case: `yield+foo > 10 ? 10 : foo` - || (parent.type === 'YieldExpression' && parent.argument === node && parent.start + 'yield'.length === node.start) - ) { - yield * fixSpaceAroundKeyword(fixer, node, sourceCode); - } - - yield fixer.replaceText(node, `${method}(${getText(left)}, ${getText(right)})`); - }, - }; } /** @param {import('eslint').Rule.RuleContext} context */ const create = context => ({ /** @param {import('estree').ConditionalExpression} node */ - ConditionalExpression(node) { - const {test, consequent, alternate} = node; + ConditionalExpression(conditionalExpression) { + const {test, consequent, alternate} = conditionalExpression; if (test.type !== 'BinaryExpression') { return; } const {operator, left, right} = test; - const [leftCode, rightCode, alternateCode, consequentCode] = [left, right, alternate, consequent].map(n => context.sourceCode.getText(n)); + const [leftCode, rightCode, alternateCode, consequentCode] = [left, right, alternate, consequent].map(node => context.sourceCode.getText(node)); - const isGreaterOrEqual = ['>', '>='].includes(operator); - const isLessOrEqual = ['<', '<='].includes(operator); + const isGreaterOrEqual = operator === '>' || operator === '>='; + const isLessOrEqual = operator === '<' || operator === '<='; + + let method; // Prefer `Math.min()` if ( - (isGreaterOrEqual && leftCode === alternateCode && rightCode === consequentCode) // Example `height > 50 ? 50 : height` - || (isLessOrEqual && leftCode === consequentCode && rightCode === alternateCode) // Example `height < 50 ? height : 50` + // `height > 50 ? 50 : height` + (isGreaterOrEqual && leftCode === alternateCode && rightCode === consequentCode) + // `height < 50 ? height : 50` + || (isLessOrEqual && leftCode === consequentCode && rightCode === alternateCode) + ) { + method = 'min'; + } else if ( + // `height > 50 ? height : 50` + (isGreaterOrEqual && leftCode === consequentCode && rightCode === alternateCode) + // `height < 50 ? 50 : height` + || (isLessOrEqual && leftCode === alternateCode && rightCode === consequentCode) ) { - return getProblem(context, node, left, right, 'Math.min'); + method = 'max'; } - // Prefer `Math.max()` - if ( - (isGreaterOrEqual && leftCode === consequentCode && rightCode === alternateCode) // Example `height > 50 ? height : 50` - || (isLessOrEqual && leftCode === alternateCode && rightCode === consequentCode) // Example `height < 50 ? 50 : height` - ) { - return getProblem(context, node, left, right, 'Math.max'); + if (!method) { + return; } + + return { + node: conditionalExpression, + messageId: MESSAGE_ID, + data: {method}, + /** @param {import('eslint').Rule.RuleFixer} fixer */ + * fix(fixer) { + const {sourceCode} = context; + + yield * fixSpaceAroundKeyword(fixer, conditionalExpression, sourceCode); + + const argumentsText = [left, right].map(node => node.type === 'SequenceExpression' ? `(${sourceCode.getText(node)})` : sourceCode.getText(node)); + + yield fixer.replaceText(conditionalExpression, `Math.${method}(${argumentsText.join(', ')})`); + }, + }; }, }); diff --git a/test/snapshots/prefer-math-min-max.mjs.md b/test/snapshots/prefer-math-min-max.mjs.md index 63458b697f..daa61b660c 100644 --- a/test/snapshots/prefer-math-min-max.mjs.md +++ b/test/snapshots/prefer-math-min-max.mjs.md @@ -435,7 +435,7 @@ Generated by [AVA](https://avajs.dev). `␊ 1 | async function foo() {␊ - 2 | await(Math.min(+foo, 10))␊ + 2 | await (Math.min(+foo, 10))␊ 3 | }␊ ` @@ -510,6 +510,19 @@ Generated by [AVA](https://avajs.dev). 1 | export default+foo > 10 ? 10 : +foo␊ ` +> Output + + `␊ + 1 | export default Math.min(+foo, 10)␊ + ` + +> Error 1/1 + + `␊ + > 1 | export default+foo > 10 ? 10 : +foo␊ + | ^^^^^^^^^^^^^^^^^^^^^ Prefer \`Math.min()\` to simplify ternary expressions.␊ + ` + ## invalid(24): foo.length > bar.length ? bar.length : foo.length > Input diff --git a/test/snapshots/prefer-math-min-max.mjs.snap b/test/snapshots/prefer-math-min-max.mjs.snap index ed0e2ac92e0554053deac1c294a02750385350b0..7847c6b5bf3a17f840a5d360abac1838e3b207f5 100644 GIT binary patch literal 1256 zcmVC~9jb+r&V}9?OoU3`!0NP@z{!-$hC%-9q$2iovvo;>(H~D`@IM{&^BN*{xiPigdXdM zJwFP6I_`E^#I_gTY`oowuXArTE}VP631GrezChD**{^q(|8rXuip(ms5twD4${AV=$L9-)?007_!zRkPfUSQc)06RPYeFq{RLTA74I`$z% zR^YMV5UgQ8u)@&sy>KB3%=sh;`~b{Fb8PFCv8_ufTQ?MZHPEUqWqcJCPQln;(C}f# zSRw?8vlUy*Gj{pJ+MA=Gl6ULSHcDo!C{TCC5)sTMV^>nPZYcO#n6ZRH-5E=TP;bUA zkF9N|YwSK7-Wk2#Ouf#p3>#>Xi`j}>EJvAcBEb*xOy^>>_s`QRZ~WHC)?rCpG7@!Q zSxpy`bi6!e_|1}7WOB2@B8}@TV7acZ!-3;<{euN!S4W?yVo{Wv01FzQU#W7isK#{` zu-p)^BwH80NpBV^SaRiNj3qy=8Dinx(3;S{Pu>lQ0%bRCHmsz7pS&Al!>Me&%8x~c z*uYQT4iO5c;H**uKSet=OTmEHbQu2<&Bx|j z{VU4#AIF=KEEc6rs9Mjrx##hrn1zSl-pM0r`+j;8Vh7BLau$*OjHqHBvUMl1m7X$F zYD&ypROEJ01YeekfSa9}PG0x^L%Hy8qj=WN6zi7mbX6vltXkgUzv>zJiS>F78d>Ja z6xv1rKUV@+G)Gage2wP1qdpgC;?7u{BCk-29IK^>WL>k_BX4u+X1eRag+#*%tqX;a zLjMiIv6bx}$fs61Q4#;I(1f2VO_;k$6w&_&(f>;4B&L&M6eWtzQ$*+SxOr2FY(xWJH8gP@PGco$ zoL$SiAN8OEyDS*L*-XDl|C`9>HnMXq)k>EVrbw+KX^m>GM}u1_RT|Xa2-F|xF1uWq SrM~>B8sh(fnB)d%CIA3BNpO$= literal 1231 zcmV;=1TgzSRzVbb{d*_^!J7?O@ z#){>vKJwll-qwB7xAfL2(|$}Wv!-aJS*YvgTHS{#R8*J`zH}a$o=qBF-SNGb+qD|; z$1i#wPf>YZG@9^j5>w zJ1sWI^kbCsl$hvVH9aSQ;j>fNV;mWoUJOSRI^-sb9 znbw4GC=T|1R;C08_h!R!##V@{;MV92ZcN4xfWVN0^XDo zFs(+KS?;4+3mIy$2V=TJejtZz%Q-}_t>GMz3siJ18hY>?QLvv&%pBw?{{X467!MEJ z53R6Jn*8@r!D~_l6VHe=^?xJve=@mL7JA& zZM3WA^N@rYMD%FLfdmv*Eo`Vt8=4BEqovzxejTc?Lfp=q`RJSIud!xZK<&*$!el03 tp2emSw8D#*?ZxE?DnDobLQeV;&6(NS%=G7ve%%#!)j#40W7%pY0092gRjvR4 From a58b6c8da5a88cabbfd949cadf0d45d5f2620f1c Mon Sep 17 00:00:00 2001 From: fisker Date: Fri, 23 Aug 2024 14:36:53 +0800 Subject: [PATCH 26/26] Linting --- rules/prefer-math-min-max.js | 28 ++++++++++------------------ 1 file changed, 10 insertions(+), 18 deletions(-) diff --git a/rules/prefer-math-min-max.js b/rules/prefer-math-min-max.js index 5043e3db6b..fdad464fa4 100644 --- a/rules/prefer-math-min-max.js +++ b/rules/prefer-math-min-max.js @@ -6,19 +6,9 @@ const messages = { [MESSAGE_ID]: 'Prefer `Math.{{method}}()` to simplify ternary expressions.', }; -/** -@param {import('eslint').Rule.RuleContext} context -@param {import('estree').ConditionalExpression} node -@param {import('estree').Node} left -@param {import('estree').Node} right -@param {string} method -*/ -function getProblem(context, node, left, right, method) { -} - /** @param {import('eslint').Rule.RuleContext} context */ const create = context => ({ - /** @param {import('estree').ConditionalExpression} node */ + /** @param {import('estree').ConditionalExpression} conditionalExpression */ ConditionalExpression(conditionalExpression) { const {test, consequent, alternate} = conditionalExpression; @@ -27,7 +17,7 @@ const create = context => ({ } const {operator, left, right} = test; - const [leftCode, rightCode, alternateCode, consequentCode] = [left, right, alternate, consequent].map(node => context.sourceCode.getText(node)); + const [leftText, rightText, alternateText, consequentText] = [left, right, alternate, consequent].map(node => context.sourceCode.getText(node)); const isGreaterOrEqual = operator === '>' || operator === '>='; const isLessOrEqual = operator === '<' || operator === '<='; @@ -37,16 +27,16 @@ const create = context => ({ // Prefer `Math.min()` if ( // `height > 50 ? 50 : height` - (isGreaterOrEqual && leftCode === alternateCode && rightCode === consequentCode) + (isGreaterOrEqual && leftText === alternateText && rightText === consequentText) // `height < 50 ? height : 50` - || (isLessOrEqual && leftCode === consequentCode && rightCode === alternateCode) + || (isLessOrEqual && leftText === consequentText && rightText === alternateText) ) { method = 'min'; } else if ( // `height > 50 ? height : 50` - (isGreaterOrEqual && leftCode === consequentCode && rightCode === alternateCode) + (isGreaterOrEqual && leftText === consequentText && rightText === alternateText) // `height < 50 ? 50 : height` - || (isLessOrEqual && leftCode === alternateCode && rightCode === consequentCode) + || (isLessOrEqual && leftText === alternateText && rightText === consequentText) ) { method = 'max'; } @@ -65,9 +55,11 @@ const create = context => ({ yield * fixSpaceAroundKeyword(fixer, conditionalExpression, sourceCode); - const argumentsText = [left, right].map(node => node.type === 'SequenceExpression' ? `(${sourceCode.getText(node)})` : sourceCode.getText(node)); + const argumentsText = [left, right] + .map(node => node.type === 'SequenceExpression' ? `(${sourceCode.getText(node)})` : sourceCode.getText(node)) + .join(', '); - yield fixer.replaceText(conditionalExpression, `Math.${method}(${argumentsText.join(', ')})`); + yield fixer.replaceText(conditionalExpression, `Math.${method}(${argumentsText})`); }, }; },