diff --git a/.cspell.json b/.cspell.json index 080101b25f08..1bf064b538c6 100644 --- a/.cspell.json +++ b/.cspell.json @@ -106,6 +106,7 @@ "preact", "Premade", "prettier's", + "quasis", "Quickstart", "recurse", "redeclaration", diff --git a/packages/eslint-plugin/src/rules/no-useless-template-literals.ts b/packages/eslint-plugin/src/rules/no-useless-template-literals.ts index 48c714edb90a..d5a8a602b6b4 100644 --- a/packages/eslint-plugin/src/rules/no-useless-template-literals.ts +++ b/packages/eslint-plugin/src/rules/no-useless-template-literals.ts @@ -1,4 +1,4 @@ -import type { TSESTree } from '@typescript-eslint/utils'; +import type { TSESLint, TSESTree } from '@typescript-eslint/utils'; import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import * as ts from 'typescript'; @@ -15,7 +15,8 @@ type MessageId = 'noUselessTemplateLiteral'; export default createRule<[], MessageId>({ name: 'no-useless-template-literals', meta: { - type: 'problem', + fixable: 'code', + type: 'suggestion', docs: { description: 'Disallow unnecessary template literals', recommended: 'strict', @@ -68,6 +69,22 @@ export default createRule<[], MessageId>({ context.report({ node: node.expressions[0], messageId: 'noUselessTemplateLiteral', + fix(fixer): TSESLint.RuleFix[] { + const [prevQuasi, nextQuasi] = node.quasis; + + // Remove the quasis and backticks. + return [ + fixer.removeRange([ + prevQuasi.range[1] - 3, + node.expressions[0].range[0], + ]), + + fixer.removeRange([ + node.expressions[0].range[1], + nextQuasi.range[0] + 2, + ]), + ]; + }, }); return; @@ -83,6 +100,40 @@ export default createRule<[], MessageId>({ context.report({ node: expression, messageId: 'noUselessTemplateLiteral', + fix(fixer): TSESLint.RuleFix[] { + const index = node.expressions.indexOf(expression); + const prevQuasi = node.quasis[index]; + const nextQuasi = node.quasis[index + 1]; + + // Remove the quasis' parts that are related to the current expression. + const fixes = [ + fixer.removeRange([ + prevQuasi.range[1] - 2, + expression.range[0], + ]), + + fixer.removeRange([ + expression.range[1], + nextQuasi.range[0] + 1, + ]), + ]; + + // Remove quotes for string literals (i.e. `'a'` will become `a`). + const isStringLiteral = + isUnderlyingTypeString(expression) && + expression.type === AST_NODE_TYPES.Literal; + + if (isStringLiteral) { + const escapedValue = expression.value.replace( + /([`$\\])/g, + '\\$1', + ); + + fixes.push(fixer.replaceText(expression, escapedValue)); + } + + return fixes; + }, }); }); }, diff --git a/packages/eslint-plugin/tests/rules/no-useless-template-literals.test.ts b/packages/eslint-plugin/tests/rules/no-useless-template-literals.test.ts index 54ac89c2c797..674b4ccefe77 100644 --- a/packages/eslint-plugin/tests/rules/no-useless-template-literals.test.ts +++ b/packages/eslint-plugin/tests/rules/no-useless-template-literals.test.ts @@ -137,6 +137,7 @@ ruleTester.run('no-useless-template-literals', rule, { invalid: [ { code: '`${1}`;', + output: '`1`;', errors: [ { messageId: 'noUselessTemplateLiteral', @@ -146,19 +147,50 @@ ruleTester.run('no-useless-template-literals', rule, { }, ], }, + { - code: '`${1n}`;', + code: noFormat`\`\${ 1 }\`;`, + output: '`1`;', + errors: [ + { + messageId: 'noUselessTemplateLiteral', + }, + ], + }, + + { + code: noFormat`\`\${ 'a' }\`;`, + output: `'a';`, errors: [ { messageId: 'noUselessTemplateLiteral', - line: 1, - column: 4, - endColumn: 6, }, ], }, + + { + code: noFormat`\`\${ "a" }\`;`, + output: `"a";`, + errors: [ + { + messageId: 'noUselessTemplateLiteral', + }, + ], + }, + + { + code: noFormat`\`\${ 'a' + 'b' }\`;`, + output: `'a' + 'b';`, + errors: [ + { + messageId: 'noUselessTemplateLiteral', + }, + ], + }, + { code: '`${true}`;', + output: '`true`;', errors: [ { messageId: 'noUselessTemplateLiteral', @@ -168,8 +200,20 @@ ruleTester.run('no-useless-template-literals', rule, { }, ], }, + + { + code: noFormat`\`\${ true }\`;`, + output: '`true`;', + errors: [ + { + messageId: 'noUselessTemplateLiteral', + }, + ], + }, + { code: '`${null}`;', + output: '`null`;', errors: [ { messageId: 'noUselessTemplateLiteral', @@ -179,8 +223,20 @@ ruleTester.run('no-useless-template-literals', rule, { }, ], }, + + { + code: noFormat`\`\${ null }\`;`, + output: '`null`;', + errors: [ + { + messageId: 'noUselessTemplateLiteral', + }, + ], + }, + { code: '`${undefined}`;', + output: '`undefined`;', errors: [ { messageId: 'noUselessTemplateLiteral', @@ -190,8 +246,20 @@ ruleTester.run('no-useless-template-literals', rule, { }, ], }, + { - code: "`${'a'}${'b'}`;", + code: noFormat`\`\${ undefined }\`;`, + output: '`undefined`;', + errors: [ + { + messageId: 'noUselessTemplateLiteral', + }, + ], + }, + + { + code: "`${'a'} ${'b'}`;", + output: '`a b`;', errors: [ { messageId: 'noUselessTemplateLiteral', @@ -202,8 +270,21 @@ ruleTester.run('no-useless-template-literals', rule, { { messageId: 'noUselessTemplateLiteral', line: 1, - column: 10, - endColumn: 13, + column: 11, + endColumn: 14, + }, + ], + }, + + { + code: noFormat`\`\${ 'a' } \${ 'b' }\`;`, + output: '`a b`;', + errors: [ + { + messageId: 'noUselessTemplateLiteral', + }, + { + messageId: 'noUselessTemplateLiteral', }, ], }, @@ -213,6 +294,10 @@ ruleTester.run('no-useless-template-literals', rule, { declare const b: 'b'; \`a\${b}\${'c'}\`; `, + output: ` + declare const b: 'b'; + \`a\${b}c\`; + `, errors: [ { messageId: 'noUselessTemplateLiteral', @@ -225,6 +310,7 @@ ruleTester.run('no-useless-template-literals', rule, { { code: "`a${'b'}`;", + output: '`ab`;', errors: [ { messageId: 'noUselessTemplateLiteral', @@ -236,13 +322,14 @@ ruleTester.run('no-useless-template-literals', rule, { }, { - code: "`${'1 + 1 = '}${2}`;", + code: "`${'1 + 1 ='} ${2}`;", + output: '`1 + 1 = 2`;', errors: [ { messageId: 'noUselessTemplateLiteral', line: 1, column: 4, - endColumn: 14, + endColumn: 13, }, { messageId: 'noUselessTemplateLiteral', @@ -254,7 +341,8 @@ ruleTester.run('no-useless-template-literals', rule, { }, { - code: "`${'a'}${true}`;", + code: "`${'a'} ${true}`;", + output: '`a true`;', errors: [ { messageId: 'noUselessTemplateLiteral', @@ -265,8 +353,8 @@ ruleTester.run('no-useless-template-literals', rule, { { messageId: 'noUselessTemplateLiteral', line: 1, - column: 10, - endColumn: 14, + column: 11, + endColumn: 15, }, ], }, @@ -276,6 +364,10 @@ ruleTester.run('no-useless-template-literals', rule, { declare const string: 'a'; \`\${string}\`; `, + output: ` + declare const string: 'a'; + string; + `, errors: [ { messageId: 'noUselessTemplateLiteral', @@ -286,8 +378,25 @@ ruleTester.run('no-useless-template-literals', rule, { ], }, + { + code: noFormat` + declare const string: 'a'; + \`\${ string }\`; + `, + output: ` + declare const string: 'a'; + string; + `, + errors: [ + { + messageId: 'noUselessTemplateLiteral', + }, + ], + }, + { code: "`${String(Symbol.for('test'))}`;", + output: "String(Symbol.for('test'));", errors: [ { messageId: 'noUselessTemplateLiteral', @@ -303,6 +412,10 @@ ruleTester.run('no-useless-template-literals', rule, { declare const intersection: string & { _brand: 'test-brand' }; \`\${intersection}\`; `, + output: ` + declare const intersection: string & { _brand: 'test-brand' }; + intersection; + `, errors: [ { messageId: 'noUselessTemplateLiteral', @@ -319,6 +432,11 @@ ruleTester.run('no-useless-template-literals', rule, { \`\${arg}\`; } `, + output: ` + function func(arg: T) { + arg; + } + `, errors: [ { messageId: 'noUselessTemplateLiteral', @@ -328,5 +446,65 @@ ruleTester.run('no-useless-template-literals', rule, { }, ], }, + + { + code: "`${'`'}`;", + output: "'`';", + errors: [ + { + messageId: 'noUselessTemplateLiteral', + }, + ], + }, + + { + code: "`back${'`'}tick`;", + output: '`back\\`tick`;', + errors: [ + { + messageId: 'noUselessTemplateLiteral', + }, + ], + }, + + { + code: "`dollar${'${`this is test`}'}sign`;", + output: '`dollar\\${\\`this is test\\`}sign`;', + errors: [ + { + messageId: 'noUselessTemplateLiteral', + }, + ], + }, + + { + code: '`complex${\'`${"`${test}`"}`\'}case`;', + output: '`complex\\`\\${"\\`\\${test}\\`"}\\`case`;', + errors: [ + { + messageId: 'noUselessTemplateLiteral', + }, + ], + }, + + { + code: "`some ${'\\\\${test}'} string`;", + output: '`some \\\\\\${test} string`;', + errors: [ + { + messageId: 'noUselessTemplateLiteral', + }, + ], + }, + + { + code: "`some ${'\\\\`'} string`;", + output: '`some \\\\\\` string`;', + errors: [ + { + messageId: 'noUselessTemplateLiteral', + }, + ], + }, ], }); pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy