Skip to content

Commit 224eedc

Browse files
committed
feat: new to-for-each and to-for-of commands
1 parent e205e59 commit 224eedc

15 files changed

+624
-119
lines changed

README.md

Lines changed: 78 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ export default [
3232

3333
### `to-function`
3434

35-
Provide a quick way to convert an arrow function to a standard function declaration.
35+
Convert an arrow function to a standard function declaration.
3636

3737
Trigger with `/// to-function` comment (triple slashes) one line above the arrow function.
3838

@@ -58,7 +58,7 @@ async function foo(msg: string): void {
5858

5959
### `to-arrow`
6060

61-
Provide a quick way to convert a standard function declaration to an arrow function.
61+
Convert a standard function declaration to an arrow function.
6262

6363
Triggers:
6464
- `/// to-arrow`
@@ -109,6 +109,82 @@ const obj = {
109109

110110
Different from the other commands, the comment will not be removed after transformation to keep the sorting.
111111

112+
### `to-for-each`
113+
114+
Convert for-of/for-in loop to `.forEach()`.
115+
116+
Triggers:
117+
- `/// to-for-each`
118+
- `/// foreach`
119+
120+
```js
121+
/// to-for-each
122+
for (const item of items) {
123+
if (!item)
124+
continue
125+
console.log(item)
126+
}
127+
```
128+
129+
Will be converted to:
130+
131+
```js
132+
items.forEach((item) => {
133+
if (!item)
134+
return
135+
console.log(item)
136+
})
137+
```
138+
139+
For for-in loop:
140+
141+
```js
142+
/// to-for-each
143+
for (const key in obj) {
144+
if (!obj[key])
145+
continue
146+
console.log(obj[key])
147+
}
148+
```
149+
150+
Will be converted to:
151+
152+
```js
153+
Object.keys(obj).forEach((key) => {
154+
if (!obj[key])
155+
return
156+
console.log(obj[key])
157+
})
158+
```
159+
160+
### `to-for-of`
161+
162+
Convert `.forEach()` to for-of loop.
163+
164+
Triggers:
165+
166+
- `/// to-for-of`
167+
- `/// forof`
168+
169+
```js
170+
/// to-for-of
171+
items.forEach((item) => {
172+
if (!item)
173+
return
174+
console.log(item)
175+
})
176+
```
177+
178+
Will be converted to:
179+
180+
```js
181+
for (const item of items) {
182+
if (!item)
183+
continue
184+
console.log(item)
185+
}
186+
```
187+
112188
## Sponsors
113189

114190
<p align="center">

src/commands/index.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
11
import { toFunction } from './to-function'
22
import { toArrow } from './to-arrow'
33
import { keepSorted } from './keep-sorted'
4+
import { toForEach } from './to-for-each'
5+
import { toForOf } from './to-for-of'
46

7+
// @keep-sorted
58
export const commands = {
6-
toFunction,
7-
toArrow,
89
keepSorted,
10+
toArrow,
11+
toForEach,
12+
toForOf,
13+
toFunction,
914
}

src/commands/keep-sorted.test.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ const invalids = [
3232
bar: () => {},
3333
foo,
3434
}`,
35-
messageId: ['fix'],
35+
messageId: ['command-fix'],
3636
},
3737
// Array elements
3838
{
@@ -50,7 +50,7 @@ const invalids = [
5050
'bar',
5151
'foo',
5252
]`,
53-
messageId: ['fix'],
53+
messageId: ['command-fix'],
5454
},
5555
// Type interface members
5656
{
@@ -72,7 +72,7 @@ const invalids = [
7272
parentPath: Path | null
7373
}
7474
`,
75-
messageId: ['fix'],
75+
messageId: ['command-fix'],
7676
},
7777
// Type type members
7878
{
@@ -94,7 +94,7 @@ const invalids = [
9494
parentPath: Path | null
9595
}
9696
`,
97-
messageId: ['fix'],
97+
messageId: ['command-fix'],
9898
},
9999
{
100100
code: d`
@@ -117,7 +117,7 @@ const invalids = [
117117
parentPath: null,
118118
})
119119
}`,
120-
messageId: ['fix'],
120+
messageId: ['command-fix'],
121121
},
122122
]
123123

src/commands/keep-sorted.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ export const keepSorted: Command = {
55
name: 'keep-sorted',
66
match: /^[\/@:]\s*(keep-sorted|sorted)$/,
77
action(ctx) {
8-
const node = ctx.getNodeBelow(
8+
const node = ctx.findNodeBelow(
99
'ObjectExpression',
1010
'ArrayExpression',
1111
'TSInterfaceBody',

src/commands/to-arrow.test.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ const invalids = [
1717
/// 2a
1818
const a = 1`,
1919
output: null,
20-
messageId: 'invalid-command',
20+
messageId: 'command-error',
2121
},
2222
// Function declaration
2323
{
@@ -30,7 +30,7 @@ const invalids = [
3030
export const foo = async <T = 1>(arg: T): Bar => {
3131
const bar = () => {}
3232
}`,
33-
messageId: ['fix', 'fix'],
33+
messageId: ['command-removal', 'command-fix'],
3434
},
3535
// Function expression
3636
{
@@ -43,7 +43,7 @@ const invalids = [
4343
const bar = async <T = 1>(arg: T): Bar => {
4444
function baz() {}
4545
}`,
46-
messageId: ['fix', 'fix'],
46+
messageId: ['command-removal', 'command-fix'],
4747
},
4848
// Object method
4949
{
@@ -66,7 +66,7 @@ const invalids = [
6666
return 1
6767
},
6868
}`,
69-
messageId: ['fix', 'fix'],
69+
messageId: ['command-removal', 'command-fix'],
7070
},
7171
// Class method
7272
{
@@ -89,7 +89,7 @@ const invalids = [
8989
return 1
9090
}
9191
}`,
92-
messageId: ['fix', 'fix'],
92+
messageId: ['command-removal', 'command-fix'],
9393
},
9494
]
9595

src/commands/to-arrow.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ export const toArrow: Command = {
44
name: 'to-arrow',
55
match: /^[\/:@]\s*(to-arrow|2a|ta)$/,
66
action(ctx) {
7-
const fn = ctx.getNodeBelow('FunctionDeclaration', 'FunctionExpression')
7+
const fn = ctx.findNodeBelow('FunctionDeclaration', 'FunctionExpression')
88
if (!fn)
99
return ctx.reportError('Unable to find function declaration to convert')
1010

src/commands/to-for-each.test.ts

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
import { RuleTester } from 'eslint'
2+
import * as tsParser from '@typescript-eslint/parser'
3+
import { createRuleWithCommands } from '../rule'
4+
import { toForEach as command } from './to-for-each'
5+
import { d } from './_test-utils'
6+
7+
const valids = [
8+
'const foo = function () {}',
9+
]
10+
11+
const invalids = [
12+
// Basic for-of
13+
{
14+
code: d`
15+
/// to-for-each
16+
for (const foo of bar) {
17+
if (foo) {
18+
continue
19+
}
20+
else if (1 + 1 === 2) {
21+
continue
22+
}
23+
}`,
24+
output: d`
25+
bar.forEach(foo => {
26+
if (foo) {
27+
return
28+
}
29+
else if (1 + 1 === 2) {
30+
return
31+
}
32+
})`,
33+
messageId: ['command-removal', 'command-fix'],
34+
},
35+
// One-line for-of
36+
{
37+
code: d`
38+
/// to-for-each
39+
for (const foo of bar)
40+
count += 1
41+
`,
42+
output: d`
43+
bar.forEach(foo => {
44+
count += 1
45+
})
46+
`,
47+
messageId: ['command-removal', 'command-fix'],
48+
},
49+
// Nested for
50+
{
51+
code: d`
52+
/// to-for-each
53+
for (const foo of bar) {
54+
for (const baz of foo) {
55+
if (foo) {
56+
continue
57+
}
58+
}
59+
const fn1 = () => {
60+
continue
61+
}
62+
function fn2() {
63+
continue
64+
}
65+
}`,
66+
output: d`
67+
bar.forEach(foo => {
68+
for (const baz of foo) {
69+
if (foo) {
70+
continue
71+
}
72+
}
73+
const fn1 = () => {
74+
continue
75+
}
76+
function fn2() {
77+
continue
78+
}
79+
})`,
80+
messageId: ['command-removal', 'command-fix'],
81+
},
82+
// Throw on return statement
83+
{
84+
code: d`
85+
/// to-for-each
86+
for (const foo of bar) {
87+
return foo
88+
}`,
89+
output: null,
90+
messageId: ['command-error', 'command-error-cause'],
91+
},
92+
]
93+
94+
const ruleTester: RuleTester = new RuleTester({
95+
languageOptions: {
96+
parser: tsParser,
97+
},
98+
})
99+
100+
ruleTester.run(command.name, createRuleWithCommands([command]) as any, {
101+
valid: valids,
102+
invalid: invalids.map(i => ({
103+
code: i.code,
104+
output: i.output,
105+
errors: (Array.isArray(i.messageId) ? i.messageId : [i.messageId])
106+
.map(id => ({ messageId: id })),
107+
})),
108+
})

0 commit comments

Comments
 (0)