| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148 | /** * @fileoverview Rule to disallow `parseInt()` in favor of binary, octal, and hexadecimal literals * @author Annie Zhang, Henry Zhu */"use strict";//------------------------------------------------------------------------------// Requirements//------------------------------------------------------------------------------const astUtils = require("./utils/ast-utils");//------------------------------------------------------------------------------// Helpers//------------------------------------------------------------------------------const radixMap = new Map([    [2, { system: "binary", literalPrefix: "0b" }],    [8, { system: "octal", literalPrefix: "0o" }],    [16, { system: "hexadecimal", literalPrefix: "0x" }]]);/** * Checks to see if a CallExpression's callee node is `parseInt` or * `Number.parseInt`. * @param {ASTNode} calleeNode The callee node to evaluate. * @returns {boolean} True if the callee is `parseInt` or `Number.parseInt`, * false otherwise. */function isParseInt(calleeNode) {    return (        astUtils.isSpecificId(calleeNode, "parseInt") ||        astUtils.isSpecificMemberAccess(calleeNode, "Number", "parseInt")    );}//------------------------------------------------------------------------------// Rule Definition//------------------------------------------------------------------------------/** @type {import('../shared/types').Rule} */module.exports = {    meta: {        type: "suggestion",        docs: {            description: "Disallow `parseInt()` and `Number.parseInt()` in favor of binary, octal, and hexadecimal literals",            recommended: false,            url: "https://eslint.org/docs/rules/prefer-numeric-literals"        },        schema: [],        messages: {            useLiteral: "Use {{system}} literals instead of {{functionName}}()."        },        fixable: "code"    },    create(context) {        const sourceCode = context.getSourceCode();        //----------------------------------------------------------------------        // Public        //----------------------------------------------------------------------        return {            "CallExpression[arguments.length=2]"(node) {                const [strNode, radixNode] = node.arguments,                    str = astUtils.getStaticStringValue(strNode),                    radix = radixNode.value;                if (                    str !== null &&                    astUtils.isStringLiteral(strNode) &&                    radixNode.type === "Literal" &&                    typeof radix === "number" &&                    radixMap.has(radix) &&                    isParseInt(node.callee)                ) {                    const { system, literalPrefix } = radixMap.get(radix);                    context.report({                        node,                        messageId: "useLiteral",                        data: {                            system,                            functionName: sourceCode.getText(node.callee)                        },                        fix(fixer) {                            if (sourceCode.getCommentsInside(node).length) {                                return null;                            }                            const replacement = `${literalPrefix}${str}`;                            if (+replacement !== parseInt(str, radix)) {                                /*                                 * If the newly-produced literal would be invalid, (e.g. 0b1234),                                 * or it would yield an incorrect parseInt result for some other reason, don't make a fix.                                 *                                 * If `str` had numeric separators, `+replacement` will evaluate to `NaN` because unary `+`                                 * per the specification doesn't support numeric separators. Thus, the above condition will be `true`                                 * (`NaN !== anything` is always `true`) regardless of the `parseInt(str, radix)` value.                                 * Consequently, no autofixes will be made. This is correct behavior because `parseInt` also                                 * doesn't support numeric separators, but it does parse part of the string before the first `_`,                                 * so the autofix would be invalid:                                 *                                 *   parseInt("1_1", 2) // === 1                                 *   0b1_1 // === 3                                 */                                return null;                            }                            const tokenBefore = sourceCode.getTokenBefore(node),                                tokenAfter = sourceCode.getTokenAfter(node);                            let prefix = "",                                suffix = "";                            if (                                tokenBefore &&                                tokenBefore.range[1] === node.range[0] &&                                !astUtils.canTokensBeAdjacent(tokenBefore, replacement)                            ) {                                prefix = " ";                            }                            if (                                tokenAfter &&                                node.range[1] === tokenAfter.range[0] &&                                !astUtils.canTokensBeAdjacent(replacement, tokenAfter)                            ) {                                suffix = " ";                            }                            return fixer.replaceText(node, `${prefix}${replacement}${suffix}`);                        }                    });                }            }        };    }};
 |