eslint-plugin-react-hooks-extra 1.14.3 → 1.15.0-beta.11
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.d.mts +1 -2
- package/dist/index.d.ts +1 -2
- package/dist/index.js +211 -39
- package/dist/index.mjs +210 -38
- package/package.json +14 -14
package/dist/index.d.mts
CHANGED
|
@@ -7,6 +7,7 @@ declare const _default: {
|
|
|
7
7
|
};
|
|
8
8
|
readonly rules: {
|
|
9
9
|
readonly "no-direct-set-state-in-use-effect": _typescript_eslint_utils_ts_eslint.RuleModule<"noDirectSetStateInUseEffect", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
|
|
10
|
+
readonly "no-direct-set-state-in-use-layout-effect": _typescript_eslint_utils_ts_eslint.RuleModule<"noDirectSetStateInUseLayoutEffect", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
|
|
10
11
|
readonly "no-redundant-custom-hook": _typescript_eslint_utils_ts_eslint.RuleModule<"noRedundantCustomHook", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
|
|
11
12
|
readonly "no-unnecessary-use-callback": _typescript_eslint_utils_ts_eslint.RuleModule<"noUnnecessaryUseCallback", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
|
|
12
13
|
readonly "no-unnecessary-use-memo": _typescript_eslint_utils_ts_eslint.RuleModule<"noUnnecessaryUseMemo", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
|
|
@@ -15,8 +16,6 @@ declare const _default: {
|
|
|
15
16
|
readonly "ensure-custom-hooks-using-other-hooks": _typescript_eslint_utils_ts_eslint.RuleModule<"noRedundantCustomHook", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
|
|
16
17
|
/** @deprecated Use `no-unnecessary-use-memo` instead */
|
|
17
18
|
readonly "ensure-use-memo-has-non-empty-deps": _typescript_eslint_utils_ts_eslint.RuleModule<"noUnnecessaryUseMemo", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
|
|
18
|
-
/** @deprecated Use `no-direct-set-state-in-use-effect` instead */
|
|
19
|
-
readonly "no-direct-set-state-in-use-layout-effect": _typescript_eslint_utils_ts_eslint.RuleModule<"noDirectSetStateInUseEffect", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
|
|
20
19
|
};
|
|
21
20
|
};
|
|
22
21
|
|
package/dist/index.d.ts
CHANGED
|
@@ -7,6 +7,7 @@ declare const _default: {
|
|
|
7
7
|
};
|
|
8
8
|
readonly rules: {
|
|
9
9
|
readonly "no-direct-set-state-in-use-effect": _typescript_eslint_utils_ts_eslint.RuleModule<"noDirectSetStateInUseEffect", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
|
|
10
|
+
readonly "no-direct-set-state-in-use-layout-effect": _typescript_eslint_utils_ts_eslint.RuleModule<"noDirectSetStateInUseLayoutEffect", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
|
|
10
11
|
readonly "no-redundant-custom-hook": _typescript_eslint_utils_ts_eslint.RuleModule<"noRedundantCustomHook", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
|
|
11
12
|
readonly "no-unnecessary-use-callback": _typescript_eslint_utils_ts_eslint.RuleModule<"noUnnecessaryUseCallback", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
|
|
12
13
|
readonly "no-unnecessary-use-memo": _typescript_eslint_utils_ts_eslint.RuleModule<"noUnnecessaryUseMemo", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
|
|
@@ -15,8 +16,6 @@ declare const _default: {
|
|
|
15
16
|
readonly "ensure-custom-hooks-using-other-hooks": _typescript_eslint_utils_ts_eslint.RuleModule<"noRedundantCustomHook", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
|
|
16
17
|
/** @deprecated Use `no-unnecessary-use-memo` instead */
|
|
17
18
|
readonly "ensure-use-memo-has-non-empty-deps": _typescript_eslint_utils_ts_eslint.RuleModule<"noUnnecessaryUseMemo", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
|
|
18
|
-
/** @deprecated Use `no-direct-set-state-in-use-effect` instead */
|
|
19
|
-
readonly "no-direct-set-state-in-use-layout-effect": _typescript_eslint_utils_ts_eslint.RuleModule<"noDirectSetStateInUseEffect", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
|
|
20
19
|
};
|
|
21
20
|
};
|
|
22
21
|
|
package/dist/index.js
CHANGED
|
@@ -4,7 +4,7 @@ var AST2 = require('@eslint-react/ast');
|
|
|
4
4
|
var core = require('@eslint-react/core');
|
|
5
5
|
var shared = require('@eslint-react/shared');
|
|
6
6
|
var tools = require('@eslint-react/tools');
|
|
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
|
var astUtils = require('@typescript-eslint/utils/ast-utils');
|
|
@@ -28,17 +28,17 @@ function _interopNamespace(e) {
|
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
var AST2__namespace = /*#__PURE__*/_interopNamespace(AST2);
|
|
31
|
-
var
|
|
31
|
+
var VAR4__namespace = /*#__PURE__*/_interopNamespace(VAR4);
|
|
32
32
|
|
|
33
33
|
// package.json
|
|
34
34
|
var name = "eslint-plugin-react-hooks-extra";
|
|
35
|
-
var version = "1.
|
|
35
|
+
var version = "1.15.0-beta.11";
|
|
36
36
|
var createRule = shared.createRuleForPlugin("hooks-extra");
|
|
37
37
|
function isFromHookCall(name2, context, settings, predicate = tools.F.constTrue) {
|
|
38
38
|
const hookAlias = settings.additionalHooks?.[name2] ?? [];
|
|
39
39
|
return (topLevelId) => tools.F.pipe(
|
|
40
|
-
|
|
41
|
-
tools.O.flatMap(
|
|
40
|
+
VAR4__namespace.findVariable(topLevelId, context.sourceCode.getScope(topLevelId)),
|
|
41
|
+
tools.O.flatMap(VAR4__namespace.getVariableNode(0)),
|
|
42
42
|
tools.O.filter(AST2__namespace.is(types.AST_NODE_TYPES.CallExpression)),
|
|
43
43
|
tools.O.filter(core.isReactHookCallWithNameAlias(name2, context, hookAlias)),
|
|
44
44
|
tools.O.exists((call) => predicate(topLevelId, call))
|
|
@@ -131,17 +131,7 @@ var no_direct_set_state_in_use_effect_default = createRule({
|
|
|
131
131
|
if (!/use\w*Effect/u.test(context.sourceCode.text)) return {};
|
|
132
132
|
const settings = shared.decodeSettings(context.settings);
|
|
133
133
|
const additionalHooks = settings.additionalHooks ?? {};
|
|
134
|
-
const isUseEffectLikeCall = core.isReactHookCallWithNameAlias(
|
|
135
|
-
"useEffect",
|
|
136
|
-
context,
|
|
137
|
-
[
|
|
138
|
-
"useLayoutEffect",
|
|
139
|
-
"useInsertionEffect",
|
|
140
|
-
...additionalHooks.useEffect ?? [],
|
|
141
|
-
...additionalHooks.useLayoutEffect ?? [],
|
|
142
|
-
...additionalHooks.useInsertionEffect ?? []
|
|
143
|
-
]
|
|
144
|
-
);
|
|
134
|
+
const isUseEffectLikeCall = core.isReactHookCallWithNameAlias("useEffect", context, additionalHooks.useEffect ?? []);
|
|
145
135
|
const isUseStateCall2 = core.isReactHookCallWithNameAlias("useState", context, additionalHooks.useState ?? []);
|
|
146
136
|
const isUseMemoCall2 = core.isReactHookCallWithNameAlias("useMemo", context, additionalHooks.useMemo ?? []);
|
|
147
137
|
const isUseCallbackCall2 = core.isReactHookCallWithNameAlias("useCallback", context, additionalHooks.useCallback ?? []);
|
|
@@ -249,7 +239,7 @@ var no_direct_set_state_in_use_effect_default = createRule({
|
|
|
249
239
|
},
|
|
250
240
|
"Program:exit"() {
|
|
251
241
|
const getSetStateCalls = (id, initialScope) => {
|
|
252
|
-
const node = tools.O.flatMap(
|
|
242
|
+
const node = tools.O.flatMap(VAR4__namespace.findVariable(id, initialScope), VAR4__namespace.getVariableNode(0)).pipe(tools.O.getOrNull);
|
|
253
243
|
switch (node?.type) {
|
|
254
244
|
case types.AST_NODE_TYPES.FunctionDeclaration:
|
|
255
245
|
case types.AST_NODE_TYPES.FunctionExpression:
|
|
@@ -300,7 +290,187 @@ var no_direct_set_state_in_use_effect_default = createRule({
|
|
|
300
290
|
},
|
|
301
291
|
defaultOptions: []
|
|
302
292
|
});
|
|
303
|
-
var RULE_NAME2 = "no-
|
|
293
|
+
var RULE_NAME2 = "no-direct-set-state-in-use-layout-effect";
|
|
294
|
+
var no_direct_set_state_in_use_layout_effect_default = createRule({
|
|
295
|
+
meta: {
|
|
296
|
+
type: "problem",
|
|
297
|
+
docs: {
|
|
298
|
+
description: "disallow direct calls to the 'set' function of 'useState' in 'useLayoutEffect'"
|
|
299
|
+
},
|
|
300
|
+
messages: {
|
|
301
|
+
noDirectSetStateInUseLayoutEffect: "Do not call the 'set' function '{{name}}' of 'useState' directly in 'useLayoutEffect'."
|
|
302
|
+
},
|
|
303
|
+
schema: []
|
|
304
|
+
},
|
|
305
|
+
name: RULE_NAME2,
|
|
306
|
+
create(context) {
|
|
307
|
+
if (!/use\w*Effect/u.test(context.sourceCode.text)) return {};
|
|
308
|
+
const settings = shared.decodeSettings(context.settings);
|
|
309
|
+
const additionalHooks = settings.additionalHooks ?? {};
|
|
310
|
+
const isUseEffectLikeCall = core.isReactHookCallWithNameAlias(
|
|
311
|
+
"useLayoutEffect",
|
|
312
|
+
context,
|
|
313
|
+
additionalHooks.useLayoutEffect ?? []
|
|
314
|
+
);
|
|
315
|
+
const isUseStateCall2 = core.isReactHookCallWithNameAlias("useState", context, additionalHooks.useState ?? []);
|
|
316
|
+
const isUseMemoCall2 = core.isReactHookCallWithNameAlias("useMemo", context, additionalHooks.useMemo ?? []);
|
|
317
|
+
const isUseCallbackCall2 = core.isReactHookCallWithNameAlias("useCallback", context, additionalHooks.useCallback ?? []);
|
|
318
|
+
const isSetStateCall = isSetFunctionCall(context, settings);
|
|
319
|
+
const isIdFromUseStateCall = isFromUseStateCall(context, settings);
|
|
320
|
+
const functionStack = [];
|
|
321
|
+
const setupFunctionRef = tools.MutRef.make(null);
|
|
322
|
+
const setupFunctionIdentifiers = [];
|
|
323
|
+
const indirectFunctionCalls = [];
|
|
324
|
+
const indirectSetStateCalls = /* @__PURE__ */ new WeakMap();
|
|
325
|
+
const indirectSetStateCallsAsArgs = /* @__PURE__ */ new WeakMap();
|
|
326
|
+
const indirectSetStateCallsAsSetups = /* @__PURE__ */ new Map();
|
|
327
|
+
const indirectSetStateCallsInHooks = /* @__PURE__ */ new WeakMap();
|
|
328
|
+
const onSetupFunctionEnter = (node) => {
|
|
329
|
+
tools.MutRef.set(setupFunctionRef, node);
|
|
330
|
+
};
|
|
331
|
+
const onSetupFunctionExit = (node) => {
|
|
332
|
+
tools.MutRef.update(setupFunctionRef, (current) => current === node ? null : current);
|
|
333
|
+
};
|
|
334
|
+
function isSetupFunction(node) {
|
|
335
|
+
return node.parent?.type === types.AST_NODE_TYPES.CallExpression && node.parent.callee !== node && isUseEffectLikeCall(node.parent);
|
|
336
|
+
}
|
|
337
|
+
function getCallKind(node) {
|
|
338
|
+
return tsPattern.match(node).when(isUseStateCall2, () => "useState").when(isUseEffectLikeCall, () => "useLayoutEffect").when(isSetStateCall, () => "setState").when(isThenCall, () => "then").otherwise(() => "other");
|
|
339
|
+
}
|
|
340
|
+
function getFunctionKind(node) {
|
|
341
|
+
return tsPattern.match(node).when(isSetupFunction, () => "setup").when(AST2__namespace.isFunctionOfImmediatelyInvoked, () => "immediate").otherwise(() => "other");
|
|
342
|
+
}
|
|
343
|
+
return {
|
|
344
|
+
":function"(node) {
|
|
345
|
+
const functionKind = getFunctionKind(node);
|
|
346
|
+
functionStack.push([node, functionKind]);
|
|
347
|
+
if (functionKind === "setup") onSetupFunctionEnter(node);
|
|
348
|
+
},
|
|
349
|
+
":function:exit"(node) {
|
|
350
|
+
const [_, functionKind] = functionStack.at(-1) ?? [];
|
|
351
|
+
functionStack.pop();
|
|
352
|
+
if (functionKind === "setup") onSetupFunctionExit(node);
|
|
353
|
+
},
|
|
354
|
+
CallExpression(node) {
|
|
355
|
+
const effectFn = tools.MutRef.get(setupFunctionRef);
|
|
356
|
+
const [parentFn, parentFnKind] = functionStack.at(-1) ?? [];
|
|
357
|
+
if (parentFn?.async) return;
|
|
358
|
+
tsPattern.match(getCallKind(node)).with("setState", () => {
|
|
359
|
+
if (!parentFn) return;
|
|
360
|
+
if (parentFn !== effectFn && parentFnKind !== "immediate") {
|
|
361
|
+
const maybeVd = AST2__namespace.traverseUpGuard(node, isVariableDeclaratorFromHookCall);
|
|
362
|
+
if (tools.O.isSome(maybeVd)) {
|
|
363
|
+
const vd = maybeVd.value;
|
|
364
|
+
const calls2 = indirectSetStateCallsInHooks.get(vd.init) ?? [];
|
|
365
|
+
indirectSetStateCallsInHooks.set(vd.init, [...calls2, node]);
|
|
366
|
+
return;
|
|
367
|
+
}
|
|
368
|
+
const calls = indirectSetStateCalls.get(parentFn) ?? [];
|
|
369
|
+
indirectSetStateCalls.set(parentFn, [...calls, node]);
|
|
370
|
+
return;
|
|
371
|
+
}
|
|
372
|
+
context.report({
|
|
373
|
+
messageId: "noDirectSetStateInUseLayoutEffect",
|
|
374
|
+
node,
|
|
375
|
+
data: { name: context.sourceCode.getText(node.callee) }
|
|
376
|
+
});
|
|
377
|
+
}).with("useLayoutEffect", () => {
|
|
378
|
+
const [firstArg] = node.arguments;
|
|
379
|
+
if (AST2__namespace.isFunction(firstArg)) return;
|
|
380
|
+
const identifiers = AST2__namespace.getNestedIdentifiers(node);
|
|
381
|
+
setupFunctionIdentifiers.push(...identifiers);
|
|
382
|
+
}).with("other", () => {
|
|
383
|
+
const isInSetupFunction = effectFn === parentFn;
|
|
384
|
+
if (!isInSetupFunction) return;
|
|
385
|
+
indirectFunctionCalls.push(node);
|
|
386
|
+
}).otherwise(tools.F.constVoid);
|
|
387
|
+
},
|
|
388
|
+
Identifier(node) {
|
|
389
|
+
if (node.parent.type === types.AST_NODE_TYPES.CallExpression && node.parent.callee === node) return;
|
|
390
|
+
if (!isIdFromUseStateCall(node)) return;
|
|
391
|
+
switch (node.parent.type) {
|
|
392
|
+
case types.AST_NODE_TYPES.CallExpression: {
|
|
393
|
+
const [firstArg] = node.parent.arguments;
|
|
394
|
+
if (node !== firstArg) break;
|
|
395
|
+
if (isUseCallbackCall2(node.parent)) {
|
|
396
|
+
const maybeVd = AST2__namespace.traverseUpGuard(node.parent, isVariableDeclaratorFromHookCall);
|
|
397
|
+
if (tools.O.isNone(maybeVd)) break;
|
|
398
|
+
const vd = maybeVd.value;
|
|
399
|
+
const calls = indirectSetStateCallsAsArgs.get(vd.init) ?? [];
|
|
400
|
+
indirectSetStateCallsAsArgs.set(vd.init, [...calls, node]);
|
|
401
|
+
}
|
|
402
|
+
if (isUseEffectLikeCall(node.parent)) {
|
|
403
|
+
const calls = indirectSetStateCallsAsArgs.get(node.parent) ?? [];
|
|
404
|
+
indirectSetStateCallsAsSetups.set(node.parent, [...calls, node]);
|
|
405
|
+
}
|
|
406
|
+
break;
|
|
407
|
+
}
|
|
408
|
+
case types.AST_NODE_TYPES.ArrowFunctionExpression: {
|
|
409
|
+
const parent = node.parent.parent;
|
|
410
|
+
if (parent.type !== types.AST_NODE_TYPES.CallExpression) break;
|
|
411
|
+
if (!isUseMemoCall2(parent)) break;
|
|
412
|
+
const maybeVd = AST2__namespace.traverseUpGuard(parent, isVariableDeclaratorFromHookCall);
|
|
413
|
+
if (tools.O.isNone(maybeVd)) break;
|
|
414
|
+
const vd = maybeVd.value;
|
|
415
|
+
const calls = indirectSetStateCallsAsArgs.get(vd.init) ?? [];
|
|
416
|
+
indirectSetStateCallsAsArgs.set(vd.init, [...calls, node]);
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
},
|
|
420
|
+
"Program:exit"() {
|
|
421
|
+
const getSetStateCalls = (id, initialScope) => {
|
|
422
|
+
const node = tools.O.flatMap(VAR4__namespace.findVariable(id, initialScope), VAR4__namespace.getVariableNode(0)).pipe(tools.O.getOrNull);
|
|
423
|
+
switch (node?.type) {
|
|
424
|
+
case types.AST_NODE_TYPES.FunctionDeclaration:
|
|
425
|
+
case types.AST_NODE_TYPES.FunctionExpression:
|
|
426
|
+
case types.AST_NODE_TYPES.ArrowFunctionExpression:
|
|
427
|
+
return indirectSetStateCalls.get(node) ?? [];
|
|
428
|
+
case types.AST_NODE_TYPES.CallExpression:
|
|
429
|
+
return indirectSetStateCallsInHooks.get(node) ?? indirectSetStateCallsAsArgs.get(node) ?? [];
|
|
430
|
+
}
|
|
431
|
+
return [];
|
|
432
|
+
};
|
|
433
|
+
for (const [_, calls] of indirectSetStateCallsAsSetups) {
|
|
434
|
+
for (const call of calls) {
|
|
435
|
+
context.report({
|
|
436
|
+
messageId: "noDirectSetStateInUseLayoutEffect",
|
|
437
|
+
node: call,
|
|
438
|
+
data: { name: call.name }
|
|
439
|
+
});
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
for (const { callee } of indirectFunctionCalls) {
|
|
443
|
+
if (!("name" in callee)) continue;
|
|
444
|
+
const { name: name2 } = callee;
|
|
445
|
+
const setStateCalls = getSetStateCalls(name2, context.sourceCode.getScope(callee));
|
|
446
|
+
for (const setStateCall of setStateCalls) {
|
|
447
|
+
context.report({
|
|
448
|
+
messageId: "noDirectSetStateInUseLayoutEffect",
|
|
449
|
+
node: setStateCall,
|
|
450
|
+
data: {
|
|
451
|
+
name: AST2__namespace.toReadableNodeName(setStateCall, (n) => context.sourceCode.getText(n))
|
|
452
|
+
}
|
|
453
|
+
});
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
for (const id of setupFunctionIdentifiers) {
|
|
457
|
+
const setStateCalls = getSetStateCalls(id.name, context.sourceCode.getScope(id));
|
|
458
|
+
for (const setStateCall of setStateCalls) {
|
|
459
|
+
context.report({
|
|
460
|
+
messageId: "noDirectSetStateInUseLayoutEffect",
|
|
461
|
+
node: setStateCall,
|
|
462
|
+
data: {
|
|
463
|
+
name: AST2__namespace.toReadableNodeName(setStateCall, (n) => context.sourceCode.getText(n))
|
|
464
|
+
}
|
|
465
|
+
});
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
};
|
|
470
|
+
},
|
|
471
|
+
defaultOptions: []
|
|
472
|
+
});
|
|
473
|
+
var RULE_NAME3 = "no-redundant-custom-hook";
|
|
304
474
|
var no_redundant_custom_hook_default = createRule({
|
|
305
475
|
meta: {
|
|
306
476
|
type: "problem",
|
|
@@ -312,7 +482,7 @@ var no_redundant_custom_hook_default = createRule({
|
|
|
312
482
|
},
|
|
313
483
|
schema: []
|
|
314
484
|
},
|
|
315
|
-
name:
|
|
485
|
+
name: RULE_NAME3,
|
|
316
486
|
create(context) {
|
|
317
487
|
const { ctx, listeners } = core.useHookCollector();
|
|
318
488
|
return {
|
|
@@ -335,7 +505,7 @@ var no_redundant_custom_hook_default = createRule({
|
|
|
335
505
|
},
|
|
336
506
|
defaultOptions: []
|
|
337
507
|
});
|
|
338
|
-
var
|
|
508
|
+
var RULE_NAME4 = "no-unnecessary-use-callback";
|
|
339
509
|
var no_unnecessary_use_callback_default = createRule({
|
|
340
510
|
meta: {
|
|
341
511
|
type: "problem",
|
|
@@ -347,7 +517,7 @@ var no_unnecessary_use_callback_default = createRule({
|
|
|
347
517
|
},
|
|
348
518
|
schema: []
|
|
349
519
|
},
|
|
350
|
-
name:
|
|
520
|
+
name: RULE_NAME4,
|
|
351
521
|
create(context) {
|
|
352
522
|
if (!context.sourceCode.text.includes("use")) return {};
|
|
353
523
|
const alias = shared.decodeSettings(context.settings).additionalHooks?.useCallback ?? [];
|
|
@@ -372,8 +542,8 @@ var no_unnecessary_use_callback_default = createRule({
|
|
|
372
542
|
const hasEmptyDeps = tools.F.pipe(
|
|
373
543
|
tsPattern.match(deps).with({ type: types.AST_NODE_TYPES.ArrayExpression }, tools.O.some).with({ type: types.AST_NODE_TYPES.Identifier }, (n) => {
|
|
374
544
|
return tools.F.pipe(
|
|
375
|
-
|
|
376
|
-
tools.O.flatMap(
|
|
545
|
+
VAR4__namespace.findVariable(n.name, initialScope),
|
|
546
|
+
tools.O.flatMap(VAR4__namespace.getVariableNode(0)),
|
|
377
547
|
tools.O.filter(AST2__namespace.is(types.AST_NODE_TYPES.ArrayExpression))
|
|
378
548
|
);
|
|
379
549
|
}).otherwise(tools.O.none),
|
|
@@ -395,13 +565,13 @@ var no_unnecessary_use_callback_default = createRule({
|
|
|
395
565
|
return tools.O.some(n);
|
|
396
566
|
}).with({ type: types.AST_NODE_TYPES.FunctionExpression }, tools.O.some).with({ type: types.AST_NODE_TYPES.Identifier }, (n) => {
|
|
397
567
|
return tools.F.pipe(
|
|
398
|
-
|
|
399
|
-
tools.O.flatMap(
|
|
568
|
+
VAR4__namespace.findVariable(n.name, initialScope),
|
|
569
|
+
tools.O.flatMap(VAR4__namespace.getVariableNode(0)),
|
|
400
570
|
tools.O.filter(AST2__namespace.isFunction)
|
|
401
571
|
);
|
|
402
572
|
}).otherwise(tools.O.none),
|
|
403
573
|
tools.O.map((n) => context.sourceCode.getScope(n)),
|
|
404
|
-
tools.O.map((s) =>
|
|
574
|
+
tools.O.map((s) => VAR4__namespace.getChidScopes(s).flatMap((x) => x.references)),
|
|
405
575
|
tools.O.exists((refs) => refs.some((x) => x.resolved?.scope.block === component))
|
|
406
576
|
);
|
|
407
577
|
if (isReferencedToComponentScope) return;
|
|
@@ -414,7 +584,7 @@ var no_unnecessary_use_callback_default = createRule({
|
|
|
414
584
|
},
|
|
415
585
|
defaultOptions: []
|
|
416
586
|
});
|
|
417
|
-
var
|
|
587
|
+
var RULE_NAME5 = "no-unnecessary-use-memo";
|
|
418
588
|
var no_unnecessary_use_memo_default = createRule({
|
|
419
589
|
meta: {
|
|
420
590
|
type: "problem",
|
|
@@ -426,7 +596,7 @@ var no_unnecessary_use_memo_default = createRule({
|
|
|
426
596
|
},
|
|
427
597
|
schema: []
|
|
428
598
|
},
|
|
429
|
-
name:
|
|
599
|
+
name: RULE_NAME5,
|
|
430
600
|
create(context) {
|
|
431
601
|
if (!context.sourceCode.text.includes("use")) return {};
|
|
432
602
|
const alias = shared.decodeSettings(context.settings).additionalHooks?.useMemo ?? [];
|
|
@@ -451,8 +621,8 @@ var no_unnecessary_use_memo_default = createRule({
|
|
|
451
621
|
const hasEmptyDeps = tools.F.pipe(
|
|
452
622
|
tsPattern.match(deps).with({ type: types.AST_NODE_TYPES.ArrayExpression }, tools.O.some).with({ type: types.AST_NODE_TYPES.Identifier }, (n) => {
|
|
453
623
|
return tools.F.pipe(
|
|
454
|
-
|
|
455
|
-
tools.O.flatMap(
|
|
624
|
+
VAR4__namespace.findVariable(n.name, initialScope),
|
|
625
|
+
tools.O.flatMap(VAR4__namespace.getVariableNode(0)),
|
|
456
626
|
tools.O.filter(AST2__namespace.is(types.AST_NODE_TYPES.ArrayExpression))
|
|
457
627
|
);
|
|
458
628
|
}).otherwise(tools.O.none),
|
|
@@ -474,13 +644,13 @@ var no_unnecessary_use_memo_default = createRule({
|
|
|
474
644
|
return tools.O.some(n);
|
|
475
645
|
}).with({ type: types.AST_NODE_TYPES.FunctionExpression }, tools.O.some).with({ type: types.AST_NODE_TYPES.Identifier }, (n) => {
|
|
476
646
|
return tools.F.pipe(
|
|
477
|
-
|
|
478
|
-
tools.O.flatMap(
|
|
647
|
+
VAR4__namespace.findVariable(n.name, initialScope),
|
|
648
|
+
tools.O.flatMap(VAR4__namespace.getVariableNode(0)),
|
|
479
649
|
tools.O.filter(AST2__namespace.isFunction)
|
|
480
650
|
);
|
|
481
651
|
}).otherwise(tools.O.none),
|
|
482
652
|
tools.O.map((n) => context.sourceCode.getScope(n)),
|
|
483
|
-
tools.O.map((s) =>
|
|
653
|
+
tools.O.map((s) => VAR4__namespace.getChidScopes(s).flatMap((x) => x.references)),
|
|
484
654
|
tools.O.exists((refs) => refs.some((x) => x.resolved?.scope.block === component))
|
|
485
655
|
);
|
|
486
656
|
if (isReferencedToComponentScope) return;
|
|
@@ -493,7 +663,7 @@ var no_unnecessary_use_memo_default = createRule({
|
|
|
493
663
|
},
|
|
494
664
|
defaultOptions: []
|
|
495
665
|
});
|
|
496
|
-
var
|
|
666
|
+
var RULE_NAME6 = "prefer-use-state-lazy-initialization";
|
|
497
667
|
var ALLOW_LIST = ["Boolean", "String", "Number"];
|
|
498
668
|
var prefer_use_state_lazy_initialization_default = createRule({
|
|
499
669
|
meta: {
|
|
@@ -506,7 +676,7 @@ var prefer_use_state_lazy_initialization_default = createRule({
|
|
|
506
676
|
},
|
|
507
677
|
schema: []
|
|
508
678
|
},
|
|
509
|
-
name:
|
|
679
|
+
name: RULE_NAME6,
|
|
510
680
|
create(context) {
|
|
511
681
|
if (!context.sourceCode.text.includes("use")) return {};
|
|
512
682
|
const alias = shared.decodeSettings(context.settings).additionalHooks?.useState ?? [];
|
|
@@ -520,7 +690,10 @@ var prefer_use_state_lazy_initialization_default = createRule({
|
|
|
520
690
|
const hasFunctionCall = nestedCallExpressions.some((n) => {
|
|
521
691
|
return "name" in n.callee && !ALLOW_LIST.includes(n.callee.name);
|
|
522
692
|
});
|
|
523
|
-
|
|
693
|
+
const hasNewCall = AST2__namespace.getNestedNewExpressions(useStateInput).some((n) => {
|
|
694
|
+
return "name" in n.callee && !ALLOW_LIST.includes(n.callee.name);
|
|
695
|
+
});
|
|
696
|
+
if (!hasFunctionCall && !hasNewCall) return;
|
|
524
697
|
context.report({
|
|
525
698
|
messageId: "preferUseStateLazyInitialization",
|
|
526
699
|
node: useStateInput
|
|
@@ -539,6 +712,7 @@ var src_default = {
|
|
|
539
712
|
},
|
|
540
713
|
rules: {
|
|
541
714
|
"no-direct-set-state-in-use-effect": no_direct_set_state_in_use_effect_default,
|
|
715
|
+
"no-direct-set-state-in-use-layout-effect": no_direct_set_state_in_use_layout_effect_default,
|
|
542
716
|
"no-redundant-custom-hook": no_redundant_custom_hook_default,
|
|
543
717
|
"no-unnecessary-use-callback": no_unnecessary_use_callback_default,
|
|
544
718
|
"no-unnecessary-use-memo": no_unnecessary_use_memo_default,
|
|
@@ -547,9 +721,7 @@ var src_default = {
|
|
|
547
721
|
/** @deprecated Use `no-redundant-custom-hook` instead */
|
|
548
722
|
"ensure-custom-hooks-using-other-hooks": no_redundant_custom_hook_default,
|
|
549
723
|
/** @deprecated Use `no-unnecessary-use-memo` instead */
|
|
550
|
-
"ensure-use-memo-has-non-empty-deps": no_unnecessary_use_memo_default
|
|
551
|
-
/** @deprecated Use `no-direct-set-state-in-use-effect` instead */
|
|
552
|
-
"no-direct-set-state-in-use-layout-effect": no_direct_set_state_in_use_effect_default
|
|
724
|
+
"ensure-use-memo-has-non-empty-deps": no_unnecessary_use_memo_default
|
|
553
725
|
}
|
|
554
726
|
};
|
|
555
727
|
|
package/dist/index.mjs
CHANGED
|
@@ -2,20 +2,20 @@ import * as AST2 from '@eslint-react/ast';
|
|
|
2
2
|
import { isReactHookCallWithNameAlias, useHookCollector, isReactHookCall, isUseCallbackCall, isReactHookCallWithNameLoose, isUseMemoCall, isUseStateCall, isReactHookName } from '@eslint-react/core';
|
|
3
3
|
import { createRuleForPlugin, decodeSettings } from '@eslint-react/shared';
|
|
4
4
|
import { MutRef, O, F } from '@eslint-react/tools';
|
|
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, isMatching } from 'ts-pattern';
|
|
8
8
|
import { getStaticValue } from '@typescript-eslint/utils/ast-utils';
|
|
9
9
|
|
|
10
10
|
// package.json
|
|
11
11
|
var name = "eslint-plugin-react-hooks-extra";
|
|
12
|
-
var version = "1.
|
|
12
|
+
var version = "1.15.0-beta.11";
|
|
13
13
|
var createRule = createRuleForPlugin("hooks-extra");
|
|
14
14
|
function isFromHookCall(name2, context, settings, predicate = F.constTrue) {
|
|
15
15
|
const hookAlias = settings.additionalHooks?.[name2] ?? [];
|
|
16
16
|
return (topLevelId) => F.pipe(
|
|
17
|
-
|
|
18
|
-
O.flatMap(
|
|
17
|
+
VAR4.findVariable(topLevelId, context.sourceCode.getScope(topLevelId)),
|
|
18
|
+
O.flatMap(VAR4.getVariableNode(0)),
|
|
19
19
|
O.filter(AST2.is(AST_NODE_TYPES.CallExpression)),
|
|
20
20
|
O.filter(isReactHookCallWithNameAlias(name2, context, hookAlias)),
|
|
21
21
|
O.exists((call) => predicate(topLevelId, call))
|
|
@@ -108,17 +108,7 @@ var no_direct_set_state_in_use_effect_default = createRule({
|
|
|
108
108
|
if (!/use\w*Effect/u.test(context.sourceCode.text)) return {};
|
|
109
109
|
const settings = decodeSettings(context.settings);
|
|
110
110
|
const additionalHooks = settings.additionalHooks ?? {};
|
|
111
|
-
const isUseEffectLikeCall = isReactHookCallWithNameAlias(
|
|
112
|
-
"useEffect",
|
|
113
|
-
context,
|
|
114
|
-
[
|
|
115
|
-
"useLayoutEffect",
|
|
116
|
-
"useInsertionEffect",
|
|
117
|
-
...additionalHooks.useEffect ?? [],
|
|
118
|
-
...additionalHooks.useLayoutEffect ?? [],
|
|
119
|
-
...additionalHooks.useInsertionEffect ?? []
|
|
120
|
-
]
|
|
121
|
-
);
|
|
111
|
+
const isUseEffectLikeCall = isReactHookCallWithNameAlias("useEffect", context, additionalHooks.useEffect ?? []);
|
|
122
112
|
const isUseStateCall2 = isReactHookCallWithNameAlias("useState", context, additionalHooks.useState ?? []);
|
|
123
113
|
const isUseMemoCall2 = isReactHookCallWithNameAlias("useMemo", context, additionalHooks.useMemo ?? []);
|
|
124
114
|
const isUseCallbackCall2 = isReactHookCallWithNameAlias("useCallback", context, additionalHooks.useCallback ?? []);
|
|
@@ -226,7 +216,7 @@ var no_direct_set_state_in_use_effect_default = createRule({
|
|
|
226
216
|
},
|
|
227
217
|
"Program:exit"() {
|
|
228
218
|
const getSetStateCalls = (id, initialScope) => {
|
|
229
|
-
const node = O.flatMap(
|
|
219
|
+
const node = O.flatMap(VAR4.findVariable(id, initialScope), VAR4.getVariableNode(0)).pipe(O.getOrNull);
|
|
230
220
|
switch (node?.type) {
|
|
231
221
|
case AST_NODE_TYPES.FunctionDeclaration:
|
|
232
222
|
case AST_NODE_TYPES.FunctionExpression:
|
|
@@ -277,7 +267,187 @@ var no_direct_set_state_in_use_effect_default = createRule({
|
|
|
277
267
|
},
|
|
278
268
|
defaultOptions: []
|
|
279
269
|
});
|
|
280
|
-
var RULE_NAME2 = "no-
|
|
270
|
+
var RULE_NAME2 = "no-direct-set-state-in-use-layout-effect";
|
|
271
|
+
var no_direct_set_state_in_use_layout_effect_default = createRule({
|
|
272
|
+
meta: {
|
|
273
|
+
type: "problem",
|
|
274
|
+
docs: {
|
|
275
|
+
description: "disallow direct calls to the 'set' function of 'useState' in 'useLayoutEffect'"
|
|
276
|
+
},
|
|
277
|
+
messages: {
|
|
278
|
+
noDirectSetStateInUseLayoutEffect: "Do not call the 'set' function '{{name}}' of 'useState' directly in 'useLayoutEffect'."
|
|
279
|
+
},
|
|
280
|
+
schema: []
|
|
281
|
+
},
|
|
282
|
+
name: RULE_NAME2,
|
|
283
|
+
create(context) {
|
|
284
|
+
if (!/use\w*Effect/u.test(context.sourceCode.text)) return {};
|
|
285
|
+
const settings = decodeSettings(context.settings);
|
|
286
|
+
const additionalHooks = settings.additionalHooks ?? {};
|
|
287
|
+
const isUseEffectLikeCall = isReactHookCallWithNameAlias(
|
|
288
|
+
"useLayoutEffect",
|
|
289
|
+
context,
|
|
290
|
+
additionalHooks.useLayoutEffect ?? []
|
|
291
|
+
);
|
|
292
|
+
const isUseStateCall2 = isReactHookCallWithNameAlias("useState", context, additionalHooks.useState ?? []);
|
|
293
|
+
const isUseMemoCall2 = isReactHookCallWithNameAlias("useMemo", context, additionalHooks.useMemo ?? []);
|
|
294
|
+
const isUseCallbackCall2 = isReactHookCallWithNameAlias("useCallback", context, additionalHooks.useCallback ?? []);
|
|
295
|
+
const isSetStateCall = isSetFunctionCall(context, settings);
|
|
296
|
+
const isIdFromUseStateCall = isFromUseStateCall(context, settings);
|
|
297
|
+
const functionStack = [];
|
|
298
|
+
const setupFunctionRef = MutRef.make(null);
|
|
299
|
+
const setupFunctionIdentifiers = [];
|
|
300
|
+
const indirectFunctionCalls = [];
|
|
301
|
+
const indirectSetStateCalls = /* @__PURE__ */ new WeakMap();
|
|
302
|
+
const indirectSetStateCallsAsArgs = /* @__PURE__ */ new WeakMap();
|
|
303
|
+
const indirectSetStateCallsAsSetups = /* @__PURE__ */ new Map();
|
|
304
|
+
const indirectSetStateCallsInHooks = /* @__PURE__ */ new WeakMap();
|
|
305
|
+
const onSetupFunctionEnter = (node) => {
|
|
306
|
+
MutRef.set(setupFunctionRef, node);
|
|
307
|
+
};
|
|
308
|
+
const onSetupFunctionExit = (node) => {
|
|
309
|
+
MutRef.update(setupFunctionRef, (current) => current === node ? null : current);
|
|
310
|
+
};
|
|
311
|
+
function isSetupFunction(node) {
|
|
312
|
+
return node.parent?.type === AST_NODE_TYPES.CallExpression && node.parent.callee !== node && isUseEffectLikeCall(node.parent);
|
|
313
|
+
}
|
|
314
|
+
function getCallKind(node) {
|
|
315
|
+
return match(node).when(isUseStateCall2, () => "useState").when(isUseEffectLikeCall, () => "useLayoutEffect").when(isSetStateCall, () => "setState").when(isThenCall, () => "then").otherwise(() => "other");
|
|
316
|
+
}
|
|
317
|
+
function getFunctionKind(node) {
|
|
318
|
+
return match(node).when(isSetupFunction, () => "setup").when(AST2.isFunctionOfImmediatelyInvoked, () => "immediate").otherwise(() => "other");
|
|
319
|
+
}
|
|
320
|
+
return {
|
|
321
|
+
":function"(node) {
|
|
322
|
+
const functionKind = getFunctionKind(node);
|
|
323
|
+
functionStack.push([node, functionKind]);
|
|
324
|
+
if (functionKind === "setup") onSetupFunctionEnter(node);
|
|
325
|
+
},
|
|
326
|
+
":function:exit"(node) {
|
|
327
|
+
const [_, functionKind] = functionStack.at(-1) ?? [];
|
|
328
|
+
functionStack.pop();
|
|
329
|
+
if (functionKind === "setup") onSetupFunctionExit(node);
|
|
330
|
+
},
|
|
331
|
+
CallExpression(node) {
|
|
332
|
+
const effectFn = MutRef.get(setupFunctionRef);
|
|
333
|
+
const [parentFn, parentFnKind] = functionStack.at(-1) ?? [];
|
|
334
|
+
if (parentFn?.async) return;
|
|
335
|
+
match(getCallKind(node)).with("setState", () => {
|
|
336
|
+
if (!parentFn) return;
|
|
337
|
+
if (parentFn !== effectFn && parentFnKind !== "immediate") {
|
|
338
|
+
const maybeVd = AST2.traverseUpGuard(node, isVariableDeclaratorFromHookCall);
|
|
339
|
+
if (O.isSome(maybeVd)) {
|
|
340
|
+
const vd = maybeVd.value;
|
|
341
|
+
const calls2 = indirectSetStateCallsInHooks.get(vd.init) ?? [];
|
|
342
|
+
indirectSetStateCallsInHooks.set(vd.init, [...calls2, node]);
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
345
|
+
const calls = indirectSetStateCalls.get(parentFn) ?? [];
|
|
346
|
+
indirectSetStateCalls.set(parentFn, [...calls, node]);
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
349
|
+
context.report({
|
|
350
|
+
messageId: "noDirectSetStateInUseLayoutEffect",
|
|
351
|
+
node,
|
|
352
|
+
data: { name: context.sourceCode.getText(node.callee) }
|
|
353
|
+
});
|
|
354
|
+
}).with("useLayoutEffect", () => {
|
|
355
|
+
const [firstArg] = node.arguments;
|
|
356
|
+
if (AST2.isFunction(firstArg)) return;
|
|
357
|
+
const identifiers = AST2.getNestedIdentifiers(node);
|
|
358
|
+
setupFunctionIdentifiers.push(...identifiers);
|
|
359
|
+
}).with("other", () => {
|
|
360
|
+
const isInSetupFunction = effectFn === parentFn;
|
|
361
|
+
if (!isInSetupFunction) return;
|
|
362
|
+
indirectFunctionCalls.push(node);
|
|
363
|
+
}).otherwise(F.constVoid);
|
|
364
|
+
},
|
|
365
|
+
Identifier(node) {
|
|
366
|
+
if (node.parent.type === AST_NODE_TYPES.CallExpression && node.parent.callee === node) return;
|
|
367
|
+
if (!isIdFromUseStateCall(node)) return;
|
|
368
|
+
switch (node.parent.type) {
|
|
369
|
+
case AST_NODE_TYPES.CallExpression: {
|
|
370
|
+
const [firstArg] = node.parent.arguments;
|
|
371
|
+
if (node !== firstArg) break;
|
|
372
|
+
if (isUseCallbackCall2(node.parent)) {
|
|
373
|
+
const maybeVd = AST2.traverseUpGuard(node.parent, isVariableDeclaratorFromHookCall);
|
|
374
|
+
if (O.isNone(maybeVd)) break;
|
|
375
|
+
const vd = maybeVd.value;
|
|
376
|
+
const calls = indirectSetStateCallsAsArgs.get(vd.init) ?? [];
|
|
377
|
+
indirectSetStateCallsAsArgs.set(vd.init, [...calls, node]);
|
|
378
|
+
}
|
|
379
|
+
if (isUseEffectLikeCall(node.parent)) {
|
|
380
|
+
const calls = indirectSetStateCallsAsArgs.get(node.parent) ?? [];
|
|
381
|
+
indirectSetStateCallsAsSetups.set(node.parent, [...calls, node]);
|
|
382
|
+
}
|
|
383
|
+
break;
|
|
384
|
+
}
|
|
385
|
+
case AST_NODE_TYPES.ArrowFunctionExpression: {
|
|
386
|
+
const parent = node.parent.parent;
|
|
387
|
+
if (parent.type !== AST_NODE_TYPES.CallExpression) break;
|
|
388
|
+
if (!isUseMemoCall2(parent)) break;
|
|
389
|
+
const maybeVd = AST2.traverseUpGuard(parent, isVariableDeclaratorFromHookCall);
|
|
390
|
+
if (O.isNone(maybeVd)) break;
|
|
391
|
+
const vd = maybeVd.value;
|
|
392
|
+
const calls = indirectSetStateCallsAsArgs.get(vd.init) ?? [];
|
|
393
|
+
indirectSetStateCallsAsArgs.set(vd.init, [...calls, node]);
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
},
|
|
397
|
+
"Program:exit"() {
|
|
398
|
+
const getSetStateCalls = (id, initialScope) => {
|
|
399
|
+
const node = O.flatMap(VAR4.findVariable(id, initialScope), VAR4.getVariableNode(0)).pipe(O.getOrNull);
|
|
400
|
+
switch (node?.type) {
|
|
401
|
+
case AST_NODE_TYPES.FunctionDeclaration:
|
|
402
|
+
case AST_NODE_TYPES.FunctionExpression:
|
|
403
|
+
case AST_NODE_TYPES.ArrowFunctionExpression:
|
|
404
|
+
return indirectSetStateCalls.get(node) ?? [];
|
|
405
|
+
case AST_NODE_TYPES.CallExpression:
|
|
406
|
+
return indirectSetStateCallsInHooks.get(node) ?? indirectSetStateCallsAsArgs.get(node) ?? [];
|
|
407
|
+
}
|
|
408
|
+
return [];
|
|
409
|
+
};
|
|
410
|
+
for (const [_, calls] of indirectSetStateCallsAsSetups) {
|
|
411
|
+
for (const call of calls) {
|
|
412
|
+
context.report({
|
|
413
|
+
messageId: "noDirectSetStateInUseLayoutEffect",
|
|
414
|
+
node: call,
|
|
415
|
+
data: { name: call.name }
|
|
416
|
+
});
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
for (const { callee } of indirectFunctionCalls) {
|
|
420
|
+
if (!("name" in callee)) continue;
|
|
421
|
+
const { name: name2 } = callee;
|
|
422
|
+
const setStateCalls = getSetStateCalls(name2, context.sourceCode.getScope(callee));
|
|
423
|
+
for (const setStateCall of setStateCalls) {
|
|
424
|
+
context.report({
|
|
425
|
+
messageId: "noDirectSetStateInUseLayoutEffect",
|
|
426
|
+
node: setStateCall,
|
|
427
|
+
data: {
|
|
428
|
+
name: AST2.toReadableNodeName(setStateCall, (n) => context.sourceCode.getText(n))
|
|
429
|
+
}
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
for (const id of setupFunctionIdentifiers) {
|
|
434
|
+
const setStateCalls = getSetStateCalls(id.name, context.sourceCode.getScope(id));
|
|
435
|
+
for (const setStateCall of setStateCalls) {
|
|
436
|
+
context.report({
|
|
437
|
+
messageId: "noDirectSetStateInUseLayoutEffect",
|
|
438
|
+
node: setStateCall,
|
|
439
|
+
data: {
|
|
440
|
+
name: AST2.toReadableNodeName(setStateCall, (n) => context.sourceCode.getText(n))
|
|
441
|
+
}
|
|
442
|
+
});
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
};
|
|
447
|
+
},
|
|
448
|
+
defaultOptions: []
|
|
449
|
+
});
|
|
450
|
+
var RULE_NAME3 = "no-redundant-custom-hook";
|
|
281
451
|
var no_redundant_custom_hook_default = createRule({
|
|
282
452
|
meta: {
|
|
283
453
|
type: "problem",
|
|
@@ -289,7 +459,7 @@ var no_redundant_custom_hook_default = createRule({
|
|
|
289
459
|
},
|
|
290
460
|
schema: []
|
|
291
461
|
},
|
|
292
|
-
name:
|
|
462
|
+
name: RULE_NAME3,
|
|
293
463
|
create(context) {
|
|
294
464
|
const { ctx, listeners } = useHookCollector();
|
|
295
465
|
return {
|
|
@@ -312,7 +482,7 @@ var no_redundant_custom_hook_default = createRule({
|
|
|
312
482
|
},
|
|
313
483
|
defaultOptions: []
|
|
314
484
|
});
|
|
315
|
-
var
|
|
485
|
+
var RULE_NAME4 = "no-unnecessary-use-callback";
|
|
316
486
|
var no_unnecessary_use_callback_default = createRule({
|
|
317
487
|
meta: {
|
|
318
488
|
type: "problem",
|
|
@@ -324,7 +494,7 @@ var no_unnecessary_use_callback_default = createRule({
|
|
|
324
494
|
},
|
|
325
495
|
schema: []
|
|
326
496
|
},
|
|
327
|
-
name:
|
|
497
|
+
name: RULE_NAME4,
|
|
328
498
|
create(context) {
|
|
329
499
|
if (!context.sourceCode.text.includes("use")) return {};
|
|
330
500
|
const alias = decodeSettings(context.settings).additionalHooks?.useCallback ?? [];
|
|
@@ -349,8 +519,8 @@ var no_unnecessary_use_callback_default = createRule({
|
|
|
349
519
|
const hasEmptyDeps = F.pipe(
|
|
350
520
|
match(deps).with({ type: AST_NODE_TYPES.ArrayExpression }, O.some).with({ type: AST_NODE_TYPES.Identifier }, (n) => {
|
|
351
521
|
return F.pipe(
|
|
352
|
-
|
|
353
|
-
O.flatMap(
|
|
522
|
+
VAR4.findVariable(n.name, initialScope),
|
|
523
|
+
O.flatMap(VAR4.getVariableNode(0)),
|
|
354
524
|
O.filter(AST2.is(AST_NODE_TYPES.ArrayExpression))
|
|
355
525
|
);
|
|
356
526
|
}).otherwise(O.none),
|
|
@@ -372,13 +542,13 @@ var no_unnecessary_use_callback_default = createRule({
|
|
|
372
542
|
return O.some(n);
|
|
373
543
|
}).with({ type: AST_NODE_TYPES.FunctionExpression }, O.some).with({ type: AST_NODE_TYPES.Identifier }, (n) => {
|
|
374
544
|
return F.pipe(
|
|
375
|
-
|
|
376
|
-
O.flatMap(
|
|
545
|
+
VAR4.findVariable(n.name, initialScope),
|
|
546
|
+
O.flatMap(VAR4.getVariableNode(0)),
|
|
377
547
|
O.filter(AST2.isFunction)
|
|
378
548
|
);
|
|
379
549
|
}).otherwise(O.none),
|
|
380
550
|
O.map((n) => context.sourceCode.getScope(n)),
|
|
381
|
-
O.map((s) =>
|
|
551
|
+
O.map((s) => VAR4.getChidScopes(s).flatMap((x) => x.references)),
|
|
382
552
|
O.exists((refs) => refs.some((x) => x.resolved?.scope.block === component))
|
|
383
553
|
);
|
|
384
554
|
if (isReferencedToComponentScope) return;
|
|
@@ -391,7 +561,7 @@ var no_unnecessary_use_callback_default = createRule({
|
|
|
391
561
|
},
|
|
392
562
|
defaultOptions: []
|
|
393
563
|
});
|
|
394
|
-
var
|
|
564
|
+
var RULE_NAME5 = "no-unnecessary-use-memo";
|
|
395
565
|
var no_unnecessary_use_memo_default = createRule({
|
|
396
566
|
meta: {
|
|
397
567
|
type: "problem",
|
|
@@ -403,7 +573,7 @@ var no_unnecessary_use_memo_default = createRule({
|
|
|
403
573
|
},
|
|
404
574
|
schema: []
|
|
405
575
|
},
|
|
406
|
-
name:
|
|
576
|
+
name: RULE_NAME5,
|
|
407
577
|
create(context) {
|
|
408
578
|
if (!context.sourceCode.text.includes("use")) return {};
|
|
409
579
|
const alias = decodeSettings(context.settings).additionalHooks?.useMemo ?? [];
|
|
@@ -428,8 +598,8 @@ var no_unnecessary_use_memo_default = createRule({
|
|
|
428
598
|
const hasEmptyDeps = F.pipe(
|
|
429
599
|
match(deps).with({ type: AST_NODE_TYPES.ArrayExpression }, O.some).with({ type: AST_NODE_TYPES.Identifier }, (n) => {
|
|
430
600
|
return F.pipe(
|
|
431
|
-
|
|
432
|
-
O.flatMap(
|
|
601
|
+
VAR4.findVariable(n.name, initialScope),
|
|
602
|
+
O.flatMap(VAR4.getVariableNode(0)),
|
|
433
603
|
O.filter(AST2.is(AST_NODE_TYPES.ArrayExpression))
|
|
434
604
|
);
|
|
435
605
|
}).otherwise(O.none),
|
|
@@ -451,13 +621,13 @@ var no_unnecessary_use_memo_default = createRule({
|
|
|
451
621
|
return O.some(n);
|
|
452
622
|
}).with({ type: AST_NODE_TYPES.FunctionExpression }, O.some).with({ type: AST_NODE_TYPES.Identifier }, (n) => {
|
|
453
623
|
return F.pipe(
|
|
454
|
-
|
|
455
|
-
O.flatMap(
|
|
624
|
+
VAR4.findVariable(n.name, initialScope),
|
|
625
|
+
O.flatMap(VAR4.getVariableNode(0)),
|
|
456
626
|
O.filter(AST2.isFunction)
|
|
457
627
|
);
|
|
458
628
|
}).otherwise(O.none),
|
|
459
629
|
O.map((n) => context.sourceCode.getScope(n)),
|
|
460
|
-
O.map((s) =>
|
|
630
|
+
O.map((s) => VAR4.getChidScopes(s).flatMap((x) => x.references)),
|
|
461
631
|
O.exists((refs) => refs.some((x) => x.resolved?.scope.block === component))
|
|
462
632
|
);
|
|
463
633
|
if (isReferencedToComponentScope) return;
|
|
@@ -470,7 +640,7 @@ var no_unnecessary_use_memo_default = createRule({
|
|
|
470
640
|
},
|
|
471
641
|
defaultOptions: []
|
|
472
642
|
});
|
|
473
|
-
var
|
|
643
|
+
var RULE_NAME6 = "prefer-use-state-lazy-initialization";
|
|
474
644
|
var ALLOW_LIST = ["Boolean", "String", "Number"];
|
|
475
645
|
var prefer_use_state_lazy_initialization_default = createRule({
|
|
476
646
|
meta: {
|
|
@@ -483,7 +653,7 @@ var prefer_use_state_lazy_initialization_default = createRule({
|
|
|
483
653
|
},
|
|
484
654
|
schema: []
|
|
485
655
|
},
|
|
486
|
-
name:
|
|
656
|
+
name: RULE_NAME6,
|
|
487
657
|
create(context) {
|
|
488
658
|
if (!context.sourceCode.text.includes("use")) return {};
|
|
489
659
|
const alias = decodeSettings(context.settings).additionalHooks?.useState ?? [];
|
|
@@ -497,7 +667,10 @@ var prefer_use_state_lazy_initialization_default = createRule({
|
|
|
497
667
|
const hasFunctionCall = nestedCallExpressions.some((n) => {
|
|
498
668
|
return "name" in n.callee && !ALLOW_LIST.includes(n.callee.name);
|
|
499
669
|
});
|
|
500
|
-
|
|
670
|
+
const hasNewCall = AST2.getNestedNewExpressions(useStateInput).some((n) => {
|
|
671
|
+
return "name" in n.callee && !ALLOW_LIST.includes(n.callee.name);
|
|
672
|
+
});
|
|
673
|
+
if (!hasFunctionCall && !hasNewCall) return;
|
|
501
674
|
context.report({
|
|
502
675
|
messageId: "preferUseStateLazyInitialization",
|
|
503
676
|
node: useStateInput
|
|
@@ -516,6 +689,7 @@ var src_default = {
|
|
|
516
689
|
},
|
|
517
690
|
rules: {
|
|
518
691
|
"no-direct-set-state-in-use-effect": no_direct_set_state_in_use_effect_default,
|
|
692
|
+
"no-direct-set-state-in-use-layout-effect": no_direct_set_state_in_use_layout_effect_default,
|
|
519
693
|
"no-redundant-custom-hook": no_redundant_custom_hook_default,
|
|
520
694
|
"no-unnecessary-use-callback": no_unnecessary_use_callback_default,
|
|
521
695
|
"no-unnecessary-use-memo": no_unnecessary_use_memo_default,
|
|
@@ -524,9 +698,7 @@ var src_default = {
|
|
|
524
698
|
/** @deprecated Use `no-redundant-custom-hook` instead */
|
|
525
699
|
"ensure-custom-hooks-using-other-hooks": no_redundant_custom_hook_default,
|
|
526
700
|
/** @deprecated Use `no-unnecessary-use-memo` instead */
|
|
527
|
-
"ensure-use-memo-has-non-empty-deps": no_unnecessary_use_memo_default
|
|
528
|
-
/** @deprecated Use `no-direct-set-state-in-use-effect` instead */
|
|
529
|
-
"no-direct-set-state-in-use-layout-effect": no_direct_set_state_in_use_effect_default
|
|
701
|
+
"ensure-use-memo-has-non-empty-deps": no_unnecessary_use_memo_default
|
|
530
702
|
}
|
|
531
703
|
};
|
|
532
704
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eslint-plugin-react-hooks-extra",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.15.0-beta.11",
|
|
4
4
|
"description": "ESLint React's ESLint plugin for React Hooks related rules.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"eslint",
|
|
@@ -41,22 +41,22 @@
|
|
|
41
41
|
"./package.json"
|
|
42
42
|
],
|
|
43
43
|
"dependencies": {
|
|
44
|
-
"@typescript-eslint/scope-manager": "^8.
|
|
45
|
-
"@typescript-eslint/type-utils": "^8.
|
|
46
|
-
"@typescript-eslint/types": "^8.
|
|
47
|
-
"@typescript-eslint/utils": "^8.
|
|
44
|
+
"@typescript-eslint/scope-manager": "^8.8.1",
|
|
45
|
+
"@typescript-eslint/type-utils": "^8.8.1",
|
|
46
|
+
"@typescript-eslint/types": "^8.8.1",
|
|
47
|
+
"@typescript-eslint/utils": "^8.8.1",
|
|
48
48
|
"ts-pattern": "^5.4.0",
|
|
49
|
-
"@eslint-react/ast": "1.
|
|
50
|
-
"@eslint-react/
|
|
51
|
-
"@eslint-react/jsx": "1.
|
|
52
|
-
"@eslint-react/tools": "1.
|
|
53
|
-
"@eslint-react/types": "1.
|
|
54
|
-
"@eslint-react/var": "1.
|
|
55
|
-
"@eslint-react/
|
|
49
|
+
"@eslint-react/ast": "1.15.0-beta.11",
|
|
50
|
+
"@eslint-react/shared": "1.15.0-beta.11",
|
|
51
|
+
"@eslint-react/jsx": "1.15.0-beta.11",
|
|
52
|
+
"@eslint-react/tools": "1.15.0-beta.11",
|
|
53
|
+
"@eslint-react/types": "1.15.0-beta.11",
|
|
54
|
+
"@eslint-react/var": "1.15.0-beta.11",
|
|
55
|
+
"@eslint-react/core": "1.15.0-beta.11"
|
|
56
56
|
},
|
|
57
57
|
"devDependencies": {
|
|
58
|
-
"@types/react": "^18.3.
|
|
59
|
-
"@types/react-dom": "^18.3.
|
|
58
|
+
"@types/react": "^18.3.11",
|
|
59
|
+
"@types/react-dom": "^18.3.1",
|
|
60
60
|
"string-ts": "^2.2.0",
|
|
61
61
|
"tsup": "^8.3.0"
|
|
62
62
|
},
|