Skip to content

Commit bff9091

Browse files
authored
feat: handle Array.fromAsync in array-callback-return (#20457)
* feat: handle `Array.fromAsync` in `array-callback-return` * don't add an option * remove outdated note
1 parent 1ece282 commit bff9091

3 files changed

Lines changed: 137 additions & 16 deletions

File tree

‎docs/src/rules/array-callback-return.md‎

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,21 +22,22 @@ Additionally, it may also enforce the `forEach` array method callback to **not**
2222
This rule finds callback functions of the following methods, then checks usage of `return` statement.
2323

2424
* [`Array.from`](https://www.ecma-international.org/ecma-262/6.0/#sec-array.from)
25+
* [`Array.fromAsync`](https://tc39.es/ecma262/#sec-array.fromasync)
2526
* [`Array.prototype.every`](https://www.ecma-international.org/ecma-262/6.0/#sec-array.prototype.every)
2627
* [`Array.prototype.filter`](https://www.ecma-international.org/ecma-262/6.0/#sec-array.prototype.filter)
2728
* [`Array.prototype.find`](https://www.ecma-international.org/ecma-262/6.0/#sec-array.prototype.find)
2829
* [`Array.prototype.findIndex`](https://www.ecma-international.org/ecma-262/6.0/#sec-array.prototype.findindex)
29-
* [`Array.prototype.findLast`](https://tc39.es/ecma262/#sec-array.prototype.findlast)
30-
* [`Array.prototype.findLastIndex`](https://tc39.es/ecma262/#sec-array.prototype.findlastindex)
30+
* [`Array.prototype.findLast`](https://www.ecma-international.org/ecma-262/14.0/#sec-array.prototype.findlast)
31+
* [`Array.prototype.findLastIndex`](https://www.ecma-international.org/ecma-262/14.0/#sec-array.prototype.findlastindex)
3132
* [`Array.prototype.flatMap`](https://www.ecma-international.org/ecma-262/10.0/#sec-array.prototype.flatmap)
3233
* [`Array.prototype.forEach`](https://www.ecma-international.org/ecma-262/6.0/#sec-array.prototype.foreach) (optional, based on `checkForEach` parameter)
3334
* [`Array.prototype.map`](https://www.ecma-international.org/ecma-262/6.0/#sec-array.prototype.map)
3435
* [`Array.prototype.reduce`](https://www.ecma-international.org/ecma-262/6.0/#sec-array.prototype.reduce)
3536
* [`Array.prototype.reduceRight`](https://www.ecma-international.org/ecma-262/6.0/#sec-array.prototype.reduceright)
3637
* [`Array.prototype.some`](https://www.ecma-international.org/ecma-262/6.0/#sec-array.prototype.some)
3738
* [`Array.prototype.sort`](https://www.ecma-international.org/ecma-262/6.0/#sec-array.prototype.sort)
38-
* [`Array.prototype.toSorted`](https://tc39.es/ecma262/#sec-array.prototype.tosorted)
39-
* And above of typed arrays.
39+
* [`Array.prototype.toSorted`](https://www.ecma-international.org/ecma-262/14.0/#sec-array.prototype.tosorted)
40+
* And above of typed arrays if applicable.
4041

4142
Examples of **incorrect** code for this rule:
4243

‎lib/rules/array-callback-return.js‎

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ function isAnySegmentReachable(segments) {
5252
* or else `Array.prototype.` if it is an instance method.
5353
*/
5454
function fullMethodName(arrayMethodName) {
55-
if (["from", "of", "isArray"].includes(arrayMethodName)) {
55+
if (["from", "fromAsync", "of", "isArray"].includes(arrayMethodName)) {
5656
return "Array.".concat(arrayMethodName);
5757
}
5858
return "Array.prototype.".concat(arrayMethodName);
@@ -61,12 +61,17 @@ function fullMethodName(arrayMethodName) {
6161
/**
6262
* Checks whether or not a given node is a function expression which is the
6363
* callback of an array method, returning the method name.
64+
* Generators are excluded. Async functions are allowed only for `Array.fromAsync`.
6465
* @param {ASTNode} node A node to check. This is one of
6566
* FunctionExpression or ArrowFunctionExpression.
6667
* @returns {string} The method name if the node is a callback method,
6768
* null otherwise.
6869
*/
6970
function getArrayMethodName(node) {
71+
// Generators are not checked for any methods.
72+
if (node.generator) {
73+
return null;
74+
}
7075
let currentNode = node;
7176

7277
while (currentNode) {
@@ -107,20 +112,32 @@ function getArrayMethodName(node) {
107112
* list.every(function() {});
108113
*/
109114
case "CallExpression":
110-
if (astUtils.isArrayFromMethod(parent.callee)) {
111-
if (
112-
parent.arguments.length >= 2 &&
113-
parent.arguments[1] === currentNode
114-
) {
115-
return "from";
115+
if (!node.async) {
116+
if (astUtils.isArrayFromMethod(parent.callee)) {
117+
if (
118+
parent.arguments.length >= 2 &&
119+
parent.arguments[1] === currentNode
120+
) {
121+
return "from";
122+
}
123+
}
124+
if (isTargetMethod(parent.callee)) {
125+
if (
126+
parent.arguments.length >= 1 &&
127+
parent.arguments[0] === currentNode
128+
) {
129+
return astUtils.getStaticPropertyName(
130+
parent.callee,
131+
);
132+
}
116133
}
117134
}
118-
if (isTargetMethod(parent.callee)) {
135+
if (astUtils.isArrayFromAsyncMethod(parent.callee)) {
119136
if (
120-
parent.arguments.length >= 1 &&
121-
parent.arguments[0] === currentNode
137+
parent.arguments.length >= 2 &&
138+
parent.arguments[1] === currentNode
122139
) {
123-
return astUtils.getStaticPropertyName(parent.callee);
140+
return "fromAsync";
124141
}
125142
}
126143
return null;
@@ -394,7 +411,7 @@ module.exports = {
394411
upper: funcInfo,
395412
codePath,
396413
hasReturn: false,
397-
shouldCheck: methodName && !node.async && !node.generator,
414+
shouldCheck: !!methodName,
398415
node,
399416
currentSegments: new Set(),
400417
};

‎tests/lib/rules/array-callback-return.js‎

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,18 @@ ruleTester.run("array-callback-return", rule, {
295295
languageOptions: { ecmaVersion: 6 },
296296
},
297297
{ code: "foo.every(() => true)", languageOptions: { ecmaVersion: 6 } },
298+
299+
// Array.fromAsync
300+
"Array.fromAsync(x, function() { return true; })",
301+
"Array.fromAsync(x, async function() { return true; })",
302+
{
303+
code: "Array.fromAsync(x, function() { return; })",
304+
options: allowImplicitOptions,
305+
},
306+
"Array.fromAsync(x, async () => true)",
307+
"Array.fromAsync(x, function * () {})",
308+
"Float64Array.fromAsync(x, function() {})",
309+
"Array.fromAsync(function() {})",
298310
],
299311
invalid: [
300312
{
@@ -2010,6 +2022,22 @@ ruleTester.run("array-callback-return", rule, {
20102022
},
20112023
],
20122024
},
2025+
{
2026+
code: "Array.fromAsync(x,\nasync\tfunction \\u0066oo // bar\n () {})",
2027+
errors: [
2028+
{
2029+
messageId: "expectedInside",
2030+
data: {
2031+
name: "async function 'foo'",
2032+
arrayMethodName: "Array.fromAsync",
2033+
},
2034+
line: 2,
2035+
column: 1,
2036+
endLine: 3,
2037+
endColumn: 4,
2038+
},
2039+
],
2040+
},
20132041

20142042
// Optional chaining
20152043
{
@@ -2077,5 +2105,80 @@ ruleTester.run("array-callback-return", rule, {
20772105
},
20782106
],
20792107
},
2108+
2109+
// Array.fromAsync
2110+
{
2111+
code: "Array.fromAsync(x, function() {})",
2112+
errors: [
2113+
{
2114+
messageId: "expectedInside",
2115+
data: {
2116+
name: "function",
2117+
arrayMethodName: "Array.fromAsync",
2118+
},
2119+
},
2120+
],
2121+
},
2122+
{
2123+
code: "Array.fromAsync(x, async function() {})",
2124+
errors: [
2125+
{
2126+
messageId: "expectedInside",
2127+
data: {
2128+
name: "async function",
2129+
arrayMethodName: "Array.fromAsync",
2130+
},
2131+
},
2132+
],
2133+
},
2134+
{
2135+
code: "Array.fromAsync(x, function() {})",
2136+
options: allowImplicitOptions,
2137+
errors: [
2138+
{
2139+
messageId: "expectedInside",
2140+
data: {
2141+
name: "function",
2142+
arrayMethodName: "Array.fromAsync",
2143+
},
2144+
},
2145+
],
2146+
},
2147+
{
2148+
code: "Array.fromAsync(x, () => {})",
2149+
errors: [
2150+
{
2151+
messageId: "expectedInside",
2152+
data: {
2153+
name: "arrow function",
2154+
arrayMethodName: "Array.fromAsync",
2155+
},
2156+
},
2157+
],
2158+
},
2159+
{
2160+
code: "Array.fromAsync(x, async () => {})",
2161+
errors: [
2162+
{
2163+
messageId: "expectedInside",
2164+
data: {
2165+
name: "async arrow function",
2166+
arrayMethodName: "Array.fromAsync",
2167+
},
2168+
},
2169+
],
2170+
},
2171+
{
2172+
code: "Array.fromAsync(x, function foo() {})",
2173+
errors: [
2174+
{
2175+
messageId: "expectedInside",
2176+
data: {
2177+
name: "function 'foo'",
2178+
arrayMethodName: "Array.fromAsync",
2179+
},
2180+
},
2181+
],
2182+
},
20802183
],
20812184
});

0 commit comments

Comments
 (0)