Skip to content

Commit e918029

Browse files
authored
Merge pull request #71 from aadito123/fix/prefer-for-chainexpression
Detect prefer-for in chain expressions
2 parents 42be78a + 722b106 commit e918029

File tree

3 files changed

+50
-22
lines changed

3 files changed

+50
-22
lines changed

docs/prefer-for.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,20 @@ function Component(props) {
6565
);
6666
}
6767

68+
function Component(props) {
69+
return (
70+
<ol>
71+
{props.data?.map((d) => (
72+
<li>{d.text}</li>
73+
))}
74+
</ol>
75+
);
76+
}
77+
// after eslint --fix:
78+
function Component(props) {
79+
return <ol>{<For each={props.data}>{(d) => <li>{d.text}</li>}</For>}</ol>;
80+
}
81+
6882
let Component = (props) => (
6983
<ol>
7084
{props.data.map(() => (

src/rules/prefer-for.ts

Lines changed: 25 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -48,29 +48,32 @@ const rule: TSESLint.RuleModule<"preferFor" | "preferForOrIndex", []> = {
4848
});
4949
};
5050

51-
return {
52-
"JSXElement > JSXExpressionContainer > CallExpression": (node: T.CallExpression) => {
53-
if (
54-
node.callee.type === "MemberExpression" &&
55-
getPropertyName(node.callee) === "map" &&
56-
node.arguments.length === 1 && // passing thisArg to Array.prototype.map is rare, deopt in that case
57-
isFunctionNode(node.arguments[0])
58-
) {
59-
const mapFnNode = node.arguments[0];
60-
if (mapFnNode.params.length === 1 && mapFnNode.params[0].type !== "RestElement") {
61-
// The map fn doesn't take an index param, so it can't possibly be an index-keyed list. Use <For />.
62-
// The returned JSX, if it's coming from React, will have an unnecessary `key` prop to be removed in
63-
// the useless-keys rule.
64-
reportPreferFor(node);
65-
} else {
66-
// Too many possible solutions to make a suggestion or fix
67-
context.report({
68-
node,
69-
messageId: "preferForOrIndex",
70-
});
71-
}
51+
const report = (node: T.CallExpression) => {
52+
if (
53+
node.callee.type === "MemberExpression" &&
54+
getPropertyName(node.callee) === "map" &&
55+
node.arguments.length === 1 && // passing thisArg to Array.prototype.map is rare, deopt in that case
56+
isFunctionNode(node.arguments[0])
57+
) {
58+
const mapFnNode = node.arguments[0];
59+
if (mapFnNode.params.length === 1 && mapFnNode.params[0].type !== "RestElement") {
60+
// The map fn doesn't take an index param, so it can't possibly be an index-keyed list. Use <For />.
61+
// The returned JSX, if it's coming from React, will have an unnecessary `key` prop to be removed in
62+
// the useless-keys rule.
63+
reportPreferFor(node);
64+
} else {
65+
// Too many possible solutions to make a suggestion or fix
66+
context.report({
67+
node,
68+
messageId: "preferForOrIndex",
69+
});
7270
}
73-
},
71+
}
72+
};
73+
74+
return {
75+
"JSXElement > JSXExpressionContainer > CallExpression": report,
76+
"JSXElement > JSXExpressionContainer > ChainExpression > CallExpression": report,
7477
};
7578
},
7679
};

test/rules/prefer-for.test.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,17 @@ export const cases = run("prefer-for", rule, {
3333
return <ol><For each={props.data}>{d => <li>{d.text}</li>}</For></ol>;
3434
}`,
3535
},
36+
{
37+
code: `
38+
function Component(props) {
39+
return <ol>{props.data?.map(d => <li>{d.text}</li>)}</ol>;
40+
}`,
41+
errors: [{ messageId: "preferFor" }],
42+
output: `
43+
function Component(props) {
44+
return <ol>{<For each={props.data}>{(d) => <li>{d.text}</li>}</For>}</ol>;
45+
}`,
46+
},
3647
// deopts
3748
{
3849
code: `let Component = (props) => <ol>{props.data.map(() => <li />)}</ol>;`,

0 commit comments

Comments
 (0)