Skip to content

Commit 59218e3

Browse files
authored
no-array-for-each: Add fix for arrow function body (#1785)
1 parent 0034e69 commit 59218e3

File tree

6 files changed

+231
-8
lines changed

6 files changed

+231
-8
lines changed

rules/ast/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
'use strict';
22
module.exports = {
3+
isArrowFunctionBody: require('./is-arrow-function-body.js'),
34
isEmptyNode: require('./is-empty-node.js'),
45
};
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
'use strict';
2+
3+
function isArrowFunctionBody(node) {
4+
return node.parent.type === 'ArrowFunctionExpression' && node.parent.body === node;
5+
}
6+
7+
module.exports = isArrowFunctionBody;

rules/no-array-for-each.js

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ const isFunctionSelfUsedInside = require('./utils/is-function-self-used-inside.j
1818
const {isNodeMatches} = require('./utils/is-node-matches.js');
1919
const assertToken = require('./utils/assert-token.js');
2020
const {fixSpaceAroundKeyword, removeParentheses} = require('./fix/index.js');
21+
const {isArrowFunctionBody} = require('./ast/index.js');
2122

2223
const MESSAGE_ID_ERROR = 'no-array-for-each/error';
2324
const MESSAGE_ID_SUGGESTION = 'no-array-for-each/suggestion';
@@ -83,7 +84,7 @@ function getFixFunction(callExpression, functionInfo, context) {
8384
const iterableObject = callExpression.callee.object;
8485
const {returnStatements} = functionInfo.get(callback);
8586
const isOptionalObject = callExpression.callee.optional;
86-
const expressionStatement = stripChainExpression(callExpression).parent;
87+
const ancestor = stripChainExpression(callExpression).parent;
8788
const objectText = sourceCode.getText(iterableObject);
8889

8990
const getForOfLoopHeadText = () => {
@@ -247,12 +248,17 @@ function getFixFunction(callExpression, functionInfo, context) {
247248
yield * replaceReturnStatement(returnStatement, fixer);
248249
}
249250

250-
const expressionStatementLastToken = sourceCode.getLastToken(expressionStatement);
251-
// Remove semicolon if it's not needed anymore
252-
// foo.forEach(bar => {});
253-
// ^
254-
if (shouldRemoveExpressionStatementLastToken(expressionStatementLastToken)) {
255-
yield fixer.remove(expressionStatementLastToken, fixer);
251+
if (ancestor.type === 'ExpressionStatement') {
252+
const expressionStatementLastToken = sourceCode.getLastToken(ancestor);
253+
// Remove semicolon if it's not needed anymore
254+
// foo.forEach(bar => {});
255+
// ^
256+
if (shouldRemoveExpressionStatementLastToken(expressionStatementLastToken)) {
257+
yield fixer.remove(expressionStatementLastToken, fixer);
258+
}
259+
} else if (ancestor.type === 'ArrowFunctionExpression') {
260+
yield fixer.insertTextBefore(callExpression, '{ ');
261+
yield fixer.insertTextAfter(callExpression, ' }');
256262
}
257263

258264
yield * fixSpaceAroundKeyword(fixer, callExpression.parent, sourceCode);
@@ -332,7 +338,11 @@ function isFixable(callExpression, {scope, functionInfo, allIdentifiers, context
332338
}
333339

334340
// Check ancestors, we only fix `ExpressionStatement`
335-
if (stripChainExpression(callExpression).parent.type !== 'ExpressionStatement') {
341+
const callOrChainExpression = stripChainExpression(callExpression);
342+
if (
343+
callOrChainExpression.parent.type !== 'ExpressionStatement'
344+
&& !isArrowFunctionBody(callOrChainExpression)
345+
) {
336346
return false;
337347
}
338348

test/no-array-for-each.mjs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -425,10 +425,21 @@ test.snapshot({
425425

426426
// Need insert space after keyword
427427
'if (true) {} else[foo].forEach((element) => {})',
428+
429+
// Arrow function body
430+
'const a = () => (( foo.forEach(element => bar(element)) ))',
431+
'const a = () => (( foo.forEach(element => bar(element)) ));',
432+
'const a = () => foo.forEach(element => bar(element))',
433+
'const a = () => foo.forEach(element => bar(element));',
434+
'const a = () => void foo.forEach(element => bar(element));',
428435
].flatMap(code => [code, code.replace('.forEach', '?.forEach')]),
429436

430437
// Should not fix to invalid code
431438
'1?.forEach((a, b) => call(a, b))',
439+
440+
// Arrow function body
441+
'array.forEach((arrayInArray) => arrayInArray.forEach(element => bar(element)));',
442+
'array.forEach((arrayInArray) => arrayInArray?.forEach(element => bar(element)));',
432443
],
433444
});
434445

test/snapshots/no-array-for-each.mjs.md

Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3897,6 +3897,154 @@ Generated by [AVA](https://avajs.dev).
38973897
`
38983898

38993899
## Invalid #216
3900+
1 | const a = () => (( foo.forEach(element => bar(element)) ))
3901+
3902+
> Output
3903+
3904+
`␊
3905+
1 | const a = () => { for (const element of foo) bar(element) } ␊
3906+
`
3907+
3908+
> Error 1/1
3909+
3910+
`␊
3911+
> 1 | const a = () => (( foo.forEach(element => bar(element)) ))␊
3912+
| ^^^^^^^ Use \`for…of\` instead of \`.forEach(…)\`.␊
3913+
`
3914+
3915+
## Invalid #217
3916+
1 | const a = () => (( foo?.forEach(element => bar(element)) ))
3917+
3918+
> Output
3919+
3920+
`␊
3921+
1 | const a = () => { if (foo) for (const element of foo) bar(element) } ␊
3922+
`
3923+
3924+
> Error 1/1
3925+
3926+
`␊
3927+
> 1 | const a = () => (( foo?.forEach(element => bar(element)) ))␊
3928+
| ^^^^^^^ Use \`for…of\` instead of \`.forEach(…)\`.␊
3929+
`
3930+
3931+
## Invalid #218
3932+
1 | const a = () => (( foo.forEach(element => bar(element)) ));
3933+
3934+
> Output
3935+
3936+
`␊
3937+
1 | const a = () => { for (const element of foo) bar(element) } ;␊
3938+
`
3939+
3940+
> Error 1/1
3941+
3942+
`␊
3943+
> 1 | const a = () => (( foo.forEach(element => bar(element)) ));␊
3944+
| ^^^^^^^ Use \`for…of\` instead of \`.forEach(…)\`.␊
3945+
`
3946+
3947+
## Invalid #219
3948+
1 | const a = () => (( foo?.forEach(element => bar(element)) ));
3949+
3950+
> Output
3951+
3952+
`␊
3953+
1 | const a = () => { if (foo) for (const element of foo) bar(element) } ;␊
3954+
`
3955+
3956+
> Error 1/1
3957+
3958+
`␊
3959+
> 1 | const a = () => (( foo?.forEach(element => bar(element)) ));␊
3960+
| ^^^^^^^ Use \`for…of\` instead of \`.forEach(…)\`.␊
3961+
`
3962+
3963+
## Invalid #220
3964+
1 | const a = () => foo.forEach(element => bar(element))
3965+
3966+
> Output
3967+
3968+
`␊
3969+
1 | const a = () => { for (const element of foo) bar(element) }␊
3970+
`
3971+
3972+
> Error 1/1
3973+
3974+
`␊
3975+
> 1 | const a = () => foo.forEach(element => bar(element))␊
3976+
| ^^^^^^^ Use \`for…of\` instead of \`.forEach(…)\`.␊
3977+
`
3978+
3979+
## Invalid #221
3980+
1 | const a = () => foo?.forEach(element => bar(element))
3981+
3982+
> Output
3983+
3984+
`␊
3985+
1 | const a = () => { if (foo) for (const element of foo) bar(element) }␊
3986+
`
3987+
3988+
> Error 1/1
3989+
3990+
`␊
3991+
> 1 | const a = () => foo?.forEach(element => bar(element))␊
3992+
| ^^^^^^^ Use \`for…of\` instead of \`.forEach(…)\`.␊
3993+
`
3994+
3995+
## Invalid #222
3996+
1 | const a = () => foo.forEach(element => bar(element));
3997+
3998+
> Output
3999+
4000+
`␊
4001+
1 | const a = () => { for (const element of foo) bar(element) };␊
4002+
`
4003+
4004+
> Error 1/1
4005+
4006+
`␊
4007+
> 1 | const a = () => foo.forEach(element => bar(element));␊
4008+
| ^^^^^^^ Use \`for…of\` instead of \`.forEach(…)\`.␊
4009+
`
4010+
4011+
## Invalid #223
4012+
1 | const a = () => foo?.forEach(element => bar(element));
4013+
4014+
> Output
4015+
4016+
`␊
4017+
1 | const a = () => { if (foo) for (const element of foo) bar(element) };␊
4018+
`
4019+
4020+
> Error 1/1
4021+
4022+
`␊
4023+
> 1 | const a = () => foo?.forEach(element => bar(element));␊
4024+
| ^^^^^^^ Use \`for…of\` instead of \`.forEach(…)\`.␊
4025+
`
4026+
4027+
## Invalid #224
4028+
1 | const a = () => void foo.forEach(element => bar(element));
4029+
4030+
> Error 1/1
4031+
4032+
`␊
4033+
> 1 | const a = () => void foo.forEach(element => bar(element));␊
4034+
| ^^^^^^^ Use \`for…of\` instead of \`.forEach(…)\`.␊
4035+
`
4036+
4037+
## Invalid #225
4038+
1 | const a = () => void foo?.forEach(element => bar(element));
4039+
4040+
> Error 1/1
4041+
4042+
`␊
4043+
> 1 | const a = () => void foo?.forEach(element => bar(element));␊
4044+
| ^^^^^^^ Use \`for…of\` instead of \`.forEach(…)\`.␊
4045+
`
4046+
4047+
## Invalid #226
39004048
1 | 1?.forEach((a, b) => call(a, b))
39014049

39024050
> Output
@@ -3911,3 +4059,49 @@ Generated by [AVA](https://avajs.dev).
39114059
> 1 | 1?.forEach((a, b) => call(a, b))␊
39124060
| ^^^^^^^ Use \`for…of\` instead of \`.forEach(…)\`.␊
39134061
`
4062+
4063+
## Invalid #227
4064+
1 | array.forEach((arrayInArray) => arrayInArray.forEach(element => bar(element)));
4065+
4066+
> Output
4067+
4068+
`␊
4069+
1 | for (const arrayInArray of array) for (const element of arrayInArray) bar(element);␊
4070+
`
4071+
4072+
> Error 1/2
4073+
4074+
`␊
4075+
> 1 | array.forEach((arrayInArray) => arrayInArray.forEach(element => bar(element)));␊
4076+
| ^^^^^^^ Use \`for…of\` instead of \`.forEach(…)\`.␊
4077+
`
4078+
4079+
> Error 2/2
4080+
4081+
`␊
4082+
> 1 | array.forEach((arrayInArray) => arrayInArray.forEach(element => bar(element)));␊
4083+
| ^^^^^^^ Use \`for…of\` instead of \`.forEach(…)\`.␊
4084+
`
4085+
4086+
## Invalid #228
4087+
1 | array.forEach((arrayInArray) => arrayInArray?.forEach(element => bar(element)));
4088+
4089+
> Output
4090+
4091+
`␊
4092+
1 | for (const arrayInArray of array) if (arrayInArray) for (const element of arrayInArray) bar(element);␊
4093+
`
4094+
4095+
> Error 1/2
4096+
4097+
`␊
4098+
> 1 | array.forEach((arrayInArray) => arrayInArray?.forEach(element => bar(element)));␊
4099+
| ^^^^^^^ Use \`for…of\` instead of \`.forEach(…)\`.␊
4100+
`
4101+
4102+
> Error 2/2
4103+
4104+
`␊
4105+
> 1 | array.forEach((arrayInArray) => arrayInArray?.forEach(element => bar(element)));␊
4106+
| ^^^^^^^ Use \`for…of\` instead of \`.forEach(…)\`.␊
4107+
`
602 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)