Skip to content

Commit 479842c

Browse files
authored
Merge pull request #10 from buildo/no-redundant-flow
Add no-redundant-flow
2 parents 1ebc455 + f59ab02 commit 479842c

File tree

7 files changed

+163
-1
lines changed

7 files changed

+163
-1
lines changed

README.md

+2
Original file line numberDiff line numberDiff line change
@@ -42,3 +42,5 @@ and enable the rules you want, for example
4242
'pipeable' module
4343
- [fp-ts/prefer-traverse](docs/rules/prefer-traverse.md): Replace map + sequence
4444
with traverse
45+
- [fp-ts/no-redundant-flow](docs/rules/no-redundant-flow.md): Remove redundant
46+
uses of flow

docs/rules/no-redundant-flow.md

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# Remove redundant uses of flow (fp-ts/no-redundant-flow)
2+
3+
Suggest removing `flow` when it only has one argument. This can happen after a
4+
refactoring that removed some combinators from a flow expression.
5+
6+
**Fixable**: This rule is automatically fixable using the `--fix` flag on the
7+
command line.
8+
9+
## Rule Details
10+
11+
Example of **incorrect** code for this rule:
12+
13+
```ts
14+
import { flow } from "fp-ts/pipeable";
15+
import { some, Option } from "fp-ts/Option";
16+
17+
const f: (n: number): Option<number> = flow(some);
18+
```
19+
20+
Example of **correct** code for this rule:
21+
22+
```ts
23+
import { flow } from "fp-ts/pipeable";
24+
import { some, filter, Option } from "fp-ts/Option";
25+
26+
const f: (n: number): Option<number> =
27+
flow(
28+
some,
29+
filter((n) => n > 2)
30+
);
31+
```

src/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@ export const rules = {
22
"no-lib-imports": require("./rules/no-lib-imports"),
33
"no-pipeable": require("./rules/no-pipeable"),
44
"prefer-traverse": require("./rules/prefer-traverse"),
5+
"no-redundant-flow": require("./rules/no-redundant-flow"),
56
};

src/rules/no-redundant-flow.ts

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import { TSESLint } from "@typescript-eslint/experimental-utils";
2+
import { isFlowExpression } from "../utils";
3+
4+
const messages = {
5+
redundantFlow: "flow can be removed because it takes only one argument",
6+
removeFlow: "remove flow",
7+
} as const;
8+
type MessageIds = keyof typeof messages;
9+
10+
export const meta: TSESLint.RuleMetaData<MessageIds> = {
11+
type: "suggestion",
12+
fixable: "code",
13+
schema: [],
14+
messages,
15+
};
16+
17+
export function create(
18+
context: TSESLint.RuleContext<MessageIds, []>
19+
): TSESLint.RuleListener {
20+
return {
21+
CallExpression(node) {
22+
if (node.arguments.length === 1 && isFlowExpression(node, context)) {
23+
context.report({
24+
node,
25+
messageId: "redundantFlow",
26+
suggest: [
27+
{
28+
messageId: "removeFlow",
29+
fix(fixer) {
30+
return [
31+
fixer.removeRange([
32+
node.callee.range[0],
33+
node.callee.range[1] + 1,
34+
]),
35+
fixer.removeRange([node.range[1] - 1, node.range[1]]),
36+
];
37+
},
38+
},
39+
],
40+
});
41+
}
42+
},
43+
};
44+
}

src/utils.ts

+15
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,21 @@ export function calleeIdentifier(
4040
return undefined;
4141
}
4242

43+
export function isFlowExpression<
44+
TMessageIds extends string,
45+
TOptions extends readonly unknown[]
46+
>(
47+
node: TSESTree.CallExpression,
48+
context: TSESLint.RuleContext<TMessageIds, TOptions>
49+
): boolean {
50+
const callee = calleeIdentifier(node);
51+
return !!(
52+
callee &&
53+
callee.name === "flow" &&
54+
isIdentifierImportedFrom(callee, /fp-ts\//, context)
55+
);
56+
}
57+
4358
export function isPipeOrFlowExpression<
4459
TMessageIds extends string,
4560
TOptions extends readonly unknown[]

tests/rules/no-pipeable.test.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ const ruleTester = new ESLintUtils.RuleTester({
88
},
99
});
1010

11-
ruleTester.run("no-lib-imports", rule, {
11+
ruleTester.run("no-pipeable", rule, {
1212
valid: [
1313
'import { pipe } from "fp-ts/function"',
1414
'import { pipe } from "fp-ts/lib/function"',

tests/rules/no-redundant-flow.test.ts

+69
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import * as rule from "../../src/rules/no-redundant-flow";
2+
import { ESLintUtils } from "@typescript-eslint/experimental-utils";
3+
4+
const ruleTester = new ESLintUtils.RuleTester({
5+
parser: "@typescript-eslint/parser",
6+
parserOptions: {
7+
sourceType: "module",
8+
},
9+
});
10+
11+
ruleTester.run("no-redundant-flow", rule, {
12+
valid: [
13+
`import { flow } from "fp-ts/function"
14+
flow(foo, bar)
15+
`,
16+
`import { flow } from "fp-ts/function"
17+
flow(
18+
foo,
19+
bar
20+
)
21+
`,
22+
],
23+
invalid: [
24+
{
25+
code: `
26+
import { flow } from "fp-ts/function"
27+
const a = flow(foo)
28+
`,
29+
errors: [
30+
{
31+
messageId: "redundantFlow",
32+
suggestions: [
33+
{
34+
messageId: "removeFlow",
35+
output: `
36+
import { flow } from "fp-ts/function"
37+
const a = foo
38+
`,
39+
},
40+
],
41+
},
42+
],
43+
},
44+
{
45+
code: `
46+
import { flow } from "fp-ts/function"
47+
const a = flow(
48+
foo
49+
)
50+
`,
51+
errors: [
52+
{
53+
messageId: "redundantFlow",
54+
suggestions: [
55+
{
56+
messageId: "removeFlow",
57+
output: `
58+
import { flow } from "fp-ts/function"
59+
const a = ${""}
60+
foo
61+
62+
`,
63+
},
64+
],
65+
},
66+
],
67+
},
68+
],
69+
});

0 commit comments

Comments
 (0)