Skip to content

Commit

Permalink
feat(eslint-plugin): support glob for call-arguments-length option
Browse files Browse the repository at this point in the history
  • Loading branch information
zanminkian committed Nov 29, 2024
1 parent b002aa9 commit 6b8d3e4
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 34 deletions.
5 changes: 5 additions & 0 deletions .changeset/witty-hotels-exercise.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@fenge/eslint-plugin": patch
---

feat(eslint-plugin): support glob for `call-arguments-length` option
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const valid = [
"foo.reduceRight(bar, baz)",
"reduce(()=>123)",
"foo.reduceLeft(()=>123)",
"Math.max(...foo)",

"new foo.Set(...bar)",
"new Set(bar)",
Expand All @@ -36,6 +37,8 @@ const invalid = [
"foo.reduceRight(bar)",
"[].reduce(...foo)",
"[].reduce(...foo, ...bar)",
"Math.max()",
"Math.max(1)",

"new Set(...foo)",
"new Set(foo,bar)",
Expand Down
92 changes: 58 additions & 34 deletions packages/eslint-plugin/src/rules/call-arguments-length.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,48 +90,72 @@ const rule: Rule.RuleModule = {

const options = Object.entries(
context.options[0] ?? {
// 1
"*.reduce": 2,
"*.reduceRight": 2,
"*.push": { min: 1 },
"Math.max": { min: 2 },
"Math.min": { min: 2 },
// 2
"new Set": { max: 1 },
"new Map": { max: 1 },
},
);
return {
CallExpression: (node) => {
// function call
if (node.callee.type === "Identifier") {
for (const [pattern, expectedLength] of options.filter(
([pattern]) =>
!pattern.startsWith("*.") && !pattern.startsWith("new "),
)) {
if (node.callee.name !== pattern) continue;
).map(([pattern, expectedLength]) => ({
regex: new RegExp(
`^${pattern.replaceAll(".", "\\.").replaceAll("*", ".*")}$`,
),
pattern,
expectedLength,
}));

const handle = (node: CallExpression | NewExpression) => {
const prefix = node.type === "NewExpression" ? "new " : "";

const { callee } = node;
// function call
if (callee.type === "Identifier") {
// code like `foo()` or `new Foo()`
options
.filter((option) => option.regex.test(`${prefix}${callee.name}`))
.forEach(({ pattern, expectedLength }) => {
report(node, pattern, expectedLength);
}
}
// method call
else if (
node.callee.type === "MemberExpression" &&
node.callee.property.type === "Identifier"
) {
for (const [pattern, expectedLength] of options.filter(([pattern]) =>
pattern.startsWith("*."),
)) {
if (node.callee.property.name !== pattern.split(".")[1]) continue;
});
}
// method call
else if (
callee.type === "MemberExpression" &&
callee.property.type === "Identifier"
) {
const { object: calleeObject, property: calleeProperty } = callee;
options
.filter((option) => {
// code like `Math.max()` or `new Foo.Bar()`, the calleeObject is `Math` or `Foo`
if (
"name" in calleeObject &&
option.regex.test(
`${prefix}${calleeObject.name}.${calleeProperty.name}`,
)
) {
return true;
}
// code like `[].reduce()` or `new ({Foo: class{}}).Foo()`, the calleeObject is `[]` or `{Foo: class{}}`
if (
!("name" in calleeObject) &&
option.regex.test(`${prefix}.${calleeProperty.name}`)
) {
return true;
}
return false;
})
.forEach(({ pattern, expectedLength }) => {
report(node, pattern, expectedLength);
}
}
},
// new call
NewExpression: (node) => {
if (node.callee.type !== "Identifier") return;
for (const [pattern, expectedLength] of options.filter(([pattern]) =>
pattern.startsWith("new "),
)) {
if (node.callee.name !== pattern.split(" ")[1]) continue;
report(node, pattern, expectedLength);
}
},
});
}
};

return {
CallExpression: handle,
NewExpression: handle,
};
},
};
Expand Down

0 comments on commit 6b8d3e4

Please sign in to comment.