Skip to content

Commit 34de1d8

Browse files
committed
feat(:sparkles:): add regexSnippets option
1 parent 7cbf975 commit 34de1d8

File tree

10 files changed

+153
-2
lines changed

10 files changed

+153
-2
lines changed

README.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,30 @@ You need to set your `tsconfig.json` file in your eslint configuration via `pars
6868
}
6969
```
7070

71+
### Regex example
72+
73+
```json
74+
{
75+
"plugins": ["ts-ban-snippets"],
76+
"parserOptions": {
77+
"project": "./tsconfig.json"
78+
},
79+
"rules": {
80+
"ts-ban-snippets/ts-ban-snippets": [
81+
"error",
82+
{
83+
"banned": [
84+
{
85+
"regexSnippets": ["return void [reject|resolve]"],
86+
"message": "Please do not return void - instead place the return statement on the following line."
87+
}
88+
]
89+
}
90+
]
91+
}
92+
}
93+
```
94+
7195
### Example with multiple banned snippets
7296

7397
```json

docs/ts-ban-snippets.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,30 @@
2727
}
2828
```
2929

30+
### Regex example
31+
32+
```json
33+
{
34+
"plugins": ["ts-ban-snippets"],
35+
"parserOptions": {
36+
"project": "./tsconfig.json"
37+
},
38+
"rules": {
39+
"ts-ban-snippets/ts-ban-snippets": [
40+
"error",
41+
{
42+
"banned": [
43+
{
44+
"regexSnippets": ["return void [reject|resolve]"],
45+
"message": "Please do not return void - instead place the return statement on the following line."
46+
}
47+
]
48+
}
49+
]
50+
}
51+
}
52+
```
53+
3054
### Example with multiple banned snippets
3155

3256
```json

src/rules/ts-ban-snippets.ts

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ import {
66
} from "@typescript-eslint/experimental-utils/dist/ts-eslint";
77

88
type Banned = {
9-
snippets: string[];
9+
snippets?: string[];
10+
regexSnippets?: string[];
1011
message: string;
1112
// TODO includePaths?: string
1213
excludePaths?: string[];
@@ -33,6 +34,24 @@ function isFileInPaths(filePath: string, paths: string[]): boolean {
3334
return paths.some((path) => filePath.indexOf(path) >= 0);
3435
}
3536

37+
const isRegexMatch = (regexRaw: string, text: string): boolean => {
38+
const regex = new RegExp(regexRaw);
39+
return regex.test(text);
40+
};
41+
42+
const filterSnippets = (
43+
bannedSnippetsRaw: string[],
44+
isRegex: boolean,
45+
text: string
46+
): string[] => {
47+
const filterFun = (s: string): boolean => {
48+
if (isRegex) return isRegexMatch(s, text);
49+
return text.startsWith(s);
50+
};
51+
52+
return bannedSnippetsRaw.filter(filterFun);
53+
};
54+
3655
const analyzeNodeFor = (
3756
node: Node,
3857
banned: Banned,
@@ -51,7 +70,16 @@ const analyzeNodeFor = (
5170
node.getEnd();
5271
node.getSourceFile().getLineAndCharacterOfPosition(node.pos);
5372

54-
const bannedSnippets = banned.snippets.filter((s) => text.startsWith(s));
73+
if (!!banned.snippets && !!banned.regexSnippets) {
74+
throw new Error(
75+
"Invalid config: 'snippets' and 'regexSnippets' are mutually exclusive."
76+
);
77+
}
78+
79+
const bannedSnippetsRaw = banned.snippets || banned.regexSnippets || [];
80+
const isRegex = !!banned.regexSnippets;
81+
82+
const bannedSnippets = filterSnippets(bannedSnippetsRaw, isRegex, text);
5583

5684
if (bannedSnippets.length > 0) {
5785
const pos = node.getSourceFile().getLineAndCharacterOfPosition(node.pos);
@@ -95,6 +123,12 @@ export default createRule<Options, MessageIds>({
95123
type: "string",
96124
},
97125
},
126+
regexSnippets: {
127+
type: "array",
128+
items: {
129+
type: "string",
130+
},
131+
},
98132
message: {
99133
type: "string",
100134
},

tests/regexSnippets/fixtures/file.tsx

Whitespace-only changes.
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
class MyClassForRegex {
2+
doSomethingResolved() {
3+
return new Promise((resolve) => {
4+
return void resolve("error!");
5+
});
6+
}
7+
}
8+
9+
new MyClassForRegex();
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"extends": "../../tsconfig.base.eslint.json",
3+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { default } from "./test1";
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
const foo = 1;
2+
3+
export default foo;
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import foo from "./comp";
2+
3+
console.log({ foo });
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import rule from "../../src/rules/ts-ban-snippets";
2+
import { creteRuleTester, getCode } from "../testUtils";
3+
4+
creteRuleTester(__dirname).run("ts-ban-snippets - regex", rule, {
5+
valid: [
6+
{
7+
code: getCode("fixtures/valid/test.ts", __dirname),
8+
filename: "valid/test.ts",
9+
options: [
10+
{
11+
banned: [
12+
{
13+
regexSnippets: ["return void [reject|resolve]"],
14+
message:
15+
"Please do not return void - instead place the return statement on the following line.",
16+
},
17+
],
18+
},
19+
],
20+
},
21+
],
22+
invalid: [
23+
{
24+
code: getCode("fixtures/invalid/test1.ts", __dirname),
25+
filename: "invalid/test1.ts",
26+
options: [
27+
{
28+
banned: [
29+
{
30+
regexSnippets: ["^return void [reject|resolve]"],
31+
message:
32+
"Please do not return void - instead place the return statement on the following line.",
33+
},
34+
],
35+
},
36+
],
37+
errors: [
38+
{
39+
messageId: "BannedSnippetMessage",
40+
data: {
41+
name: "^return void [reject|resolve]",
42+
message:
43+
"Please do not return void - instead place the return statement on the following line.",
44+
ruleName: "ts-ban-snippets",
45+
},
46+
},
47+
],
48+
},
49+
],
50+
});

0 commit comments

Comments
 (0)