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

Commit 8e3a683

Browse files
Update no-duplicate-string: Add ignoreStrings option (#405)
1 parent c053acc commit 8e3a683

File tree

4 files changed

+61
-11
lines changed

4 files changed

+61
-11
lines changed

docs/rules/no-duplicate-string.md

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,19 @@ To prevent generating some false-positives, literals having less than 10 charact
77

88
## Configuration
99

10-
Number of times a literal must be duplicated to trigger an issue. Default is 3.
10+
Number of times a literal must be duplicated to trigger an issue. Default is `3`.
1111

1212
```json
1313
{
1414
"no-duplicate-string": "error",
15-
"no-duplicate-string": ["error", 5]
15+
"no-duplicate-string": ["error", { "threshold": 5 }]
1616
}
1717
```
18+
19+
Comma-separated list of strings that must be ignored. Default is `"application/json"`.
20+
21+
```json
22+
{
23+
"no-duplicate-string": "error",
24+
"no-duplicate-string": ["error", { "ignoreStrings": "foo,bar,baz" }]
25+
}

ruling/snapshots/no-duplicate-string

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,6 @@ src/express/lib/response.js: 132,194
142142
src/freeCodeCamp/common/app/redux/utils.test.js: 54
143143
src/freeCodeCamp/common/app/routes/Challenges/utils/index.test.js: 24,25,28,90,112,164,274,321,322,350,405,477,597,811
144144
src/freeCodeCamp/common/app/routes/Challenges/views/step/redux/step-challenge-epic.test.js: 48,62,69
145-
src/freeCodeCamp/common/utils/ajax-stream.js: 283
146145
src/freeCodeCamp/public/js/calculator.js: 9,16,149
147146
src/freeCodeCamp/public/js/lib/loop-protect/loop-protect.js: 283
148147
src/freeCodeCamp/seed/unpackedChallenge.js: 218,246

src/rules/no-duplicate-string.ts

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import { issueLocation, report } from '../utils/locations';
2525

2626
// Number of times a literal must be duplicated to trigger an issue
2727
const DEFAULT_THRESHOLD = 3;
28+
const DEFAULT_IGNORE_STRINGS = 'application/json';
2829
const MIN_LENGTH = 10;
2930
const NO_SEPARATOR_REGEXP = /^\w*$/;
3031
const EXCLUDED_CONTEXTS = [
@@ -36,7 +37,9 @@ const EXCLUDED_CONTEXTS = [
3637
];
3738
const message = 'Define a constant instead of duplicating this literal {{times}} times.';
3839

39-
type Options = (number | 'sonar-runtime')[];
40+
type Options =
41+
| [{ threshold?: number; ignoreStrings?: string } | undefined, 'sonar-runtime']
42+
| [{ threshold?: number; ignoreStrings?: string } | undefined];
4043
type Context = TSESLint.RuleContext<string, Options>;
4144

4245
const rule: TSESLint.RuleModule<string, Options> = {
@@ -52,16 +55,21 @@ const rule: TSESLint.RuleModule<string, Options> = {
5255
url: docsUrl(__filename),
5356
},
5457
schema: [
55-
{ type: 'integer', minimum: 2 },
58+
{
59+
type: 'object',
60+
properties: {
61+
threshold: { type: 'integer', minimum: 2 },
62+
ignoreStrings: { type: 'string', default: DEFAULT_IGNORE_STRINGS },
63+
},
64+
},
5665
{ enum: ['sonar-runtime'] /* internal parameter for rules having secondary locations */ },
5766
],
5867
},
5968

6069
create(context) {
6170
const literalsByValue: Map<string, TSESTree.Literal[]> = new Map();
62-
const threshold: number =
63-
typeof context.options[0] === 'number' ? context.options[0] : DEFAULT_THRESHOLD;
64-
71+
const { threshold, ignoreStrings } = extractOptions(context);
72+
const whitelist = ignoreStrings.split(',');
6573
return {
6674
Literal: (node: TSESTree.Node) => {
6775
const literal = node as TSESTree.Literal;
@@ -74,6 +82,7 @@ const rule: TSESLint.RuleModule<string, Options> = {
7482
const stringContent = literal.value.trim();
7583

7684
if (
85+
!whitelist.includes(literal.value) &&
7786
!isExcludedByUsageContext(context, literal) &&
7887
stringContent.length >= MIN_LENGTH &&
7988
!stringContent.match(NO_SEPARATOR_REGEXP)
@@ -130,4 +139,17 @@ function isObjectPropertyKey(parent: TSESTree.Node, literal: TSESTree.Literal) {
130139
return parent.type === 'Property' && parent.key === literal;
131140
}
132141

142+
function extractOptions(context: Context) {
143+
let threshold: number = DEFAULT_THRESHOLD;
144+
let ignoreStrings: string = DEFAULT_IGNORE_STRINGS;
145+
const options = context.options[0];
146+
if (typeof options?.threshold === 'number') {
147+
threshold = options.threshold;
148+
}
149+
if (typeof options?.ignoreStrings === 'string') {
150+
ignoreStrings = options.ignoreStrings;
151+
}
152+
return { threshold, ignoreStrings };
153+
}
154+
133155
export = rule;

tests/rules/no-duplicate-string.test.ts

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,13 @@ ruleTester.run('no-duplicate-string', rule, {
2727
console.log("some message");
2828
console.log("some message");
2929
console.log('some message');`,
30-
options: [4],
30+
options: [{ threshold: 4 }],
31+
},
32+
{
33+
code: ` // too small
34+
console.log("a&b");
35+
console.log("a&b");
36+
console.log("a&b");`,
3137
},
3238
{
3339
code: ` // too small
@@ -151,6 +157,21 @@ ruleTester.run('no-duplicate-string', rule, {
151157
}
152158
`,
153159
},
160+
{
161+
code: `
162+
'application/json';
163+
'application/json';
164+
'application/json';;
165+
`,
166+
},
167+
{
168+
code: `
169+
console.log('Hello world!');
170+
console.log('Hello world!');
171+
console.log('Hello world!');
172+
`,
173+
options: [{ threshold: 2, ignoreStrings: 'Hello world!' }, 'sonar-runtime'],
174+
},
154175
],
155176
invalid: [
156177
{
@@ -196,7 +217,7 @@ ruleTester.run('no-duplicate-string', rule, {
196217
endColumn: 31,
197218
},
198219
],
199-
options: [2, 'sonar-runtime'],
220+
options: [{ threshold: 2 }, 'sonar-runtime'],
200221
},
201222
{
202223
code: `
@@ -228,7 +249,7 @@ ruleTester.run('no-duplicate-string', rule, {
228249
line: 2,
229250
},
230251
],
231-
options: [2],
252+
options: [{ threshold: 2 }],
232253
},
233254
{
234255
code: `

0 commit comments

Comments
 (0)