Skip to content

Commit 419166b

Browse files
authored
Merge pull request #224 from OliverJAsh/oja/no-redundant-flow/fix-fixes
2 parents 58d6998 + 66cbbfc commit 419166b

File tree

3 files changed

+116
-23
lines changed

3 files changed

+116
-23
lines changed

src/rules/no-redundant-flow.ts

+40-20
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,12 @@
1-
import { contextUtils, createRule } from "../utils";
1+
import { pipe } from "fp-ts/function";
2+
import * as O from "fp-ts/Option";
3+
import {
4+
contextUtils,
5+
createRule,
6+
createSequenceExpressionFromCallExpressionWithExpressionArgs,
7+
getCallExpressionWithExpressionArgs,
8+
prettyPrint,
9+
} from "../utils";
210

311
export default createRule({
412
name: "no-redundant-flow",
@@ -22,26 +30,38 @@ export default createRule({
2230

2331
return {
2432
CallExpression(node) {
25-
if (node.arguments.length === 1 && isFlowExpression(node)) {
26-
context.report({
27-
node,
28-
messageId: "redundantFlow",
29-
suggest: [
30-
{
31-
messageId: "removeFlow",
32-
fix(fixer) {
33-
return [
34-
fixer.removeRange([
35-
node.callee.range[0],
36-
node.callee.range[1] + 1,
37-
]),
38-
fixer.removeRange([node.range[1] - 1, node.range[1]]),
39-
];
33+
pipe(
34+
node,
35+
O.fromPredicate(isFlowExpression),
36+
/**
37+
* We ignore flow calls which contain a spread argument because these are never invalid.
38+
*/
39+
O.chain(getCallExpressionWithExpressionArgs),
40+
O.filter((flowCall) => flowCall.node.arguments.length === 1),
41+
O.map((redundantFlowCall) => {
42+
context.report({
43+
node: redundantFlowCall.node,
44+
messageId: "redundantFlow",
45+
suggest: [
46+
{
47+
messageId: "removeFlow",
48+
fix(fixer) {
49+
const sequenceExpression =
50+
createSequenceExpressionFromCallExpressionWithExpressionArgs(
51+
redundantFlowCall
52+
);
53+
return [
54+
fixer.replaceText(
55+
redundantFlowCall.node,
56+
prettyPrint(sequenceExpression)
57+
),
58+
];
59+
},
4060
},
41-
},
42-
],
43-
});
44-
}
61+
],
62+
});
63+
})
64+
);
4565
},
4666
};
4767
},

src/utils.ts

+50
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ import {
1818
} from "@typescript-eslint/experimental-utils/dist/ts-eslint";
1919
import ts from "typescript";
2020
import { Option } from "fp-ts/Option";
21+
import * as NonEmptyArray from "fp-ts/NonEmptyArray";
22+
import * as O from "fp-ts/Option";
2123

2224
declare module "typescript" {
2325
interface TypeChecker {
@@ -471,3 +473,51 @@ export const contextUtils = <
471473
parserServices,
472474
};
473475
};
476+
477+
/**
478+
* Ideally we could implement this predicate in terms of an existing
479+
* `isExpression` predicate but it seems like this doesn't exist anywhere.
480+
*
481+
* There is an `isExpression` in `tsutils`. However, in the TS AST, spread is
482+
* classed as an expression (!).
483+
*/
484+
const getArgumentExpression = (
485+
x: TSESTree.CallExpressionArgument
486+
): O.Option<TSESTree.Expression> =>
487+
x.type !== AST_NODE_TYPES.SpreadElement ? O.some(x) : O.none;
488+
489+
const checkIsArgumentExpression = O.getRefinement(getArgumentExpression);
490+
491+
type CallExpressionWithExpressionArgs = {
492+
node: TSESTree.CallExpression;
493+
args: NonEmptyArray.NonEmptyArray<TSESTree.Expression>;
494+
};
495+
496+
export const getCallExpressionWithExpressionArgs = (
497+
node: TSESTree.CallExpression
498+
): O.Option<CallExpressionWithExpressionArgs> =>
499+
node.arguments.every(checkIsArgumentExpression)
500+
? pipe(
501+
node.arguments,
502+
NonEmptyArray.fromArray,
503+
O.map(
504+
(args): CallExpressionWithExpressionArgs => ({
505+
node,
506+
args,
507+
})
508+
)
509+
)
510+
: O.none;
511+
512+
export const createSequenceExpressionFromCallExpressionWithExpressionArgs = (
513+
call: CallExpressionWithExpressionArgs
514+
): TSESTree.SequenceExpression => {
515+
const firstArg = pipe(call.args, NonEmptyArray.head);
516+
const lastArg = pipe(call.args, NonEmptyArray.last);
517+
return {
518+
loc: call.node.loc,
519+
range: [firstArg.range[0], lastArg.range[1]],
520+
type: AST_NODE_TYPES.SequenceExpression,
521+
expressions: call.args,
522+
};
523+
};

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

+26-3
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ ruleTester.run("no-redundant-flow", rule, {
1919
bar
2020
)
2121
`,
22+
`import { flow } from "fp-ts/function"
23+
flow(...fns)
24+
`,
2225
],
2326
invalid: [
2427
{
@@ -56,14 +59,34 @@ const a = flow(
5659
messageId: "removeFlow",
5760
output: `
5861
import { flow } from "fp-ts/function"
59-
const a = ${""}
60-
foo
61-
62+
const a = foo
6263
`,
6364
},
6465
],
6566
},
6667
],
6768
},
69+
{
70+
code: `
71+
import { flow } from "fp-ts/function"
72+
const a = flow(
73+
foo,
74+
);
75+
`,
76+
errors: [
77+
{
78+
messageId: "redundantFlow",
79+
suggestions: [
80+
{
81+
messageId: "removeFlow",
82+
output: `
83+
import { flow } from "fp-ts/function"
84+
const a = foo;
85+
`,
86+
},
87+
],
88+
},
89+
],
90+
}
6891
],
6992
});

0 commit comments

Comments
 (0)