Skip to content

Commit 3e07a7b

Browse files
fix: check namespace imports
1 parent 582604c commit 3e07a7b

File tree

2 files changed

+111
-46
lines changed

2 files changed

+111
-46
lines changed

src/rules/no-sharereplay-before-takeuntil.ts

Lines changed: 58 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -16,56 +16,68 @@ export const noSharereplayBeforeTakeuntilRule = ruleCreator({
1616
},
1717
name: 'no-sharereplay-before-takeuntil',
1818
create: (context) => {
19-
return {
20-
'CallExpression[callee.name="shareReplay"]': (node: es.CallExpression) => {
21-
const pipeCallExpression = node.parent as es.CallExpression;
22-
if (
23-
!pipeCallExpression.arguments
24-
|| !isMemberExpression(pipeCallExpression.callee)
25-
|| !isIdentifier(pipeCallExpression.callee.property)
26-
|| pipeCallExpression.callee.property.name !== 'pipe'
27-
) {
28-
return;
29-
}
19+
function checkCallExpression(node: es.CallExpression) {
20+
const pipeCallExpression = node.parent as es.CallExpression;
21+
if (
22+
!pipeCallExpression.arguments
23+
|| !isMemberExpression(pipeCallExpression.callee)
24+
|| !isIdentifier(pipeCallExpression.callee.property)
25+
|| pipeCallExpression.callee.property.name !== 'pipe'
26+
) {
27+
return;
28+
}
29+
30+
const takeUntilIndex = pipeCallExpression.arguments.findIndex(arg =>
31+
isCallExpression(arg)
32+
&& (
33+
(isIdentifier(arg.callee)
34+
&& arg.callee.name === 'takeUntil')
35+
|| (isMemberExpression(arg.callee)
36+
&& isIdentifier(arg.callee.property)
37+
&& arg.callee.property.name === 'takeUntil')
38+
),
39+
);
3040

31-
const takeUntilIndex = pipeCallExpression.arguments.findIndex(arg =>
32-
isCallExpression(arg)
33-
&& isIdentifier(arg.callee)
34-
&& arg.callee.name === 'takeUntil',
35-
);
41+
if (takeUntilIndex === -1) {
42+
return;
43+
}
3644

37-
if (takeUntilIndex === -1) {
38-
return;
39-
}
45+
const shareReplayConfig = node.arguments[0];
46+
if (
47+
!shareReplayConfig
48+
|| !isObjectExpression(shareReplayConfig)
49+
) {
50+
// refCount defaults to false if no config is provided.
51+
context.report({
52+
messageId: 'forbidden',
53+
node: node.callee,
54+
});
55+
return;
56+
}
4057

41-
const shareReplayConfig = node.arguments[0];
42-
if (
43-
!shareReplayConfig
44-
|| !isObjectExpression(shareReplayConfig)
45-
) {
46-
// refCount defaults to false if no config is provided.
47-
context.report({
48-
messageId: 'forbidden',
49-
node: node.callee,
50-
});
51-
return;
52-
}
58+
const refCountElement = shareReplayConfig.properties
59+
.filter(isProperty)
60+
.find(prop =>
61+
isIdentifier(prop.key)
62+
&& prop.key.name === 'refCount');
63+
if (
64+
!refCountElement
65+
|| (isLiteral(refCountElement.value)
66+
&& refCountElement.value.value === false)
67+
) {
68+
context.report({
69+
messageId: 'forbidden',
70+
node: node.callee,
71+
});
72+
}
73+
}
5374

54-
const refCountElement = shareReplayConfig.properties
55-
.filter(isProperty)
56-
.find(prop =>
57-
isIdentifier(prop.key)
58-
&& prop.key.name === 'refCount');
59-
if (
60-
!refCountElement
61-
|| (isLiteral(refCountElement.value)
62-
&& refCountElement.value.value === false)
63-
) {
64-
context.report({
65-
messageId: 'forbidden',
66-
node: node.callee,
67-
});
68-
}
75+
return {
76+
'CallExpression[callee.name="shareReplay"]': (node: es.CallExpression) => {
77+
checkCallExpression(node);
78+
},
79+
'CallExpression[callee.property.name="shareReplay"]': (node: es.CallExpression) => {
80+
checkCallExpression(node);
6981
},
7082
};
7183
},

tests/rules/no-sharereplay-before-takeuntil.test.ts

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,38 @@ ruleTester({ types: false }).run('no-sharereplay-before-takeuntil', noSharerepla
1212
const a = of("a");
1313
const b = of("b");
1414
15+
a.pipe(shareReplay({ refCount: true }), takeUntil(b));
16+
`,
17+
stripIndent`
18+
// with refCount true as const
19+
import { of, takeUntil, shareReplay } from "rxjs";
20+
21+
const a = of("a");
22+
const b = of("b");
23+
1524
const t = true as const;
1625
a.pipe(shareReplay({ refCount: t }), takeUntil(b));
1726
`,
27+
stripIndent`
28+
// refCount variable not supported
29+
import { of, takeUntil, shareReplay } from "rxjs";
30+
31+
const a = of("a");
32+
const b = of("b");
33+
34+
const t = false;
35+
a.pipe(shareReplay({ refCount: t }), takeUntil(b));
36+
`,
37+
stripIndent`
38+
// composed observables not supported
39+
import { of, takeUntil, shareReplay } from "rxjs";
40+
41+
const a = of("a");
42+
const b = of("b");
43+
44+
const sr = shareReplay({ refCount: false });
45+
a.pipe(sr, takeUntil(b));
46+
`,
1847
stripIndent`
1948
// unrelated
2049
import { of, takeUntil, toArray } from "rxjs";
@@ -50,5 +79,29 @@ ruleTester({ types: false }).run('no-sharereplay-before-takeuntil', noSharerepla
5079
~~~~~~~~~~~ [forbidden]
5180
`,
5281
),
82+
fromFixture(
83+
stripIndent`
84+
// with operators in between
85+
import { of, takeUntil, shareReplay, map, filter } from "rxjs";
86+
87+
const a = of("a");
88+
const b = of("b");
89+
90+
a.pipe(shareReplay(), map(x => x), filter(x => !!x), takeUntil(b));
91+
~~~~~~~~~~~ [forbidden]
92+
`,
93+
),
94+
fromFixture(
95+
stripIndent`
96+
// namespace import
97+
import * as Rx from "rxjs";
98+
99+
const a = Rx.of("a");
100+
const b = Rx.of("b");
101+
102+
a.pipe(Rx.shareReplay(), Rx.takeUntil(b));
103+
~~~~~~~~~~~~~~ [forbidden]
104+
`,
105+
),
53106
],
54107
});

0 commit comments

Comments
 (0)