eslint-plugin-react-hooks-extra 2.0.0-next.58 → 2.0.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/README.md CHANGED
@@ -17,28 +17,30 @@ npm install --save-dev eslint-plugin-react-hooks-extra
17
17
  // @ts-check
18
18
  import js from "@eslint/js";
19
19
  import reactHooksExtra from "eslint-plugin-react-hooks-extra";
20
+ import { defineConfig } from "eslint/config";
20
21
  import tseslint from "typescript-eslint";
21
22
 
22
- export default tseslint.config({
23
- files: ["**/*.ts", "**/*.tsx"],
24
- extends: [
25
- js.configs.recommended,
26
- tseslint.configs.recommended,
27
- reactHooksExtra.configs.recommended,
28
- ],
29
- languageOptions: {
30
- parser: tseslint.parser,
31
- parserOptions: {
32
- projectService: true,
33
- tsconfigRootDir: import.meta.dirname,
23
+ export default defineConfig([
24
+ {
25
+ files: ["**/*.ts", "**/*.tsx"],
26
+ extends: [
27
+ js.configs.recommended,
28
+ tseslint.configs.recommended,
29
+ reactHooksExtra.configs.recommended,
30
+ ],
31
+ languageOptions: {
32
+ parser: tseslint.parser,
33
+ parserOptions: {
34
+ projectService: true,
35
+ tsconfigRootDir: import.meta.dirname,
36
+ },
37
+ },
38
+ rules: {
39
+ // Put rules you want to override here
40
+ "react-hooks-extra/no-direct-set-state-in-use-effect": "warn",
34
41
  },
35
42
  },
36
- rules: {
37
- // Put rules you want to override here
38
- "react-hooks-extra/no-unnecessary-use-prefix": "warn",
39
- "react-hooks-extra/prefer-use-state-lazy-initialization": "warn",
40
- },
41
- });
43
+ ]);
42
44
  ```
43
45
 
44
46
  ## Rules
package/dist/index.d.ts CHANGED
@@ -1,45 +1,20 @@
1
- import * as _typescript_eslint_utils_ts_eslint from '@typescript-eslint/utils/ts-eslint';
2
- import { RulePreset } from '@eslint-react/kit';
1
+ import * as _eslint_react_kit0 from "@eslint-react/kit";
3
2
 
3
+ //#region src/index.d.ts
4
4
  declare const _default: {
5
- configs: {
6
- recommended: {
7
- plugins: {
8
- "react-hooks-extra": {
9
- readonly meta: {
10
- readonly name: string;
11
- readonly version: string;
12
- };
13
- readonly rules: {
14
- readonly "no-direct-set-state-in-use-effect": _typescript_eslint_utils_ts_eslint.RuleModule<"noDirectSetStateInUseEffect", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
15
- readonly "no-direct-set-state-in-use-layout-effect": _typescript_eslint_utils_ts_eslint.RuleModule<"noDirectSetStateInUseLayoutEffect", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
16
- readonly "no-unnecessary-use-callback": _typescript_eslint_utils_ts_eslint.RuleModule<"noUnnecessaryUseCallback", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
17
- readonly "no-unnecessary-use-memo": _typescript_eslint_utils_ts_eslint.RuleModule<"noUnnecessaryUseMemo", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
18
- readonly "no-unnecessary-use-prefix": _typescript_eslint_utils_ts_eslint.RuleModule<"noUnnecessaryUsePrefix", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
19
- readonly "prefer-use-state-lazy-initialization": _typescript_eslint_utils_ts_eslint.RuleModule<"preferUseStateLazyInitialization", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
20
- };
21
- };
22
- };
23
- name: string;
24
- rules: RulePreset;
25
- };
26
- "recommended-legacy": {
27
- plugins: string[];
28
- rules: RulePreset;
29
- };
30
- };
31
- meta: {
32
- readonly name: string;
33
- readonly version: string;
34
- };
35
- rules: {
36
- readonly "no-direct-set-state-in-use-effect": _typescript_eslint_utils_ts_eslint.RuleModule<"noDirectSetStateInUseEffect", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
37
- readonly "no-direct-set-state-in-use-layout-effect": _typescript_eslint_utils_ts_eslint.RuleModule<"noDirectSetStateInUseLayoutEffect", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
38
- readonly "no-unnecessary-use-callback": _typescript_eslint_utils_ts_eslint.RuleModule<"noUnnecessaryUseCallback", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
39
- readonly "no-unnecessary-use-memo": _typescript_eslint_utils_ts_eslint.RuleModule<"noUnnecessaryUseMemo", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
40
- readonly "no-unnecessary-use-prefix": _typescript_eslint_utils_ts_eslint.RuleModule<"noUnnecessaryUsePrefix", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
41
- readonly "prefer-use-state-lazy-initialization": _typescript_eslint_utils_ts_eslint.RuleModule<"preferUseStateLazyInitialization", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
5
+ configs: {
6
+ recommended: {
7
+ plugins: {};
8
+ name?: string;
9
+ rules?: Record<string, _eslint_react_kit0.RuleConfig>;
10
+ settings?: _eslint_react_kit0.SettingsConfig;
42
11
  };
12
+ };
13
+ meta: {
14
+ name: string;
15
+ version: string;
16
+ };
17
+ rules: Record<string, _eslint_react_kit0.CompatibleRule>;
43
18
  };
44
-
45
- export { _default as default };
19
+ //#endregion
20
+ export { _default as default };
package/dist/index.js CHANGED
@@ -1,816 +1,266 @@
1
- import * as AST from '@eslint-react/ast';
2
- import * as ER from '@eslint-react/core';
3
- import { identity, getOrElseUpdate, constVoid, not } from '@eslint-react/eff';
4
- import { getDocsUrl, getSettingsFromContext } from '@eslint-react/shared';
5
- import * as VAR 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';
1
+ import { getConfigAdapters, getDocsUrl } from "@eslint-react/shared";
2
+ import * as AST from "@eslint-react/ast";
3
+ import * as ER from "@eslint-react/core";
4
+ import { constVoid, getOrElseUpdate, not } from "@eslint-react/eff";
5
+ import * as VAR from "@eslint-react/var";
6
+ import { AST_NODE_TYPES } from "@typescript-eslint/types";
7
+ import { getStaticValue } from "@typescript-eslint/utils/ast-utils";
8
+ import { match } from "ts-pattern";
9
+ import { ESLintUtils } from "@typescript-eslint/utils";
9
10
 
11
+ //#region rolldown:runtime
10
12
  var __defProp = Object.defineProperty;
11
- var __export = (target, all) => {
12
- for (var name3 in all)
13
- __defProp(target, name3, { get: all[name3], enumerable: true });
13
+ var __export = (all) => {
14
+ let target = {};
15
+ for (var name$2 in all) __defProp(target, name$2, {
16
+ get: all[name$2],
17
+ enumerable: true
18
+ });
19
+ return target;
14
20
  };
15
21
 
16
- // src/configs/recommended.ts
17
- var recommended_exports = {};
18
- __export(recommended_exports, {
19
- name: () => name,
20
- rules: () => rules
22
+ //#endregion
23
+ //#region src/configs/recommended.ts
24
+ var recommended_exports = /* @__PURE__ */ __export({
25
+ name: () => name$1,
26
+ rules: () => rules
21
27
  });
22
- var name = "react-hooks-extra/recommended";
23
- var rules = {
24
- "react-hooks-extra/no-direct-set-state-in-use-effect": "warn",
25
- "react-hooks-extra/no-unnecessary-use-prefix": "warn"
26
- };
28
+ const name$1 = "react-hooks-extra/recommended";
29
+ const rules = { "react-hooks-extra/no-direct-set-state-in-use-effect": "warn" };
27
30
 
28
- // package.json
29
- var name2 = "eslint-plugin-react-hooks-extra";
30
- var version = "2.0.0-next.58";
31
- function useNoDirectSetStateInUseEffect(context, options) {
32
- const { onViolation, useEffectKind } = options;
33
- const settings = getSettingsFromContext(context);
34
- const hooks = settings.additionalHooks;
35
- const getText = (n) => context.sourceCode.getText(n);
36
- const isUseEffectLikeCall = ER.isReactHookCallWithNameAlias(context, useEffectKind, hooks[useEffectKind]);
37
- const isUseStateCall = ER.isReactHookCallWithNameAlias(context, "useState", hooks.useState);
38
- const isUseMemoCall = ER.isReactHookCallWithNameAlias(context, "useMemo", hooks.useMemo);
39
- const isUseCallbackCall = ER.isReactHookCallWithNameAlias(context, "useCallback", hooks.useCallback);
40
- const functionEntries = [];
41
- const setupFunctionRef = { current: null };
42
- const setupFunctionIdentifiers = [];
43
- const indFunctionCalls = [];
44
- const indSetStateCalls = /* @__PURE__ */ new WeakMap();
45
- const indSetStateCallsInUseEffectArg0 = /* @__PURE__ */ new WeakMap();
46
- const indSetStateCallsInUseEffectSetup = /* @__PURE__ */ new Map();
47
- const indSetStateCallsInUseMemoOrCallback = /* @__PURE__ */ new WeakMap();
48
- const onSetupFunctionEnter = (node) => {
49
- setupFunctionRef.current = node;
50
- };
51
- const onSetupFunctionExit = (node) => {
52
- if (setupFunctionRef.current === node) {
53
- setupFunctionRef.current = null;
54
- }
55
- };
56
- function isFunctionOfUseEffectSetup(node) {
57
- return node.parent?.type === AST_NODE_TYPES.CallExpression && node.parent.callee !== node && isUseEffectLikeCall(node.parent);
58
- }
59
- function getCallName(node) {
60
- if (node.type === AST_NODE_TYPES.CallExpression) {
61
- return AST.toStringFormat(node.callee, getText);
62
- }
63
- return AST.toStringFormat(node, getText);
64
- }
65
- function getCallKind(node) {
66
- return match(node).when(isUseStateCall, () => "useState").when(isUseEffectLikeCall, () => useEffectKind).when(isSetStateCall, () => "setState").when(AST.isThenCall, () => "then").otherwise(() => "other");
67
- }
68
- function getFunctionKind(node) {
69
- const parent = AST.findParentNode(node, not(AST.isTypeExpression)) ?? node.parent;
70
- switch (true) {
71
- case node.async:
72
- case (parent.type === AST_NODE_TYPES.CallExpression && AST.isThenCall(parent)):
73
- return "deferred";
74
- case (node.type !== AST_NODE_TYPES.FunctionDeclaration && parent.type === AST_NODE_TYPES.CallExpression && parent.callee === node):
75
- return "immediate";
76
- case isFunctionOfUseEffectSetup(node):
77
- return "setup";
78
- default:
79
- return "other";
80
- }
81
- }
82
- function isIdFromUseStateCall(topLevelId, at) {
83
- const variable = VAR.findVariable(topLevelId, context.sourceCode.getScope(topLevelId));
84
- const variableNode = VAR.getVariableInitNode(variable, 0);
85
- if (variableNode == null) return false;
86
- if (variableNode.type !== AST_NODE_TYPES.CallExpression) return false;
87
- if (!ER.isReactHookCallWithNameAlias(context, "useState", hooks.useState)(variableNode)) return false;
88
- const variableNodeParent = variableNode.parent;
89
- if (!("id" in variableNodeParent) || variableNodeParent.id?.type !== AST_NODE_TYPES.ArrayPattern) {
90
- return true;
91
- }
92
- return variableNodeParent.id.elements.findIndex((e) => e?.type === AST_NODE_TYPES.Identifier && e.name === topLevelId.name) === at;
93
- }
94
- function isSetStateCall(node) {
95
- switch (node.callee.type) {
96
- // const data = useState();
97
- // data.at(1)();
98
- case AST_NODE_TYPES.CallExpression: {
99
- const { callee } = node.callee;
100
- if (callee.type !== AST_NODE_TYPES.MemberExpression) {
101
- return false;
102
- }
103
- if (!("name" in callee.object)) {
104
- return false;
105
- }
106
- const isAt = callee.property.type === AST_NODE_TYPES.Identifier && callee.property.name === "at";
107
- const [index] = node.callee.arguments;
108
- if (!isAt || index == null) {
109
- return false;
110
- }
111
- const indexScope = context.sourceCode.getScope(node);
112
- const indexValue = VAR.toStaticValue({
113
- kind: "lazy",
114
- node: index,
115
- initialScope: indexScope
116
- }).value;
117
- return indexValue === 1 && isIdFromUseStateCall(callee.object);
118
- }
119
- // const [data, setData] = useState();
120
- // setData();
121
- case AST_NODE_TYPES.Identifier: {
122
- return isIdFromUseStateCall(node.callee, 1);
123
- }
124
- // const data = useState();
125
- // data[1]();
126
- case AST_NODE_TYPES.MemberExpression: {
127
- if (!("name" in node.callee.object)) {
128
- return false;
129
- }
130
- const property = node.callee.property;
131
- const propertyScope = context.sourceCode.getScope(node);
132
- const propertyValue = VAR.toStaticValue({
133
- kind: "lazy",
134
- node: property,
135
- initialScope: propertyScope
136
- }).value;
137
- return propertyValue === 1 && isIdFromUseStateCall(node.callee.object, 1);
138
- }
139
- default: {
140
- return false;
141
- }
142
- }
143
- }
144
- return {
145
- ":function"(node) {
146
- const kind = getFunctionKind(node);
147
- functionEntries.push({ kind, node });
148
- if (kind === "setup") {
149
- onSetupFunctionEnter(node);
150
- }
151
- },
152
- ":function:exit"(node) {
153
- const { kind } = functionEntries.at(-1) ?? {};
154
- if (kind === "setup") {
155
- onSetupFunctionExit(node);
156
- }
157
- functionEntries.pop();
158
- },
159
- CallExpression(node) {
160
- const setupFunction = setupFunctionRef.current;
161
- const pEntry = functionEntries.at(-1);
162
- if (pEntry == null || pEntry.node.async) {
163
- return;
164
- }
165
- match(getCallKind(node)).with("setState", () => {
166
- switch (true) {
167
- case pEntry.kind === "deferred":
168
- case pEntry.node.async:
169
- break;
170
- case pEntry.node === setupFunction:
171
- case (pEntry.kind === "immediate" && AST.findParentNode(pEntry.node, AST.isFunction) === setupFunction): {
172
- onViolation(context, node, {
173
- name: context.sourceCode.getText(node.callee)
174
- });
175
- return;
176
- }
177
- default: {
178
- const vd = AST.findParentNode(node, isVariableDeclaratorFromHookCall);
179
- if (vd == null) getOrElseUpdate(indSetStateCalls, pEntry.node, () => []).push(node);
180
- else getOrElseUpdate(indSetStateCallsInUseMemoOrCallback, vd.init, () => []).push(node);
181
- }
182
- }
183
- }).with(useEffectKind, () => {
184
- if (AST.isFunction(node.arguments.at(0))) return;
185
- setupFunctionIdentifiers.push(...AST.getNestedIdentifiers(node));
186
- }).with("other", () => {
187
- if (pEntry.node !== setupFunction) return;
188
- indFunctionCalls.push(node);
189
- }).otherwise(constVoid);
190
- },
191
- Identifier(node) {
192
- if (node.parent.type === AST_NODE_TYPES.CallExpression && node.parent.callee === node) {
193
- return;
194
- }
195
- if (!isIdFromUseStateCall(node, 1)) {
196
- return;
197
- }
198
- switch (node.parent.type) {
199
- case AST_NODE_TYPES.ArrowFunctionExpression: {
200
- const parent = node.parent.parent;
201
- if (parent.type !== AST_NODE_TYPES.CallExpression) {
202
- break;
203
- }
204
- if (!isUseMemoCall(parent)) {
205
- break;
206
- }
207
- const vd = AST.findParentNode(parent, isVariableDeclaratorFromHookCall);
208
- if (vd != null) {
209
- getOrElseUpdate(indSetStateCallsInUseEffectArg0, vd.init, () => []).push(node);
210
- }
211
- break;
212
- }
213
- case AST_NODE_TYPES.CallExpression: {
214
- if (node !== node.parent.arguments.at(0)) {
215
- break;
216
- }
217
- if (isUseCallbackCall(node.parent)) {
218
- const vd = AST.findParentNode(node.parent, isVariableDeclaratorFromHookCall);
219
- if (vd != null) {
220
- getOrElseUpdate(indSetStateCallsInUseEffectArg0, vd.init, () => []).push(node);
221
- }
222
- break;
223
- }
224
- if (isUseEffectLikeCall(node.parent)) {
225
- getOrElseUpdate(indSetStateCallsInUseEffectSetup, node.parent, () => []).push(node);
226
- }
227
- }
228
- }
229
- },
230
- "Program:exit"() {
231
- const getSetStateCalls = (id, initialScope) => {
232
- const node = VAR.getVariableInitNode(VAR.findVariable(id, initialScope), 0);
233
- switch (node?.type) {
234
- case AST_NODE_TYPES.ArrowFunctionExpression:
235
- case AST_NODE_TYPES.FunctionDeclaration:
236
- case AST_NODE_TYPES.FunctionExpression:
237
- return indSetStateCalls.get(node) ?? [];
238
- case AST_NODE_TYPES.CallExpression:
239
- return indSetStateCallsInUseMemoOrCallback.get(node) ?? indSetStateCallsInUseEffectArg0.get(node) ?? [];
240
- }
241
- return [];
242
- };
243
- for (const [, calls] of indSetStateCallsInUseEffectSetup) {
244
- for (const call of calls) {
245
- onViolation(context, call, { name: call.name });
246
- }
247
- }
248
- for (const { callee } of indFunctionCalls) {
249
- if (!("name" in callee)) {
250
- continue;
251
- }
252
- const { name: name3 } = callee;
253
- const setStateCalls = getSetStateCalls(name3, context.sourceCode.getScope(callee));
254
- for (const setStateCall of setStateCalls) {
255
- onViolation(context, setStateCall, {
256
- name: getCallName(setStateCall)
257
- });
258
- }
259
- }
260
- for (const id of setupFunctionIdentifiers) {
261
- const setStateCalls = getSetStateCalls(id.name, context.sourceCode.getScope(id));
262
- for (const setStateCall of setStateCalls) {
263
- onViolation(context, setStateCall, {
264
- name: getCallName(setStateCall)
265
- });
266
- }
267
- }
268
- }
269
- };
270
- }
31
+ //#endregion
32
+ //#region package.json
33
+ var name = "eslint-plugin-react-hooks-extra";
34
+ var version = "2.0.0";
35
+
36
+ //#endregion
37
+ //#region src/utils/create-rule.ts
38
+ const createRule = ESLintUtils.RuleCreator(getDocsUrl("hooks-extra"));
39
+
40
+ //#endregion
41
+ //#region src/utils/is-variable-declarator-from-hook-call.ts
271
42
  function isInitFromHookCall(init) {
272
- if (init?.type !== AST_NODE_TYPES.CallExpression) return false;
273
- switch (init.callee.type) {
274
- case AST_NODE_TYPES.Identifier:
275
- return ER.isReactHookName(init.callee.name);
276
- case AST_NODE_TYPES.MemberExpression:
277
- return init.callee.property.type === AST_NODE_TYPES.Identifier && ER.isReactHookName(init.callee.property.name);
278
- default:
279
- return false;
280
- }
43
+ if (init?.type !== AST_NODE_TYPES.CallExpression) return false;
44
+ switch (init.callee.type) {
45
+ case AST_NODE_TYPES.Identifier: return ER.isReactHookName(init.callee.name);
46
+ case AST_NODE_TYPES.MemberExpression: return init.callee.property.type === AST_NODE_TYPES.Identifier && ER.isReactHookName(init.callee.property.name);
47
+ default: return false;
48
+ }
281
49
  }
282
50
  function isVariableDeclaratorFromHookCall(node) {
283
- if (node.type !== AST_NODE_TYPES.VariableDeclarator) return false;
284
- if (node.id.type !== AST_NODE_TYPES.Identifier) return false;
285
- return isInitFromHookCall(node.init);
51
+ if (node.type !== AST_NODE_TYPES.VariableDeclarator) return false;
52
+ if (node.id.type !== AST_NODE_TYPES.Identifier) return false;
53
+ return isInitFromHookCall(node.init);
286
54
  }
287
- var createRule = ESLintUtils.RuleCreator(getDocsUrl("hooks-extra"));
288
55
 
289
- // src/rules/no-direct-set-state-in-use-effect.ts
290
- var RULE_NAME = "no-direct-set-state-in-use-effect";
291
- var RULE_FEATURES = [
292
- "EXP"
293
- ];
56
+ //#endregion
57
+ //#region src/rules/no-direct-set-state-in-use-effect.ts
58
+ const RULE_NAME = "no-direct-set-state-in-use-effect";
59
+ const RULE_FEATURES = ["EXP"];
294
60
  var no_direct_set_state_in_use_effect_default = createRule({
295
- meta: {
296
- type: "problem",
297
- docs: {
298
- description: "Disallow direct calls to the `set` function of `useState` in `useEffect`.",
299
- [Symbol.for("rule_features")]: RULE_FEATURES
300
- },
301
- messages: {
302
- noDirectSetStateInUseEffect: "Do not call the 'set' function '{{name}}' of 'useState' directly in 'useEffect'."
303
- },
304
- schema: []
305
- },
306
- name: RULE_NAME,
307
- create,
308
- defaultOptions: []
61
+ meta: {
62
+ type: "problem",
63
+ docs: {
64
+ description: "Disallow direct calls to the `set` function of `useState` in `useEffect`.",
65
+ [Symbol.for("rule_features")]: RULE_FEATURES
66
+ },
67
+ messages: { noDirectSetStateInUseEffect: "Do not call the 'set' function '{{name}}' of 'useState' directly in 'useEffect'." },
68
+ schema: []
69
+ },
70
+ name: RULE_NAME,
71
+ create,
72
+ defaultOptions: []
309
73
  });
310
74
  function create(context) {
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 });
315
- },
316
- useEffectKind: "useEffect"
317
- });
75
+ if (!/use\w*Effect/u.test(context.sourceCode.text)) return {};
76
+ const functionEntries = [];
77
+ const setupFnRef = { current: null };
78
+ const setupFnIds = [];
79
+ const trackedFnCalls = [];
80
+ const setStateCallsByFn = /* @__PURE__ */ new WeakMap();
81
+ const setStateInEffectArg = /* @__PURE__ */ new WeakMap();
82
+ const setStateInEffectSetup = /* @__PURE__ */ new Map();
83
+ const setStateInHookCallbacks = /* @__PURE__ */ new WeakMap();
84
+ const getText = (n) => context.sourceCode.getText(n);
85
+ const onSetupFunctionEnter = (node) => {
86
+ setupFnRef.current = node;
87
+ };
88
+ const onSetupFunctionExit = (node) => {
89
+ if (setupFnRef.current === node) setupFnRef.current = null;
90
+ };
91
+ function isFunctionOfUseEffectSetup(node) {
92
+ return node.parent?.type === AST_NODE_TYPES.CallExpression && node.parent.callee !== node && ER.isUseEffectLikeCall(node.parent);
93
+ }
94
+ function getCallName(node) {
95
+ if (node.type === AST_NODE_TYPES.CallExpression) return AST.toStringFormat(node.callee, getText);
96
+ return AST.toStringFormat(node, getText);
97
+ }
98
+ function getCallKind(node) {
99
+ return match(node).when(ER.isUseStateCall, () => "useState").when(ER.isUseEffectLikeCall, () => "useEffect").when(isSetStateCall, () => "setState").when(AST.isThenCall, () => "then").otherwise(() => "other");
100
+ }
101
+ function getFunctionKind(node) {
102
+ const parent = AST.findParentNode(node, not(AST.isTypeExpression)) ?? node.parent;
103
+ switch (true) {
104
+ case node.async:
105
+ case parent.type === AST_NODE_TYPES.CallExpression && AST.isThenCall(parent): return "deferred";
106
+ case node.type !== AST_NODE_TYPES.FunctionDeclaration && parent.type === AST_NODE_TYPES.CallExpression && parent.callee === node: return "immediate";
107
+ case isFunctionOfUseEffectSetup(node): return "setup";
108
+ default: return "other";
109
+ }
110
+ }
111
+ function isIdFromUseStateCall(topLevelId, at) {
112
+ const variable = VAR.findVariable(topLevelId, context.sourceCode.getScope(topLevelId));
113
+ const variableNode = VAR.getVariableDefinitionNode(variable, 0);
114
+ if (variableNode == null) return false;
115
+ if (variableNode.type !== AST_NODE_TYPES.CallExpression) return false;
116
+ if (!ER.isUseStateCall(variableNode)) return false;
117
+ const variableNodeParent = variableNode.parent;
118
+ if (!("id" in variableNodeParent) || variableNodeParent.id?.type !== AST_NODE_TYPES.ArrayPattern) return true;
119
+ return variableNodeParent.id.elements.findIndex((e) => e?.type === AST_NODE_TYPES.Identifier && e.name === topLevelId.name) === at;
120
+ }
121
+ function isSetStateCall(node) {
122
+ switch (node.callee.type) {
123
+ case AST_NODE_TYPES.CallExpression: {
124
+ const { callee } = node.callee;
125
+ if (callee.type !== AST_NODE_TYPES.MemberExpression) return false;
126
+ if (!("name" in callee.object)) return false;
127
+ const isAt = callee.property.type === AST_NODE_TYPES.Identifier && callee.property.name === "at";
128
+ const [index] = node.callee.arguments;
129
+ if (!isAt || index == null) return false;
130
+ const indexScope = context.sourceCode.getScope(node);
131
+ return getStaticValue(index, indexScope)?.value === 1 && isIdFromUseStateCall(callee.object);
132
+ }
133
+ case AST_NODE_TYPES.Identifier: return isIdFromUseStateCall(node.callee, 1);
134
+ case AST_NODE_TYPES.MemberExpression: {
135
+ if (!("name" in node.callee.object)) return false;
136
+ const property = node.callee.property;
137
+ const propertyScope = context.sourceCode.getScope(node);
138
+ return getStaticValue(property, propertyScope)?.value === 1 && isIdFromUseStateCall(node.callee.object, 1);
139
+ }
140
+ default: return false;
141
+ }
142
+ }
143
+ return {
144
+ ":function"(node) {
145
+ const kind = getFunctionKind(node);
146
+ functionEntries.push({
147
+ kind,
148
+ node
149
+ });
150
+ if (kind === "setup") onSetupFunctionEnter(node);
151
+ },
152
+ ":function:exit"(node) {
153
+ const { kind } = functionEntries.at(-1) ?? {};
154
+ if (kind === "setup") onSetupFunctionExit(node);
155
+ functionEntries.pop();
156
+ },
157
+ CallExpression(node) {
158
+ const setupFunction = setupFnRef.current;
159
+ const pEntry = functionEntries.at(-1);
160
+ if (pEntry == null || pEntry.node.async) return;
161
+ match(getCallKind(node)).with("setState", () => {
162
+ switch (true) {
163
+ case pEntry.kind === "deferred":
164
+ case pEntry.node.async: break;
165
+ case pEntry.node === setupFunction:
166
+ case pEntry.kind === "immediate" && AST.findParentNode(pEntry.node, AST.isFunction) === setupFunction:
167
+ context.report({
168
+ messageId: "noDirectSetStateInUseEffect",
169
+ node,
170
+ data: { name: context.sourceCode.getText(node.callee) }
171
+ });
172
+ return;
173
+ default: {
174
+ const init = AST.findParentNode(node, isVariableDeclaratorFromHookCall)?.init;
175
+ if (init == null) getOrElseUpdate(setStateCallsByFn, pEntry.node, () => []).push(node);
176
+ else getOrElseUpdate(setStateInHookCallbacks, init, () => []).push(node);
177
+ }
178
+ }
179
+ }).with("useEffect", () => {
180
+ if (AST.isFunction(node.arguments.at(0))) return;
181
+ setupFnIds.push(...AST.getNestedIdentifiers(node));
182
+ }).with("other", () => {
183
+ if (pEntry.node !== setupFunction) return;
184
+ trackedFnCalls.push(node);
185
+ }).otherwise(constVoid);
186
+ },
187
+ Identifier(node) {
188
+ if (node.parent.type === AST_NODE_TYPES.CallExpression && node.parent.callee === node) return;
189
+ if (!isIdFromUseStateCall(node, 1)) return;
190
+ switch (node.parent.type) {
191
+ case AST_NODE_TYPES.ArrowFunctionExpression: {
192
+ const parent = node.parent.parent;
193
+ if (parent.type !== AST_NODE_TYPES.CallExpression) break;
194
+ if (!ER.isUseMemoCall(parent)) break;
195
+ const init = AST.findParentNode(parent, isVariableDeclaratorFromHookCall)?.init;
196
+ if (init != null) getOrElseUpdate(setStateInEffectArg, init, () => []).push(node);
197
+ break;
198
+ }
199
+ case AST_NODE_TYPES.CallExpression:
200
+ if (node !== node.parent.arguments.at(0)) break;
201
+ if (ER.isUseCallbackCall(node.parent)) {
202
+ const init = AST.findParentNode(node.parent, isVariableDeclaratorFromHookCall)?.init;
203
+ if (init != null) getOrElseUpdate(setStateInEffectArg, init, () => []).push(node);
204
+ break;
205
+ }
206
+ if (ER.isUseEffectLikeCall(node.parent)) getOrElseUpdate(setStateInEffectSetup, node.parent, () => []).push(node);
207
+ }
208
+ },
209
+ "Program:exit"() {
210
+ const getSetStateCalls = (id, initialScope) => {
211
+ const node = VAR.getVariableDefinitionNode(VAR.findVariable(id, initialScope), 0);
212
+ switch (node?.type) {
213
+ case AST_NODE_TYPES.ArrowFunctionExpression:
214
+ case AST_NODE_TYPES.FunctionDeclaration:
215
+ case AST_NODE_TYPES.FunctionExpression: return setStateCallsByFn.get(node) ?? [];
216
+ case AST_NODE_TYPES.CallExpression: return setStateInHookCallbacks.get(node) ?? setStateInEffectArg.get(node) ?? [];
217
+ }
218
+ return [];
219
+ };
220
+ for (const [, calls] of setStateInEffectSetup) for (const call of calls) context.report({
221
+ messageId: "noDirectSetStateInUseEffect",
222
+ node: call,
223
+ data: { name: call.name }
224
+ });
225
+ for (const { callee } of trackedFnCalls) {
226
+ if (!("name" in callee)) continue;
227
+ const { name: name$2 } = callee;
228
+ const setStateCalls = getSetStateCalls(name$2, context.sourceCode.getScope(callee));
229
+ for (const setStateCall of setStateCalls) context.report({
230
+ messageId: "noDirectSetStateInUseEffect",
231
+ node: setStateCall,
232
+ data: { name: getCallName(setStateCall) }
233
+ });
234
+ }
235
+ for (const id of setupFnIds) {
236
+ const setStateCalls = getSetStateCalls(id.name, context.sourceCode.getScope(id));
237
+ for (const setStateCall of setStateCalls) context.report({
238
+ messageId: "noDirectSetStateInUseEffect",
239
+ node: setStateCall,
240
+ data: { name: getCallName(setStateCall) }
241
+ });
242
+ }
243
+ }
244
+ };
318
245
  }
319
246
 
320
- // src/rules/no-direct-set-state-in-use-layout-effect.ts
321
- var RULE_NAME2 = "no-direct-set-state-in-use-layout-effect";
322
- var RULE_FEATURES2 = [
323
- "EXP"
324
- ];
325
- var no_direct_set_state_in_use_layout_effect_default = createRule({
326
- meta: {
327
- type: "problem",
328
- docs: {
329
- description: "Disallow direct calls to the `set` function of `useState` in `useLayoutEffect`.",
330
- [Symbol.for("rule_features")]: RULE_FEATURES2
331
- },
332
- messages: {
333
- noDirectSetStateInUseLayoutEffect: "Do not call the 'set' function '{{name}}' of 'useState' directly in 'useLayoutEffect'."
334
- },
335
- schema: []
336
- },
337
- name: RULE_NAME2,
338
- create: create2,
339
- defaultOptions: []
340
- });
341
- function create2(context) {
342
- if (!/use\w*Effect/u.test(context.sourceCode.text)) return {};
343
- return useNoDirectSetStateInUseEffect(context, {
344
- onViolation(ctx, node, data) {
345
- ctx.report({ messageId: "noDirectSetStateInUseLayoutEffect", node, data });
346
- },
347
- useEffectKind: "useLayoutEffect"
348
- });
349
- }
350
- var RULE_NAME3 = "no-unnecessary-use-callback";
351
- var RULE_FEATURES3 = [
352
- "EXP"
353
- ];
354
- var no_unnecessary_use_callback_default = createRule({
355
- meta: {
356
- type: "problem",
357
- deprecated: {
358
- deprecatedSince: "2.0.0",
359
- replacedBy: [
360
- {
361
- message: "Use the same rule from `eslint-plugin-react-x` or `@eslint-react/eslint-plugin` instead.",
362
- plugin: {
363
- name: "eslint-plugin-react-x",
364
- url: "https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x"
365
- },
366
- rule: {
367
- name: "no-unnecessary-use-callback",
368
- url: "https://next.eslint-react.xyz/docs/rules/no-unnecessary-use-callback"
369
- }
370
- },
371
- {
372
- message: "Use the same rule from `eslint-plugin-react-x` or `@eslint-react/eslint-plugin` instead.",
373
- plugin: {
374
- name: "@eslint-react/eslint-plugin",
375
- url: "https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin"
376
- },
377
- rule: {
378
- name: "no-unnecessary-use-callback",
379
- url: "https://next.eslint-react.xyz/docs/rules/no-unnecessary-use-callback"
380
- }
381
- }
382
- ]
383
- },
384
- docs: {
385
- description: "Disallow unnecessary usage of `useCallback`.",
386
- [Symbol.for("rule_features")]: RULE_FEATURES3
387
- },
388
- messages: {
389
- noUnnecessaryUseCallback: "An 'useCallback' with empty deps and no references to the component scope may be unnecessary."
390
- },
391
- schema: []
392
- },
393
- name: RULE_NAME3,
394
- create: create3,
395
- defaultOptions: []
396
- });
397
- function create3(context) {
398
- if (!context.sourceCode.text.includes("use")) return {};
399
- const alias = getSettingsFromContext(context).additionalHooks.useCallback ?? [];
400
- const isUseCallbackCall = ER.isReactHookCallWithNameAlias(context, "useCallback", alias);
401
- return {
402
- CallExpression(node) {
403
- if (!ER.isReactHookCall(node)) {
404
- return;
405
- }
406
- const initialScope = context.sourceCode.getScope(node);
407
- if (!isUseCallbackCall(node)) {
408
- return;
409
- }
410
- const scope = context.sourceCode.getScope(node);
411
- const component = scope.block;
412
- if (!AST.isFunction(component)) {
413
- return;
414
- }
415
- const [arg0, arg1] = node.arguments;
416
- if (arg0 == null || arg1 == null) {
417
- return;
418
- }
419
- const hasEmptyDeps = match(arg1).with({ type: AST_NODE_TYPES.ArrayExpression }, (n) => n.elements.length === 0).with({ type: AST_NODE_TYPES.Identifier }, (n) => {
420
- const variable = VAR.findVariable(n.name, initialScope);
421
- const variableNode = VAR.getVariableInitNode(variable, 0);
422
- if (variableNode?.type !== AST_NODE_TYPES.ArrayExpression) {
423
- return false;
424
- }
425
- return variableNode.elements.length === 0;
426
- }).otherwise(() => false);
427
- if (!hasEmptyDeps) {
428
- return;
429
- }
430
- const arg0Node = match(arg0).with({ type: AST_NODE_TYPES.ArrowFunctionExpression }, (n) => {
431
- if (n.body.type === AST_NODE_TYPES.ArrowFunctionExpression) {
432
- return n.body;
433
- }
434
- return n;
435
- }).with({ type: AST_NODE_TYPES.FunctionExpression }, identity).with({ type: AST_NODE_TYPES.Identifier }, (n) => {
436
- const variable = VAR.findVariable(n.name, initialScope);
437
- const variableNode = VAR.getVariableInitNode(variable, 0);
438
- if (variableNode?.type !== AST_NODE_TYPES.ArrowFunctionExpression && variableNode?.type !== AST_NODE_TYPES.FunctionExpression) {
439
- return null;
440
- }
441
- return variableNode;
442
- }).otherwise(() => null);
443
- if (arg0Node == null) return;
444
- const arg0NodeScope = context.sourceCode.getScope(arg0Node);
445
- const arg0NodeReferences = VAR.getChidScopes(arg0NodeScope).flatMap((x) => x.references);
446
- const isReferencedToComponentScope = arg0NodeReferences.some((x) => x.resolved?.scope.block === component);
447
- if (!isReferencedToComponentScope) {
448
- context.report({
449
- messageId: "noUnnecessaryUseCallback",
450
- node
451
- });
452
- }
453
- }
454
- };
455
- }
456
- var RULE_NAME4 = "no-unnecessary-use-memo";
457
- var RULE_FEATURES4 = [
458
- "EXP"
459
- ];
460
- var no_unnecessary_use_memo_default = createRule({
461
- meta: {
462
- type: "problem",
463
- deprecated: {
464
- deprecatedSince: "2.0.0",
465
- replacedBy: [
466
- {
467
- message: "Use the same rule from `eslint-plugin-react-x` or `@eslint-react/eslint-plugin` instead.",
468
- plugin: {
469
- name: "eslint-plugin-react-x",
470
- url: "https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x"
471
- },
472
- rule: {
473
- name: "no-unnecessary-use-memo",
474
- url: "https://next.eslint-react.xyz/docs/rules/no-unnecessary-use-memo"
475
- }
476
- },
477
- {
478
- message: "Use the same rule from `eslint-plugin-react-x` or `@eslint-react/eslint-plugin` instead.",
479
- plugin: {
480
- name: "@eslint-react/eslint-plugin",
481
- url: "https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin"
482
- },
483
- rule: {
484
- name: "no-unnecessary-use-memo",
485
- url: "https://next.eslint-react.xyz/docs/rules/no-unnecessary-use-memo"
486
- }
487
- }
488
- ]
489
- },
490
- docs: {
491
- description: "Disallow unnecessary usage of `useMemo`.",
492
- [Symbol.for("rule_features")]: RULE_FEATURES4
493
- },
494
- messages: {
495
- noUnnecessaryUseMemo: "An 'useMemo' with empty deps and no references to the component scope may be unnecessary."
496
- },
497
- schema: []
498
- },
499
- name: RULE_NAME4,
500
- create: create4,
501
- defaultOptions: []
502
- });
503
- function create4(context) {
504
- if (!context.sourceCode.text.includes("use")) return {};
505
- const alias = getSettingsFromContext(context).additionalHooks.useMemo ?? [];
506
- const isUseMemoCall = ER.isReactHookCallWithNameAlias(context, "useMemo", alias);
507
- return {
508
- CallExpression(node) {
509
- if (!ER.isReactHookCall(node)) {
510
- return;
511
- }
512
- const initialScope = context.sourceCode.getScope(node);
513
- if (!isUseMemoCall(node)) {
514
- return;
515
- }
516
- const scope = context.sourceCode.getScope(node);
517
- const component = scope.block;
518
- if (!AST.isFunction(component)) {
519
- return;
520
- }
521
- const [arg0, arg1] = node.arguments;
522
- if (arg0 == null || arg1 == null) {
523
- return;
524
- }
525
- const hasCallInArg0 = AST.isFunction(arg0) && [...AST.getNestedCallExpressions(arg0.body), ...AST.getNestedNewExpressions(arg0.body)].length > 0;
526
- if (hasCallInArg0) {
527
- return;
528
- }
529
- const hasEmptyDeps = match(arg1).with({ type: AST_NODE_TYPES.ArrayExpression }, (n) => n.elements.length === 0).with({ type: AST_NODE_TYPES.Identifier }, (n) => {
530
- const variable = VAR.findVariable(n.name, initialScope);
531
- const variableNode = VAR.getVariableInitNode(variable, 0);
532
- if (variableNode?.type !== AST_NODE_TYPES.ArrayExpression) {
533
- return false;
534
- }
535
- return variableNode.elements.length === 0;
536
- }).otherwise(() => false);
537
- if (!hasEmptyDeps) {
538
- return;
539
- }
540
- const arg0Node = match(arg0).with({ type: AST_NODE_TYPES.ArrowFunctionExpression }, (n) => {
541
- if (n.body.type === AST_NODE_TYPES.ArrowFunctionExpression) {
542
- return n.body;
543
- }
544
- return n;
545
- }).with({ type: AST_NODE_TYPES.FunctionExpression }, identity).with({ type: AST_NODE_TYPES.Identifier }, (n) => {
546
- const variable = VAR.findVariable(n.name, initialScope);
547
- const variableNode = VAR.getVariableInitNode(variable, 0);
548
- if (variableNode?.type !== AST_NODE_TYPES.ArrowFunctionExpression && variableNode?.type !== AST_NODE_TYPES.FunctionExpression) {
549
- return null;
550
- }
551
- return variableNode;
552
- }).otherwise(() => null);
553
- if (arg0Node == null) return;
554
- const arg0NodeScope = context.sourceCode.getScope(arg0Node);
555
- const arg0NodeReferences = VAR.getChidScopes(arg0NodeScope).flatMap((x) => x.references);
556
- const isReferencedToComponentScope = arg0NodeReferences.some((x) => x.resolved?.scope.block === component);
557
- if (!isReferencedToComponentScope) {
558
- context.report({
559
- messageId: "noUnnecessaryUseMemo",
560
- node
561
- });
562
- }
563
- }
564
- };
565
- }
566
- var RULE_NAME5 = "no-unnecessary-use-prefix";
567
- var RULE_FEATURES5 = [];
568
- var WELL_KNOWN_HOOKS = [
569
- "useMDXComponents"
570
- ];
571
- function containsUseComments(context, node) {
572
- return context.sourceCode.getCommentsInside(node).some(({ value }) => /use\([\s\S]*?\)/u.test(value) || /use[A-Z0-9]\w*\([\s\S]*?\)/u.test(value));
573
- }
574
- var no_unnecessary_use_prefix_default = createRule({
575
- meta: {
576
- type: "problem",
577
- deprecated: {
578
- deprecatedSince: "2.0.0",
579
- replacedBy: [
580
- {
581
- message: "Use the same rule from `eslint-plugin-react-x` or `@eslint-react/eslint-plugin` instead.",
582
- plugin: {
583
- name: "eslint-plugin-react-x",
584
- url: "https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x"
585
- },
586
- rule: {
587
- name: "no-unnecessary-use-prefix",
588
- url: "https://next.eslint-react.xyz/docs/rules/no-unnecessary-use-prefix"
589
- }
590
- },
591
- {
592
- message: "Use the same rule from `eslint-plugin-react-x` or `@eslint-react/eslint-plugin` instead.",
593
- plugin: {
594
- name: "@eslint-react/eslint-plugin",
595
- url: "https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin"
596
- },
597
- rule: {
598
- name: "no-unnecessary-use-prefix",
599
- url: "https://next.eslint-react.xyz/docs/rules/no-unnecessary-use-prefix"
600
- }
601
- }
602
- ]
603
- },
604
- docs: {
605
- description: "Enforces that a function with the `use` prefix should use at least one Hook inside of it.",
606
- [Symbol.for("rule_features")]: RULE_FEATURES5
607
- },
608
- messages: {
609
- noUnnecessaryUsePrefix: "If your function doesn't call any Hooks, avoid the 'use' prefix. Instead, write it as a regular function without the 'use' prefix."
610
- },
611
- schema: []
612
- },
613
- name: RULE_NAME5,
614
- create: create5,
615
- defaultOptions: []
616
- });
617
- function create5(context) {
618
- const { ctx, listeners } = ER.useHookCollector();
619
- return {
620
- ...listeners,
621
- "Program:exit"(program) {
622
- const allHooks = ctx.getAllHooks(program);
623
- for (const { id, name: name3, node, hookCalls } of allHooks.values()) {
624
- if (WELL_KNOWN_HOOKS.includes(name3)) {
625
- continue;
626
- }
627
- if (AST.isEmptyFunction(node)) {
628
- continue;
629
- }
630
- if (hookCalls.length > 0) {
631
- continue;
632
- }
633
- if (containsUseComments(context, node)) {
634
- continue;
635
- }
636
- if (id != null) {
637
- context.report({
638
- messageId: "noUnnecessaryUsePrefix",
639
- data: {
640
- name: name3
641
- },
642
- loc: getPreferredLoc(context, id)
643
- });
644
- continue;
645
- }
646
- context.report({
647
- messageId: "noUnnecessaryUsePrefix",
648
- node,
649
- data: {
650
- name: name3
651
- }
652
- });
653
- }
654
- }
655
- };
656
- }
657
- function getPreferredLoc(context, id) {
658
- if (AST.isMultiLine(id)) return id.loc;
659
- if (!context.sourceCode.getText(id).startsWith("use")) return id.loc;
660
- return {
661
- end: {
662
- column: id.loc.start.column + 3,
663
- line: id.loc.start.line
664
- },
665
- start: {
666
- column: id.loc.start.column,
667
- line: id.loc.start.line
668
- }
669
- };
670
- }
671
- var RULE_NAME6 = "prefer-use-state-lazy-initialization";
672
- var RULE_FEATURES6 = [
673
- "EXP"
674
- ];
675
- var ALLOW_LIST = [
676
- "Boolean",
677
- "String",
678
- "Number"
679
- ];
680
- var prefer_use_state_lazy_initialization_default = createRule({
681
- meta: {
682
- type: "problem",
683
- deprecated: {
684
- deprecatedSince: "2.0.0",
685
- replacedBy: [
686
- {
687
- message: "Use the same rule from `eslint-plugin-react-x` or `@eslint-react/eslint-plugin` instead.",
688
- plugin: {
689
- name: "eslint-plugin-react-x",
690
- url: "https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x"
691
- },
692
- rule: {
693
- name: "prefer-use-state-lazy-initialization",
694
- url: "https://next.eslint-react.xyz/docs/rules/prefer-use-state-lazy-initialization"
695
- }
696
- },
697
- {
698
- message: "Use the same rule from `eslint-plugin-react-x` or `@eslint-react/eslint-plugin` instead.",
699
- plugin: {
700
- name: "@eslint-react/eslint-plugin",
701
- url: "https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin"
702
- },
703
- rule: {
704
- name: "prefer-use-state-lazy-initialization",
705
- url: "https://next.eslint-react.xyz/docs/rules/prefer-use-state-lazy-initialization"
706
- }
707
- }
708
- ]
709
- },
710
- docs: {
711
- description: "Enforces function calls made inside `useState` to be wrapped in an `initializer function`.",
712
- [Symbol.for("rule_features")]: RULE_FEATURES6
713
- },
714
- messages: {
715
- preferUseStateLazyInitialization: "To prevent re-computation, consider using lazy initial state for useState calls that involve function calls. Ex: 'useState(() => getValue())'."
716
- },
717
- schema: []
718
- },
719
- name: RULE_NAME6,
720
- create: create6,
721
- defaultOptions: []
722
- });
723
- function create6(context) {
724
- const alias = getSettingsFromContext(context).additionalHooks.useState ?? [];
725
- const isUseStateCall = ER.isReactHookCallWithNameAlias(context, "useState", alias);
726
- return {
727
- CallExpression(node) {
728
- if (!ER.isReactHookCall(node)) {
729
- return;
730
- }
731
- if (!isUseStateCall(node)) {
732
- return;
733
- }
734
- const [useStateInput] = node.arguments;
735
- if (useStateInput == null) {
736
- return;
737
- }
738
- for (const expr of AST.getNestedNewExpressions(useStateInput)) {
739
- if (!("name" in expr.callee)) continue;
740
- if (ALLOW_LIST.includes(expr.callee.name)) continue;
741
- if (AST.findParentNode(expr, (n) => ER.isUseCall(context, n)) != null) continue;
742
- context.report({
743
- messageId: "preferUseStateLazyInitialization",
744
- node: expr
745
- });
746
- }
747
- for (const expr of AST.getNestedCallExpressions(useStateInput)) {
748
- if (!("name" in expr.callee)) continue;
749
- if (ER.isReactHookName(expr.callee.name)) continue;
750
- if (ALLOW_LIST.includes(expr.callee.name)) continue;
751
- if (AST.findParentNode(expr, (n) => ER.isUseCall(context, n)) != null) continue;
752
- context.report({
753
- messageId: "preferUseStateLazyInitialization",
754
- node: expr
755
- });
756
- }
757
- }
758
- };
759
- }
760
-
761
- // src/plugin.ts
762
- var plugin = {
763
- meta: {
764
- name: name2,
765
- version
766
- },
767
- rules: {
768
- "no-direct-set-state-in-use-effect": no_direct_set_state_in_use_effect_default,
769
- "no-direct-set-state-in-use-layout-effect": no_direct_set_state_in_use_layout_effect_default,
770
- /**
771
- * @deprecated Use the same rule from `eslint-plugin-react-x` or `@eslint-react/eslint-plugin` instead.
772
- * @see https://next.eslint-react.xyz/docs/rules/no-unnecessary-use-callback
773
- */
774
- "no-unnecessary-use-callback": no_unnecessary_use_callback_default,
775
- /**
776
- * @deprecated Use the same rule from `eslint-plugin-react-x` or `@eslint-react/eslint-plugin` instead.
777
- * @see https://next.eslint-react.xyz/docs/rules/no-unnecessary-use-memo
778
- */
779
- "no-unnecessary-use-memo": no_unnecessary_use_memo_default,
780
- /**
781
- * @deprecated Use the same rule from `eslint-plugin-react-x` or `@eslint-react/eslint-plugin` instead.
782
- * @see https://next.eslint-react.xyz/docs/rules/no-unnecessary-use-prefix
783
- */
784
- "no-unnecessary-use-prefix": no_unnecessary_use_prefix_default,
785
- /**
786
- * @deprecated Use the same rule from `eslint-plugin-react-x` or `@eslint-react/eslint-plugin` instead.
787
- * @see https://next.eslint-react.xyz/docs/rules/prefer-use-state-lazy-initialization
788
- */
789
- "prefer-use-state-lazy-initialization": prefer_use_state_lazy_initialization_default
790
- }
247
+ //#endregion
248
+ //#region src/plugin.ts
249
+ const plugin = {
250
+ meta: {
251
+ name,
252
+ version
253
+ },
254
+ rules: { "no-direct-set-state-in-use-effect": no_direct_set_state_in_use_effect_default }
791
255
  };
792
256
 
793
- // src/index.ts
794
- function makeConfig(config) {
795
- return {
796
- ...config,
797
- plugins: {
798
- "react-hooks-extra": plugin
799
- }
800
- };
801
- }
802
- function makeLegacyConfig({ rules: rules2 }) {
803
- return {
804
- plugins: ["react-hooks-extra"],
805
- rules: rules2
806
- };
807
- }
808
- var index_default = {
809
- ...plugin,
810
- configs: {
811
- ["recommended"]: makeConfig(recommended_exports),
812
- ["recommended-legacy"]: makeLegacyConfig(recommended_exports)
813
- }
257
+ //#endregion
258
+ //#region src/index.ts
259
+ const { toFlatConfig } = getConfigAdapters("react-hooks-extra", plugin);
260
+ var src_default = {
261
+ ...plugin,
262
+ configs: { ["recommended"]: toFlatConfig(recommended_exports) }
814
263
  };
815
264
 
816
- export { index_default as default };
265
+ //#endregion
266
+ export { src_default as default };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-react-hooks-extra",
3
- "version": "2.0.0-next.58",
3
+ "version": "2.0.0",
4
4
  "description": "ESLint React's ESLint plugin for React Hooks related rules.",
5
5
  "keywords": [
6
6
  "react",
@@ -36,46 +36,37 @@
36
36
  "./package.json"
37
37
  ],
38
38
  "dependencies": {
39
- "@typescript-eslint/scope-manager": "^8.35.0",
40
- "@typescript-eslint/type-utils": "^8.35.0",
41
- "@typescript-eslint/types": "^8.35.0",
42
- "@typescript-eslint/utils": "^8.35.0",
39
+ "@typescript-eslint/scope-manager": "^8.44.1",
40
+ "@typescript-eslint/type-utils": "^8.44.1",
41
+ "@typescript-eslint/types": "^8.44.1",
42
+ "@typescript-eslint/utils": "^8.44.1",
43
43
  "string-ts": "^2.2.1",
44
- "ts-pattern": "^5.7.1",
45
- "@eslint-react/ast": "2.0.0-next.58",
46
- "@eslint-react/core": "2.0.0-next.58",
47
- "@eslint-react/kit": "2.0.0-next.58",
48
- "@eslint-react/eff": "2.0.0-next.58",
49
- "@eslint-react/var": "2.0.0-next.58",
50
- "@eslint-react/shared": "2.0.0-next.58"
44
+ "ts-pattern": "^5.8.0",
45
+ "@eslint-react/ast": "2.0.0",
46
+ "@eslint-react/core": "2.0.0",
47
+ "@eslint-react/kit": "2.0.0",
48
+ "@eslint-react/shared": "2.0.0",
49
+ "@eslint-react/eff": "2.0.0",
50
+ "@eslint-react/var": "2.0.0"
51
51
  },
52
52
  "devDependencies": {
53
- "@types/react": "^19.1.8",
54
- "@types/react-dom": "^19.1.6",
55
- "tsup": "^8.5.0",
53
+ "@types/react": "^19.1.13",
54
+ "@types/react-dom": "^19.1.9",
55
+ "tsdown": "^0.15.4",
56
56
  "@local/configs": "0.0.0"
57
57
  },
58
58
  "peerDependencies": {
59
- "eslint": "^8.57.0 || ^9.0.0",
60
- "typescript": "^4.9.5 || ^5.3.3"
61
- },
62
- "peerDependenciesMeta": {
63
- "eslint": {
64
- "optional": false
65
- },
66
- "typescript": {
67
- "optional": true
68
- }
59
+ "eslint": "^9.36.0",
60
+ "typescript": "^5.9.2"
69
61
  },
70
62
  "engines": {
71
- "bun": ">=1.0.15",
72
- "node": ">=18.18.0"
63
+ "node": ">=20.0.0"
73
64
  },
74
65
  "publishConfig": {
75
66
  "access": "public"
76
67
  },
77
68
  "scripts": {
78
- "build": "tsup --dts-resolve",
69
+ "build": "tsdown",
79
70
  "lint:publish": "publint",
80
71
  "lint:ts": "tsc --noEmit"
81
72
  }