From d832a020f197b47518053d5b1fa07c59e9d54335 Mon Sep 17 00:00:00 2001 From: StyleShit Date: Thu, 14 Dec 2023 11:11:43 +0200 Subject: [PATCH 1/5] feat(eslint-plugin): [no-useless-template-literals] add fix suggestions --- .../src/rules/no-useless-template-literals.ts | 60 +++- .../no-useless-template-literals.test.ts | 260 +++++++++++++++++- 2 files changed, 314 insertions(+), 6 deletions(-) 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..db2692304828 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', + hasSuggestions: true, + type: 'suggestion', docs: { description: 'Disallow unnecessary template literals', recommended: 'strict', @@ -68,6 +69,27 @@ export default createRule<[], MessageId>({ context.report({ node: node.expressions[0], messageId: 'noUselessTemplateLiteral', + suggest: [ + { + 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 +105,40 @@ export default createRule<[], MessageId>({ context.report({ node: expression, messageId: 'noUselessTemplateLiteral', + suggest: [ + { + 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) { + fixes.push(fixer.replaceText(expression, expression.value)); + } + + 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..a19495ce4abe 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 @@ -143,20 +143,76 @@ ruleTester.run('no-useless-template-literals', rule, { line: 1, column: 4, endColumn: 5, + suggestions: [ + { + messageId: 'noUselessTemplateLiteral', + output: '`1`;', + }, + ], }, ], }, + { - code: '`${1n}`;', + code: noFormat`\`\${ 1 }\`;`, errors: [ { messageId: 'noUselessTemplateLiteral', - line: 1, - column: 4, - endColumn: 6, + suggestions: [ + { + messageId: 'noUselessTemplateLiteral', + output: '`1`;', + }, + ], + }, + ], + }, + + { + code: noFormat`\`\${ 'a' }\`;`, + errors: [ + { + messageId: 'noUselessTemplateLiteral', + suggestions: [ + { + messageId: 'noUselessTemplateLiteral', + output: `'a';`, + }, + ], + }, + ], + }, + + { + code: noFormat`\`\${ "a" }\`;`, + errors: [ + { + messageId: 'noUselessTemplateLiteral', + suggestions: [ + { + messageId: 'noUselessTemplateLiteral', + output: `"a";`, + }, + ], + }, + ], + }, + + { + code: noFormat`\`\${ 'a' + 'b' }\`;`, + errors: [ + { + messageId: 'noUselessTemplateLiteral', + suggestions: [ + { + messageId: 'noUselessTemplateLiteral', + output: `'a' + 'b';`, + }, + ], }, ], }, + { code: '`${true}`;', errors: [ @@ -165,9 +221,31 @@ ruleTester.run('no-useless-template-literals', rule, { line: 1, column: 4, endColumn: 8, + suggestions: [ + { + messageId: 'noUselessTemplateLiteral', + output: '`true`;', + }, + ], + }, + ], + }, + + { + code: noFormat`\`\${ true }\`;`, + errors: [ + { + messageId: 'noUselessTemplateLiteral', + suggestions: [ + { + messageId: 'noUselessTemplateLiteral', + output: '`true`;', + }, + ], }, ], }, + { code: '`${null}`;', errors: [ @@ -176,9 +254,31 @@ ruleTester.run('no-useless-template-literals', rule, { line: 1, column: 4, endColumn: 8, + suggestions: [ + { + messageId: 'noUselessTemplateLiteral', + output: '`null`;', + }, + ], }, ], }, + + { + code: noFormat`\`\${ null }\`;`, + errors: [ + { + messageId: 'noUselessTemplateLiteral', + suggestions: [ + { + messageId: 'noUselessTemplateLiteral', + output: '`null`;', + }, + ], + }, + ], + }, + { code: '`${undefined}`;', errors: [ @@ -187,9 +287,31 @@ ruleTester.run('no-useless-template-literals', rule, { line: 1, column: 4, endColumn: 13, + suggestions: [ + { + messageId: 'noUselessTemplateLiteral', + output: '`undefined`;', + }, + ], }, ], }, + + { + code: noFormat`\`\${ undefined }\`;`, + errors: [ + { + messageId: 'noUselessTemplateLiteral', + suggestions: [ + { + messageId: 'noUselessTemplateLiteral', + output: '`undefined`;', + }, + ], + }, + ], + }, + { code: "`${'a'}${'b'}`;", errors: [ @@ -198,12 +320,48 @@ ruleTester.run('no-useless-template-literals', rule, { line: 1, column: 4, endColumn: 7, + suggestions: [ + { + messageId: 'noUselessTemplateLiteral', + output: "`a${'b'}`;", + }, + ], }, { messageId: 'noUselessTemplateLiteral', line: 1, column: 10, endColumn: 13, + suggestions: [ + { + messageId: 'noUselessTemplateLiteral', + output: "`${'a'}b`;", + }, + ], + }, + ], + }, + + { + code: noFormat`\`\${ 'a' }\${ 'b' }\`;`, + errors: [ + { + messageId: 'noUselessTemplateLiteral', + suggestions: [ + { + messageId: 'noUselessTemplateLiteral', + output: noFormat`\`a\${ 'b' }\`;`, + }, + ], + }, + { + messageId: 'noUselessTemplateLiteral', + suggestions: [ + { + messageId: 'noUselessTemplateLiteral', + output: noFormat`\`\${ 'a' }b\`;`, + }, + ], }, ], }, @@ -219,6 +377,15 @@ ruleTester.run('no-useless-template-literals', rule, { line: 3, column: 17, endColumn: 20, + suggestions: [ + { + messageId: 'noUselessTemplateLiteral', + output: ` + declare const b: 'b'; + \`a\${b}c\`; + `, + }, + ], }, ], }, @@ -231,6 +398,12 @@ ruleTester.run('no-useless-template-literals', rule, { line: 1, column: 5, endColumn: 8, + suggestions: [ + { + messageId: 'noUselessTemplateLiteral', + output: '`ab`;', + }, + ], }, ], }, @@ -243,12 +416,24 @@ ruleTester.run('no-useless-template-literals', rule, { line: 1, column: 4, endColumn: 14, + suggestions: [ + { + messageId: 'noUselessTemplateLiteral', + output: '`1 + 1 = ${2}`;', + }, + ], }, { messageId: 'noUselessTemplateLiteral', line: 1, column: 17, endColumn: 18, + suggestions: [ + { + messageId: 'noUselessTemplateLiteral', + output: "`${'1 + 1 = '}2`;", + }, + ], }, ], }, @@ -261,12 +446,24 @@ ruleTester.run('no-useless-template-literals', rule, { line: 1, column: 4, endColumn: 7, + suggestions: [ + { + messageId: 'noUselessTemplateLiteral', + output: '`a${true}`;', + }, + ], }, { messageId: 'noUselessTemplateLiteral', line: 1, column: 10, endColumn: 14, + suggestions: [ + { + messageId: 'noUselessTemplateLiteral', + output: "`${'a'}true`;", + }, + ], }, ], }, @@ -282,6 +479,36 @@ ruleTester.run('no-useless-template-literals', rule, { line: 3, column: 12, endColumn: 18, + suggestions: [ + { + messageId: 'noUselessTemplateLiteral', + output: ` + declare const string: 'a'; + string; + `, + }, + ], + }, + ], + }, + + { + code: noFormat` + declare const string: 'a'; + \`\${ string }\`; + `, + errors: [ + { + messageId: 'noUselessTemplateLiteral', + suggestions: [ + { + messageId: 'noUselessTemplateLiteral', + output: ` + declare const string: 'a'; + string; + `, + }, + ], }, ], }, @@ -294,6 +521,12 @@ ruleTester.run('no-useless-template-literals', rule, { line: 1, column: 4, endColumn: 30, + suggestions: [ + { + messageId: 'noUselessTemplateLiteral', + output: "String(Symbol.for('test'));", + }, + ], }, ], }, @@ -309,6 +542,15 @@ ruleTester.run('no-useless-template-literals', rule, { line: 3, column: 12, endColumn: 24, + suggestions: [ + { + messageId: 'noUselessTemplateLiteral', + output: ` + declare const intersection: string & { _brand: 'test-brand' }; + intersection; + `, + }, + ], }, ], }, @@ -325,6 +567,16 @@ ruleTester.run('no-useless-template-literals', rule, { line: 3, column: 14, endColumn: 17, + suggestions: [ + { + messageId: 'noUselessTemplateLiteral', + output: ` + function func(arg: T) { + arg; + } + `, + }, + ], }, ], }, From 985a0624dddc349252894646cd5094349f86114a Mon Sep 17 00:00:00 2001 From: StyleShit Date: Thu, 14 Dec 2023 11:16:32 +0200 Subject: [PATCH 2/5] change message --- .../src/rules/no-useless-template-literals.ts | 9 ++-- .../no-useless-template-literals.test.ts | 52 +++++++++---------- 2 files changed, 32 insertions(+), 29 deletions(-) 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 db2692304828..c09073126043 100644 --- a/packages/eslint-plugin/src/rules/no-useless-template-literals.ts +++ b/packages/eslint-plugin/src/rules/no-useless-template-literals.ts @@ -10,7 +10,7 @@ import { isUndefinedIdentifier, } from '../util'; -type MessageId = 'noUselessTemplateLiteral'; +type MessageId = 'noUselessTemplateLiteral' | 'removeUselessTemplateLiteral'; export default createRule<[], MessageId>({ name: 'no-useless-template-literals', @@ -25,6 +25,9 @@ export default createRule<[], MessageId>({ messages: { noUselessTemplateLiteral: 'Template literal expression is unnecessary and can be simplified.', + + removeUselessTemplateLiteral: + 'Remove unnecessary template literal expression.', }, schema: [], }, @@ -71,7 +74,7 @@ export default createRule<[], MessageId>({ messageId: 'noUselessTemplateLiteral', suggest: [ { - messageId: 'noUselessTemplateLiteral', + messageId: 'removeUselessTemplateLiteral', fix(fixer): TSESLint.RuleFix[] { const [prevQuasi, nextQuasi] = node.quasis; @@ -107,7 +110,7 @@ export default createRule<[], MessageId>({ messageId: 'noUselessTemplateLiteral', suggest: [ { - messageId: 'noUselessTemplateLiteral', + messageId: 'removeUselessTemplateLiteral', fix(fixer): TSESLint.RuleFix[] { const index = node.expressions.indexOf(expression); const prevQuasi = node.quasis[index]; 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 a19495ce4abe..eb49fb999503 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 @@ -145,7 +145,7 @@ ruleTester.run('no-useless-template-literals', rule, { endColumn: 5, suggestions: [ { - messageId: 'noUselessTemplateLiteral', + messageId: 'removeUselessTemplateLiteral', output: '`1`;', }, ], @@ -160,7 +160,7 @@ ruleTester.run('no-useless-template-literals', rule, { messageId: 'noUselessTemplateLiteral', suggestions: [ { - messageId: 'noUselessTemplateLiteral', + messageId: 'removeUselessTemplateLiteral', output: '`1`;', }, ], @@ -175,7 +175,7 @@ ruleTester.run('no-useless-template-literals', rule, { messageId: 'noUselessTemplateLiteral', suggestions: [ { - messageId: 'noUselessTemplateLiteral', + messageId: 'removeUselessTemplateLiteral', output: `'a';`, }, ], @@ -190,7 +190,7 @@ ruleTester.run('no-useless-template-literals', rule, { messageId: 'noUselessTemplateLiteral', suggestions: [ { - messageId: 'noUselessTemplateLiteral', + messageId: 'removeUselessTemplateLiteral', output: `"a";`, }, ], @@ -205,7 +205,7 @@ ruleTester.run('no-useless-template-literals', rule, { messageId: 'noUselessTemplateLiteral', suggestions: [ { - messageId: 'noUselessTemplateLiteral', + messageId: 'removeUselessTemplateLiteral', output: `'a' + 'b';`, }, ], @@ -223,7 +223,7 @@ ruleTester.run('no-useless-template-literals', rule, { endColumn: 8, suggestions: [ { - messageId: 'noUselessTemplateLiteral', + messageId: 'removeUselessTemplateLiteral', output: '`true`;', }, ], @@ -238,7 +238,7 @@ ruleTester.run('no-useless-template-literals', rule, { messageId: 'noUselessTemplateLiteral', suggestions: [ { - messageId: 'noUselessTemplateLiteral', + messageId: 'removeUselessTemplateLiteral', output: '`true`;', }, ], @@ -256,7 +256,7 @@ ruleTester.run('no-useless-template-literals', rule, { endColumn: 8, suggestions: [ { - messageId: 'noUselessTemplateLiteral', + messageId: 'removeUselessTemplateLiteral', output: '`null`;', }, ], @@ -271,7 +271,7 @@ ruleTester.run('no-useless-template-literals', rule, { messageId: 'noUselessTemplateLiteral', suggestions: [ { - messageId: 'noUselessTemplateLiteral', + messageId: 'removeUselessTemplateLiteral', output: '`null`;', }, ], @@ -289,7 +289,7 @@ ruleTester.run('no-useless-template-literals', rule, { endColumn: 13, suggestions: [ { - messageId: 'noUselessTemplateLiteral', + messageId: 'removeUselessTemplateLiteral', output: '`undefined`;', }, ], @@ -304,7 +304,7 @@ ruleTester.run('no-useless-template-literals', rule, { messageId: 'noUselessTemplateLiteral', suggestions: [ { - messageId: 'noUselessTemplateLiteral', + messageId: 'removeUselessTemplateLiteral', output: '`undefined`;', }, ], @@ -322,7 +322,7 @@ ruleTester.run('no-useless-template-literals', rule, { endColumn: 7, suggestions: [ { - messageId: 'noUselessTemplateLiteral', + messageId: 'removeUselessTemplateLiteral', output: "`a${'b'}`;", }, ], @@ -334,7 +334,7 @@ ruleTester.run('no-useless-template-literals', rule, { endColumn: 13, suggestions: [ { - messageId: 'noUselessTemplateLiteral', + messageId: 'removeUselessTemplateLiteral', output: "`${'a'}b`;", }, ], @@ -349,7 +349,7 @@ ruleTester.run('no-useless-template-literals', rule, { messageId: 'noUselessTemplateLiteral', suggestions: [ { - messageId: 'noUselessTemplateLiteral', + messageId: 'removeUselessTemplateLiteral', output: noFormat`\`a\${ 'b' }\`;`, }, ], @@ -358,7 +358,7 @@ ruleTester.run('no-useless-template-literals', rule, { messageId: 'noUselessTemplateLiteral', suggestions: [ { - messageId: 'noUselessTemplateLiteral', + messageId: 'removeUselessTemplateLiteral', output: noFormat`\`\${ 'a' }b\`;`, }, ], @@ -379,7 +379,7 @@ ruleTester.run('no-useless-template-literals', rule, { endColumn: 20, suggestions: [ { - messageId: 'noUselessTemplateLiteral', + messageId: 'removeUselessTemplateLiteral', output: ` declare const b: 'b'; \`a\${b}c\`; @@ -400,7 +400,7 @@ ruleTester.run('no-useless-template-literals', rule, { endColumn: 8, suggestions: [ { - messageId: 'noUselessTemplateLiteral', + messageId: 'removeUselessTemplateLiteral', output: '`ab`;', }, ], @@ -418,7 +418,7 @@ ruleTester.run('no-useless-template-literals', rule, { endColumn: 14, suggestions: [ { - messageId: 'noUselessTemplateLiteral', + messageId: 'removeUselessTemplateLiteral', output: '`1 + 1 = ${2}`;', }, ], @@ -430,7 +430,7 @@ ruleTester.run('no-useless-template-literals', rule, { endColumn: 18, suggestions: [ { - messageId: 'noUselessTemplateLiteral', + messageId: 'removeUselessTemplateLiteral', output: "`${'1 + 1 = '}2`;", }, ], @@ -448,7 +448,7 @@ ruleTester.run('no-useless-template-literals', rule, { endColumn: 7, suggestions: [ { - messageId: 'noUselessTemplateLiteral', + messageId: 'removeUselessTemplateLiteral', output: '`a${true}`;', }, ], @@ -460,7 +460,7 @@ ruleTester.run('no-useless-template-literals', rule, { endColumn: 14, suggestions: [ { - messageId: 'noUselessTemplateLiteral', + messageId: 'removeUselessTemplateLiteral', output: "`${'a'}true`;", }, ], @@ -481,7 +481,7 @@ ruleTester.run('no-useless-template-literals', rule, { endColumn: 18, suggestions: [ { - messageId: 'noUselessTemplateLiteral', + messageId: 'removeUselessTemplateLiteral', output: ` declare const string: 'a'; string; @@ -502,7 +502,7 @@ ruleTester.run('no-useless-template-literals', rule, { messageId: 'noUselessTemplateLiteral', suggestions: [ { - messageId: 'noUselessTemplateLiteral', + messageId: 'removeUselessTemplateLiteral', output: ` declare const string: 'a'; string; @@ -523,7 +523,7 @@ ruleTester.run('no-useless-template-literals', rule, { endColumn: 30, suggestions: [ { - messageId: 'noUselessTemplateLiteral', + messageId: 'removeUselessTemplateLiteral', output: "String(Symbol.for('test'));", }, ], @@ -544,7 +544,7 @@ ruleTester.run('no-useless-template-literals', rule, { endColumn: 24, suggestions: [ { - messageId: 'noUselessTemplateLiteral', + messageId: 'removeUselessTemplateLiteral', output: ` declare const intersection: string & { _brand: 'test-brand' }; intersection; @@ -569,7 +569,7 @@ ruleTester.run('no-useless-template-literals', rule, { endColumn: 17, suggestions: [ { - messageId: 'noUselessTemplateLiteral', + messageId: 'removeUselessTemplateLiteral', output: ` function func(arg: T) { arg; From d36ea2216e8c225a9c588f44a40b5f12b8063205 Mon Sep 17 00:00:00 2001 From: StyleShit Date: Thu, 14 Dec 2023 11:18:50 +0200 Subject: [PATCH 3/5] add quasis to cspell --- .cspell.json | 1 + 1 file changed, 1 insertion(+) 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", From aa148cd968a23fa6a2b6af5016cfa17e4f22b4c5 Mon Sep 17 00:00:00 2001 From: StyleShit Date: Sat, 16 Dec 2023 22:19:55 +0200 Subject: [PATCH 4/5] add some test cases --- .../src/rules/no-useless-template-literals.ts | 7 ++- .../no-useless-template-literals.test.ts | 60 +++++++++++++++++++ 2 files changed, 66 insertions(+), 1 deletion(-) 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 c09073126043..183c76a16900 100644 --- a/packages/eslint-plugin/src/rules/no-useless-template-literals.ts +++ b/packages/eslint-plugin/src/rules/no-useless-template-literals.ts @@ -135,7 +135,12 @@ export default createRule<[], MessageId>({ expression.type === AST_NODE_TYPES.Literal; if (isStringLiteral) { - fixes.push(fixer.replaceText(expression, expression.value)); + 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 eb49fb999503..2c22b9b89f98 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 @@ -580,5 +580,65 @@ ruleTester.run('no-useless-template-literals', rule, { }, ], }, + + { + code: "`${'`'}`;", + errors: [ + { + messageId: 'noUselessTemplateLiteral', + suggestions: [ + { + messageId: 'removeUselessTemplateLiteral', + output: "'`';", + }, + ], + }, + ], + }, + + { + code: "`back${'`'}tick`;", + errors: [ + { + messageId: 'noUselessTemplateLiteral', + suggestions: [ + { + messageId: 'removeUselessTemplateLiteral', + output: '`back\\`tick`;', + }, + ], + }, + ], + }, + + { + code: "`dollar${'${`this is test`}'}sign`;", + errors: [ + { + messageId: 'noUselessTemplateLiteral', + suggestions: [ + { + messageId: 'removeUselessTemplateLiteral', + output: '`dollar\\${\\`this is test\\`}sign`;', + }, + ], + }, + ], + }, + + { + code: '`complex${\'`${"`${test}`"}`\'}case`;', + errors: [ + { + messageId: 'noUselessTemplateLiteral', + suggestions: [ + { + messageId: 'removeUselessTemplateLiteral', + output: '`complex\\`\\${"\\`\\${test}\\`"}\\`case`;', + }, + ], + }, + ], + }, ], }); From 1d172274ba403058ad3eeb2afe913a3f27e6bdea Mon Sep 17 00:00:00 2001 From: StyleShit Date: Sun, 17 Dec 2023 19:08:51 +0200 Subject: [PATCH 5/5] use fix instead of suggestion --- .../src/rules/no-useless-template-literals.ts | 117 ++++---- .../no-useless-template-literals.test.ts | 276 +++++------------- 2 files changed, 123 insertions(+), 270 deletions(-) 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 183c76a16900..d5a8a602b6b4 100644 --- a/packages/eslint-plugin/src/rules/no-useless-template-literals.ts +++ b/packages/eslint-plugin/src/rules/no-useless-template-literals.ts @@ -10,12 +10,12 @@ import { isUndefinedIdentifier, } from '../util'; -type MessageId = 'noUselessTemplateLiteral' | 'removeUselessTemplateLiteral'; +type MessageId = 'noUselessTemplateLiteral'; export default createRule<[], MessageId>({ name: 'no-useless-template-literals', meta: { - hasSuggestions: true, + fixable: 'code', type: 'suggestion', docs: { description: 'Disallow unnecessary template literals', @@ -25,9 +25,6 @@ export default createRule<[], MessageId>({ messages: { noUselessTemplateLiteral: 'Template literal expression is unnecessary and can be simplified.', - - removeUselessTemplateLiteral: - 'Remove unnecessary template literal expression.', }, schema: [], }, @@ -72,27 +69,22 @@ export default createRule<[], MessageId>({ context.report({ node: node.expressions[0], messageId: 'noUselessTemplateLiteral', - suggest: [ - { - messageId: 'removeUselessTemplateLiteral', - 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, - ]), - ]; - }, - }, - ], + 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; @@ -108,45 +100,40 @@ export default createRule<[], MessageId>({ context.report({ node: expression, messageId: 'noUselessTemplateLiteral', - suggest: [ - { - messageId: 'removeUselessTemplateLiteral', - 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; - }, - }, - ], + 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 2c22b9b89f98..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,231 +137,154 @@ ruleTester.run('no-useless-template-literals', rule, { invalid: [ { code: '`${1}`;', + output: '`1`;', errors: [ { messageId: 'noUselessTemplateLiteral', line: 1, column: 4, endColumn: 5, - suggestions: [ - { - messageId: 'removeUselessTemplateLiteral', - output: '`1`;', - }, - ], }, ], }, { code: noFormat`\`\${ 1 }\`;`, + output: '`1`;', errors: [ { messageId: 'noUselessTemplateLiteral', - suggestions: [ - { - messageId: 'removeUselessTemplateLiteral', - output: '`1`;', - }, - ], }, ], }, { code: noFormat`\`\${ 'a' }\`;`, + output: `'a';`, errors: [ { messageId: 'noUselessTemplateLiteral', - suggestions: [ - { - messageId: 'removeUselessTemplateLiteral', - output: `'a';`, - }, - ], }, ], }, { code: noFormat`\`\${ "a" }\`;`, + output: `"a";`, errors: [ { messageId: 'noUselessTemplateLiteral', - suggestions: [ - { - messageId: 'removeUselessTemplateLiteral', - output: `"a";`, - }, - ], }, ], }, { code: noFormat`\`\${ 'a' + 'b' }\`;`, + output: `'a' + 'b';`, errors: [ { messageId: 'noUselessTemplateLiteral', - suggestions: [ - { - messageId: 'removeUselessTemplateLiteral', - output: `'a' + 'b';`, - }, - ], }, ], }, { code: '`${true}`;', + output: '`true`;', errors: [ { messageId: 'noUselessTemplateLiteral', line: 1, column: 4, endColumn: 8, - suggestions: [ - { - messageId: 'removeUselessTemplateLiteral', - output: '`true`;', - }, - ], }, ], }, { code: noFormat`\`\${ true }\`;`, + output: '`true`;', errors: [ { messageId: 'noUselessTemplateLiteral', - suggestions: [ - { - messageId: 'removeUselessTemplateLiteral', - output: '`true`;', - }, - ], }, ], }, { code: '`${null}`;', + output: '`null`;', errors: [ { messageId: 'noUselessTemplateLiteral', line: 1, column: 4, endColumn: 8, - suggestions: [ - { - messageId: 'removeUselessTemplateLiteral', - output: '`null`;', - }, - ], }, ], }, { code: noFormat`\`\${ null }\`;`, + output: '`null`;', errors: [ { messageId: 'noUselessTemplateLiteral', - suggestions: [ - { - messageId: 'removeUselessTemplateLiteral', - output: '`null`;', - }, - ], }, ], }, { code: '`${undefined}`;', + output: '`undefined`;', errors: [ { messageId: 'noUselessTemplateLiteral', line: 1, column: 4, endColumn: 13, - suggestions: [ - { - messageId: 'removeUselessTemplateLiteral', - output: '`undefined`;', - }, - ], }, ], }, { code: noFormat`\`\${ undefined }\`;`, + output: '`undefined`;', errors: [ { messageId: 'noUselessTemplateLiteral', - suggestions: [ - { - messageId: 'removeUselessTemplateLiteral', - output: '`undefined`;', - }, - ], }, ], }, { - code: "`${'a'}${'b'}`;", + code: "`${'a'} ${'b'}`;", + output: '`a b`;', errors: [ { messageId: 'noUselessTemplateLiteral', line: 1, column: 4, endColumn: 7, - suggestions: [ - { - messageId: 'removeUselessTemplateLiteral', - output: "`a${'b'}`;", - }, - ], }, { messageId: 'noUselessTemplateLiteral', line: 1, - column: 10, - endColumn: 13, - suggestions: [ - { - messageId: 'removeUselessTemplateLiteral', - output: "`${'a'}b`;", - }, - ], + column: 11, + endColumn: 14, }, ], }, { - code: noFormat`\`\${ 'a' }\${ 'b' }\`;`, + code: noFormat`\`\${ 'a' } \${ 'b' }\`;`, + output: '`a b`;', errors: [ { messageId: 'noUselessTemplateLiteral', - suggestions: [ - { - messageId: 'removeUselessTemplateLiteral', - output: noFormat`\`a\${ 'b' }\`;`, - }, - ], }, { messageId: 'noUselessTemplateLiteral', - suggestions: [ - { - messageId: 'removeUselessTemplateLiteral', - output: noFormat`\`\${ 'a' }b\`;`, - }, - ], }, ], }, @@ -371,99 +294,67 @@ 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', line: 3, column: 17, endColumn: 20, - suggestions: [ - { - messageId: 'removeUselessTemplateLiteral', - output: ` - declare const b: 'b'; - \`a\${b}c\`; - `, - }, - ], }, ], }, { code: "`a${'b'}`;", + output: '`ab`;', errors: [ { messageId: 'noUselessTemplateLiteral', line: 1, column: 5, endColumn: 8, - suggestions: [ - { - messageId: 'removeUselessTemplateLiteral', - output: '`ab`;', - }, - ], }, ], }, { - code: "`${'1 + 1 = '}${2}`;", + code: "`${'1 + 1 ='} ${2}`;", + output: '`1 + 1 = 2`;', errors: [ { messageId: 'noUselessTemplateLiteral', line: 1, column: 4, - endColumn: 14, - suggestions: [ - { - messageId: 'removeUselessTemplateLiteral', - output: '`1 + 1 = ${2}`;', - }, - ], + endColumn: 13, }, { messageId: 'noUselessTemplateLiteral', line: 1, column: 17, endColumn: 18, - suggestions: [ - { - messageId: 'removeUselessTemplateLiteral', - output: "`${'1 + 1 = '}2`;", - }, - ], }, ], }, { - code: "`${'a'}${true}`;", + code: "`${'a'} ${true}`;", + output: '`a true`;', errors: [ { messageId: 'noUselessTemplateLiteral', line: 1, column: 4, endColumn: 7, - suggestions: [ - { - messageId: 'removeUselessTemplateLiteral', - output: '`a${true}`;', - }, - ], }, { messageId: 'noUselessTemplateLiteral', line: 1, - column: 10, - endColumn: 14, - suggestions: [ - { - messageId: 'removeUselessTemplateLiteral', - output: "`${'a'}true`;", - }, - ], + column: 11, + endColumn: 15, }, ], }, @@ -473,21 +364,16 @@ ruleTester.run('no-useless-template-literals', rule, { declare const string: 'a'; \`\${string}\`; `, + output: ` + declare const string: 'a'; + string; + `, errors: [ { messageId: 'noUselessTemplateLiteral', line: 3, column: 12, endColumn: 18, - suggestions: [ - { - messageId: 'removeUselessTemplateLiteral', - output: ` - declare const string: 'a'; - string; - `, - }, - ], }, ], }, @@ -497,36 +383,26 @@ ruleTester.run('no-useless-template-literals', rule, { declare const string: 'a'; \`\${ string }\`; `, - errors: [ - { - messageId: 'noUselessTemplateLiteral', - suggestions: [ - { - messageId: 'removeUselessTemplateLiteral', - output: ` + output: ` declare const string: 'a'; string; `, - }, - ], + errors: [ + { + messageId: 'noUselessTemplateLiteral', }, ], }, { code: "`${String(Symbol.for('test'))}`;", + output: "String(Symbol.for('test'));", errors: [ { messageId: 'noUselessTemplateLiteral', line: 1, column: 4, endColumn: 30, - suggestions: [ - { - messageId: 'removeUselessTemplateLiteral', - output: "String(Symbol.for('test'));", - }, - ], }, ], }, @@ -536,21 +412,16 @@ 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', line: 3, column: 12, endColumn: 24, - suggestions: [ - { - messageId: 'removeUselessTemplateLiteral', - output: ` - declare const intersection: string & { _brand: 'test-brand' }; - intersection; - `, - }, - ], }, ], }, @@ -561,82 +432,77 @@ ruleTester.run('no-useless-template-literals', rule, { \`\${arg}\`; } `, + output: ` + function func(arg: T) { + arg; + } + `, errors: [ { messageId: 'noUselessTemplateLiteral', line: 3, column: 14, endColumn: 17, - suggestions: [ - { - messageId: 'removeUselessTemplateLiteral', - output: ` - function func(arg: T) { - arg; - } - `, - }, - ], }, ], }, { code: "`${'`'}`;", + output: "'`';", errors: [ { messageId: 'noUselessTemplateLiteral', - suggestions: [ - { - messageId: 'removeUselessTemplateLiteral', - output: "'`';", - }, - ], }, ], }, { code: "`back${'`'}tick`;", + output: '`back\\`tick`;', errors: [ { messageId: 'noUselessTemplateLiteral', - suggestions: [ - { - messageId: 'removeUselessTemplateLiteral', - output: '`back\\`tick`;', - }, - ], }, ], }, { code: "`dollar${'${`this is test`}'}sign`;", + output: '`dollar\\${\\`this is test\\`}sign`;', errors: [ { messageId: 'noUselessTemplateLiteral', - suggestions: [ - { - messageId: 'removeUselessTemplateLiteral', - output: '`dollar\\${\\`this is test\\`}sign`;', - }, - ], }, ], }, { 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', - suggestions: [ - { - messageId: 'removeUselessTemplateLiteral', - output: '`complex\\`\\${"\\`\\${test}\\`"}\\`case`;', - }, - ], }, ], }, 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