Skip to content

Commit c9c646c

Browse files
authoredJun 24, 2024··
fix(eslint-plugin): only take return statement into account with no-multiple-actions-in-effects (#4410)
Closes #4409
1 parent a15d53e commit c9c646c

File tree

4 files changed

+50
-10
lines changed

4 files changed

+50
-10
lines changed
 

‎modules/eslint-plugin/spec/rules/effects/no-multiple-actions-in-effects.spec.ts

+30
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,36 @@ export class Effects {
6464
)
6565
}
6666
}`,
67+
`
68+
export const saveSearchCriteria$ = createEffect(
69+
(actions$ = inject(Actions$), store = inject(Store), saveLoadService = inject(SaveLoadService)) => {
70+
return actions$.pipe(
71+
ofType(SearchCriteriaActions.save),
72+
concatLatestFrom(() => store.select(inventoryFeature.selectInventoryItems)),
73+
concatMap(([{ searchCriteriaName }, inventoryItems]) => {
74+
const tags = Object.keys(inventoryItems)
75+
.filter((inventoryType) => {
76+
const [, inventorySearchType] = splitInventoryType(inventoryType as InventoryType);
77+
return inventorySearchType === 'costCenter' || inventorySearchType === 'wbs';
78+
})
79+
.map((inventoryType) => splitInventoryType(inventoryType as InventoryType))
80+
.map(([value, type]) => ({ value, type }));
81+
return saveLoadService.saveSearch(searchCriteriaName, tags, false).pipe(
82+
map(() => SearchCriteriaActions.saveSucceeded()),
83+
catchError((error: Error) => {
84+
if (error instanceof HttpErrorResponse && error.status === 409) {
85+
return of(SearchCriteriaActions.saveAlreadyExists({ searchCriteriaName, tags }));
86+
}
87+
88+
return defaultErrorHadnler(error, 'inventoryDomain.messages.saveSearchFailed', SearchCriteriaActions.saveFailed());
89+
})
90+
);
91+
})
92+
);
93+
},
94+
{ functional: true }
95+
);
96+
`,
6797
];
6898

6999
const invalid: () => RunTests['invalid'] = () => [

‎modules/eslint-plugin/src/rules/effects/no-multiple-actions-in-effects.ts

+17-5
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,9 @@ import * as path from 'path';
77
import { createRule } from '../../rule-creator';
88
import {
99
createEffectExpression,
10-
mapLikeOperatorsExplicitReturn,
11-
mapLikeOperatorsImplicitReturn,
10+
isBlockStatement,
11+
isReturnStatement,
12+
mapLikeOperatorCallExpressions,
1213
} from '../../utils';
1314

1415
export const messageId = 'noMultipleActionsInEffects';
@@ -18,6 +19,7 @@ type Options = readonly unknown[];
1819
type EffectsMapLikeOperatorsReturn =
1920
| TSESTree.ArrowFunctionExpression
2021
| TSESTree.CallExpression
22+
| TSESTree.FunctionExpression
2123
| TSESTree.ReturnStatement;
2224

2325
export default createRule<Options, MessageIds>({
@@ -37,7 +39,7 @@ export default createRule<Options, MessageIds>({
3739
defaultOptions: [],
3840
create: (context) => {
3941
return {
40-
[`${createEffectExpression} :matches(${mapLikeOperatorsImplicitReturn}, ${mapLikeOperatorsExplicitReturn})`](
42+
[`${createEffectExpression} ${mapLikeOperatorCallExpressions}`](
4143
node: EffectsMapLikeOperatorsReturn
4244
) {
4345
const nodeToReport = getNodeToReport(node);
@@ -71,10 +73,20 @@ export default createRule<Options, MessageIds>({
7173
function getNodeToReport(node: EffectsMapLikeOperatorsReturn) {
7274
switch (node.type) {
7375
case AST_NODE_TYPES.ArrowFunctionExpression:
74-
return node.body;
76+
case AST_NODE_TYPES.FunctionExpression:
77+
return isBlockStatement(node.body)
78+
? findReturnStatement(node.body.body)
79+
: node.body;
7580
case AST_NODE_TYPES.CallExpression:
76-
return node.arguments[0];
81+
return findReturnStatement(node.arguments) ?? node.arguments[0];
7782
default:
7883
return node.argument;
7984
}
8085
}
86+
87+
function findReturnStatement(nodes: TSESTree.Node[]) {
88+
const returnNode = nodes.find((n): n is TSESTree.ReturnStatement =>
89+
isReturnStatement(n)
90+
);
91+
return returnNode?.argument;
92+
}

‎modules/eslint-plugin/src/utils/helper-functions/guards.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ export const isTSInstantiationExpression = isNodeOfType(
4747
);
4848
export const isProperty = isNodeOfType(AST_NODE_TYPES.Property);
4949
export const isArrayExpression = isNodeOfType(AST_NODE_TYPES.ArrayExpression);
50-
50+
export const isBlockStatement = isNodeOfType(AST_NODE_TYPES.BlockStatement);
5151
export function isIdentifierOrMemberExpression(
5252
node: TSESTree.Node
5353
): node is TSESTree.Identifier | TSESTree.MemberExpression {

‎modules/eslint-plugin/src/utils/selectors/index.ts

+2-4
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,5 @@ export const actionReducerMap = `VariableDeclarator[id.typeAnnotation.typeAnnota
7878

7979
const mapLikeOperators = '/^(concat|exhaust|flat|merge|switch)Map$/';
8080
const mapLikeToOperators = '/^(concat|merge|switch)MapTo$/';
81-
export const mapLikeOperatorsExplicitReturn =
82-
`CallExpression[callee.name=${mapLikeOperators}] ReturnStatement` as const;
83-
export const mapLikeOperatorsImplicitReturn =
84-
`:matches(CallExpression[callee.name=${mapLikeToOperators}], CallExpression[callee.name=${mapLikeOperators}] > ArrowFunctionExpression)` as const;
81+
export const mapLikeOperatorCallExpressions =
82+
`:matches(CallExpression[callee.name=${mapLikeToOperators}], CallExpression[callee.name=${mapLikeOperators}] > :matches(ReturnStatement,ArrowFunctionExpression,FunctionExpression))` as const;

0 commit comments

Comments
 (0)
Please sign in to comment.