Skip to content
This repository was archived by the owner on Oct 3, 2024. It is now read-only.

Commit b720cff

Browse files
Rule S4624: Template literals should not be nested (#233)
Co-authored-by: Tibor Blenessy <[email protected]>
1 parent 87d2bc1 commit b720cff

File tree

6 files changed

+230
-0
lines changed

6 files changed

+230
-0
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ Code Smells, or maintainability issues, are raised for places of code which migh
3030
* Two branches in a conditional structure should not have exactly the same implementation ([`no-duplicated-branches`])
3131
* Functions should not have identical implementations ([`no-identical-functions`])
3232
* Boolean checks should not be inverted ([`no-inverted-boolean-check`]) (:wrench: *fixable*)
33+
* Template literals should not be nested ([`no-nested-template-literals`])
3334
* Boolean literals should not be redundant ([`no-redundant-boolean`])
3435
* Jump statements should not be redundant ([`no-redundant-jump`])
3536
* Conditionals should start on new lines ([`no-same-line-conditional`])
@@ -55,6 +56,7 @@ Code Smells, or maintainability issues, are raised for places of code which migh
5556
[`no-identical-expressions`]: ./docs/rules/no-identical-expressions.md
5657
[`no-identical-functions`]: ./docs/rules/no-identical-functions.md
5758
[`no-inverted-boolean-check`]: ./docs/rules/no-inverted-boolean-check.md
59+
[`no-nested-template-literals`]: ./docs/rules/no-nested-template-literals.md
5860
[`no-one-iteration-loop`]: ./docs/rules/no-one-iteration-loop.md
5961
[`no-redundant-boolean`]: ./docs/rules/no-redundant-boolean.md
6062
[`no-redundant-jump`]: ./docs/rules/no-redundant-jump.md
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# no-nested-template-literals
2+
3+
Template literals (previously named "template strings") are an elegant way to build a string without using the `+` operator to make strings concatenation more readable.
4+
5+
However, it’s possible to build complex string literals by nesting together multiple template literals, and therefore lose readability and maintainability.
6+
7+
In such situations, it’s preferable to move the nested template into a separate statement.
8+
9+
## Noncompliant Code Example
10+
11+
```javascript
12+
let color = "red";
13+
let count = 3;
14+
let message = `I have ${color ? `${count} ${color}` : count} apples`; // Noncompliant; nested template strings not easy to read
15+
```
16+
17+
## Compliant Solution
18+
19+
```javascript
20+
let color = "red";
21+
let count = 3;
22+
let apples = color ? `${count} ${color}` : count;
23+
let message = `I have ${apples} apples`;
24+
```
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
src/create-react-app/packages/create-react-app/createReactApp.js: 141,380,550
2+
src/create-react-app/packages/react-dev-utils/WebpackDevServerUtils.js: 111,407
3+
src/create-react-app/packages/react-error-overlay/src/utils/stack-frame.js: 115,115
4+
src/create-react-app/packages/react-scripts/scripts/eject.js: 195,196
5+
src/create-react-app/packages/react-scripts/scripts/init.js: 235
6+
src/create-react-app/packages/react-scripts/scripts/utils/verifyPackageTree.js: 132
7+
src/jest/packages/jest-cli/src/get_no_test_found.js: 13
8+
src/jest/packages/jest-cli/src/get_no_test_found_verbose.js: 40
9+
src/jest/packages/jest-cli/src/pattern_prompt.js: 22
10+
src/jest/packages/jest-config/src/deprecated.js: 50
11+
src/jest/packages/jest-matcher-utils/src/index.js: 171
12+
src/jest/packages/jest-validate/src/errors.js: 22,29
13+
src/jest/packages/jest-validate/src/warnings.js: 31
14+
src/react/scripts/jest/setupTests.js: 102
15+
src/react/scripts/release/build-commands/add-git-tag.js: 21
16+
src/react/scripts/rollup/build.js: 446
17+
src/react/scripts/rollup/wrappers.js: 61,62,84,85
18+
src/spectrum/admin/src/api/index.js: 36
19+
src/spectrum/admin/src/components/avatar/index.js: 17,20,31,32,33
20+
src/spectrum/admin/src/components/buttons/style.js: 30,56,56,62,63,73,81,92,102,112,116,125,131,147,156,158
21+
src/spectrum/admin/src/components/formElements/style.js: 72,142
22+
src/spectrum/admin/src/components/globals/index.js: 62,63,72,73,75,77,82,86,139,417,418,464,464,505,507
23+
src/spectrum/admin/src/components/loading/style.js: 19,20,28,29,30,31,33,35
24+
src/spectrum/admin/src/views/navbar/style.js: 50,87,89,89
25+
src/spectrum/athena/index.js: 73
26+
src/spectrum/athena/queues/community-invoice-paid.js: 48
27+
src/spectrum/hermes/index.js: 91
28+
src/spectrum/hermes/queues/send-new-message-email.js: 73,74,90,91
29+
src/spectrum/hyperion/renderer/html-template.js: 77
30+
src/spectrum/mercury/index.js: 19
31+
src/spectrum/mobile/components/Avatar/style.js: 8,9,10,35,36,37,44,45,46
32+
src/spectrum/mobile/components/Text/index.js: 36,39,39,41
33+
src/spectrum/shared/graphql/index.js: 27
34+
src/spectrum/src/components/avatar/style.js: 25,26,33,35,36,90,91,97,99,100,108,109,115,117,118
35+
src/spectrum/src/components/buttons/style.js: 33,58,58,64,65,75,83,94,98,101,112,123,127,136,143,153,157,166,173,188,197,199
36+
src/spectrum/src/components/chatInput/style.js: 114
37+
src/spectrum/src/components/column/index.js: 18
38+
src/spectrum/src/components/draftjs-editor/Image.js: 26,49
39+
src/spectrum/src/components/draftjs-editor/style.js: 89,128
40+
src/spectrum/src/components/emailInvitationForm/index.js: 153,154
41+
src/spectrum/src/components/formElements/style.js: 74,144,217,219
42+
src/spectrum/src/components/fullscreenView/style.js: 29
43+
src/spectrum/src/components/globals/index.js: 99,100,109,110,112,114,119,123,176,492,539
44+
src/spectrum/src/components/goop/index.js: 37
45+
src/spectrum/src/components/linkPreview/style.js: 38
46+
src/spectrum/src/components/loginButtonSet/style.js: 36,58
47+
src/spectrum/src/components/menu/style.js: 15
48+
src/spectrum/src/components/message/style.js: 15,15,19,74,75,151,222,222,242,242,275,275,283
49+
src/spectrum/src/components/profile/coverPhoto.js: 17
50+
src/spectrum/src/components/reaction/style.js: 19,28,39
51+
src/spectrum/src/components/segmentedControl/index.js: 43
52+
src/spectrum/src/components/themedSection/index.js: 27,37,46,55,69,78
53+
src/spectrum/src/components/threadComposer/style.js: 46,59
54+
src/spectrum/src/components/toasts/style.js: 53
55+
src/spectrum/src/components/upsell/newUserUpsellStyles.js: 17
56+
src/spectrum/src/components/upsell/style.js: 145,214,306,339
57+
src/spectrum/src/views/communitySettings/style.js: 172
58+
src/spectrum/src/views/dashboard/style.js: 162,205,219,807
59+
src/spectrum/src/views/directMessages/components/style.js: 30
60+
src/spectrum/src/views/directMessages/style.js: 48
61+
src/spectrum/src/views/explore/style.js: 105,117
62+
src/spectrum/src/views/login/style.js: 100,119,188
63+
src/spectrum/src/views/navbar/style.js: 45,54,63,148,159,209
64+
src/spectrum/src/views/newCommunity/components/createCommunityForm/style.js: 20,21
65+
src/spectrum/src/views/newCommunity/index.js: 110,126,147
66+
src/spectrum/src/views/newCommunity/style.js: 8
67+
src/spectrum/src/views/newUserOnboarding/style.js: 87
68+
src/spectrum/src/views/notifications/style.js: 126,137,138
69+
src/spectrum/src/views/pages/pricing/style.js: 105,105,114,388,389,390
70+
src/spectrum/src/views/pages/style.js: 191,409,439,669,684
71+
src/spectrum/src/views/thread/style.js: 37,113,231,239,544
72+
src/spectrum/src/views/threadSlider/style.js: 16
73+
src/spectrum/vulcan/index.js: 27
74+
src/vue/scripts/gen-release-note.js: 3,3
75+
src/vue/scripts/verify-commit-msg.js: 10,12,13,15
76+
src/vue/src/compiler/codegen/index.js: 75,77,301,303,305,326,353,399,459,460,482
77+
src/vue/src/core/instance/render-helpers/render-static.js: 36,36
78+
src/vue/src/core/util/debug.js: 92
79+
src/vue/src/core/vdom/create-component.js: 191
80+
src/vue/src/server/optimizing-compiler/codegen.js: 87,89,118
81+
src/vue/src/server/optimizing-compiler/modules.js: 121
82+
src/vue/src/server/template-renderer/index.js: 158

src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ const sonarjsRules: [string, TSESLint.Linter.RuleLevel][] = [
3434
['no-identical-functions', 'error'],
3535
['no-identical-expressions', 'error'],
3636
['no-inverted-boolean-check', 'error'],
37+
['no-nested-template-literals', 'error'],
3738
['no-one-iteration-loop', 'error'],
3839
['no-redundant-boolean', 'error'],
3940
['no-redundant-jump', 'error'],
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/*
2+
* eslint-plugin-sonarjs
3+
* Copyright (C) 2018-2021 SonarSource SA
4+
* mailto:info AT sonarsource DOT com
5+
*
6+
* This program is free software; you can redistribute it and/or
7+
* modify it under the terms of the GNU Lesser General Public
8+
* License as published by the Free Software Foundation; either
9+
* version 3 of the License, or (at your option) any later version.
10+
*
11+
* This program is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14+
* Lesser General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU Lesser General Public License
17+
* along with this program; if not, write to the Free Software Foundation,
18+
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19+
*/
20+
// https://jira.sonarsource.com/browse/RSPEC-4624
21+
22+
import { TSESTree } from '@typescript-eslint/experimental-utils';
23+
import { Rule } from '../utils/types';
24+
25+
const message = 'Refactor this code to not use nested template literals.';
26+
27+
const rule: Rule.RuleModule = {
28+
meta: {
29+
type: 'suggestion',
30+
},
31+
create(context: Rule.RuleContext) {
32+
return {
33+
'TemplateLiteral TemplateLiteral': (node: TSESTree.Node) => {
34+
context.report({
35+
message,
36+
node,
37+
});
38+
},
39+
};
40+
},
41+
};
42+
43+
export = rule;
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
/*
2+
* eslint-plugin-sonarjs
3+
* Copyright (C) 2018-2021 SonarSource SA
4+
* mailto:info AT sonarsource DOT com
5+
*
6+
* This program is free software; you can redistribute it and/or
7+
* modify it under the terms of the GNU Lesser General Public
8+
* License as published by the Free Software Foundation; either
9+
* version 3 of the License, or (at your option) any later version.
10+
*
11+
* This program is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14+
* Lesser General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU Lesser General Public License
17+
* along with this program; if not, write to the Free Software Foundation,
18+
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19+
*/
20+
21+
/* eslint-disable no-template-curly-in-string */
22+
import { ruleTester } from '../rule-tester';
23+
import * as rule from '../../src/rules/no-nested-template-literals';
24+
25+
ruleTester.run('Template literals should not be nested', rule, {
26+
valid: [
27+
{
28+
code: 'let nestedMessage = `${count} ${color}`;',
29+
},
30+
{
31+
code: 'let message = `I have ${color ? nestedMessage : count} apples`;',
32+
},
33+
],
34+
invalid: [
35+
{
36+
code: 'let message = `I have ${color ? `${x ? `indeed 0` : count} ${color}` : count} apples`;',
37+
errors: [
38+
{
39+
message: `Refactor this code to not use nested template literals.`,
40+
line: 1,
41+
endLine: 1,
42+
column: 33,
43+
endColumn: 69,
44+
},
45+
{
46+
message: `Refactor this code to not use nested template literals.`,
47+
line: 1,
48+
endLine: 1,
49+
column: 40,
50+
endColumn: 50,
51+
},
52+
],
53+
},
54+
{
55+
code: 'let message = `I have ${color ? `${count} ${color}` : count} apples`;',
56+
errors: 1,
57+
},
58+
{
59+
code: 'let message = `I have ${color ? `${x ? `indeed ${0}` : count} ${color}` : count} apples`;',
60+
errors: 2,
61+
},
62+
{
63+
code:
64+
'function tag(strings, ...keys) {console.log(strings[2]);}\n' +
65+
'let message1 = tag`I have ${color ? `${count} ${color}` : count} apples`;\n' +
66+
'let message2 = tag`I have ${color ? tag`${count} ${color}` : count} apples`;',
67+
errors: 2,
68+
},
69+
{
70+
code: 'let message = `I have ${color ? `${count} ${color}` : `this is ${count}`} apples`;',
71+
errors: 2,
72+
},
73+
{
74+
code: 'let message = `I have ${`${count} ${color}`} ${`this is ${count}`} apples`;',
75+
errors: 2,
76+
},
77+
],
78+
});

0 commit comments

Comments
 (0)