eslint-plugin-react-hooks-extra 1.30.0 → 1.30.1-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +191 -367
- package/dist/index.mjs +192 -368
- package/package.json +7 -7
package/dist/index.js
CHANGED
|
@@ -4,7 +4,7 @@ var AST = require('@eslint-react/ast');
|
|
|
4
4
|
var core = require('@eslint-react/core');
|
|
5
5
|
var eff = require('@eslint-react/eff');
|
|
6
6
|
var shared = require('@eslint-react/shared');
|
|
7
|
-
var
|
|
7
|
+
var VAR4 = require('@eslint-react/var');
|
|
8
8
|
var types = require('@typescript-eslint/types');
|
|
9
9
|
var tsPattern = require('ts-pattern');
|
|
10
10
|
|
|
@@ -27,7 +27,7 @@ function _interopNamespace(e) {
|
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
var AST__namespace = /*#__PURE__*/_interopNamespace(AST);
|
|
30
|
-
var
|
|
30
|
+
var VAR4__namespace = /*#__PURE__*/_interopNamespace(VAR4);
|
|
31
31
|
|
|
32
32
|
var __defProp = Object.defineProperty;
|
|
33
33
|
var __export = (target, all) => {
|
|
@@ -50,13 +50,13 @@ var rules = {
|
|
|
50
50
|
|
|
51
51
|
// package.json
|
|
52
52
|
var name2 = "eslint-plugin-react-hooks-extra";
|
|
53
|
-
var version = "1.30.0";
|
|
53
|
+
var version = "1.30.1-beta.0";
|
|
54
54
|
var createRule = shared.createRuleForPlugin("hooks-extra");
|
|
55
55
|
function isFromHookCall(context, name3, settings, predicate = eff.constTrue) {
|
|
56
56
|
const hookAlias = settings.additionalHooks[name3] ?? [];
|
|
57
57
|
return (topLevelId) => {
|
|
58
|
-
const variable =
|
|
59
|
-
const variableNode =
|
|
58
|
+
const variable = VAR4__namespace.findVariable(topLevelId, context.sourceCode.getScope(topLevelId));
|
|
59
|
+
const variableNode = VAR4__namespace.getVariableNode(variable, 0);
|
|
60
60
|
if (variableNode == null) return false;
|
|
61
61
|
if (variableNode.type !== types.AST_NODE_TYPES.CallExpression) return false;
|
|
62
62
|
if (!core.isReactHookCallWithNameAlias(context, name3, hookAlias)(variableNode)) return false;
|
|
@@ -96,7 +96,7 @@ function isSetFunctionCall(context, settings) {
|
|
|
96
96
|
return false;
|
|
97
97
|
}
|
|
98
98
|
const indexScope = context.sourceCode.getScope(node);
|
|
99
|
-
const indexValue =
|
|
99
|
+
const indexValue = VAR4__namespace.toStaticValue({
|
|
100
100
|
kind: "lazy",
|
|
101
101
|
node: index,
|
|
102
102
|
initialScope: indexScope
|
|
@@ -116,7 +116,7 @@ function isSetFunctionCall(context, settings) {
|
|
|
116
116
|
}
|
|
117
117
|
const property = node.callee.property;
|
|
118
118
|
const propertyScope = context.sourceCode.getScope(node);
|
|
119
|
-
const propertyValue =
|
|
119
|
+
const propertyValue = VAR4__namespace.toStaticValue({
|
|
120
120
|
kind: "lazy",
|
|
121
121
|
node: property,
|
|
122
122
|
initialScope: propertyScope
|
|
@@ -152,6 +152,166 @@ function isVariableDeclaratorFromHookCall(node) {
|
|
|
152
152
|
}
|
|
153
153
|
}
|
|
154
154
|
|
|
155
|
+
// src/hooks/use-no-direct-set-state-in-use-effect.ts
|
|
156
|
+
function useNoDirectSetStateInUseEffect(context, options) {
|
|
157
|
+
const { onViolation, useEffectKind } = options;
|
|
158
|
+
const settings = shared.getSettingsFromContext(context);
|
|
159
|
+
const additionalHooks = settings.additionalHooks;
|
|
160
|
+
const isUseEffectLikeCall = core.isReactHookCallWithNameAlias(context, useEffectKind, additionalHooks[useEffectKind]);
|
|
161
|
+
const isUseStateCall2 = core.isReactHookCallWithNameAlias(context, "useState", additionalHooks.useState);
|
|
162
|
+
const isUseMemoCall2 = core.isReactHookCallWithNameAlias(context, "useMemo", additionalHooks.useMemo);
|
|
163
|
+
const isUseCallbackCall2 = core.isReactHookCallWithNameAlias(context, "useCallback", additionalHooks.useCallback);
|
|
164
|
+
const isSetStateCall = isSetFunctionCall(context, settings);
|
|
165
|
+
const isIdFromUseStateCall = isFromUseStateCall(context, settings);
|
|
166
|
+
const functionEntries = [];
|
|
167
|
+
const setupFunctionRef = { current: null };
|
|
168
|
+
const setupFunctionIdentifiers = [];
|
|
169
|
+
const indFunctionCalls = [];
|
|
170
|
+
const indSetStateCalls = /* @__PURE__ */ new Map();
|
|
171
|
+
const indSetStateCallsInUseEffectArg0 = /* @__PURE__ */ new Map();
|
|
172
|
+
const indSetStateCallsInUseEffectSetup = /* @__PURE__ */ new Map();
|
|
173
|
+
const indSetStateCallsInUseMemoOrCallback = /* @__PURE__ */ new Map();
|
|
174
|
+
const onSetupFunctionEnter = (node) => {
|
|
175
|
+
setupFunctionRef.current = node;
|
|
176
|
+
};
|
|
177
|
+
const onSetupFunctionExit = (node) => {
|
|
178
|
+
if (setupFunctionRef.current === node) {
|
|
179
|
+
setupFunctionRef.current = null;
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
function isFunctionOfUseEffectSetup(node) {
|
|
183
|
+
return node.parent?.type === types.AST_NODE_TYPES.CallExpression && node.parent.callee !== node && isUseEffectLikeCall(node.parent);
|
|
184
|
+
}
|
|
185
|
+
function getCallKind(node) {
|
|
186
|
+
return tsPattern.match(node).when(isUseStateCall2, () => "useState").when(isUseEffectLikeCall, () => useEffectKind).when(isSetStateCall, () => "setState").when(isThenCall, () => "then").otherwise(() => "other");
|
|
187
|
+
}
|
|
188
|
+
function getFunctionKind(node) {
|
|
189
|
+
return tsPattern.match(node).when(isFunctionOfUseEffectSetup, () => "setup").when(isFunctionOfImmediatelyInvoked, () => "immediate").otherwise(() => "other");
|
|
190
|
+
}
|
|
191
|
+
return {
|
|
192
|
+
":function"(node) {
|
|
193
|
+
const kind = getFunctionKind(node);
|
|
194
|
+
functionEntries.push({ kind, node });
|
|
195
|
+
if (kind === "setup") {
|
|
196
|
+
onSetupFunctionEnter(node);
|
|
197
|
+
}
|
|
198
|
+
},
|
|
199
|
+
":function:exit"(node) {
|
|
200
|
+
const { kind } = functionEntries.at(-1) ?? {};
|
|
201
|
+
if (kind === "setup") {
|
|
202
|
+
onSetupFunctionExit(node);
|
|
203
|
+
}
|
|
204
|
+
functionEntries.pop();
|
|
205
|
+
},
|
|
206
|
+
CallExpression(node) {
|
|
207
|
+
const setupFunction = setupFunctionRef.current;
|
|
208
|
+
const pEntry = functionEntries.at(-1);
|
|
209
|
+
if (pEntry == null || pEntry.node.async) {
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
tsPattern.match(getCallKind(node)).with("setState", () => {
|
|
213
|
+
switch (true) {
|
|
214
|
+
case (pEntry.node === setupFunction || pEntry.kind === "immediate"): {
|
|
215
|
+
onViolation(context, node, {
|
|
216
|
+
name: context.sourceCode.getText(node.callee)
|
|
217
|
+
});
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
default: {
|
|
221
|
+
const vd = AST__namespace.findParentNode(node, isVariableDeclaratorFromHookCall);
|
|
222
|
+
if (vd == null) eff.getOrUpdate(indSetStateCalls, pEntry.node, () => []).push(node);
|
|
223
|
+
else eff.getOrUpdate(indSetStateCallsInUseMemoOrCallback, vd.init, () => []).push(node);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}).with(useEffectKind, () => {
|
|
227
|
+
if (AST__namespace.isFunction(node.arguments.at(0))) return;
|
|
228
|
+
setupFunctionIdentifiers.push(...AST__namespace.getNestedIdentifiers(node));
|
|
229
|
+
}).with("other", () => {
|
|
230
|
+
if (pEntry.node !== setupFunction) return;
|
|
231
|
+
indFunctionCalls.push(node);
|
|
232
|
+
}).otherwise(() => eff._);
|
|
233
|
+
},
|
|
234
|
+
Identifier(node) {
|
|
235
|
+
if (node.parent.type === types.AST_NODE_TYPES.CallExpression && node.parent.callee === node) {
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
if (!isIdFromUseStateCall(node)) {
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
switch (node.parent.type) {
|
|
242
|
+
case types.AST_NODE_TYPES.ArrowFunctionExpression: {
|
|
243
|
+
const parent = node.parent.parent;
|
|
244
|
+
if (parent.type !== types.AST_NODE_TYPES.CallExpression) {
|
|
245
|
+
break;
|
|
246
|
+
}
|
|
247
|
+
if (!isUseMemoCall2(parent)) {
|
|
248
|
+
break;
|
|
249
|
+
}
|
|
250
|
+
const vd = AST__namespace.findParentNode(parent, isVariableDeclaratorFromHookCall);
|
|
251
|
+
if (vd != null) {
|
|
252
|
+
eff.getOrUpdate(indSetStateCallsInUseEffectArg0, vd.init, () => []).push(node);
|
|
253
|
+
}
|
|
254
|
+
break;
|
|
255
|
+
}
|
|
256
|
+
case types.AST_NODE_TYPES.CallExpression: {
|
|
257
|
+
if (node !== node.parent.arguments.at(0)) {
|
|
258
|
+
break;
|
|
259
|
+
}
|
|
260
|
+
if (isUseCallbackCall2(node.parent)) {
|
|
261
|
+
const vd = AST__namespace.findParentNode(node.parent, isVariableDeclaratorFromHookCall);
|
|
262
|
+
if (vd != null) {
|
|
263
|
+
eff.getOrUpdate(indSetStateCallsInUseEffectArg0, vd.init, () => []).push(node);
|
|
264
|
+
}
|
|
265
|
+
break;
|
|
266
|
+
}
|
|
267
|
+
if (isUseEffectLikeCall(node.parent)) {
|
|
268
|
+
eff.getOrUpdate(indSetStateCallsInUseEffectSetup, node.parent, () => []).push(node);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
},
|
|
273
|
+
"Program:exit"() {
|
|
274
|
+
const getSetStateCalls = (id, initialScope) => {
|
|
275
|
+
const node = VAR4__namespace.getVariableNode(VAR4__namespace.findVariable(id, initialScope), 0);
|
|
276
|
+
switch (node?.type) {
|
|
277
|
+
case types.AST_NODE_TYPES.ArrowFunctionExpression:
|
|
278
|
+
case types.AST_NODE_TYPES.FunctionDeclaration:
|
|
279
|
+
case types.AST_NODE_TYPES.FunctionExpression:
|
|
280
|
+
return indSetStateCalls.get(node) ?? [];
|
|
281
|
+
case types.AST_NODE_TYPES.CallExpression:
|
|
282
|
+
return indSetStateCallsInUseMemoOrCallback.get(node) ?? indSetStateCallsInUseEffectArg0.get(node) ?? [];
|
|
283
|
+
}
|
|
284
|
+
return [];
|
|
285
|
+
};
|
|
286
|
+
for (const [, calls] of indSetStateCallsInUseEffectSetup) {
|
|
287
|
+
for (const call of calls) {
|
|
288
|
+
onViolation(context, call, { name: call.name });
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
for (const { callee } of indFunctionCalls) {
|
|
292
|
+
if (!("name" in callee)) {
|
|
293
|
+
continue;
|
|
294
|
+
}
|
|
295
|
+
const { name: name3 } = callee;
|
|
296
|
+
const setStateCalls = getSetStateCalls(name3, context.sourceCode.getScope(callee));
|
|
297
|
+
for (const setStateCall of setStateCalls) {
|
|
298
|
+
onViolation(context, setStateCall, {
|
|
299
|
+
name: AST__namespace.toReadableNodeName(setStateCall, (n) => context.sourceCode.getText(n))
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
for (const id of setupFunctionIdentifiers) {
|
|
304
|
+
const setStateCalls = getSetStateCalls(id.name, context.sourceCode.getScope(id));
|
|
305
|
+
for (const setStateCall of setStateCalls) {
|
|
306
|
+
onViolation(context, setStateCall, {
|
|
307
|
+
name: AST__namespace.toReadableNodeName(setStateCall, (n) => context.sourceCode.getText(n))
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
|
|
155
315
|
// src/rules/no-direct-set-state-in-use-effect.ts
|
|
156
316
|
var RULE_NAME = "no-direct-set-state-in-use-effect";
|
|
157
317
|
var RULE_FEATURES = [
|
|
@@ -171,183 +331,18 @@ var no_direct_set_state_in_use_effect_default = createRule({
|
|
|
171
331
|
},
|
|
172
332
|
name: RULE_NAME,
|
|
173
333
|
create(context) {
|
|
174
|
-
if (!/use\w*Effect/u.test(context.sourceCode.text)) {
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
const additionalHooks = settings.additionalHooks;
|
|
179
|
-
const isUseEffectLikeCall = core.isReactHookCallWithNameAlias(context, "useEffect", additionalHooks.useEffect ?? []);
|
|
180
|
-
const isUseStateCall2 = core.isReactHookCallWithNameAlias(context, "useState", additionalHooks.useState ?? []);
|
|
181
|
-
const isUseMemoCall2 = core.isReactHookCallWithNameAlias(context, "useMemo", additionalHooks.useMemo ?? []);
|
|
182
|
-
const isUseCallbackCall2 = core.isReactHookCallWithNameAlias(context, "useCallback", additionalHooks.useCallback ?? []);
|
|
183
|
-
const isSetStateCall = isSetFunctionCall(context, settings);
|
|
184
|
-
const isIdFromUseStateCall = isFromUseStateCall(context, settings);
|
|
185
|
-
const functionEntries = [];
|
|
186
|
-
const setupFunctionRef = { current: null };
|
|
187
|
-
const setupFunctionIdentifiers = [];
|
|
188
|
-
const indFunctionCalls = [];
|
|
189
|
-
const indSetStateCalls = /* @__PURE__ */ new Map();
|
|
190
|
-
const indSetStateCallsInUseEffectArg0 = /* @__PURE__ */ new Map();
|
|
191
|
-
const indSetStateCallsInUseEffectSetup = /* @__PURE__ */ new Map();
|
|
192
|
-
const indSetStateCallsInUseMemoOrCallback = /* @__PURE__ */ new Map();
|
|
193
|
-
const onSetupFunctionEnter = (node) => {
|
|
194
|
-
setupFunctionRef.current = node;
|
|
195
|
-
};
|
|
196
|
-
const onSetupFunctionExit = (node) => {
|
|
197
|
-
if (setupFunctionRef.current === node) {
|
|
198
|
-
setupFunctionRef.current = null;
|
|
199
|
-
}
|
|
200
|
-
};
|
|
201
|
-
function isFunctionOfUseEffectSetup(node) {
|
|
202
|
-
return node.parent?.type === types.AST_NODE_TYPES.CallExpression && node.parent.callee !== node && isUseEffectLikeCall(node.parent);
|
|
203
|
-
}
|
|
204
|
-
function getCallKind(node) {
|
|
205
|
-
return tsPattern.match(node).when(isUseStateCall2, () => "useState").when(isUseEffectLikeCall, () => "useEffect").when(isSetStateCall, () => "setState").when(isThenCall, () => "then").otherwise(() => "other");
|
|
206
|
-
}
|
|
207
|
-
function getFunctionKind(node) {
|
|
208
|
-
return tsPattern.match(node).when(isFunctionOfUseEffectSetup, () => "setup").when(isFunctionOfImmediatelyInvoked, () => "immediate").otherwise(() => "other");
|
|
209
|
-
}
|
|
210
|
-
return {
|
|
211
|
-
":function"(node) {
|
|
212
|
-
const kind = getFunctionKind(node);
|
|
213
|
-
functionEntries.push({ kind, node });
|
|
214
|
-
if (kind === "setup") {
|
|
215
|
-
onSetupFunctionEnter(node);
|
|
216
|
-
}
|
|
217
|
-
},
|
|
218
|
-
":function:exit"(node) {
|
|
219
|
-
const { kind } = functionEntries.at(-1) ?? {};
|
|
220
|
-
if (kind === "setup") {
|
|
221
|
-
onSetupFunctionExit(node);
|
|
222
|
-
}
|
|
223
|
-
functionEntries.pop();
|
|
334
|
+
if (!/use\w*Effect/u.test(context.sourceCode.text)) return {};
|
|
335
|
+
return useNoDirectSetStateInUseEffect(context, {
|
|
336
|
+
onViolation(ctx, node, data) {
|
|
337
|
+
ctx.report({ messageId: "noDirectSetStateInUseEffect", node, data });
|
|
224
338
|
},
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
const pEntry = functionEntries.at(-1);
|
|
228
|
-
if (pEntry == null || pEntry.node.async) {
|
|
229
|
-
return;
|
|
230
|
-
}
|
|
231
|
-
tsPattern.match(getCallKind(node)).with("setState", () => {
|
|
232
|
-
switch (true) {
|
|
233
|
-
case (pEntry.node === setupFunction || pEntry.kind === "immediate"): {
|
|
234
|
-
context.report({
|
|
235
|
-
messageId: "noDirectSetStateInUseEffect",
|
|
236
|
-
node,
|
|
237
|
-
data: {
|
|
238
|
-
name: context.sourceCode.getText(node.callee)
|
|
239
|
-
}
|
|
240
|
-
});
|
|
241
|
-
return;
|
|
242
|
-
}
|
|
243
|
-
default: {
|
|
244
|
-
const vd = AST__namespace.findParentNode(node, isVariableDeclaratorFromHookCall);
|
|
245
|
-
if (vd == null) eff.getOrUpdate(indSetStateCalls, pEntry.node, () => []).push(node);
|
|
246
|
-
else eff.getOrUpdate(indSetStateCallsInUseMemoOrCallback, vd.init, () => []).push(node);
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
}).with("useEffect", () => {
|
|
250
|
-
if (AST__namespace.isFunction(node.arguments.at(0))) return;
|
|
251
|
-
setupFunctionIdentifiers.push(...AST__namespace.getNestedIdentifiers(node));
|
|
252
|
-
}).with("other", () => {
|
|
253
|
-
if (pEntry.node !== setupFunction) return;
|
|
254
|
-
indFunctionCalls.push(node);
|
|
255
|
-
}).otherwise(() => eff._);
|
|
256
|
-
},
|
|
257
|
-
Identifier(node) {
|
|
258
|
-
if (node.parent.type === types.AST_NODE_TYPES.CallExpression && node.parent.callee === node) {
|
|
259
|
-
return;
|
|
260
|
-
}
|
|
261
|
-
if (!isIdFromUseStateCall(node)) {
|
|
262
|
-
return;
|
|
263
|
-
}
|
|
264
|
-
switch (node.parent.type) {
|
|
265
|
-
case types.AST_NODE_TYPES.ArrowFunctionExpression: {
|
|
266
|
-
const parent = node.parent.parent;
|
|
267
|
-
if (parent.type !== types.AST_NODE_TYPES.CallExpression) {
|
|
268
|
-
break;
|
|
269
|
-
}
|
|
270
|
-
if (!isUseMemoCall2(parent)) {
|
|
271
|
-
break;
|
|
272
|
-
}
|
|
273
|
-
const vd = AST__namespace.findParentNode(parent, isVariableDeclaratorFromHookCall);
|
|
274
|
-
if (vd != null) {
|
|
275
|
-
eff.getOrUpdate(indSetStateCallsInUseEffectArg0, vd.init, () => []).push(node);
|
|
276
|
-
}
|
|
277
|
-
break;
|
|
278
|
-
}
|
|
279
|
-
case types.AST_NODE_TYPES.CallExpression: {
|
|
280
|
-
if (node !== node.parent.arguments.at(0)) {
|
|
281
|
-
break;
|
|
282
|
-
}
|
|
283
|
-
if (isUseCallbackCall2(node.parent)) {
|
|
284
|
-
const vd = AST__namespace.findParentNode(node.parent, isVariableDeclaratorFromHookCall);
|
|
285
|
-
if (vd != null) {
|
|
286
|
-
eff.getOrUpdate(indSetStateCallsInUseEffectArg0, vd.init, () => []).push(node);
|
|
287
|
-
}
|
|
288
|
-
break;
|
|
289
|
-
}
|
|
290
|
-
if (isUseEffectLikeCall(node.parent)) {
|
|
291
|
-
eff.getOrUpdate(indSetStateCallsInUseEffectSetup, node.parent, () => []).push(node);
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
},
|
|
296
|
-
"Program:exit"() {
|
|
297
|
-
const getSetStateCalls = (id, initialScope) => {
|
|
298
|
-
const node = VAR5__namespace.getVariableNode(VAR5__namespace.findVariable(id, initialScope), 0);
|
|
299
|
-
switch (node?.type) {
|
|
300
|
-
case types.AST_NODE_TYPES.ArrowFunctionExpression:
|
|
301
|
-
case types.AST_NODE_TYPES.FunctionDeclaration:
|
|
302
|
-
case types.AST_NODE_TYPES.FunctionExpression:
|
|
303
|
-
return indSetStateCalls.get(node) ?? [];
|
|
304
|
-
case types.AST_NODE_TYPES.CallExpression:
|
|
305
|
-
return indSetStateCallsInUseMemoOrCallback.get(node) ?? indSetStateCallsInUseEffectArg0.get(node) ?? [];
|
|
306
|
-
}
|
|
307
|
-
return [];
|
|
308
|
-
};
|
|
309
|
-
for (const [, calls] of indSetStateCallsInUseEffectSetup) {
|
|
310
|
-
for (const call of calls) {
|
|
311
|
-
context.report({
|
|
312
|
-
messageId: "noDirectSetStateInUseEffect",
|
|
313
|
-
node: call,
|
|
314
|
-
data: { name: call.name }
|
|
315
|
-
});
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
for (const { callee } of indFunctionCalls) {
|
|
319
|
-
if (!("name" in callee)) {
|
|
320
|
-
continue;
|
|
321
|
-
}
|
|
322
|
-
const { name: name3 } = callee;
|
|
323
|
-
const setStateCalls = getSetStateCalls(name3, context.sourceCode.getScope(callee));
|
|
324
|
-
for (const setStateCall of setStateCalls) {
|
|
325
|
-
context.report({
|
|
326
|
-
messageId: "noDirectSetStateInUseEffect",
|
|
327
|
-
node: setStateCall,
|
|
328
|
-
data: {
|
|
329
|
-
name: AST__namespace.toReadableNodeName(setStateCall, (n) => context.sourceCode.getText(n))
|
|
330
|
-
}
|
|
331
|
-
});
|
|
332
|
-
}
|
|
333
|
-
}
|
|
334
|
-
for (const id of setupFunctionIdentifiers) {
|
|
335
|
-
const setStateCalls = getSetStateCalls(id.name, context.sourceCode.getScope(id));
|
|
336
|
-
for (const setStateCall of setStateCalls) {
|
|
337
|
-
context.report({
|
|
338
|
-
messageId: "noDirectSetStateInUseEffect",
|
|
339
|
-
node: setStateCall,
|
|
340
|
-
data: {
|
|
341
|
-
name: AST__namespace.toReadableNodeName(setStateCall, (n) => context.sourceCode.getText(n))
|
|
342
|
-
}
|
|
343
|
-
});
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
}
|
|
347
|
-
};
|
|
339
|
+
useEffectKind: "useEffect"
|
|
340
|
+
});
|
|
348
341
|
},
|
|
349
342
|
defaultOptions: []
|
|
350
343
|
});
|
|
344
|
+
|
|
345
|
+
// src/rules/no-direct-set-state-in-use-layout-effect.ts
|
|
351
346
|
var RULE_NAME2 = "no-direct-set-state-in-use-layout-effect";
|
|
352
347
|
var RULE_FEATURES2 = [
|
|
353
348
|
"CHK"
|
|
@@ -366,184 +361,13 @@ var no_direct_set_state_in_use_layout_effect_default = createRule({
|
|
|
366
361
|
},
|
|
367
362
|
name: RULE_NAME2,
|
|
368
363
|
create(context) {
|
|
369
|
-
if (!/use\w*Effect/u.test(context.sourceCode.text)) {
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
const additionalHooks = settings.additionalHooks;
|
|
374
|
-
const isUseLayoutEffectLikeCall = core.isReactHookCallWithNameAlias(
|
|
375
|
-
context,
|
|
376
|
-
"useLayoutEffect",
|
|
377
|
-
additionalHooks.useLayoutEffect ?? []
|
|
378
|
-
);
|
|
379
|
-
const isUseStateCall2 = core.isReactHookCallWithNameAlias(context, "useState", additionalHooks.useState ?? []);
|
|
380
|
-
const isUseMemoCall2 = core.isReactHookCallWithNameAlias(context, "useMemo", additionalHooks.useMemo ?? []);
|
|
381
|
-
const isUseCallbackCall2 = core.isReactHookCallWithNameAlias(context, "useCallback", additionalHooks.useCallback ?? []);
|
|
382
|
-
const isSetStateCall = isSetFunctionCall(context, settings);
|
|
383
|
-
const isIdFromUseStateCall = isFromUseStateCall(context, settings);
|
|
384
|
-
const functionEntries = [];
|
|
385
|
-
const setupFunctionRef = { current: null };
|
|
386
|
-
const setupFunctionIdentifiers = [];
|
|
387
|
-
const indFunctionCalls = [];
|
|
388
|
-
const indSetStateCalls = /* @__PURE__ */ new Map();
|
|
389
|
-
const indSetStateCallsInUseLayoutEffectArg0 = /* @__PURE__ */ new Map();
|
|
390
|
-
const indSetStateCallsInUseLayoutEffectSetup = /* @__PURE__ */ new Map();
|
|
391
|
-
const indSetStateCallsInUseMemoOrCallback = /* @__PURE__ */ new Map();
|
|
392
|
-
const onSetupFunctionEnter = (node) => {
|
|
393
|
-
setupFunctionRef.current = node;
|
|
394
|
-
};
|
|
395
|
-
const onSetupFunctionExit = (node) => {
|
|
396
|
-
if (setupFunctionRef.current === node) {
|
|
397
|
-
setupFunctionRef.current = null;
|
|
398
|
-
}
|
|
399
|
-
};
|
|
400
|
-
function isFunctionOfUseEffectSetup(node) {
|
|
401
|
-
return node.parent?.type === types.AST_NODE_TYPES.CallExpression && node.parent.callee !== node && isUseLayoutEffectLikeCall(node.parent);
|
|
402
|
-
}
|
|
403
|
-
function getCallKind(node) {
|
|
404
|
-
return tsPattern.match(node).when(isUseStateCall2, () => "useState").when(isUseLayoutEffectLikeCall, () => "useLayoutEffect").when(isSetStateCall, () => "setState").when(isThenCall, () => "then").otherwise(() => "other");
|
|
405
|
-
}
|
|
406
|
-
function getFunctionKind(node) {
|
|
407
|
-
return tsPattern.match(node).when(isFunctionOfUseEffectSetup, () => "setup").when(isFunctionOfImmediatelyInvoked, () => "immediate").otherwise(() => "other");
|
|
408
|
-
}
|
|
409
|
-
return {
|
|
410
|
-
":function"(node) {
|
|
411
|
-
const kind = getFunctionKind(node);
|
|
412
|
-
functionEntries.push({ kind, node });
|
|
413
|
-
if (kind === "setup") {
|
|
414
|
-
onSetupFunctionEnter(node);
|
|
415
|
-
}
|
|
416
|
-
},
|
|
417
|
-
":function:exit"(node) {
|
|
418
|
-
const { kind } = functionEntries.at(-1) ?? {};
|
|
419
|
-
if (kind === "setup") {
|
|
420
|
-
onSetupFunctionExit(node);
|
|
421
|
-
}
|
|
422
|
-
functionEntries.pop();
|
|
423
|
-
},
|
|
424
|
-
CallExpression(node) {
|
|
425
|
-
const setupFunction = setupFunctionRef.current;
|
|
426
|
-
const pEntry = functionEntries.at(-1);
|
|
427
|
-
if (pEntry == null || pEntry.node.async) {
|
|
428
|
-
return;
|
|
429
|
-
}
|
|
430
|
-
tsPattern.match(getCallKind(node)).with("setState", () => {
|
|
431
|
-
switch (true) {
|
|
432
|
-
case (pEntry.node === setupFunction || pEntry.kind === "immediate"): {
|
|
433
|
-
context.report({
|
|
434
|
-
messageId: "noDirectSetStateInUseLayoutEffect",
|
|
435
|
-
node,
|
|
436
|
-
data: {
|
|
437
|
-
name: context.sourceCode.getText(node.callee)
|
|
438
|
-
}
|
|
439
|
-
});
|
|
440
|
-
return;
|
|
441
|
-
}
|
|
442
|
-
default: {
|
|
443
|
-
const vd = AST__namespace.findParentNode(node, isVariableDeclaratorFromHookCall);
|
|
444
|
-
if (vd == null) eff.getOrUpdate(indSetStateCalls, pEntry.node, () => []).push(node);
|
|
445
|
-
else eff.getOrUpdate(indSetStateCallsInUseMemoOrCallback, vd.init, () => []).push(node);
|
|
446
|
-
}
|
|
447
|
-
}
|
|
448
|
-
}).with("useLayoutEffect", () => {
|
|
449
|
-
if (AST__namespace.isFunction(node.arguments.at(0))) return;
|
|
450
|
-
setupFunctionIdentifiers.push(...AST__namespace.getNestedIdentifiers(node));
|
|
451
|
-
}).with("other", () => {
|
|
452
|
-
if (pEntry.node !== setupFunction) return;
|
|
453
|
-
indFunctionCalls.push(node);
|
|
454
|
-
}).otherwise(() => eff._);
|
|
455
|
-
},
|
|
456
|
-
Identifier(node) {
|
|
457
|
-
if (node.parent.type === types.AST_NODE_TYPES.CallExpression && node.parent.callee === node) {
|
|
458
|
-
return;
|
|
459
|
-
}
|
|
460
|
-
if (!isIdFromUseStateCall(node)) {
|
|
461
|
-
return;
|
|
462
|
-
}
|
|
463
|
-
switch (node.parent.type) {
|
|
464
|
-
case types.AST_NODE_TYPES.ArrowFunctionExpression: {
|
|
465
|
-
const parent = node.parent.parent;
|
|
466
|
-
if (parent.type !== types.AST_NODE_TYPES.CallExpression) {
|
|
467
|
-
break;
|
|
468
|
-
}
|
|
469
|
-
if (!isUseMemoCall2(parent)) {
|
|
470
|
-
break;
|
|
471
|
-
}
|
|
472
|
-
const vd = AST__namespace.findParentNode(parent, isVariableDeclaratorFromHookCall);
|
|
473
|
-
if (vd != null) {
|
|
474
|
-
eff.getOrUpdate(indSetStateCallsInUseLayoutEffectArg0, vd.init, () => []).push(node);
|
|
475
|
-
}
|
|
476
|
-
break;
|
|
477
|
-
}
|
|
478
|
-
case types.AST_NODE_TYPES.CallExpression: {
|
|
479
|
-
if (node !== node.parent.arguments.at(0)) {
|
|
480
|
-
break;
|
|
481
|
-
}
|
|
482
|
-
if (isUseCallbackCall2(node.parent)) {
|
|
483
|
-
const vd = AST__namespace.findParentNode(node.parent, isVariableDeclaratorFromHookCall);
|
|
484
|
-
if (vd != null) {
|
|
485
|
-
eff.getOrUpdate(indSetStateCallsInUseLayoutEffectArg0, vd.init, () => []).push(node);
|
|
486
|
-
}
|
|
487
|
-
break;
|
|
488
|
-
}
|
|
489
|
-
if (isUseLayoutEffectLikeCall(node.parent)) {
|
|
490
|
-
eff.getOrUpdate(indSetStateCallsInUseLayoutEffectSetup, node.parent, () => []).push(node);
|
|
491
|
-
}
|
|
492
|
-
}
|
|
493
|
-
}
|
|
364
|
+
if (!/use\w*Effect/u.test(context.sourceCode.text)) return {};
|
|
365
|
+
return useNoDirectSetStateInUseEffect(context, {
|
|
366
|
+
onViolation(ctx, node, data) {
|
|
367
|
+
ctx.report({ messageId: "noDirectSetStateInUseLayoutEffect", node, data });
|
|
494
368
|
},
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
const node = VAR5__namespace.getVariableNode(VAR5__namespace.findVariable(id, initialScope), 0);
|
|
498
|
-
switch (node?.type) {
|
|
499
|
-
case types.AST_NODE_TYPES.ArrowFunctionExpression:
|
|
500
|
-
case types.AST_NODE_TYPES.FunctionDeclaration:
|
|
501
|
-
case types.AST_NODE_TYPES.FunctionExpression:
|
|
502
|
-
return indSetStateCalls.get(node) ?? [];
|
|
503
|
-
case types.AST_NODE_TYPES.CallExpression:
|
|
504
|
-
return indSetStateCallsInUseMemoOrCallback.get(node) ?? indSetStateCallsInUseLayoutEffectArg0.get(node) ?? [];
|
|
505
|
-
}
|
|
506
|
-
return [];
|
|
507
|
-
};
|
|
508
|
-
for (const [, calls] of indSetStateCallsInUseLayoutEffectSetup) {
|
|
509
|
-
for (const call of calls) {
|
|
510
|
-
context.report({
|
|
511
|
-
messageId: "noDirectSetStateInUseLayoutEffect",
|
|
512
|
-
node: call,
|
|
513
|
-
data: { name: call.name }
|
|
514
|
-
});
|
|
515
|
-
}
|
|
516
|
-
}
|
|
517
|
-
for (const { callee } of indFunctionCalls) {
|
|
518
|
-
if (!("name" in callee)) {
|
|
519
|
-
continue;
|
|
520
|
-
}
|
|
521
|
-
const { name: name3 } = callee;
|
|
522
|
-
const setStateCalls = getSetStateCalls(name3, context.sourceCode.getScope(callee));
|
|
523
|
-
for (const setStateCall of setStateCalls) {
|
|
524
|
-
context.report({
|
|
525
|
-
messageId: "noDirectSetStateInUseLayoutEffect",
|
|
526
|
-
node: setStateCall,
|
|
527
|
-
data: {
|
|
528
|
-
name: AST__namespace.toReadableNodeName(setStateCall, (n) => context.sourceCode.getText(n))
|
|
529
|
-
}
|
|
530
|
-
});
|
|
531
|
-
}
|
|
532
|
-
}
|
|
533
|
-
for (const id of setupFunctionIdentifiers) {
|
|
534
|
-
const setStateCalls = getSetStateCalls(id.name, context.sourceCode.getScope(id));
|
|
535
|
-
for (const setStateCall of setStateCalls) {
|
|
536
|
-
context.report({
|
|
537
|
-
messageId: "noDirectSetStateInUseLayoutEffect",
|
|
538
|
-
node: setStateCall,
|
|
539
|
-
data: {
|
|
540
|
-
name: AST__namespace.toReadableNodeName(setStateCall, (n) => context.sourceCode.getText(n))
|
|
541
|
-
}
|
|
542
|
-
});
|
|
543
|
-
}
|
|
544
|
-
}
|
|
545
|
-
}
|
|
546
|
-
};
|
|
369
|
+
useEffectKind: "useLayoutEffect"
|
|
370
|
+
});
|
|
547
371
|
},
|
|
548
372
|
defaultOptions: []
|
|
549
373
|
});
|
|
@@ -588,8 +412,8 @@ var no_unnecessary_use_callback_default = createRule({
|
|
|
588
412
|
return;
|
|
589
413
|
}
|
|
590
414
|
const hasEmptyDeps = tsPattern.match(arg1).with({ type: types.AST_NODE_TYPES.ArrayExpression }, (n) => n.elements.length === 0).with({ type: types.AST_NODE_TYPES.Identifier }, (n) => {
|
|
591
|
-
const variable =
|
|
592
|
-
const variableNode =
|
|
415
|
+
const variable = VAR4__namespace.findVariable(n.name, initialScope);
|
|
416
|
+
const variableNode = VAR4__namespace.getVariableNode(variable, 0);
|
|
593
417
|
if (variableNode?.type !== types.AST_NODE_TYPES.ArrayExpression) {
|
|
594
418
|
return false;
|
|
595
419
|
}
|
|
@@ -604,8 +428,8 @@ var no_unnecessary_use_callback_default = createRule({
|
|
|
604
428
|
}
|
|
605
429
|
return n;
|
|
606
430
|
}).with({ type: types.AST_NODE_TYPES.FunctionExpression }, eff.identity).with({ type: types.AST_NODE_TYPES.Identifier }, (n) => {
|
|
607
|
-
const variable =
|
|
608
|
-
const variableNode =
|
|
431
|
+
const variable = VAR4__namespace.findVariable(n.name, initialScope);
|
|
432
|
+
const variableNode = VAR4__namespace.getVariableNode(variable, 0);
|
|
609
433
|
if (variableNode?.type !== types.AST_NODE_TYPES.ArrowFunctionExpression && variableNode?.type !== types.AST_NODE_TYPES.FunctionExpression) {
|
|
610
434
|
return eff._;
|
|
611
435
|
}
|
|
@@ -613,7 +437,7 @@ var no_unnecessary_use_callback_default = createRule({
|
|
|
613
437
|
}).otherwise(() => eff._);
|
|
614
438
|
if (arg0Node == null) return;
|
|
615
439
|
const arg0NodeScope = context.sourceCode.getScope(arg0Node);
|
|
616
|
-
const arg0NodeReferences =
|
|
440
|
+
const arg0NodeReferences = VAR4__namespace.getChidScopes(arg0NodeScope).flatMap((x) => x.references);
|
|
617
441
|
const isReferencedToComponentScope = arg0NodeReferences.some((x) => x.resolved?.scope.block === component);
|
|
618
442
|
if (!isReferencedToComponentScope) {
|
|
619
443
|
context.report({
|
|
@@ -671,8 +495,8 @@ var no_unnecessary_use_memo_default = createRule({
|
|
|
671
495
|
return;
|
|
672
496
|
}
|
|
673
497
|
const hasEmptyDeps = tsPattern.match(arg1).with({ type: types.AST_NODE_TYPES.ArrayExpression }, (n) => n.elements.length === 0).with({ type: types.AST_NODE_TYPES.Identifier }, (n) => {
|
|
674
|
-
const variable =
|
|
675
|
-
const variableNode =
|
|
498
|
+
const variable = VAR4__namespace.findVariable(n.name, initialScope);
|
|
499
|
+
const variableNode = VAR4__namespace.getVariableNode(variable, 0);
|
|
676
500
|
if (variableNode?.type !== types.AST_NODE_TYPES.ArrayExpression) {
|
|
677
501
|
return false;
|
|
678
502
|
}
|
|
@@ -687,8 +511,8 @@ var no_unnecessary_use_memo_default = createRule({
|
|
|
687
511
|
}
|
|
688
512
|
return n;
|
|
689
513
|
}).with({ type: types.AST_NODE_TYPES.FunctionExpression }, eff.identity).with({ type: types.AST_NODE_TYPES.Identifier }, (n) => {
|
|
690
|
-
const variable =
|
|
691
|
-
const variableNode =
|
|
514
|
+
const variable = VAR4__namespace.findVariable(n.name, initialScope);
|
|
515
|
+
const variableNode = VAR4__namespace.getVariableNode(variable, 0);
|
|
692
516
|
if (variableNode?.type !== types.AST_NODE_TYPES.ArrowFunctionExpression && variableNode?.type !== types.AST_NODE_TYPES.FunctionExpression) {
|
|
693
517
|
return eff._;
|
|
694
518
|
}
|
|
@@ -696,7 +520,7 @@ var no_unnecessary_use_memo_default = createRule({
|
|
|
696
520
|
}).otherwise(() => eff._);
|
|
697
521
|
if (arg0Node == null) return;
|
|
698
522
|
const arg0NodeScope = context.sourceCode.getScope(arg0Node);
|
|
699
|
-
const arg0NodeReferences =
|
|
523
|
+
const arg0NodeReferences = VAR4__namespace.getChidScopes(arg0NodeScope).flatMap((x) => x.references);
|
|
700
524
|
const isReferencedToComponentScope = arg0NodeReferences.some((x) => x.resolved?.scope.block === component);
|
|
701
525
|
if (!isReferencedToComponentScope) {
|
|
702
526
|
context.report({
|
package/dist/index.mjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import * as AST from '@eslint-react/ast';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { isReactHookCall, isUseCallbackCall, isReactHookCallWithNameLoose, isUseMemoCall, useHookCollector, isUseStateCall, isReactHookCallWithNameAlias, isReactHookName } from '@eslint-react/core';
|
|
3
|
+
import { identity, _, getOrUpdate, constTrue } from '@eslint-react/eff';
|
|
4
4
|
import { createRuleForPlugin, getSettingsFromContext } from '@eslint-react/shared';
|
|
5
|
-
import * as
|
|
5
|
+
import * as VAR4 from '@eslint-react/var';
|
|
6
6
|
import { AST_NODE_TYPES } from '@typescript-eslint/types';
|
|
7
7
|
import { match } from 'ts-pattern';
|
|
8
8
|
|
|
@@ -27,13 +27,13 @@ var rules = {
|
|
|
27
27
|
|
|
28
28
|
// package.json
|
|
29
29
|
var name2 = "eslint-plugin-react-hooks-extra";
|
|
30
|
-
var version = "1.30.0";
|
|
30
|
+
var version = "1.30.1-beta.0";
|
|
31
31
|
var createRule = createRuleForPlugin("hooks-extra");
|
|
32
32
|
function isFromHookCall(context, name3, settings, predicate = constTrue) {
|
|
33
33
|
const hookAlias = settings.additionalHooks[name3] ?? [];
|
|
34
34
|
return (topLevelId) => {
|
|
35
|
-
const variable =
|
|
36
|
-
const variableNode =
|
|
35
|
+
const variable = VAR4.findVariable(topLevelId, context.sourceCode.getScope(topLevelId));
|
|
36
|
+
const variableNode = VAR4.getVariableNode(variable, 0);
|
|
37
37
|
if (variableNode == null) return false;
|
|
38
38
|
if (variableNode.type !== AST_NODE_TYPES.CallExpression) return false;
|
|
39
39
|
if (!isReactHookCallWithNameAlias(context, name3, hookAlias)(variableNode)) return false;
|
|
@@ -73,7 +73,7 @@ function isSetFunctionCall(context, settings) {
|
|
|
73
73
|
return false;
|
|
74
74
|
}
|
|
75
75
|
const indexScope = context.sourceCode.getScope(node);
|
|
76
|
-
const indexValue =
|
|
76
|
+
const indexValue = VAR4.toStaticValue({
|
|
77
77
|
kind: "lazy",
|
|
78
78
|
node: index,
|
|
79
79
|
initialScope: indexScope
|
|
@@ -93,7 +93,7 @@ function isSetFunctionCall(context, settings) {
|
|
|
93
93
|
}
|
|
94
94
|
const property = node.callee.property;
|
|
95
95
|
const propertyScope = context.sourceCode.getScope(node);
|
|
96
|
-
const propertyValue =
|
|
96
|
+
const propertyValue = VAR4.toStaticValue({
|
|
97
97
|
kind: "lazy",
|
|
98
98
|
node: property,
|
|
99
99
|
initialScope: propertyScope
|
|
@@ -129,6 +129,166 @@ function isVariableDeclaratorFromHookCall(node) {
|
|
|
129
129
|
}
|
|
130
130
|
}
|
|
131
131
|
|
|
132
|
+
// src/hooks/use-no-direct-set-state-in-use-effect.ts
|
|
133
|
+
function useNoDirectSetStateInUseEffect(context, options) {
|
|
134
|
+
const { onViolation, useEffectKind } = options;
|
|
135
|
+
const settings = getSettingsFromContext(context);
|
|
136
|
+
const additionalHooks = settings.additionalHooks;
|
|
137
|
+
const isUseEffectLikeCall = isReactHookCallWithNameAlias(context, useEffectKind, additionalHooks[useEffectKind]);
|
|
138
|
+
const isUseStateCall2 = isReactHookCallWithNameAlias(context, "useState", additionalHooks.useState);
|
|
139
|
+
const isUseMemoCall2 = isReactHookCallWithNameAlias(context, "useMemo", additionalHooks.useMemo);
|
|
140
|
+
const isUseCallbackCall2 = isReactHookCallWithNameAlias(context, "useCallback", additionalHooks.useCallback);
|
|
141
|
+
const isSetStateCall = isSetFunctionCall(context, settings);
|
|
142
|
+
const isIdFromUseStateCall = isFromUseStateCall(context, settings);
|
|
143
|
+
const functionEntries = [];
|
|
144
|
+
const setupFunctionRef = { current: null };
|
|
145
|
+
const setupFunctionIdentifiers = [];
|
|
146
|
+
const indFunctionCalls = [];
|
|
147
|
+
const indSetStateCalls = /* @__PURE__ */ new Map();
|
|
148
|
+
const indSetStateCallsInUseEffectArg0 = /* @__PURE__ */ new Map();
|
|
149
|
+
const indSetStateCallsInUseEffectSetup = /* @__PURE__ */ new Map();
|
|
150
|
+
const indSetStateCallsInUseMemoOrCallback = /* @__PURE__ */ new Map();
|
|
151
|
+
const onSetupFunctionEnter = (node) => {
|
|
152
|
+
setupFunctionRef.current = node;
|
|
153
|
+
};
|
|
154
|
+
const onSetupFunctionExit = (node) => {
|
|
155
|
+
if (setupFunctionRef.current === node) {
|
|
156
|
+
setupFunctionRef.current = null;
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
function isFunctionOfUseEffectSetup(node) {
|
|
160
|
+
return node.parent?.type === AST_NODE_TYPES.CallExpression && node.parent.callee !== node && isUseEffectLikeCall(node.parent);
|
|
161
|
+
}
|
|
162
|
+
function getCallKind(node) {
|
|
163
|
+
return match(node).when(isUseStateCall2, () => "useState").when(isUseEffectLikeCall, () => useEffectKind).when(isSetStateCall, () => "setState").when(isThenCall, () => "then").otherwise(() => "other");
|
|
164
|
+
}
|
|
165
|
+
function getFunctionKind(node) {
|
|
166
|
+
return match(node).when(isFunctionOfUseEffectSetup, () => "setup").when(isFunctionOfImmediatelyInvoked, () => "immediate").otherwise(() => "other");
|
|
167
|
+
}
|
|
168
|
+
return {
|
|
169
|
+
":function"(node) {
|
|
170
|
+
const kind = getFunctionKind(node);
|
|
171
|
+
functionEntries.push({ kind, node });
|
|
172
|
+
if (kind === "setup") {
|
|
173
|
+
onSetupFunctionEnter(node);
|
|
174
|
+
}
|
|
175
|
+
},
|
|
176
|
+
":function:exit"(node) {
|
|
177
|
+
const { kind } = functionEntries.at(-1) ?? {};
|
|
178
|
+
if (kind === "setup") {
|
|
179
|
+
onSetupFunctionExit(node);
|
|
180
|
+
}
|
|
181
|
+
functionEntries.pop();
|
|
182
|
+
},
|
|
183
|
+
CallExpression(node) {
|
|
184
|
+
const setupFunction = setupFunctionRef.current;
|
|
185
|
+
const pEntry = functionEntries.at(-1);
|
|
186
|
+
if (pEntry == null || pEntry.node.async) {
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
match(getCallKind(node)).with("setState", () => {
|
|
190
|
+
switch (true) {
|
|
191
|
+
case (pEntry.node === setupFunction || pEntry.kind === "immediate"): {
|
|
192
|
+
onViolation(context, node, {
|
|
193
|
+
name: context.sourceCode.getText(node.callee)
|
|
194
|
+
});
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
default: {
|
|
198
|
+
const vd = AST.findParentNode(node, isVariableDeclaratorFromHookCall);
|
|
199
|
+
if (vd == null) getOrUpdate(indSetStateCalls, pEntry.node, () => []).push(node);
|
|
200
|
+
else getOrUpdate(indSetStateCallsInUseMemoOrCallback, vd.init, () => []).push(node);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}).with(useEffectKind, () => {
|
|
204
|
+
if (AST.isFunction(node.arguments.at(0))) return;
|
|
205
|
+
setupFunctionIdentifiers.push(...AST.getNestedIdentifiers(node));
|
|
206
|
+
}).with("other", () => {
|
|
207
|
+
if (pEntry.node !== setupFunction) return;
|
|
208
|
+
indFunctionCalls.push(node);
|
|
209
|
+
}).otherwise(() => _);
|
|
210
|
+
},
|
|
211
|
+
Identifier(node) {
|
|
212
|
+
if (node.parent.type === AST_NODE_TYPES.CallExpression && node.parent.callee === node) {
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
if (!isIdFromUseStateCall(node)) {
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
switch (node.parent.type) {
|
|
219
|
+
case AST_NODE_TYPES.ArrowFunctionExpression: {
|
|
220
|
+
const parent = node.parent.parent;
|
|
221
|
+
if (parent.type !== AST_NODE_TYPES.CallExpression) {
|
|
222
|
+
break;
|
|
223
|
+
}
|
|
224
|
+
if (!isUseMemoCall2(parent)) {
|
|
225
|
+
break;
|
|
226
|
+
}
|
|
227
|
+
const vd = AST.findParentNode(parent, isVariableDeclaratorFromHookCall);
|
|
228
|
+
if (vd != null) {
|
|
229
|
+
getOrUpdate(indSetStateCallsInUseEffectArg0, vd.init, () => []).push(node);
|
|
230
|
+
}
|
|
231
|
+
break;
|
|
232
|
+
}
|
|
233
|
+
case AST_NODE_TYPES.CallExpression: {
|
|
234
|
+
if (node !== node.parent.arguments.at(0)) {
|
|
235
|
+
break;
|
|
236
|
+
}
|
|
237
|
+
if (isUseCallbackCall2(node.parent)) {
|
|
238
|
+
const vd = AST.findParentNode(node.parent, isVariableDeclaratorFromHookCall);
|
|
239
|
+
if (vd != null) {
|
|
240
|
+
getOrUpdate(indSetStateCallsInUseEffectArg0, vd.init, () => []).push(node);
|
|
241
|
+
}
|
|
242
|
+
break;
|
|
243
|
+
}
|
|
244
|
+
if (isUseEffectLikeCall(node.parent)) {
|
|
245
|
+
getOrUpdate(indSetStateCallsInUseEffectSetup, node.parent, () => []).push(node);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
},
|
|
250
|
+
"Program:exit"() {
|
|
251
|
+
const getSetStateCalls = (id, initialScope) => {
|
|
252
|
+
const node = VAR4.getVariableNode(VAR4.findVariable(id, initialScope), 0);
|
|
253
|
+
switch (node?.type) {
|
|
254
|
+
case AST_NODE_TYPES.ArrowFunctionExpression:
|
|
255
|
+
case AST_NODE_TYPES.FunctionDeclaration:
|
|
256
|
+
case AST_NODE_TYPES.FunctionExpression:
|
|
257
|
+
return indSetStateCalls.get(node) ?? [];
|
|
258
|
+
case AST_NODE_TYPES.CallExpression:
|
|
259
|
+
return indSetStateCallsInUseMemoOrCallback.get(node) ?? indSetStateCallsInUseEffectArg0.get(node) ?? [];
|
|
260
|
+
}
|
|
261
|
+
return [];
|
|
262
|
+
};
|
|
263
|
+
for (const [, calls] of indSetStateCallsInUseEffectSetup) {
|
|
264
|
+
for (const call of calls) {
|
|
265
|
+
onViolation(context, call, { name: call.name });
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
for (const { callee } of indFunctionCalls) {
|
|
269
|
+
if (!("name" in callee)) {
|
|
270
|
+
continue;
|
|
271
|
+
}
|
|
272
|
+
const { name: name3 } = callee;
|
|
273
|
+
const setStateCalls = getSetStateCalls(name3, context.sourceCode.getScope(callee));
|
|
274
|
+
for (const setStateCall of setStateCalls) {
|
|
275
|
+
onViolation(context, setStateCall, {
|
|
276
|
+
name: AST.toReadableNodeName(setStateCall, (n) => context.sourceCode.getText(n))
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
for (const id of setupFunctionIdentifiers) {
|
|
281
|
+
const setStateCalls = getSetStateCalls(id.name, context.sourceCode.getScope(id));
|
|
282
|
+
for (const setStateCall of setStateCalls) {
|
|
283
|
+
onViolation(context, setStateCall, {
|
|
284
|
+
name: AST.toReadableNodeName(setStateCall, (n) => context.sourceCode.getText(n))
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
|
|
132
292
|
// src/rules/no-direct-set-state-in-use-effect.ts
|
|
133
293
|
var RULE_NAME = "no-direct-set-state-in-use-effect";
|
|
134
294
|
var RULE_FEATURES = [
|
|
@@ -148,183 +308,18 @@ var no_direct_set_state_in_use_effect_default = createRule({
|
|
|
148
308
|
},
|
|
149
309
|
name: RULE_NAME,
|
|
150
310
|
create(context) {
|
|
151
|
-
if (!/use\w*Effect/u.test(context.sourceCode.text)) {
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
const additionalHooks = settings.additionalHooks;
|
|
156
|
-
const isUseEffectLikeCall = isReactHookCallWithNameAlias(context, "useEffect", additionalHooks.useEffect ?? []);
|
|
157
|
-
const isUseStateCall2 = isReactHookCallWithNameAlias(context, "useState", additionalHooks.useState ?? []);
|
|
158
|
-
const isUseMemoCall2 = isReactHookCallWithNameAlias(context, "useMemo", additionalHooks.useMemo ?? []);
|
|
159
|
-
const isUseCallbackCall2 = isReactHookCallWithNameAlias(context, "useCallback", additionalHooks.useCallback ?? []);
|
|
160
|
-
const isSetStateCall = isSetFunctionCall(context, settings);
|
|
161
|
-
const isIdFromUseStateCall = isFromUseStateCall(context, settings);
|
|
162
|
-
const functionEntries = [];
|
|
163
|
-
const setupFunctionRef = { current: null };
|
|
164
|
-
const setupFunctionIdentifiers = [];
|
|
165
|
-
const indFunctionCalls = [];
|
|
166
|
-
const indSetStateCalls = /* @__PURE__ */ new Map();
|
|
167
|
-
const indSetStateCallsInUseEffectArg0 = /* @__PURE__ */ new Map();
|
|
168
|
-
const indSetStateCallsInUseEffectSetup = /* @__PURE__ */ new Map();
|
|
169
|
-
const indSetStateCallsInUseMemoOrCallback = /* @__PURE__ */ new Map();
|
|
170
|
-
const onSetupFunctionEnter = (node) => {
|
|
171
|
-
setupFunctionRef.current = node;
|
|
172
|
-
};
|
|
173
|
-
const onSetupFunctionExit = (node) => {
|
|
174
|
-
if (setupFunctionRef.current === node) {
|
|
175
|
-
setupFunctionRef.current = null;
|
|
176
|
-
}
|
|
177
|
-
};
|
|
178
|
-
function isFunctionOfUseEffectSetup(node) {
|
|
179
|
-
return node.parent?.type === AST_NODE_TYPES.CallExpression && node.parent.callee !== node && isUseEffectLikeCall(node.parent);
|
|
180
|
-
}
|
|
181
|
-
function getCallKind(node) {
|
|
182
|
-
return match(node).when(isUseStateCall2, () => "useState").when(isUseEffectLikeCall, () => "useEffect").when(isSetStateCall, () => "setState").when(isThenCall, () => "then").otherwise(() => "other");
|
|
183
|
-
}
|
|
184
|
-
function getFunctionKind(node) {
|
|
185
|
-
return match(node).when(isFunctionOfUseEffectSetup, () => "setup").when(isFunctionOfImmediatelyInvoked, () => "immediate").otherwise(() => "other");
|
|
186
|
-
}
|
|
187
|
-
return {
|
|
188
|
-
":function"(node) {
|
|
189
|
-
const kind = getFunctionKind(node);
|
|
190
|
-
functionEntries.push({ kind, node });
|
|
191
|
-
if (kind === "setup") {
|
|
192
|
-
onSetupFunctionEnter(node);
|
|
193
|
-
}
|
|
311
|
+
if (!/use\w*Effect/u.test(context.sourceCode.text)) return {};
|
|
312
|
+
return useNoDirectSetStateInUseEffect(context, {
|
|
313
|
+
onViolation(ctx, node, data) {
|
|
314
|
+
ctx.report({ messageId: "noDirectSetStateInUseEffect", node, data });
|
|
194
315
|
},
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
if (kind === "setup") {
|
|
198
|
-
onSetupFunctionExit(node);
|
|
199
|
-
}
|
|
200
|
-
functionEntries.pop();
|
|
201
|
-
},
|
|
202
|
-
CallExpression(node) {
|
|
203
|
-
const setupFunction = setupFunctionRef.current;
|
|
204
|
-
const pEntry = functionEntries.at(-1);
|
|
205
|
-
if (pEntry == null || pEntry.node.async) {
|
|
206
|
-
return;
|
|
207
|
-
}
|
|
208
|
-
match(getCallKind(node)).with("setState", () => {
|
|
209
|
-
switch (true) {
|
|
210
|
-
case (pEntry.node === setupFunction || pEntry.kind === "immediate"): {
|
|
211
|
-
context.report({
|
|
212
|
-
messageId: "noDirectSetStateInUseEffect",
|
|
213
|
-
node,
|
|
214
|
-
data: {
|
|
215
|
-
name: context.sourceCode.getText(node.callee)
|
|
216
|
-
}
|
|
217
|
-
});
|
|
218
|
-
return;
|
|
219
|
-
}
|
|
220
|
-
default: {
|
|
221
|
-
const vd = AST.findParentNode(node, isVariableDeclaratorFromHookCall);
|
|
222
|
-
if (vd == null) getOrUpdate(indSetStateCalls, pEntry.node, () => []).push(node);
|
|
223
|
-
else getOrUpdate(indSetStateCallsInUseMemoOrCallback, vd.init, () => []).push(node);
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
}).with("useEffect", () => {
|
|
227
|
-
if (AST.isFunction(node.arguments.at(0))) return;
|
|
228
|
-
setupFunctionIdentifiers.push(...AST.getNestedIdentifiers(node));
|
|
229
|
-
}).with("other", () => {
|
|
230
|
-
if (pEntry.node !== setupFunction) return;
|
|
231
|
-
indFunctionCalls.push(node);
|
|
232
|
-
}).otherwise(() => _);
|
|
233
|
-
},
|
|
234
|
-
Identifier(node) {
|
|
235
|
-
if (node.parent.type === AST_NODE_TYPES.CallExpression && node.parent.callee === node) {
|
|
236
|
-
return;
|
|
237
|
-
}
|
|
238
|
-
if (!isIdFromUseStateCall(node)) {
|
|
239
|
-
return;
|
|
240
|
-
}
|
|
241
|
-
switch (node.parent.type) {
|
|
242
|
-
case AST_NODE_TYPES.ArrowFunctionExpression: {
|
|
243
|
-
const parent = node.parent.parent;
|
|
244
|
-
if (parent.type !== AST_NODE_TYPES.CallExpression) {
|
|
245
|
-
break;
|
|
246
|
-
}
|
|
247
|
-
if (!isUseMemoCall2(parent)) {
|
|
248
|
-
break;
|
|
249
|
-
}
|
|
250
|
-
const vd = AST.findParentNode(parent, isVariableDeclaratorFromHookCall);
|
|
251
|
-
if (vd != null) {
|
|
252
|
-
getOrUpdate(indSetStateCallsInUseEffectArg0, vd.init, () => []).push(node);
|
|
253
|
-
}
|
|
254
|
-
break;
|
|
255
|
-
}
|
|
256
|
-
case AST_NODE_TYPES.CallExpression: {
|
|
257
|
-
if (node !== node.parent.arguments.at(0)) {
|
|
258
|
-
break;
|
|
259
|
-
}
|
|
260
|
-
if (isUseCallbackCall2(node.parent)) {
|
|
261
|
-
const vd = AST.findParentNode(node.parent, isVariableDeclaratorFromHookCall);
|
|
262
|
-
if (vd != null) {
|
|
263
|
-
getOrUpdate(indSetStateCallsInUseEffectArg0, vd.init, () => []).push(node);
|
|
264
|
-
}
|
|
265
|
-
break;
|
|
266
|
-
}
|
|
267
|
-
if (isUseEffectLikeCall(node.parent)) {
|
|
268
|
-
getOrUpdate(indSetStateCallsInUseEffectSetup, node.parent, () => []).push(node);
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
},
|
|
273
|
-
"Program:exit"() {
|
|
274
|
-
const getSetStateCalls = (id, initialScope) => {
|
|
275
|
-
const node = VAR5.getVariableNode(VAR5.findVariable(id, initialScope), 0);
|
|
276
|
-
switch (node?.type) {
|
|
277
|
-
case AST_NODE_TYPES.ArrowFunctionExpression:
|
|
278
|
-
case AST_NODE_TYPES.FunctionDeclaration:
|
|
279
|
-
case AST_NODE_TYPES.FunctionExpression:
|
|
280
|
-
return indSetStateCalls.get(node) ?? [];
|
|
281
|
-
case AST_NODE_TYPES.CallExpression:
|
|
282
|
-
return indSetStateCallsInUseMemoOrCallback.get(node) ?? indSetStateCallsInUseEffectArg0.get(node) ?? [];
|
|
283
|
-
}
|
|
284
|
-
return [];
|
|
285
|
-
};
|
|
286
|
-
for (const [, calls] of indSetStateCallsInUseEffectSetup) {
|
|
287
|
-
for (const call of calls) {
|
|
288
|
-
context.report({
|
|
289
|
-
messageId: "noDirectSetStateInUseEffect",
|
|
290
|
-
node: call,
|
|
291
|
-
data: { name: call.name }
|
|
292
|
-
});
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
for (const { callee } of indFunctionCalls) {
|
|
296
|
-
if (!("name" in callee)) {
|
|
297
|
-
continue;
|
|
298
|
-
}
|
|
299
|
-
const { name: name3 } = callee;
|
|
300
|
-
const setStateCalls = getSetStateCalls(name3, context.sourceCode.getScope(callee));
|
|
301
|
-
for (const setStateCall of setStateCalls) {
|
|
302
|
-
context.report({
|
|
303
|
-
messageId: "noDirectSetStateInUseEffect",
|
|
304
|
-
node: setStateCall,
|
|
305
|
-
data: {
|
|
306
|
-
name: AST.toReadableNodeName(setStateCall, (n) => context.sourceCode.getText(n))
|
|
307
|
-
}
|
|
308
|
-
});
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
|
-
for (const id of setupFunctionIdentifiers) {
|
|
312
|
-
const setStateCalls = getSetStateCalls(id.name, context.sourceCode.getScope(id));
|
|
313
|
-
for (const setStateCall of setStateCalls) {
|
|
314
|
-
context.report({
|
|
315
|
-
messageId: "noDirectSetStateInUseEffect",
|
|
316
|
-
node: setStateCall,
|
|
317
|
-
data: {
|
|
318
|
-
name: AST.toReadableNodeName(setStateCall, (n) => context.sourceCode.getText(n))
|
|
319
|
-
}
|
|
320
|
-
});
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
};
|
|
316
|
+
useEffectKind: "useEffect"
|
|
317
|
+
});
|
|
325
318
|
},
|
|
326
319
|
defaultOptions: []
|
|
327
320
|
});
|
|
321
|
+
|
|
322
|
+
// src/rules/no-direct-set-state-in-use-layout-effect.ts
|
|
328
323
|
var RULE_NAME2 = "no-direct-set-state-in-use-layout-effect";
|
|
329
324
|
var RULE_FEATURES2 = [
|
|
330
325
|
"CHK"
|
|
@@ -343,184 +338,13 @@ var no_direct_set_state_in_use_layout_effect_default = createRule({
|
|
|
343
338
|
},
|
|
344
339
|
name: RULE_NAME2,
|
|
345
340
|
create(context) {
|
|
346
|
-
if (!/use\w*Effect/u.test(context.sourceCode.text)) {
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
const additionalHooks = settings.additionalHooks;
|
|
351
|
-
const isUseLayoutEffectLikeCall = isReactHookCallWithNameAlias(
|
|
352
|
-
context,
|
|
353
|
-
"useLayoutEffect",
|
|
354
|
-
additionalHooks.useLayoutEffect ?? []
|
|
355
|
-
);
|
|
356
|
-
const isUseStateCall2 = isReactHookCallWithNameAlias(context, "useState", additionalHooks.useState ?? []);
|
|
357
|
-
const isUseMemoCall2 = isReactHookCallWithNameAlias(context, "useMemo", additionalHooks.useMemo ?? []);
|
|
358
|
-
const isUseCallbackCall2 = isReactHookCallWithNameAlias(context, "useCallback", additionalHooks.useCallback ?? []);
|
|
359
|
-
const isSetStateCall = isSetFunctionCall(context, settings);
|
|
360
|
-
const isIdFromUseStateCall = isFromUseStateCall(context, settings);
|
|
361
|
-
const functionEntries = [];
|
|
362
|
-
const setupFunctionRef = { current: null };
|
|
363
|
-
const setupFunctionIdentifiers = [];
|
|
364
|
-
const indFunctionCalls = [];
|
|
365
|
-
const indSetStateCalls = /* @__PURE__ */ new Map();
|
|
366
|
-
const indSetStateCallsInUseLayoutEffectArg0 = /* @__PURE__ */ new Map();
|
|
367
|
-
const indSetStateCallsInUseLayoutEffectSetup = /* @__PURE__ */ new Map();
|
|
368
|
-
const indSetStateCallsInUseMemoOrCallback = /* @__PURE__ */ new Map();
|
|
369
|
-
const onSetupFunctionEnter = (node) => {
|
|
370
|
-
setupFunctionRef.current = node;
|
|
371
|
-
};
|
|
372
|
-
const onSetupFunctionExit = (node) => {
|
|
373
|
-
if (setupFunctionRef.current === node) {
|
|
374
|
-
setupFunctionRef.current = null;
|
|
375
|
-
}
|
|
376
|
-
};
|
|
377
|
-
function isFunctionOfUseEffectSetup(node) {
|
|
378
|
-
return node.parent?.type === AST_NODE_TYPES.CallExpression && node.parent.callee !== node && isUseLayoutEffectLikeCall(node.parent);
|
|
379
|
-
}
|
|
380
|
-
function getCallKind(node) {
|
|
381
|
-
return match(node).when(isUseStateCall2, () => "useState").when(isUseLayoutEffectLikeCall, () => "useLayoutEffect").when(isSetStateCall, () => "setState").when(isThenCall, () => "then").otherwise(() => "other");
|
|
382
|
-
}
|
|
383
|
-
function getFunctionKind(node) {
|
|
384
|
-
return match(node).when(isFunctionOfUseEffectSetup, () => "setup").when(isFunctionOfImmediatelyInvoked, () => "immediate").otherwise(() => "other");
|
|
385
|
-
}
|
|
386
|
-
return {
|
|
387
|
-
":function"(node) {
|
|
388
|
-
const kind = getFunctionKind(node);
|
|
389
|
-
functionEntries.push({ kind, node });
|
|
390
|
-
if (kind === "setup") {
|
|
391
|
-
onSetupFunctionEnter(node);
|
|
392
|
-
}
|
|
393
|
-
},
|
|
394
|
-
":function:exit"(node) {
|
|
395
|
-
const { kind } = functionEntries.at(-1) ?? {};
|
|
396
|
-
if (kind === "setup") {
|
|
397
|
-
onSetupFunctionExit(node);
|
|
398
|
-
}
|
|
399
|
-
functionEntries.pop();
|
|
341
|
+
if (!/use\w*Effect/u.test(context.sourceCode.text)) return {};
|
|
342
|
+
return useNoDirectSetStateInUseEffect(context, {
|
|
343
|
+
onViolation(ctx, node, data) {
|
|
344
|
+
ctx.report({ messageId: "noDirectSetStateInUseLayoutEffect", node, data });
|
|
400
345
|
},
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
const pEntry = functionEntries.at(-1);
|
|
404
|
-
if (pEntry == null || pEntry.node.async) {
|
|
405
|
-
return;
|
|
406
|
-
}
|
|
407
|
-
match(getCallKind(node)).with("setState", () => {
|
|
408
|
-
switch (true) {
|
|
409
|
-
case (pEntry.node === setupFunction || pEntry.kind === "immediate"): {
|
|
410
|
-
context.report({
|
|
411
|
-
messageId: "noDirectSetStateInUseLayoutEffect",
|
|
412
|
-
node,
|
|
413
|
-
data: {
|
|
414
|
-
name: context.sourceCode.getText(node.callee)
|
|
415
|
-
}
|
|
416
|
-
});
|
|
417
|
-
return;
|
|
418
|
-
}
|
|
419
|
-
default: {
|
|
420
|
-
const vd = AST.findParentNode(node, isVariableDeclaratorFromHookCall);
|
|
421
|
-
if (vd == null) getOrUpdate(indSetStateCalls, pEntry.node, () => []).push(node);
|
|
422
|
-
else getOrUpdate(indSetStateCallsInUseMemoOrCallback, vd.init, () => []).push(node);
|
|
423
|
-
}
|
|
424
|
-
}
|
|
425
|
-
}).with("useLayoutEffect", () => {
|
|
426
|
-
if (AST.isFunction(node.arguments.at(0))) return;
|
|
427
|
-
setupFunctionIdentifiers.push(...AST.getNestedIdentifiers(node));
|
|
428
|
-
}).with("other", () => {
|
|
429
|
-
if (pEntry.node !== setupFunction) return;
|
|
430
|
-
indFunctionCalls.push(node);
|
|
431
|
-
}).otherwise(() => _);
|
|
432
|
-
},
|
|
433
|
-
Identifier(node) {
|
|
434
|
-
if (node.parent.type === AST_NODE_TYPES.CallExpression && node.parent.callee === node) {
|
|
435
|
-
return;
|
|
436
|
-
}
|
|
437
|
-
if (!isIdFromUseStateCall(node)) {
|
|
438
|
-
return;
|
|
439
|
-
}
|
|
440
|
-
switch (node.parent.type) {
|
|
441
|
-
case AST_NODE_TYPES.ArrowFunctionExpression: {
|
|
442
|
-
const parent = node.parent.parent;
|
|
443
|
-
if (parent.type !== AST_NODE_TYPES.CallExpression) {
|
|
444
|
-
break;
|
|
445
|
-
}
|
|
446
|
-
if (!isUseMemoCall2(parent)) {
|
|
447
|
-
break;
|
|
448
|
-
}
|
|
449
|
-
const vd = AST.findParentNode(parent, isVariableDeclaratorFromHookCall);
|
|
450
|
-
if (vd != null) {
|
|
451
|
-
getOrUpdate(indSetStateCallsInUseLayoutEffectArg0, vd.init, () => []).push(node);
|
|
452
|
-
}
|
|
453
|
-
break;
|
|
454
|
-
}
|
|
455
|
-
case AST_NODE_TYPES.CallExpression: {
|
|
456
|
-
if (node !== node.parent.arguments.at(0)) {
|
|
457
|
-
break;
|
|
458
|
-
}
|
|
459
|
-
if (isUseCallbackCall2(node.parent)) {
|
|
460
|
-
const vd = AST.findParentNode(node.parent, isVariableDeclaratorFromHookCall);
|
|
461
|
-
if (vd != null) {
|
|
462
|
-
getOrUpdate(indSetStateCallsInUseLayoutEffectArg0, vd.init, () => []).push(node);
|
|
463
|
-
}
|
|
464
|
-
break;
|
|
465
|
-
}
|
|
466
|
-
if (isUseLayoutEffectLikeCall(node.parent)) {
|
|
467
|
-
getOrUpdate(indSetStateCallsInUseLayoutEffectSetup, node.parent, () => []).push(node);
|
|
468
|
-
}
|
|
469
|
-
}
|
|
470
|
-
}
|
|
471
|
-
},
|
|
472
|
-
"Program:exit"() {
|
|
473
|
-
const getSetStateCalls = (id, initialScope) => {
|
|
474
|
-
const node = VAR5.getVariableNode(VAR5.findVariable(id, initialScope), 0);
|
|
475
|
-
switch (node?.type) {
|
|
476
|
-
case AST_NODE_TYPES.ArrowFunctionExpression:
|
|
477
|
-
case AST_NODE_TYPES.FunctionDeclaration:
|
|
478
|
-
case AST_NODE_TYPES.FunctionExpression:
|
|
479
|
-
return indSetStateCalls.get(node) ?? [];
|
|
480
|
-
case AST_NODE_TYPES.CallExpression:
|
|
481
|
-
return indSetStateCallsInUseMemoOrCallback.get(node) ?? indSetStateCallsInUseLayoutEffectArg0.get(node) ?? [];
|
|
482
|
-
}
|
|
483
|
-
return [];
|
|
484
|
-
};
|
|
485
|
-
for (const [, calls] of indSetStateCallsInUseLayoutEffectSetup) {
|
|
486
|
-
for (const call of calls) {
|
|
487
|
-
context.report({
|
|
488
|
-
messageId: "noDirectSetStateInUseLayoutEffect",
|
|
489
|
-
node: call,
|
|
490
|
-
data: { name: call.name }
|
|
491
|
-
});
|
|
492
|
-
}
|
|
493
|
-
}
|
|
494
|
-
for (const { callee } of indFunctionCalls) {
|
|
495
|
-
if (!("name" in callee)) {
|
|
496
|
-
continue;
|
|
497
|
-
}
|
|
498
|
-
const { name: name3 } = callee;
|
|
499
|
-
const setStateCalls = getSetStateCalls(name3, context.sourceCode.getScope(callee));
|
|
500
|
-
for (const setStateCall of setStateCalls) {
|
|
501
|
-
context.report({
|
|
502
|
-
messageId: "noDirectSetStateInUseLayoutEffect",
|
|
503
|
-
node: setStateCall,
|
|
504
|
-
data: {
|
|
505
|
-
name: AST.toReadableNodeName(setStateCall, (n) => context.sourceCode.getText(n))
|
|
506
|
-
}
|
|
507
|
-
});
|
|
508
|
-
}
|
|
509
|
-
}
|
|
510
|
-
for (const id of setupFunctionIdentifiers) {
|
|
511
|
-
const setStateCalls = getSetStateCalls(id.name, context.sourceCode.getScope(id));
|
|
512
|
-
for (const setStateCall of setStateCalls) {
|
|
513
|
-
context.report({
|
|
514
|
-
messageId: "noDirectSetStateInUseLayoutEffect",
|
|
515
|
-
node: setStateCall,
|
|
516
|
-
data: {
|
|
517
|
-
name: AST.toReadableNodeName(setStateCall, (n) => context.sourceCode.getText(n))
|
|
518
|
-
}
|
|
519
|
-
});
|
|
520
|
-
}
|
|
521
|
-
}
|
|
522
|
-
}
|
|
523
|
-
};
|
|
346
|
+
useEffectKind: "useLayoutEffect"
|
|
347
|
+
});
|
|
524
348
|
},
|
|
525
349
|
defaultOptions: []
|
|
526
350
|
});
|
|
@@ -565,8 +389,8 @@ var no_unnecessary_use_callback_default = createRule({
|
|
|
565
389
|
return;
|
|
566
390
|
}
|
|
567
391
|
const hasEmptyDeps = match(arg1).with({ type: AST_NODE_TYPES.ArrayExpression }, (n) => n.elements.length === 0).with({ type: AST_NODE_TYPES.Identifier }, (n) => {
|
|
568
|
-
const variable =
|
|
569
|
-
const variableNode =
|
|
392
|
+
const variable = VAR4.findVariable(n.name, initialScope);
|
|
393
|
+
const variableNode = VAR4.getVariableNode(variable, 0);
|
|
570
394
|
if (variableNode?.type !== AST_NODE_TYPES.ArrayExpression) {
|
|
571
395
|
return false;
|
|
572
396
|
}
|
|
@@ -581,8 +405,8 @@ var no_unnecessary_use_callback_default = createRule({
|
|
|
581
405
|
}
|
|
582
406
|
return n;
|
|
583
407
|
}).with({ type: AST_NODE_TYPES.FunctionExpression }, identity).with({ type: AST_NODE_TYPES.Identifier }, (n) => {
|
|
584
|
-
const variable =
|
|
585
|
-
const variableNode =
|
|
408
|
+
const variable = VAR4.findVariable(n.name, initialScope);
|
|
409
|
+
const variableNode = VAR4.getVariableNode(variable, 0);
|
|
586
410
|
if (variableNode?.type !== AST_NODE_TYPES.ArrowFunctionExpression && variableNode?.type !== AST_NODE_TYPES.FunctionExpression) {
|
|
587
411
|
return _;
|
|
588
412
|
}
|
|
@@ -590,7 +414,7 @@ var no_unnecessary_use_callback_default = createRule({
|
|
|
590
414
|
}).otherwise(() => _);
|
|
591
415
|
if (arg0Node == null) return;
|
|
592
416
|
const arg0NodeScope = context.sourceCode.getScope(arg0Node);
|
|
593
|
-
const arg0NodeReferences =
|
|
417
|
+
const arg0NodeReferences = VAR4.getChidScopes(arg0NodeScope).flatMap((x) => x.references);
|
|
594
418
|
const isReferencedToComponentScope = arg0NodeReferences.some((x) => x.resolved?.scope.block === component);
|
|
595
419
|
if (!isReferencedToComponentScope) {
|
|
596
420
|
context.report({
|
|
@@ -648,8 +472,8 @@ var no_unnecessary_use_memo_default = createRule({
|
|
|
648
472
|
return;
|
|
649
473
|
}
|
|
650
474
|
const hasEmptyDeps = match(arg1).with({ type: AST_NODE_TYPES.ArrayExpression }, (n) => n.elements.length === 0).with({ type: AST_NODE_TYPES.Identifier }, (n) => {
|
|
651
|
-
const variable =
|
|
652
|
-
const variableNode =
|
|
475
|
+
const variable = VAR4.findVariable(n.name, initialScope);
|
|
476
|
+
const variableNode = VAR4.getVariableNode(variable, 0);
|
|
653
477
|
if (variableNode?.type !== AST_NODE_TYPES.ArrayExpression) {
|
|
654
478
|
return false;
|
|
655
479
|
}
|
|
@@ -664,8 +488,8 @@ var no_unnecessary_use_memo_default = createRule({
|
|
|
664
488
|
}
|
|
665
489
|
return n;
|
|
666
490
|
}).with({ type: AST_NODE_TYPES.FunctionExpression }, identity).with({ type: AST_NODE_TYPES.Identifier }, (n) => {
|
|
667
|
-
const variable =
|
|
668
|
-
const variableNode =
|
|
491
|
+
const variable = VAR4.findVariable(n.name, initialScope);
|
|
492
|
+
const variableNode = VAR4.getVariableNode(variable, 0);
|
|
669
493
|
if (variableNode?.type !== AST_NODE_TYPES.ArrowFunctionExpression && variableNode?.type !== AST_NODE_TYPES.FunctionExpression) {
|
|
670
494
|
return _;
|
|
671
495
|
}
|
|
@@ -673,7 +497,7 @@ var no_unnecessary_use_memo_default = createRule({
|
|
|
673
497
|
}).otherwise(() => _);
|
|
674
498
|
if (arg0Node == null) return;
|
|
675
499
|
const arg0NodeScope = context.sourceCode.getScope(arg0Node);
|
|
676
|
-
const arg0NodeReferences =
|
|
500
|
+
const arg0NodeReferences = VAR4.getChidScopes(arg0NodeScope).flatMap((x) => x.references);
|
|
677
501
|
const isReferencedToComponentScope = arg0NodeReferences.some((x) => x.resolved?.scope.block === component);
|
|
678
502
|
if (!isReferencedToComponentScope) {
|
|
679
503
|
context.report({
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eslint-plugin-react-hooks-extra",
|
|
3
|
-
"version": "1.30.0",
|
|
3
|
+
"version": "1.30.1-beta.0",
|
|
4
4
|
"description": "ESLint React's ESLint plugin for React Hooks related rules.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react",
|
|
@@ -50,12 +50,12 @@
|
|
|
50
50
|
"@typescript-eslint/utils": "^8.25.0",
|
|
51
51
|
"string-ts": "^2.2.1",
|
|
52
52
|
"ts-pattern": "^5.6.2",
|
|
53
|
-
"@eslint-react/ast": "1.30.0",
|
|
54
|
-
"@eslint-react/core": "1.30.0",
|
|
55
|
-
"@eslint-react/eff": "1.30.0",
|
|
56
|
-
"@eslint-react/
|
|
57
|
-
"@eslint-react/
|
|
58
|
-
"@eslint-react/var": "1.30.0"
|
|
53
|
+
"@eslint-react/ast": "1.30.1-beta.0",
|
|
54
|
+
"@eslint-react/core": "1.30.1-beta.0",
|
|
55
|
+
"@eslint-react/eff": "1.30.1-beta.0",
|
|
56
|
+
"@eslint-react/shared": "1.30.1-beta.0",
|
|
57
|
+
"@eslint-react/jsx": "1.30.1-beta.0",
|
|
58
|
+
"@eslint-react/var": "1.30.1-beta.0"
|
|
59
59
|
},
|
|
60
60
|
"devDependencies": {
|
|
61
61
|
"@types/react": "^19.0.10",
|