massive update, probably broken
[dotfiles/.git] / .config / coc / extensions / node_modules / coc-prettier / node_modules / eslint / lib / rules / no-implicit-coercion.js
index c80f9813020be57d0ae758a170df35805f1cc0fc..993b8d1f1c8d4457dacddcda450a6b251cca3a21 100644 (file)
@@ -24,6 +24,7 @@ function parseOptions(options) {
         boolean: "boolean" in options ? options.boolean : true,
         number: "number" in options ? options.number : true,
         string: "string" in options ? options.string : true,
+        disallowTemplateShorthand: "disallowTemplateShorthand" in options ? options.disallowTemplateShorthand : false,
         allow: options.allow || []
     };
 }
@@ -47,12 +48,14 @@ function isDoubleLogicalNegating(node) {
  * @returns {boolean} Whether or not the node is a binary negating of `.indexOf()` method calling.
  */
 function isBinaryNegatingOfIndexOf(node) {
+    if (node.operator !== "~") {
+        return false;
+    }
+    const callNode = astUtils.skipChainExpression(node.argument);
+
     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)
+        callNode.type === "CallExpression" &&
+        astUtils.isSpecificMemberAccess(callNode.callee, null, INDEX_OF_PATTERN)
     );
 }
 
@@ -106,6 +109,20 @@ function getNonNumericOperand(node) {
     return null;
 }
 
+/**
+ * Checks whether an expression evaluates to a string.
+ * @param {ASTNode} node node that represents the expression to check.
+ * @returns {boolean} Whether or not the expression evaluates to a string.
+ */
+function isStringType(node) {
+    return astUtils.isStringLiteral(node) ||
+        (
+            node.type === "CallExpression" &&
+            node.callee.type === "Identifier" &&
+            node.callee.name === "String"
+        );
+}
+
 /**
  * Checks whether a node is an empty string literal or not.
  * @param {ASTNode} node The node to check.
@@ -123,8 +140,8 @@ function isEmptyString(node) {
  */
 function isConcatWithEmptyString(node) {
     return node.operator === "+" && (
-        (isEmptyString(node.left) && !astUtils.isStringLiteral(node.right)) ||
-        (isEmptyString(node.right) && !astUtils.isStringLiteral(node.left))
+        (isEmptyString(node.left) && !isStringType(node.right)) ||
+        (isEmptyString(node.right) && !isStringType(node.left))
     );
 }
 
@@ -178,6 +195,10 @@ module.exports = {
                     type: "boolean",
                     default: true
                 },
+                disallowTemplateShorthand: {
+                    type: "boolean",
+                    default: false
+                },
                 allow: {
                     type: "array",
                     items: {
@@ -187,7 +208,11 @@ module.exports = {
                 }
             },
             additionalProperties: false
-        }]
+        }],
+
+        messages: {
+            useRecommendation: "use `{{recommendation}}` instead."
+        }
     },
 
     create(context) {
@@ -204,7 +229,7 @@ module.exports = {
         function report(node, recommendation, shouldFix) {
             context.report({
                 node,
-                message: "use `{{recommendation}}` instead.",
+                messageId: "useRecommendation",
                 data: {
                     recommendation
                 },
@@ -242,7 +267,10 @@ module.exports = {
                 // ~foo.indexOf(bar)
                 operatorAllowed = options.allow.indexOf("~") >= 0;
                 if (!operatorAllowed && options.boolean && isBinaryNegatingOfIndexOf(node)) {
-                    const recommendation = `${sourceCode.getText(node.argument)} !== -1`;
+
+                    // `foo?.indexOf(bar) !== -1` will be true (== found) if the `foo` is nullish. So use `>= 0` in that case.
+                    const comparison = node.argument.type === "ChainExpression" ? ">= 0" : "!== -1";
+                    const recommendation = `${sourceCode.getText(node.argument)} ${comparison}`;
 
                     report(node, recommendation, false);
                 }
@@ -290,6 +318,43 @@ module.exports = {
 
                     report(node, recommendation, true);
                 }
+            },
+
+            TemplateLiteral(node) {
+                if (!options.disallowTemplateShorthand) {
+                    return;
+                }
+
+                // tag`${foo}`
+                if (node.parent.type === "TaggedTemplateExpression") {
+                    return;
+                }
+
+                // `` or `${foo}${bar}`
+                if (node.expressions.length !== 1) {
+                    return;
+                }
+
+
+                //  `prefix${foo}`
+                if (node.quasis[0].value.cooked !== "") {
+                    return;
+                }
+
+                //  `${foo}postfix`
+                if (node.quasis[1].value.cooked !== "") {
+                    return;
+                }
+
+                // if the expression is already a string, then this isn't a coercion
+                if (isStringType(node.expressions[0])) {
+                    return;
+                }
+
+                const code = sourceCode.getText(node.expressions[0]);
+                const recommendation = `String(${code})`;
+
+                report(node, recommendation, true);
             }
         };
     }