Add components
This commit is contained in:
268
slider/node_modules/eslint/lib/rules/use-isnan.js
generated
vendored
Normal file
268
slider/node_modules/eslint/lib/rules/use-isnan.js
generated
vendored
Normal file
@@ -0,0 +1,268 @@
|
||||
/**
|
||||
* @fileoverview Rule to flag comparisons to the value NaN
|
||||
* @author James Allardice
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Requirements
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
const astUtils = require("./utils/ast-utils");
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Helpers
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Determines if the given node is a NaN `Identifier` node.
|
||||
* @param {ASTNode|null} node The node to check.
|
||||
* @returns {boolean} `true` if the node is 'NaN' identifier.
|
||||
*/
|
||||
function isNaNIdentifier(node) {
|
||||
if (!node) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const nodeToCheck =
|
||||
node.type === "SequenceExpression" ? node.expressions.at(-1) : node;
|
||||
|
||||
return (
|
||||
astUtils.isSpecificId(nodeToCheck, "NaN") ||
|
||||
astUtils.isSpecificMemberAccess(nodeToCheck, "Number", "NaN")
|
||||
);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Rule Definition
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** @type {import('../types').Rule.RuleModule} */
|
||||
module.exports = {
|
||||
meta: {
|
||||
hasSuggestions: true,
|
||||
type: "problem",
|
||||
|
||||
docs: {
|
||||
description: "Require calls to `isNaN()` when checking for `NaN`",
|
||||
recommended: true,
|
||||
url: "https://eslint.org/docs/latest/rules/use-isnan",
|
||||
},
|
||||
|
||||
schema: [
|
||||
{
|
||||
type: "object",
|
||||
properties: {
|
||||
enforceForSwitchCase: {
|
||||
type: "boolean",
|
||||
},
|
||||
enforceForIndexOf: {
|
||||
type: "boolean",
|
||||
},
|
||||
},
|
||||
additionalProperties: false,
|
||||
},
|
||||
],
|
||||
|
||||
defaultOptions: [
|
||||
{
|
||||
enforceForIndexOf: false,
|
||||
enforceForSwitchCase: true,
|
||||
},
|
||||
],
|
||||
|
||||
messages: {
|
||||
comparisonWithNaN: "Use the isNaN function to compare with NaN.",
|
||||
switchNaN:
|
||||
"'switch(NaN)' can never match a case clause. Use Number.isNaN instead of the switch.",
|
||||
caseNaN:
|
||||
"'case NaN' can never match. Use Number.isNaN before the switch.",
|
||||
indexOfNaN:
|
||||
"Array prototype method '{{ methodName }}' cannot find NaN.",
|
||||
replaceWithIsNaN: "Replace with Number.isNaN.",
|
||||
replaceWithCastingAndIsNaN:
|
||||
"Replace with Number.isNaN and cast to a Number.",
|
||||
replaceWithFindIndex:
|
||||
"Replace with Array.prototype.{{ methodName }}.",
|
||||
},
|
||||
},
|
||||
|
||||
create(context) {
|
||||
const [{ enforceForIndexOf, enforceForSwitchCase }] = context.options;
|
||||
const sourceCode = context.sourceCode;
|
||||
|
||||
const fixableOperators = new Set(["==", "===", "!=", "!=="]);
|
||||
const castableOperators = new Set(["==", "!="]);
|
||||
|
||||
/**
|
||||
* Get a fixer for a binary expression that compares to NaN.
|
||||
* @param {ASTNode} node The node to fix.
|
||||
* @param {function(string): string} wrapValue A function that wraps the compared value with a fix.
|
||||
* @returns {function(Fixer): Fix} The fixer function.
|
||||
*/
|
||||
function getBinaryExpressionFixer(node, wrapValue) {
|
||||
return fixer => {
|
||||
const comparedValue = isNaNIdentifier(node.left)
|
||||
? node.right
|
||||
: node.left;
|
||||
const shouldWrap = comparedValue.type === "SequenceExpression";
|
||||
const shouldNegate = node.operator[0] === "!";
|
||||
|
||||
const negation = shouldNegate ? "!" : "";
|
||||
let comparedValueText = sourceCode.getText(comparedValue);
|
||||
|
||||
if (shouldWrap) {
|
||||
comparedValueText = `(${comparedValueText})`;
|
||||
}
|
||||
|
||||
const fixedValue = wrapValue(comparedValueText);
|
||||
|
||||
return fixer.replaceText(node, `${negation}${fixedValue}`);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the given `BinaryExpression` node for `foo === NaN` and other comparisons.
|
||||
* @param {ASTNode} node The node to check.
|
||||
* @returns {void}
|
||||
*/
|
||||
function checkBinaryExpression(node) {
|
||||
if (
|
||||
/^(?:[<>]|[!=]=)=?$/u.test(node.operator) &&
|
||||
(isNaNIdentifier(node.left) || isNaNIdentifier(node.right))
|
||||
) {
|
||||
const suggestedFixes = [];
|
||||
const NaNNode = isNaNIdentifier(node.left)
|
||||
? node.left
|
||||
: node.right;
|
||||
|
||||
const isSequenceExpression =
|
||||
NaNNode.type === "SequenceExpression";
|
||||
const isSuggestable =
|
||||
fixableOperators.has(node.operator) &&
|
||||
!isSequenceExpression;
|
||||
const isCastable = castableOperators.has(node.operator);
|
||||
|
||||
if (isSuggestable) {
|
||||
suggestedFixes.push({
|
||||
messageId: "replaceWithIsNaN",
|
||||
fix: getBinaryExpressionFixer(
|
||||
node,
|
||||
value => `Number.isNaN(${value})`,
|
||||
),
|
||||
});
|
||||
|
||||
if (isCastable) {
|
||||
suggestedFixes.push({
|
||||
messageId: "replaceWithCastingAndIsNaN",
|
||||
fix: getBinaryExpressionFixer(
|
||||
node,
|
||||
value => `Number.isNaN(Number(${value}))`,
|
||||
),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
context.report({
|
||||
node,
|
||||
messageId: "comparisonWithNaN",
|
||||
suggest: suggestedFixes,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the discriminant and all case clauses of the given `SwitchStatement` node for `switch(NaN)` and `case NaN:`
|
||||
* @param {ASTNode} node The node to check.
|
||||
* @returns {void}
|
||||
*/
|
||||
function checkSwitchStatement(node) {
|
||||
if (isNaNIdentifier(node.discriminant)) {
|
||||
context.report({ node, messageId: "switchNaN" });
|
||||
}
|
||||
|
||||
for (const switchCase of node.cases) {
|
||||
if (isNaNIdentifier(switchCase.test)) {
|
||||
context.report({ node: switchCase, messageId: "caseNaN" });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the given `CallExpression` node for `.indexOf(NaN)` and `.lastIndexOf(NaN)`.
|
||||
* @param {ASTNode} node The node to check.
|
||||
* @returns {void}
|
||||
*/
|
||||
function checkCallExpression(node) {
|
||||
const callee = astUtils.skipChainExpression(node.callee);
|
||||
|
||||
if (callee.type === "MemberExpression") {
|
||||
const methodName = astUtils.getStaticPropertyName(callee);
|
||||
|
||||
if (
|
||||
(methodName === "indexOf" ||
|
||||
methodName === "lastIndexOf") &&
|
||||
node.arguments.length <= 2 &&
|
||||
isNaNIdentifier(node.arguments[0])
|
||||
) {
|
||||
/*
|
||||
* To retain side effects, it's essential to address `NaN` beforehand, which
|
||||
* is not possible with fixes like `arr.findIndex(Number.isNaN)`.
|
||||
*/
|
||||
const isSuggestable =
|
||||
node.arguments[0].type !== "SequenceExpression" &&
|
||||
!node.arguments[1];
|
||||
const suggestedFixes = [];
|
||||
|
||||
if (isSuggestable) {
|
||||
const shouldWrap = callee.computed;
|
||||
const findIndexMethod =
|
||||
methodName === "indexOf"
|
||||
? "findIndex"
|
||||
: "findLastIndex";
|
||||
const propertyName = shouldWrap
|
||||
? `"${findIndexMethod}"`
|
||||
: findIndexMethod;
|
||||
|
||||
suggestedFixes.push({
|
||||
messageId: "replaceWithFindIndex",
|
||||
data: { methodName: findIndexMethod },
|
||||
fix: fixer => [
|
||||
fixer.replaceText(
|
||||
callee.property,
|
||||
propertyName,
|
||||
),
|
||||
fixer.replaceText(
|
||||
node.arguments[0],
|
||||
"Number.isNaN",
|
||||
),
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
context.report({
|
||||
node,
|
||||
messageId: "indexOfNaN",
|
||||
data: { methodName },
|
||||
suggest: suggestedFixes,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const listeners = {
|
||||
BinaryExpression: checkBinaryExpression,
|
||||
};
|
||||
|
||||
if (enforceForSwitchCase) {
|
||||
listeners.SwitchStatement = checkSwitchStatement;
|
||||
}
|
||||
|
||||
if (enforceForIndexOf) {
|
||||
listeners.CallExpression = checkCallExpression;
|
||||
}
|
||||
|
||||
return listeners;
|
||||
},
|
||||
};
|
Reference in New Issue
Block a user