eslint-plugin-react-hooks-extra 1.48.3 → 2.0.0-next.1
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.ts +0 -10
- package/dist/index.js +110 -145
- package/package.json +13 -21
- package/dist/index.d.mts +0 -55
- package/dist/index.mjs +0 -715
package/dist/index.d.ts
CHANGED
|
@@ -17,11 +17,6 @@ declare const _default: {
|
|
|
17
17
|
readonly "no-unnecessary-use-memo": _typescript_eslint_utils_ts_eslint.RuleModule<"noUnnecessaryUseMemo", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
|
|
18
18
|
readonly "no-unnecessary-use-prefix": _typescript_eslint_utils_ts_eslint.RuleModule<"noUnnecessaryUsePrefix", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
|
|
19
19
|
readonly "prefer-use-state-lazy-initialization": _typescript_eslint_utils_ts_eslint.RuleModule<"preferUseStateLazyInitialization", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
|
|
20
|
-
readonly "ensure-custom-hooks-using-other-hooks": _typescript_eslint_utils_ts_eslint.RuleModule<"noUnnecessaryUsePrefix", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
|
|
21
|
-
readonly "ensure-use-callback-has-non-empty-deps": _typescript_eslint_utils_ts_eslint.RuleModule<"noUnnecessaryUseCallback", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
|
|
22
|
-
readonly "ensure-use-memo-has-non-empty-deps": _typescript_eslint_utils_ts_eslint.RuleModule<"noUnnecessaryUseMemo", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
|
|
23
|
-
readonly "no-redundant-custom-hook": _typescript_eslint_utils_ts_eslint.RuleModule<"noUnnecessaryUsePrefix", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
|
|
24
|
-
readonly "no-useless-custom-hooks": _typescript_eslint_utils_ts_eslint.RuleModule<"noUnnecessaryUsePrefix", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
|
|
25
20
|
};
|
|
26
21
|
};
|
|
27
22
|
};
|
|
@@ -44,11 +39,6 @@ declare const _default: {
|
|
|
44
39
|
readonly "no-unnecessary-use-memo": _typescript_eslint_utils_ts_eslint.RuleModule<"noUnnecessaryUseMemo", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
|
|
45
40
|
readonly "no-unnecessary-use-prefix": _typescript_eslint_utils_ts_eslint.RuleModule<"noUnnecessaryUsePrefix", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
|
|
46
41
|
readonly "prefer-use-state-lazy-initialization": _typescript_eslint_utils_ts_eslint.RuleModule<"preferUseStateLazyInitialization", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
|
|
47
|
-
readonly "ensure-custom-hooks-using-other-hooks": _typescript_eslint_utils_ts_eslint.RuleModule<"noUnnecessaryUsePrefix", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
|
|
48
|
-
readonly "ensure-use-callback-has-non-empty-deps": _typescript_eslint_utils_ts_eslint.RuleModule<"noUnnecessaryUseCallback", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
|
|
49
|
-
readonly "ensure-use-memo-has-non-empty-deps": _typescript_eslint_utils_ts_eslint.RuleModule<"noUnnecessaryUseMemo", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
|
|
50
|
-
readonly "no-redundant-custom-hook": _typescript_eslint_utils_ts_eslint.RuleModule<"noUnnecessaryUsePrefix", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
|
|
51
|
-
readonly "no-useless-custom-hooks": _typescript_eslint_utils_ts_eslint.RuleModule<"noUnnecessaryUsePrefix", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
|
|
52
42
|
};
|
|
53
43
|
};
|
|
54
44
|
|
package/dist/index.js
CHANGED
|
@@ -1,35 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
var tsPattern = require('ts-pattern');
|
|
10
|
-
var utils = require('@typescript-eslint/utils');
|
|
11
|
-
|
|
12
|
-
function _interopNamespace(e) {
|
|
13
|
-
if (e && e.__esModule) return e;
|
|
14
|
-
var n = Object.create(null);
|
|
15
|
-
if (e) {
|
|
16
|
-
Object.keys(e).forEach(function (k) {
|
|
17
|
-
if (k !== 'default') {
|
|
18
|
-
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
19
|
-
Object.defineProperty(n, k, d.get ? d : {
|
|
20
|
-
enumerable: true,
|
|
21
|
-
get: function () { return e[k]; }
|
|
22
|
-
});
|
|
23
|
-
}
|
|
24
|
-
});
|
|
25
|
-
}
|
|
26
|
-
n.default = e;
|
|
27
|
-
return Object.freeze(n);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
var AST__namespace = /*#__PURE__*/_interopNamespace(AST);
|
|
31
|
-
var ER7__namespace = /*#__PURE__*/_interopNamespace(ER7);
|
|
32
|
-
var VAR4__namespace = /*#__PURE__*/_interopNamespace(VAR4);
|
|
1
|
+
import * as AST from '@eslint-react/ast';
|
|
2
|
+
import * as ER7 from '@eslint-react/core';
|
|
3
|
+
import { identity, getOrElseUpdate, constVoid, constTrue } from '@eslint-react/eff';
|
|
4
|
+
import { getDocsUrl, getSettingsFromContext } from '@eslint-react/shared';
|
|
5
|
+
import * as VAR4 from '@eslint-react/var';
|
|
6
|
+
import { AST_NODE_TYPES } from '@typescript-eslint/types';
|
|
7
|
+
import { match } from 'ts-pattern';
|
|
8
|
+
import { ESLintUtils } from '@typescript-eslint/utils';
|
|
33
9
|
|
|
34
10
|
var __defProp = Object.defineProperty;
|
|
35
11
|
var __export = (target, all) => {
|
|
@@ -52,31 +28,31 @@ var rules = {
|
|
|
52
28
|
|
|
53
29
|
// package.json
|
|
54
30
|
var name2 = "eslint-plugin-react-hooks-extra";
|
|
55
|
-
var version = "
|
|
56
|
-
var createRule =
|
|
57
|
-
function isFromHookCall(context, name3, settings, predicate =
|
|
31
|
+
var version = "2.0.0-next.1";
|
|
32
|
+
var createRule = ESLintUtils.RuleCreator(getDocsUrl("hooks-extra"));
|
|
33
|
+
function isFromHookCall(context, name3, settings, predicate = constTrue) {
|
|
58
34
|
const hookAlias = settings.additionalHooks[name3] ?? [];
|
|
59
35
|
return (topLevelId) => {
|
|
60
|
-
const variable =
|
|
61
|
-
const variableNode =
|
|
36
|
+
const variable = VAR4.findVariable(topLevelId, context.sourceCode.getScope(topLevelId));
|
|
37
|
+
const variableNode = VAR4.getVariableInitNode(variable, 0);
|
|
62
38
|
if (variableNode == null) return false;
|
|
63
|
-
if (variableNode.type !==
|
|
64
|
-
if (!
|
|
39
|
+
if (variableNode.type !== AST_NODE_TYPES.CallExpression) return false;
|
|
40
|
+
if (!ER7.isReactHookCallWithNameAlias(context, name3, hookAlias)(variableNode)) return false;
|
|
65
41
|
return predicate(topLevelId, variableNode);
|
|
66
42
|
};
|
|
67
43
|
}
|
|
68
44
|
function isFromUseStateCall(context, settings) {
|
|
69
45
|
const predicate = (topLevelId, call) => {
|
|
70
46
|
const { parent } = call;
|
|
71
|
-
if (!("id" in parent) || parent.id?.type !==
|
|
47
|
+
if (!("id" in parent) || parent.id?.type !== AST_NODE_TYPES.ArrayPattern) {
|
|
72
48
|
return true;
|
|
73
49
|
}
|
|
74
|
-
return parent.id.elements.findIndex((e) => e?.type ===
|
|
50
|
+
return parent.id.elements.findIndex((e) => e?.type === AST_NODE_TYPES.Identifier && e.name === topLevelId.name) === 1;
|
|
75
51
|
};
|
|
76
52
|
return isFromHookCall(context, "useState", settings, predicate);
|
|
77
53
|
}
|
|
78
54
|
function isFunctionOfImmediatelyInvoked(node) {
|
|
79
|
-
return node.type !==
|
|
55
|
+
return node.type !== AST_NODE_TYPES.FunctionDeclaration && node.parent.type === AST_NODE_TYPES.CallExpression && node.parent.callee === node;
|
|
80
56
|
}
|
|
81
57
|
function isSetFunctionCall(context, settings) {
|
|
82
58
|
const isIdFromUseStateCall = isFromUseStateCall(context, settings);
|
|
@@ -84,21 +60,21 @@ function isSetFunctionCall(context, settings) {
|
|
|
84
60
|
switch (node.callee.type) {
|
|
85
61
|
// const data = useState();
|
|
86
62
|
// data.at(1)();
|
|
87
|
-
case
|
|
63
|
+
case AST_NODE_TYPES.CallExpression: {
|
|
88
64
|
const { callee } = node.callee;
|
|
89
|
-
if (callee.type !==
|
|
65
|
+
if (callee.type !== AST_NODE_TYPES.MemberExpression) {
|
|
90
66
|
return false;
|
|
91
67
|
}
|
|
92
68
|
if (!("name" in callee.object)) {
|
|
93
69
|
return false;
|
|
94
70
|
}
|
|
95
|
-
const isAt = callee.property.type ===
|
|
71
|
+
const isAt = callee.property.type === AST_NODE_TYPES.Identifier && callee.property.name === "at";
|
|
96
72
|
const [index] = node.callee.arguments;
|
|
97
73
|
if (!isAt || index == null) {
|
|
98
74
|
return false;
|
|
99
75
|
}
|
|
100
76
|
const indexScope = context.sourceCode.getScope(node);
|
|
101
|
-
const indexValue =
|
|
77
|
+
const indexValue = VAR4.toStaticValue({
|
|
102
78
|
kind: "lazy",
|
|
103
79
|
node: index,
|
|
104
80
|
initialScope: indexScope
|
|
@@ -107,18 +83,18 @@ function isSetFunctionCall(context, settings) {
|
|
|
107
83
|
}
|
|
108
84
|
// const [data, setData] = useState();
|
|
109
85
|
// setData();
|
|
110
|
-
case
|
|
86
|
+
case AST_NODE_TYPES.Identifier: {
|
|
111
87
|
return isIdFromUseStateCall(node.callee);
|
|
112
88
|
}
|
|
113
89
|
// const data = useState();
|
|
114
90
|
// data[1]();
|
|
115
|
-
case
|
|
91
|
+
case AST_NODE_TYPES.MemberExpression: {
|
|
116
92
|
if (!("name" in node.callee.object)) {
|
|
117
93
|
return false;
|
|
118
94
|
}
|
|
119
95
|
const property = node.callee.property;
|
|
120
96
|
const propertyScope = context.sourceCode.getScope(node);
|
|
121
|
-
const propertyValue =
|
|
97
|
+
const propertyValue = VAR4.toStaticValue({
|
|
122
98
|
kind: "lazy",
|
|
123
99
|
node: property,
|
|
124
100
|
initialScope: propertyScope
|
|
@@ -132,23 +108,23 @@ function isSetFunctionCall(context, settings) {
|
|
|
132
108
|
};
|
|
133
109
|
}
|
|
134
110
|
function isThenCall(node) {
|
|
135
|
-
return node.callee.type ===
|
|
111
|
+
return node.callee.type === AST_NODE_TYPES.MemberExpression && node.callee.property.type === AST_NODE_TYPES.Identifier && node.callee.property.name === "then";
|
|
136
112
|
}
|
|
137
113
|
function isVariableDeclaratorFromHookCall(node) {
|
|
138
|
-
if (node.type !==
|
|
114
|
+
if (node.type !== AST_NODE_TYPES.VariableDeclarator) {
|
|
139
115
|
return false;
|
|
140
116
|
}
|
|
141
|
-
if (node.id.type !==
|
|
117
|
+
if (node.id.type !== AST_NODE_TYPES.Identifier) {
|
|
142
118
|
return false;
|
|
143
119
|
}
|
|
144
|
-
if (node.init?.type !==
|
|
120
|
+
if (node.init?.type !== AST_NODE_TYPES.CallExpression) {
|
|
145
121
|
return false;
|
|
146
122
|
}
|
|
147
123
|
switch (node.init.callee.type) {
|
|
148
|
-
case
|
|
149
|
-
return
|
|
150
|
-
case
|
|
151
|
-
return node.init.callee.property.type ===
|
|
124
|
+
case AST_NODE_TYPES.Identifier:
|
|
125
|
+
return ER7.isReactHookName(node.init.callee.name);
|
|
126
|
+
case AST_NODE_TYPES.MemberExpression:
|
|
127
|
+
return node.init.callee.property.type === AST_NODE_TYPES.Identifier && ER7.isReactHookName(node.init.callee.property.name);
|
|
152
128
|
default:
|
|
153
129
|
return false;
|
|
154
130
|
}
|
|
@@ -157,13 +133,13 @@ function isVariableDeclaratorFromHookCall(node) {
|
|
|
157
133
|
// src/hooks/use-no-direct-set-state-in-use-effect.ts
|
|
158
134
|
function useNoDirectSetStateInUseEffect(context, options) {
|
|
159
135
|
const { onViolation, useEffectKind } = options;
|
|
160
|
-
const settings =
|
|
136
|
+
const settings = getSettingsFromContext(context);
|
|
161
137
|
const hooks = settings.additionalHooks;
|
|
162
138
|
const getText = (n) => context.sourceCode.getText(n);
|
|
163
|
-
const isUseEffectLikeCall =
|
|
164
|
-
const isUseStateCall =
|
|
165
|
-
const isUseMemoCall =
|
|
166
|
-
const isUseCallbackCall =
|
|
139
|
+
const isUseEffectLikeCall = ER7.isReactHookCallWithNameAlias(context, useEffectKind, hooks[useEffectKind]);
|
|
140
|
+
const isUseStateCall = ER7.isReactHookCallWithNameAlias(context, "useState", hooks.useState);
|
|
141
|
+
const isUseMemoCall = ER7.isReactHookCallWithNameAlias(context, "useMemo", hooks.useMemo);
|
|
142
|
+
const isUseCallbackCall = ER7.isReactHookCallWithNameAlias(context, "useCallback", hooks.useCallback);
|
|
167
143
|
const isSetStateCall = isSetFunctionCall(context, settings);
|
|
168
144
|
const isIdFromUseStateCall = isFromUseStateCall(context, settings);
|
|
169
145
|
const functionEntries = [];
|
|
@@ -183,19 +159,19 @@ function useNoDirectSetStateInUseEffect(context, options) {
|
|
|
183
159
|
}
|
|
184
160
|
};
|
|
185
161
|
function isFunctionOfUseEffectSetup(node) {
|
|
186
|
-
return node.parent?.type ===
|
|
162
|
+
return node.parent?.type === AST_NODE_TYPES.CallExpression && node.parent.callee !== node && isUseEffectLikeCall(node.parent);
|
|
187
163
|
}
|
|
188
164
|
function getCallName(node) {
|
|
189
|
-
if (node.type ===
|
|
190
|
-
return
|
|
165
|
+
if (node.type === AST_NODE_TYPES.CallExpression) {
|
|
166
|
+
return AST.toString(node.callee, getText);
|
|
191
167
|
}
|
|
192
|
-
return
|
|
168
|
+
return AST.toString(node, getText);
|
|
193
169
|
}
|
|
194
170
|
function getCallKind(node) {
|
|
195
|
-
return
|
|
171
|
+
return match(node).when(isUseStateCall, () => "useState").when(isUseEffectLikeCall, () => useEffectKind).when(isSetStateCall, () => "setState").when(isThenCall, () => "then").otherwise(() => "other");
|
|
196
172
|
}
|
|
197
173
|
function getFunctionKind(node) {
|
|
198
|
-
return
|
|
174
|
+
return match(node).when(isFunctionOfUseEffectSetup, () => "setup").when(isFunctionOfImmediatelyInvoked, () => "immediate").otherwise(() => "other");
|
|
199
175
|
}
|
|
200
176
|
return {
|
|
201
177
|
":function"(node) {
|
|
@@ -218,77 +194,77 @@ function useNoDirectSetStateInUseEffect(context, options) {
|
|
|
218
194
|
if (pEntry == null || pEntry.node.async) {
|
|
219
195
|
return;
|
|
220
196
|
}
|
|
221
|
-
|
|
197
|
+
match(getCallKind(node)).with("setState", () => {
|
|
222
198
|
switch (true) {
|
|
223
199
|
case pEntry.node === setupFunction:
|
|
224
|
-
case (pEntry.kind === "immediate" &&
|
|
200
|
+
case (pEntry.kind === "immediate" && AST.findParentNode(pEntry.node, AST.isFunction) === setupFunction): {
|
|
225
201
|
onViolation(context, node, {
|
|
226
202
|
name: context.sourceCode.getText(node.callee)
|
|
227
203
|
});
|
|
228
204
|
return;
|
|
229
205
|
}
|
|
230
206
|
default: {
|
|
231
|
-
const vd =
|
|
232
|
-
if (vd == null)
|
|
233
|
-
else
|
|
207
|
+
const vd = AST.findParentNode(node, isVariableDeclaratorFromHookCall);
|
|
208
|
+
if (vd == null) getOrElseUpdate(indSetStateCalls, pEntry.node, () => []).push(node);
|
|
209
|
+
else getOrElseUpdate(indSetStateCallsInUseMemoOrCallback, vd.init, () => []).push(node);
|
|
234
210
|
}
|
|
235
211
|
}
|
|
236
212
|
}).with(useEffectKind, () => {
|
|
237
|
-
if (
|
|
238
|
-
setupFunctionIdentifiers.push(...
|
|
213
|
+
if (AST.isFunction(node.arguments.at(0))) return;
|
|
214
|
+
setupFunctionIdentifiers.push(...AST.getNestedIdentifiers(node));
|
|
239
215
|
}).with("other", () => {
|
|
240
216
|
if (pEntry.node !== setupFunction) return;
|
|
241
217
|
indFunctionCalls.push(node);
|
|
242
|
-
}).otherwise(
|
|
218
|
+
}).otherwise(constVoid);
|
|
243
219
|
},
|
|
244
220
|
Identifier(node) {
|
|
245
|
-
if (node.parent.type ===
|
|
221
|
+
if (node.parent.type === AST_NODE_TYPES.CallExpression && node.parent.callee === node) {
|
|
246
222
|
return;
|
|
247
223
|
}
|
|
248
224
|
if (!isIdFromUseStateCall(node)) {
|
|
249
225
|
return;
|
|
250
226
|
}
|
|
251
227
|
switch (node.parent.type) {
|
|
252
|
-
case
|
|
228
|
+
case AST_NODE_TYPES.ArrowFunctionExpression: {
|
|
253
229
|
const parent = node.parent.parent;
|
|
254
|
-
if (parent.type !==
|
|
230
|
+
if (parent.type !== AST_NODE_TYPES.CallExpression) {
|
|
255
231
|
break;
|
|
256
232
|
}
|
|
257
233
|
if (!isUseMemoCall(parent)) {
|
|
258
234
|
break;
|
|
259
235
|
}
|
|
260
|
-
const vd =
|
|
236
|
+
const vd = AST.findParentNode(parent, isVariableDeclaratorFromHookCall);
|
|
261
237
|
if (vd != null) {
|
|
262
|
-
|
|
238
|
+
getOrElseUpdate(indSetStateCallsInUseEffectArg0, vd.init, () => []).push(node);
|
|
263
239
|
}
|
|
264
240
|
break;
|
|
265
241
|
}
|
|
266
|
-
case
|
|
242
|
+
case AST_NODE_TYPES.CallExpression: {
|
|
267
243
|
if (node !== node.parent.arguments.at(0)) {
|
|
268
244
|
break;
|
|
269
245
|
}
|
|
270
246
|
if (isUseCallbackCall(node.parent)) {
|
|
271
|
-
const vd =
|
|
247
|
+
const vd = AST.findParentNode(node.parent, isVariableDeclaratorFromHookCall);
|
|
272
248
|
if (vd != null) {
|
|
273
|
-
|
|
249
|
+
getOrElseUpdate(indSetStateCallsInUseEffectArg0, vd.init, () => []).push(node);
|
|
274
250
|
}
|
|
275
251
|
break;
|
|
276
252
|
}
|
|
277
253
|
if (isUseEffectLikeCall(node.parent)) {
|
|
278
|
-
|
|
254
|
+
getOrElseUpdate(indSetStateCallsInUseEffectSetup, node.parent, () => []).push(node);
|
|
279
255
|
}
|
|
280
256
|
}
|
|
281
257
|
}
|
|
282
258
|
},
|
|
283
259
|
"Program:exit"() {
|
|
284
260
|
const getSetStateCalls = (id, initialScope) => {
|
|
285
|
-
const node =
|
|
261
|
+
const node = VAR4.getVariableInitNode(VAR4.findVariable(id, initialScope), 0);
|
|
286
262
|
switch (node?.type) {
|
|
287
|
-
case
|
|
288
|
-
case
|
|
289
|
-
case
|
|
263
|
+
case AST_NODE_TYPES.ArrowFunctionExpression:
|
|
264
|
+
case AST_NODE_TYPES.FunctionDeclaration:
|
|
265
|
+
case AST_NODE_TYPES.FunctionExpression:
|
|
290
266
|
return indSetStateCalls.get(node) ?? [];
|
|
291
|
-
case
|
|
267
|
+
case AST_NODE_TYPES.CallExpression:
|
|
292
268
|
return indSetStateCallsInUseMemoOrCallback.get(node) ?? indSetStateCallsInUseEffectArg0.get(node) ?? [];
|
|
293
269
|
}
|
|
294
270
|
return [];
|
|
@@ -405,11 +381,11 @@ var no_unnecessary_use_callback_default = createRule({
|
|
|
405
381
|
});
|
|
406
382
|
function create3(context) {
|
|
407
383
|
if (!context.sourceCode.text.includes("use")) return {};
|
|
408
|
-
const alias =
|
|
409
|
-
const isUseCallbackCall =
|
|
384
|
+
const alias = getSettingsFromContext(context).additionalHooks.useCallback ?? [];
|
|
385
|
+
const isUseCallbackCall = ER7.isReactHookCallWithNameAlias(context, "useCallback", alias);
|
|
410
386
|
return {
|
|
411
387
|
CallExpression(node) {
|
|
412
|
-
if (!
|
|
388
|
+
if (!ER7.isReactHookCall(node)) {
|
|
413
389
|
return;
|
|
414
390
|
}
|
|
415
391
|
const initialScope = context.sourceCode.getScope(node);
|
|
@@ -418,17 +394,17 @@ function create3(context) {
|
|
|
418
394
|
}
|
|
419
395
|
const scope = context.sourceCode.getScope(node);
|
|
420
396
|
const component = scope.block;
|
|
421
|
-
if (!
|
|
397
|
+
if (!AST.isFunction(component)) {
|
|
422
398
|
return;
|
|
423
399
|
}
|
|
424
400
|
const [arg0, arg1] = node.arguments;
|
|
425
401
|
if (arg0 == null || arg1 == null) {
|
|
426
402
|
return;
|
|
427
403
|
}
|
|
428
|
-
const hasEmptyDeps =
|
|
429
|
-
const variable =
|
|
430
|
-
const variableNode =
|
|
431
|
-
if (variableNode?.type !==
|
|
404
|
+
const hasEmptyDeps = match(arg1).with({ type: AST_NODE_TYPES.ArrayExpression }, (n) => n.elements.length === 0).with({ type: AST_NODE_TYPES.Identifier }, (n) => {
|
|
405
|
+
const variable = VAR4.findVariable(n.name, initialScope);
|
|
406
|
+
const variableNode = VAR4.getVariableInitNode(variable, 0);
|
|
407
|
+
if (variableNode?.type !== AST_NODE_TYPES.ArrayExpression) {
|
|
432
408
|
return false;
|
|
433
409
|
}
|
|
434
410
|
return variableNode.elements.length === 0;
|
|
@@ -436,22 +412,22 @@ function create3(context) {
|
|
|
436
412
|
if (!hasEmptyDeps) {
|
|
437
413
|
return;
|
|
438
414
|
}
|
|
439
|
-
const arg0Node =
|
|
440
|
-
if (n.body.type ===
|
|
415
|
+
const arg0Node = match(arg0).with({ type: AST_NODE_TYPES.ArrowFunctionExpression }, (n) => {
|
|
416
|
+
if (n.body.type === AST_NODE_TYPES.ArrowFunctionExpression) {
|
|
441
417
|
return n.body;
|
|
442
418
|
}
|
|
443
419
|
return n;
|
|
444
|
-
}).with({ type:
|
|
445
|
-
const variable =
|
|
446
|
-
const variableNode =
|
|
447
|
-
if (variableNode?.type !==
|
|
420
|
+
}).with({ type: AST_NODE_TYPES.FunctionExpression }, identity).with({ type: AST_NODE_TYPES.Identifier }, (n) => {
|
|
421
|
+
const variable = VAR4.findVariable(n.name, initialScope);
|
|
422
|
+
const variableNode = VAR4.getVariableInitNode(variable, 0);
|
|
423
|
+
if (variableNode?.type !== AST_NODE_TYPES.ArrowFunctionExpression && variableNode?.type !== AST_NODE_TYPES.FunctionExpression) {
|
|
448
424
|
return null;
|
|
449
425
|
}
|
|
450
426
|
return variableNode;
|
|
451
427
|
}).otherwise(() => null);
|
|
452
428
|
if (arg0Node == null) return;
|
|
453
429
|
const arg0NodeScope = context.sourceCode.getScope(arg0Node);
|
|
454
|
-
const arg0NodeReferences =
|
|
430
|
+
const arg0NodeReferences = VAR4.getChidScopes(arg0NodeScope).flatMap((x) => x.references);
|
|
455
431
|
const isReferencedToComponentScope = arg0NodeReferences.some((x) => x.resolved?.scope.block === component);
|
|
456
432
|
if (!isReferencedToComponentScope) {
|
|
457
433
|
context.report({
|
|
@@ -484,11 +460,11 @@ var no_unnecessary_use_memo_default = createRule({
|
|
|
484
460
|
});
|
|
485
461
|
function create4(context) {
|
|
486
462
|
if (!context.sourceCode.text.includes("use")) return {};
|
|
487
|
-
const alias =
|
|
488
|
-
const isUseMemoCall =
|
|
463
|
+
const alias = getSettingsFromContext(context).additionalHooks.useMemo ?? [];
|
|
464
|
+
const isUseMemoCall = ER7.isReactHookCallWithNameAlias(context, "useMemo", alias);
|
|
489
465
|
return {
|
|
490
466
|
CallExpression(node) {
|
|
491
|
-
if (!
|
|
467
|
+
if (!ER7.isReactHookCall(node)) {
|
|
492
468
|
return;
|
|
493
469
|
}
|
|
494
470
|
const initialScope = context.sourceCode.getScope(node);
|
|
@@ -497,21 +473,21 @@ function create4(context) {
|
|
|
497
473
|
}
|
|
498
474
|
const scope = context.sourceCode.getScope(node);
|
|
499
475
|
const component = scope.block;
|
|
500
|
-
if (!
|
|
476
|
+
if (!AST.isFunction(component)) {
|
|
501
477
|
return;
|
|
502
478
|
}
|
|
503
479
|
const [arg0, arg1] = node.arguments;
|
|
504
480
|
if (arg0 == null || arg1 == null) {
|
|
505
481
|
return;
|
|
506
482
|
}
|
|
507
|
-
const hasCallInArg0 =
|
|
483
|
+
const hasCallInArg0 = AST.isFunction(arg0) && [...AST.getNestedCallExpressions(arg0.body), ...AST.getNestedNewExpressions(arg0.body)].length > 0;
|
|
508
484
|
if (hasCallInArg0) {
|
|
509
485
|
return;
|
|
510
486
|
}
|
|
511
|
-
const hasEmptyDeps =
|
|
512
|
-
const variable =
|
|
513
|
-
const variableNode =
|
|
514
|
-
if (variableNode?.type !==
|
|
487
|
+
const hasEmptyDeps = match(arg1).with({ type: AST_NODE_TYPES.ArrayExpression }, (n) => n.elements.length === 0).with({ type: AST_NODE_TYPES.Identifier }, (n) => {
|
|
488
|
+
const variable = VAR4.findVariable(n.name, initialScope);
|
|
489
|
+
const variableNode = VAR4.getVariableInitNode(variable, 0);
|
|
490
|
+
if (variableNode?.type !== AST_NODE_TYPES.ArrayExpression) {
|
|
515
491
|
return false;
|
|
516
492
|
}
|
|
517
493
|
return variableNode.elements.length === 0;
|
|
@@ -519,22 +495,22 @@ function create4(context) {
|
|
|
519
495
|
if (!hasEmptyDeps) {
|
|
520
496
|
return;
|
|
521
497
|
}
|
|
522
|
-
const arg0Node =
|
|
523
|
-
if (n.body.type ===
|
|
498
|
+
const arg0Node = match(arg0).with({ type: AST_NODE_TYPES.ArrowFunctionExpression }, (n) => {
|
|
499
|
+
if (n.body.type === AST_NODE_TYPES.ArrowFunctionExpression) {
|
|
524
500
|
return n.body;
|
|
525
501
|
}
|
|
526
502
|
return n;
|
|
527
|
-
}).with({ type:
|
|
528
|
-
const variable =
|
|
529
|
-
const variableNode =
|
|
530
|
-
if (variableNode?.type !==
|
|
503
|
+
}).with({ type: AST_NODE_TYPES.FunctionExpression }, identity).with({ type: AST_NODE_TYPES.Identifier }, (n) => {
|
|
504
|
+
const variable = VAR4.findVariable(n.name, initialScope);
|
|
505
|
+
const variableNode = VAR4.getVariableInitNode(variable, 0);
|
|
506
|
+
if (variableNode?.type !== AST_NODE_TYPES.ArrowFunctionExpression && variableNode?.type !== AST_NODE_TYPES.FunctionExpression) {
|
|
531
507
|
return null;
|
|
532
508
|
}
|
|
533
509
|
return variableNode;
|
|
534
510
|
}).otherwise(() => null);
|
|
535
511
|
if (arg0Node == null) return;
|
|
536
512
|
const arg0NodeScope = context.sourceCode.getScope(arg0Node);
|
|
537
|
-
const arg0NodeReferences =
|
|
513
|
+
const arg0NodeReferences = VAR4.getChidScopes(arg0NodeScope).flatMap((x) => x.references);
|
|
538
514
|
const isReferencedToComponentScope = arg0NodeReferences.some((x) => x.resolved?.scope.block === component);
|
|
539
515
|
if (!isReferencedToComponentScope) {
|
|
540
516
|
context.report({
|
|
@@ -570,7 +546,7 @@ var no_unnecessary_use_prefix_default = createRule({
|
|
|
570
546
|
defaultOptions: []
|
|
571
547
|
});
|
|
572
548
|
function create5(context) {
|
|
573
|
-
const { ctx, listeners } =
|
|
549
|
+
const { ctx, listeners } = ER7.useHookCollector();
|
|
574
550
|
return {
|
|
575
551
|
...listeners,
|
|
576
552
|
"Program:exit"(program) {
|
|
@@ -579,7 +555,7 @@ function create5(context) {
|
|
|
579
555
|
if (WELL_KNOWN_HOOKS.includes(name3)) {
|
|
580
556
|
continue;
|
|
581
557
|
}
|
|
582
|
-
if (
|
|
558
|
+
if (AST.isEmptyFunction(node)) {
|
|
583
559
|
continue;
|
|
584
560
|
}
|
|
585
561
|
if (hookCalls.length > 0) {
|
|
@@ -610,7 +586,7 @@ function create5(context) {
|
|
|
610
586
|
};
|
|
611
587
|
}
|
|
612
588
|
function getPreferredLoc(context, id) {
|
|
613
|
-
if (
|
|
589
|
+
if (AST.isMultiLine(id)) return id.loc;
|
|
614
590
|
if (!context.sourceCode.getText(id).startsWith("use")) return id.loc;
|
|
615
591
|
return {
|
|
616
592
|
end: {
|
|
@@ -649,11 +625,11 @@ var prefer_use_state_lazy_initialization_default = createRule({
|
|
|
649
625
|
defaultOptions: []
|
|
650
626
|
});
|
|
651
627
|
function create6(context) {
|
|
652
|
-
const alias =
|
|
653
|
-
const isUseStateCall =
|
|
628
|
+
const alias = getSettingsFromContext(context).additionalHooks.useState ?? [];
|
|
629
|
+
const isUseStateCall = ER7.isReactHookCallWithNameAlias(context, "useState", alias);
|
|
654
630
|
return {
|
|
655
631
|
CallExpression(node) {
|
|
656
|
-
if (!
|
|
632
|
+
if (!ER7.isReactHookCall(node)) {
|
|
657
633
|
return;
|
|
658
634
|
}
|
|
659
635
|
if (!isUseStateCall(node)) {
|
|
@@ -663,20 +639,20 @@ function create6(context) {
|
|
|
663
639
|
if (useStateInput == null) {
|
|
664
640
|
return;
|
|
665
641
|
}
|
|
666
|
-
for (const expr of
|
|
642
|
+
for (const expr of AST.getNestedNewExpressions(useStateInput)) {
|
|
667
643
|
if (!("name" in expr.callee)) continue;
|
|
668
644
|
if (ALLOW_LIST.includes(expr.callee.name)) continue;
|
|
669
|
-
if (
|
|
645
|
+
if (AST.findParentNode(expr, (n) => ER7.isUseCall(context, n)) != null) continue;
|
|
670
646
|
context.report({
|
|
671
647
|
messageId: "preferUseStateLazyInitialization",
|
|
672
648
|
node: expr
|
|
673
649
|
});
|
|
674
650
|
}
|
|
675
|
-
for (const expr of
|
|
651
|
+
for (const expr of AST.getNestedCallExpressions(useStateInput)) {
|
|
676
652
|
if (!("name" in expr.callee)) continue;
|
|
677
|
-
if (
|
|
653
|
+
if (ER7.isReactHookName(expr.callee.name)) continue;
|
|
678
654
|
if (ALLOW_LIST.includes(expr.callee.name)) continue;
|
|
679
|
-
if (
|
|
655
|
+
if (AST.findParentNode(expr, (n) => ER7.isUseCall(context, n)) != null) continue;
|
|
680
656
|
context.report({
|
|
681
657
|
messageId: "preferUseStateLazyInitialization",
|
|
682
658
|
node: expr
|
|
@@ -698,18 +674,7 @@ var plugin = {
|
|
|
698
674
|
"no-unnecessary-use-callback": no_unnecessary_use_callback_default,
|
|
699
675
|
"no-unnecessary-use-memo": no_unnecessary_use_memo_default,
|
|
700
676
|
"no-unnecessary-use-prefix": no_unnecessary_use_prefix_default,
|
|
701
|
-
"prefer-use-state-lazy-initialization": prefer_use_state_lazy_initialization_default
|
|
702
|
-
// Part: deprecated rules
|
|
703
|
-
/** @deprecated Use `no-unnecessary-use-prefix` instead */
|
|
704
|
-
"ensure-custom-hooks-using-other-hooks": no_unnecessary_use_prefix_default,
|
|
705
|
-
/** @deprecated Use `no-unnecessary-use-callback` instead */
|
|
706
|
-
"ensure-use-callback-has-non-empty-deps": no_unnecessary_use_callback_default,
|
|
707
|
-
/** @deprecated Use `no-unnecessary-use-memo` instead */
|
|
708
|
-
"ensure-use-memo-has-non-empty-deps": no_unnecessary_use_memo_default,
|
|
709
|
-
/** @deprecated Use `no-unnecessary-use-prefix` instead */
|
|
710
|
-
"no-redundant-custom-hook": no_unnecessary_use_prefix_default,
|
|
711
|
-
/** @deprecated Use `no-unnecessary-use-prefix` instead */
|
|
712
|
-
"no-useless-custom-hooks": no_unnecessary_use_prefix_default
|
|
677
|
+
"prefer-use-state-lazy-initialization": prefer_use_state_lazy_initialization_default
|
|
713
678
|
}
|
|
714
679
|
};
|
|
715
680
|
|
|
@@ -736,4 +701,4 @@ var index_default = {
|
|
|
736
701
|
}
|
|
737
702
|
};
|
|
738
703
|
|
|
739
|
-
|
|
704
|
+
export { index_default as default };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eslint-plugin-react-hooks-extra",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0-next.1",
|
|
4
4
|
"description": "ESLint React's ESLint plugin for React Hooks related rules.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react",
|
|
@@ -23,22 +23,14 @@
|
|
|
23
23
|
"license": "MIT",
|
|
24
24
|
"author": "Rel1cx<rel1cx@proton.me>",
|
|
25
25
|
"sideEffects": false,
|
|
26
|
+
"type": "module",
|
|
26
27
|
"exports": {
|
|
27
28
|
".": {
|
|
28
|
-
"
|
|
29
|
-
|
|
30
|
-
"default": "./dist/index.mjs"
|
|
31
|
-
},
|
|
32
|
-
"require": {
|
|
33
|
-
"types": "./dist/index.d.ts",
|
|
34
|
-
"default": "./dist/index.js"
|
|
35
|
-
}
|
|
29
|
+
"types": "./dist/index.d.ts",
|
|
30
|
+
"import": "./dist/index.js"
|
|
36
31
|
},
|
|
37
32
|
"./package.json": "./package.json"
|
|
38
33
|
},
|
|
39
|
-
"main": "dist/index.js",
|
|
40
|
-
"module": "dist/index.mjs",
|
|
41
|
-
"types": "dist/index.d.ts",
|
|
42
34
|
"files": [
|
|
43
35
|
"dist",
|
|
44
36
|
"./package.json"
|
|
@@ -50,12 +42,12 @@
|
|
|
50
42
|
"@typescript-eslint/utils": "^8.30.1",
|
|
51
43
|
"string-ts": "^2.2.1",
|
|
52
44
|
"ts-pattern": "^5.7.0",
|
|
53
|
-
"@eslint-react/
|
|
54
|
-
"@eslint-react/
|
|
55
|
-
"@eslint-react/
|
|
56
|
-
"@eslint-react/
|
|
57
|
-
"@eslint-react/shared": "
|
|
58
|
-
"@eslint-react/var": "
|
|
45
|
+
"@eslint-react/ast": "2.0.0-next.1",
|
|
46
|
+
"@eslint-react/core": "2.0.0-next.1",
|
|
47
|
+
"@eslint-react/eff": "2.0.0-next.1",
|
|
48
|
+
"@eslint-react/kit": "2.0.0-next.1",
|
|
49
|
+
"@eslint-react/shared": "2.0.0-next.1",
|
|
50
|
+
"@eslint-react/var": "2.0.0-next.1"
|
|
59
51
|
},
|
|
60
52
|
"devDependencies": {
|
|
61
53
|
"@types/react": "^19.1.2",
|
|
@@ -64,8 +56,8 @@
|
|
|
64
56
|
"@local/configs": "0.0.0"
|
|
65
57
|
},
|
|
66
58
|
"peerDependencies": {
|
|
67
|
-
"eslint": "^
|
|
68
|
-
"typescript": "^4.9.5 || ^5.
|
|
59
|
+
"eslint": "^9.24.0",
|
|
60
|
+
"typescript": "^4.9.5 || ^5.4.5"
|
|
69
61
|
},
|
|
70
62
|
"peerDependenciesMeta": {
|
|
71
63
|
"eslint": {
|
|
@@ -77,7 +69,7 @@
|
|
|
77
69
|
},
|
|
78
70
|
"engines": {
|
|
79
71
|
"bun": ">=1.0.15",
|
|
80
|
-
"node": ">=
|
|
72
|
+
"node": ">=20.19.0"
|
|
81
73
|
},
|
|
82
74
|
"publishConfig": {
|
|
83
75
|
"access": "public"
|