Repository: eslint/eslint
Pull request #14303 (2021-04-09 01:26:33)

Chore: ignore `pnpm-lock.yaml`
<!--
    Thank you for contributing!

    ESLint adheres to the [JS Foundation Code of Conduct](https://js.foundation/community/code-of-conduct).
-->

#### Prerequisites checklist

- [ ] I have read the [contributing guidelines](https://github.com/eslint/eslint/blob/master/CONTRIBUTING.md).

#### What is the purpose of this pull request? (put an "X" next to an item)

[ ] Documentation update
[ ] Bug fix ([template](https://raw.githubusercontent.com/eslint/eslint/master/templates/bug-report.md))
[ ] New rule ([template](https://raw.githubusercontent.com/eslint/eslint/master/templates/rule-proposal.md))
[ ] Changes an existing rule ([template](https://raw.githubusercontent.com/eslint/eslint/master/templates/rule-change-proposal.md))
[ ] Add autofixing to a rule
[ ] Add a CLI option
[ ] Add something to the core
[✅ ] Other, please explain:

<!--
    If the item you've checked above has a template, please paste the template questions below and answer them. (If this pull request is addressing an issue, you can just paste a link to the issue here instead.)
-->

<!--
    Please ensure your pull request is ready:

    - Read the pull request guide (https://eslint.org/docs/developer-guide/contributing/pull-requests)
    - Include tests for this change
    - Update documentation for this change (if appropriate)
-->

<!--
    The following is required for all pull requests:
-->

#### What changes did you make? (Give an overview)
Ignore `pnpm-lock.yaml` for `pnpm` users. We already ignore `package-lock.json` and `yarn.lock`

#### Is there anything you'd like reviewers to focus on?
No


Patch:
diff --git a/.gitignore b/.gitignore
index a24ba1b65..a00550b00 100644
--- a/.gitignore
+++ b/.gitignore
@@ -22,4 +22,5 @@ versions.json
 .temp-eslintcache
 /tests/fixtures/autofix-integration/temp.js
 yarn.lock
-package-lock.json
\ No newline at end of file
+package-lock.json
+pnpm-lock.yaml
diff --git a/docs/rules/no-implicit-coercion.md b/docs/rules/no-implicit-coercion.md
index f78e40c66..8ccee339f 100644
--- a/docs/rules/no-implicit-coercion.md
+++ b/docs/rules/no-implicit-coercion.md
@@ -33,10 +33,11 @@ This rule is aimed to flag shorter notations for the type conversion, then sugge
 
 This rule has three main options and one override option to allow some coercions as required.
 
-* `"boolean"` (`true` by default) - When this is `true`, this rule warns shorter type conversions for `boolean` type.
-* `"number"` (`true` by default) - When this is `true`, this rule warns shorter type conversions for `number` type.
-* `"string"` (`true` by default) - When this is `true`, this rule warns shorter type conversions for `string` type.
-* `"allow"` (`empty` by default) - Each entry in this array can be one of `~`, `!!`, `+` or `*` that are to be allowed.
+-   `"boolean"` (`true` by default) - When this is `true`, this rule warns shorter type conversions for `boolean` type.
+-   `"number"` (`true` by default) - When this is `true`, this rule warns shorter type conversions for `number` type.
+-   `"string"` (`true` by default) - When this is `true`, this rule warns shorter type conversions for `string` type.
+-   `"disallowTemplateShorthand"` (`false` by default) - When this is `true`, this rule warns `string` type conversions using `${expression}` form.
+-   `"allow"` (`empty` by default) - Each entry in this array can be one of `~`, `!!`, `+` or `*` that are to be allowed.
 
 Note that operator `+` in `allow` list would allow `+foo` (number coercion) as well as `"" + foo` (string coercion).
 
@@ -106,6 +107,42 @@ var s = String(foo);
 foo = String(foo);
 ```
 
+### disallowTemplateShorthand
+
+This option is **not** affected by the `string` option.
+
+Examples of **incorrect** code for the `{ "disallowTemplateShorthand": true }` option:
+
+```js
+/*eslint no-implicit-coercion: ["error", { "disallowTemplateShorthand": true }]*/
+
+var s = `${foo}`;
+```
+
+Examples of **correct** code for the `{ "disallowTemplateShorthand": true }` option:
+
+```js
+/*eslint no-implicit-coercion: ["error", { "disallowTemplateShorthand": true }]*/
+
+var s = String(foo);
+
+var s = `a${foo}`;
+
+var s = `${foo}b`;
+
+var s = `${foo}${bar}`;
+
+var s = tag`${foo}`;
+```
+
+Examples of **correct** code for the default `{ "disallowTemplateShorthand": false }` option:
+
+```js
+/*eslint no-implicit-coercion: ["error", { "disallowTemplateShorthand": false }]*/
+
+var s = `${foo}`;
+```
+
 ### allow
 
 Using `allow` list, we can override and allow specific operators.
diff --git a/lib/rules/no-implicit-coercion.js b/lib/rules/no-implicit-coercion.js
index a639711ec..b9bb45489 100644
--- a/lib/rules/no-implicit-coercion.js
+++ b/lib/rules/no-implicit-coercion.js
@@ -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 || []
     };
 }
@@ -180,6 +181,10 @@ module.exports = {
                     type: "boolean",
                     default: true
                 },
+                disallowTemplateShorthand: {
+                    type: "boolean",
+                    default: false
+                },
                 allow: {
                     type: "array",
                     items: {
@@ -299,6 +304,38 @@ 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;
+                }
+
+                const code = sourceCode.getText(node.expressions[0]);
+                const recommendation = `String(${code})`;
+
+                report(node, recommendation, true);
             }
         };
     }


Test patch:
diff --git a/tests/lib/rules/no-implicit-coercion.js b/tests/lib/rules/no-implicit-coercion.js
index fa2b68b49..fa0a63824 100644
--- a/tests/lib/rules/no-implicit-coercion.js
+++ b/tests/lib/rules/no-implicit-coercion.js
@@ -88,6 +88,13 @@ ruleTester.run("no-implicit-coercion", rule, {
         { code: "`${foo}` + ''", parserOptions: { ecmaVersion: 6 } },
         "foo += 'bar'",
         { code: "foo += `${bar}`", parserOptions: { ecmaVersion: 6 } },
+        { code: "`a${foo}`", options: [{ disallowTemplateShorthand: true }], parserOptions: { ecmaVersion: 6 } },
+        { code: "`${foo}b`", options: [{ disallowTemplateShorthand: true }], parserOptions: { ecmaVersion: 6 } },
+        { code: "`${foo}${bar}`", options: [{ disallowTemplateShorthand: true }], parserOptions: { ecmaVersion: 6 } },
+        { code: "tag`${foo}`", options: [{ disallowTemplateShorthand: true }], parserOptions: { ecmaVersion: 6 } },
+        { code: "`${foo}`", parserOptions: { ecmaVersion: 6 } },
+        { code: "`${foo}`", options: [{ }], parserOptions: { ecmaVersion: 6 } },
+        { code: "`${foo}`", options: [{ disallowTemplateShorthand: false }], parserOptions: { ecmaVersion: 6 } },
         "+42"
     ],
     invalid: [
@@ -248,6 +255,39 @@ ruleTester.run("no-implicit-coercion", rule, {
                 type: "BinaryExpression"
             }]
         },
+        {
+            code: "`${foo}`",
+            output: "String(foo)",
+            options: [{ disallowTemplateShorthand: true }],
+            parserOptions: { ecmaVersion: 6 },
+            errors: [{
+                messageId: "useRecommendation",
+                data: { recommendation: "String(foo)" },
+                type: "TemplateLiteral"
+            }]
+        },
+        {
+            code: "`\\\n${foo}`",
+            output: "String(foo)",
+            options: [{ disallowTemplateShorthand: true }],
+            parserOptions: { ecmaVersion: 6 },
+            errors: [{
+                messageId: "useRecommendation",
+                data: { recommendation: "String(foo)" },
+                type: "TemplateLiteral"
+            }]
+        },
+        {
+            code: "`${foo}\\\n`",
+            output: "String(foo)",
+            options: [{ disallowTemplateShorthand: true }],
+            parserOptions: { ecmaVersion: 6 },
+            errors: [{
+                messageId: "useRecommendation",
+                data: { recommendation: "String(foo)" },
+                type: "TemplateLiteral"
+            }]
+        },
         {
             code: "foo += \"\"",
             output: "foo = String(foo)",