Actualizacion maquina principal
[dotfiles/.git] / .config / coc / extensions / node_modules / coc-prettier / node_modules / eslint / lib / rules / no-implicit-coercion.js
diff --git a/.config/coc/extensions/node_modules/coc-prettier/node_modules/eslint/lib/rules/no-implicit-coercion.js b/.config/coc/extensions/node_modules/coc-prettier/node_modules/eslint/lib/rules/no-implicit-coercion.js
new file mode 100644 (file)
index 0000000..c80f981
--- /dev/null
@@ -0,0 +1,296 @@
+/**
+ * @fileoverview A rule to disallow the type conversions with shorter notations.
+ * @author Toru Nagashima
+ */
+
+"use strict";
+
+const astUtils = require("./utils/ast-utils");
+
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+
+const INDEX_OF_PATTERN = /^(?:i|lastI)ndexOf$/u;
+const ALLOWABLE_OPERATORS = ["~", "!!", "+", "*"];
+
+/**
+ * Parses and normalizes an option object.
+ * @param {Object} options An option object to parse.
+ * @returns {Object} The parsed and normalized option object.
+ */
+function parseOptions(options) {
+    return {
+        boolean: "boolean" in options ? options.boolean : true,
+        number: "number" in options ? options.number : true,
+        string: "string" in options ? options.string : true,
+        allow: options.allow || []
+    };
+}
+
+/**
+ * Checks whether or not a node is a double logical nigating.
+ * @param {ASTNode} node An UnaryExpression node to check.
+ * @returns {boolean} Whether or not the node is a double logical nigating.
+ */
+function isDoubleLogicalNegating(node) {
+    return (
+        node.operator === "!" &&
+        node.argument.type === "UnaryExpression" &&
+        node.argument.operator === "!"
+    );
+}
+
+/**
+ * Checks whether or not a node is a binary negating of `.indexOf()` method calling.
+ * @param {ASTNode} node An UnaryExpression node to check.
+ * @returns {boolean} Whether or not the node is a binary negating of `.indexOf()` method calling.
+ */
+function isBinaryNegatingOfIndexOf(node) {
+    return (
+        node.operator === "~" &&
+        node.argument.type === "CallExpression" &&
+        node.argument.callee.type === "MemberExpression" &&
+        node.argument.callee.property.type === "Identifier" &&
+        INDEX_OF_PATTERN.test(node.argument.callee.property.name)
+    );
+}
+
+/**
+ * Checks whether or not a node is a multiplying by one.
+ * @param {BinaryExpression} node A BinaryExpression node to check.
+ * @returns {boolean} Whether or not the node is a multiplying by one.
+ */
+function isMultiplyByOne(node) {
+    return node.operator === "*" && (
+        node.left.type === "Literal" && node.left.value === 1 ||
+        node.right.type === "Literal" && node.right.value === 1
+    );
+}
+
+/**
+ * Checks whether the result of a node is numeric or not
+ * @param {ASTNode} node The node to test
+ * @returns {boolean} true if the node is a number literal or a `Number()`, `parseInt` or `parseFloat` call
+ */
+function isNumeric(node) {
+    return (
+        node.type === "Literal" && typeof node.value === "number" ||
+        node.type === "CallExpression" && (
+            node.callee.name === "Number" ||
+            node.callee.name === "parseInt" ||
+            node.callee.name === "parseFloat"
+        )
+    );
+}
+
+/**
+ * Returns the first non-numeric operand in a BinaryExpression. Designed to be
+ * used from bottom to up since it walks up the BinaryExpression trees using
+ * node.parent to find the result.
+ * @param {BinaryExpression} node The BinaryExpression node to be walked up on
+ * @returns {ASTNode|null} The first non-numeric item in the BinaryExpression tree or null
+ */
+function getNonNumericOperand(node) {
+    const left = node.left,
+        right = node.right;
+
+    if (right.type !== "BinaryExpression" && !isNumeric(right)) {
+        return right;
+    }
+
+    if (left.type !== "BinaryExpression" && !isNumeric(left)) {
+        return left;
+    }
+
+    return null;
+}
+
+/**
+ * Checks whether a node is an empty string literal or not.
+ * @param {ASTNode} node The node to check.
+ * @returns {boolean} Whether or not the passed in node is an
+ * empty string literal or not.
+ */
+function isEmptyString(node) {
+    return astUtils.isStringLiteral(node) && (node.value === "" || (node.type === "TemplateLiteral" && node.quasis.length === 1 && node.quasis[0].value.cooked === ""));
+}
+
+/**
+ * Checks whether or not a node is a concatenating with an empty string.
+ * @param {ASTNode} node A BinaryExpression node to check.
+ * @returns {boolean} Whether or not the node is a concatenating with an empty string.
+ */
+function isConcatWithEmptyString(node) {
+    return node.operator === "+" && (
+        (isEmptyString(node.left) && !astUtils.isStringLiteral(node.right)) ||
+        (isEmptyString(node.right) && !astUtils.isStringLiteral(node.left))
+    );
+}
+
+/**
+ * Checks whether or not a node is appended with an empty string.
+ * @param {ASTNode} node An AssignmentExpression node to check.
+ * @returns {boolean} Whether or not the node is appended with an empty string.
+ */
+function isAppendEmptyString(node) {
+    return node.operator === "+=" && isEmptyString(node.right);
+}
+
+/**
+ * Returns the operand that is not an empty string from a flagged BinaryExpression.
+ * @param {ASTNode} node The flagged BinaryExpression node to check.
+ * @returns {ASTNode} The operand that is not an empty string from a flagged BinaryExpression.
+ */
+function getNonEmptyOperand(node) {
+    return isEmptyString(node.left) ? node.right : node.left;
+}
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+    meta: {
+        type: "suggestion",
+
+        docs: {
+            description: "disallow shorthand type conversions",
+            category: "Best Practices",
+            recommended: false,
+            url: "https://eslint.org/docs/rules/no-implicit-coercion"
+        },
+
+        fixable: "code",
+
+        schema: [{
+            type: "object",
+            properties: {
+                boolean: {
+                    type: "boolean",
+                    default: true
+                },
+                number: {
+                    type: "boolean",
+                    default: true
+                },
+                string: {
+                    type: "boolean",
+                    default: true
+                },
+                allow: {
+                    type: "array",
+                    items: {
+                        enum: ALLOWABLE_OPERATORS
+                    },
+                    uniqueItems: true
+                }
+            },
+            additionalProperties: false
+        }]
+    },
+
+    create(context) {
+        const options = parseOptions(context.options[0] || {});
+        const sourceCode = context.getSourceCode();
+
+        /**
+         * Reports an error and autofixes the node
+         * @param {ASTNode} node An ast node to report the error on.
+         * @param {string} recommendation The recommended code for the issue
+         * @param {bool} shouldFix Whether this report should fix the node
+         * @returns {void}
+         */
+        function report(node, recommendation, shouldFix) {
+            context.report({
+                node,
+                message: "use `{{recommendation}}` instead.",
+                data: {
+                    recommendation
+                },
+                fix(fixer) {
+                    if (!shouldFix) {
+                        return null;
+                    }
+
+                    const tokenBefore = sourceCode.getTokenBefore(node);
+
+                    if (
+                        tokenBefore &&
+                        tokenBefore.range[1] === node.range[0] &&
+                        !astUtils.canTokensBeAdjacent(tokenBefore, recommendation)
+                    ) {
+                        return fixer.replaceText(node, ` ${recommendation}`);
+                    }
+                    return fixer.replaceText(node, recommendation);
+                }
+            });
+        }
+
+        return {
+            UnaryExpression(node) {
+                let operatorAllowed;
+
+                // !!foo
+                operatorAllowed = options.allow.indexOf("!!") >= 0;
+                if (!operatorAllowed && options.boolean && isDoubleLogicalNegating(node)) {
+                    const recommendation = `Boolean(${sourceCode.getText(node.argument.argument)})`;
+
+                    report(node, recommendation, true);
+                }
+
+                // ~foo.indexOf(bar)
+                operatorAllowed = options.allow.indexOf("~") >= 0;
+                if (!operatorAllowed && options.boolean && isBinaryNegatingOfIndexOf(node)) {
+                    const recommendation = `${sourceCode.getText(node.argument)} !== -1`;
+
+                    report(node, recommendation, false);
+                }
+
+                // +foo
+                operatorAllowed = options.allow.indexOf("+") >= 0;
+                if (!operatorAllowed && options.number && node.operator === "+" && !isNumeric(node.argument)) {
+                    const recommendation = `Number(${sourceCode.getText(node.argument)})`;
+
+                    report(node, recommendation, true);
+                }
+            },
+
+            // Use `:exit` to prevent double reporting
+            "BinaryExpression:exit"(node) {
+                let operatorAllowed;
+
+                // 1 * foo
+                operatorAllowed = options.allow.indexOf("*") >= 0;
+                const nonNumericOperand = !operatorAllowed && options.number && isMultiplyByOne(node) && getNonNumericOperand(node);
+
+                if (nonNumericOperand) {
+                    const recommendation = `Number(${sourceCode.getText(nonNumericOperand)})`;
+
+                    report(node, recommendation, true);
+                }
+
+                // "" + foo
+                operatorAllowed = options.allow.indexOf("+") >= 0;
+                if (!operatorAllowed && options.string && isConcatWithEmptyString(node)) {
+                    const recommendation = `String(${sourceCode.getText(getNonEmptyOperand(node))})`;
+
+                    report(node, recommendation, true);
+                }
+            },
+
+            AssignmentExpression(node) {
+
+                // foo += ""
+                const operatorAllowed = options.allow.indexOf("+") >= 0;
+
+                if (!operatorAllowed && options.string && isAppendEmptyString(node)) {
+                    const code = sourceCode.getText(getNonEmptyOperand(node));
+                    const recommendation = `${code} = String(${code})`;
+
+                    report(node, recommendation, true);
+                }
+            }
+        };
+    }
+};