eslint-plugin-react-hooks-extra 1.5.26-next.1 → 1.5.26-next.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -9,7 +9,7 @@ var astUtils = require('@typescript-eslint/utils/ast-utils');
9
9
 
10
10
  // package.json
11
11
  var name = "eslint-plugin-react-hooks-extra";
12
- var version = "1.5.26-next.1";
12
+ var version = "1.5.26-next.3";
13
13
  var createRule = shared.createRuleForPlugin("hooks-extra");
14
14
 
15
15
  // src/rules/ensure-custom-hooks-using-other-hooks.ts
@@ -227,7 +227,7 @@ var ensure_use_callback_has_non_empty_deps_default = createRule({
227
227
  description: "enforce 'useCallback' has non-empty dependencies array"
228
228
  },
229
229
  messages: {
230
- ENSURE_USE_CALLBACK_HAS_NON_EMPTY_DEPS: "An useCallback should have a non-empty dependencies array."
230
+ ENSURE_USE_CALLBACK_HAS_NON_EMPTY_DEPS: "An 'useCallback' should have a non-empty dependencies array."
231
231
  },
232
232
  schema: []
233
233
  },
@@ -236,12 +236,15 @@ var ensure_use_callback_has_non_empty_deps_default = createRule({
236
236
  const alias = shared.getESLintReactSettings(context.settings).additionalHooks?.useCallback ?? [];
237
237
  return {
238
238
  CallExpression(node) {
239
- const initialScope = context.sourceCode.getScope(node);
240
239
  if (!core.isReactHookCall(node)) return;
240
+ const initialScope = context.sourceCode.getScope(node);
241
241
  if (!core.isUseCallbackCall(node, context) && !alias.some(tools.F.flip(core.isReactHookCallWithNameLoose)(node))) {
242
242
  return;
243
243
  }
244
- const [_2, deps] = node.arguments;
244
+ const scope = context.sourceCode.getScope(node);
245
+ const component = scope.block;
246
+ if (!ast.isFunction(component)) return;
247
+ const [cb, deps] = node.arguments;
245
248
  if (!deps) {
246
249
  context.report({
247
250
  messageId: "ENSURE_USE_CALLBACK_HAS_NON_EMPTY_DEPS",
@@ -249,7 +252,7 @@ var ensure_use_callback_has_non_empty_deps_default = createRule({
249
252
  });
250
253
  return;
251
254
  }
252
- const maybeDescriptor = tools.F.pipe(
255
+ const hasEmptyDeps = tools.F.pipe(
253
256
  $(deps).with({ type: ast.NodeType.ArrayExpression }, tools.O.some).with({ type: ast.NodeType.Identifier }, (n2) => {
254
257
  return tools.F.pipe(
255
258
  _var.findVariable(n2.name, initialScope),
@@ -257,13 +260,33 @@ var ensure_use_callback_has_non_empty_deps_default = createRule({
257
260
  tools.O.filter(ast.is(ast.NodeType.ArrayExpression))
258
261
  );
259
262
  }).otherwise(tools.O.none),
260
- tools.O.filter((x2) => x2.elements.length === 0),
261
- tools.O.map(() => ({
263
+ tools.O.exists((x2) => x2.elements.length === 0)
264
+ );
265
+ if (!hasEmptyDeps) return;
266
+ if (!cb) {
267
+ context.report({
262
268
  messageId: "ENSURE_USE_CALLBACK_HAS_NON_EMPTY_DEPS",
263
269
  node
264
- }))
270
+ });
271
+ return;
272
+ }
273
+ const isReferencedToComponentScope = tools.F.pipe(
274
+ $(cb).with({ type: ast.NodeType.ArrowFunctionExpression }, tools.O.some).with({ type: ast.NodeType.FunctionExpression }, tools.O.some).with({ type: ast.NodeType.Identifier }, (n2) => {
275
+ return tools.F.pipe(
276
+ _var.findVariable(n2.name, initialScope),
277
+ tools.O.flatMap(_var.getVariableInit(0)),
278
+ tools.O.filter(ast.isFunction)
279
+ );
280
+ }).otherwise(tools.O.none),
281
+ tools.O.map((n2) => context.sourceCode.getScope(n2)),
282
+ tools.O.map((s2) => [...s2.childScopes, s2].flatMap((x2) => x2.references)),
283
+ tools.O.exists((refs) => refs.some((x2) => x2.resolved?.scope.block === component))
265
284
  );
266
- tools.O.map(maybeDescriptor, context.report);
285
+ if (isReferencedToComponentScope) return;
286
+ context.report({
287
+ messageId: "ENSURE_USE_CALLBACK_HAS_NON_EMPTY_DEPS",
288
+ node
289
+ });
267
290
  }
268
291
  };
269
292
  },
@@ -277,7 +300,7 @@ var ensure_use_memo_has_non_empty_deps_default = createRule({
277
300
  description: "enforce 'useMemo' has non-empty dependencies array"
278
301
  },
279
302
  messages: {
280
- ENSURE_USE_MEMO_HAS_NON_EMPTY_DEPS: "An useMemo should have a non-empty dependencies array."
303
+ ENSURE_USE_MEMO_HAS_NON_EMPTY_DEPS: "An 'useMemo' should have a non-empty dependencies array."
281
304
  },
282
305
  schema: []
283
306
  },
@@ -286,10 +309,15 @@ var ensure_use_memo_has_non_empty_deps_default = createRule({
286
309
  const alias = shared.getESLintReactSettings(context.settings).additionalHooks?.useMemo ?? [];
287
310
  return {
288
311
  CallExpression(node) {
289
- const initialScope = context.sourceCode.getScope(node);
290
312
  if (!core.isReactHookCall(node)) return;
291
- if (!core.isUseMemoCall(node, context) && !alias.some(tools.F.flip(core.isReactHookCallWithNameLoose)(node))) return;
292
- const [_2, deps] = node.arguments;
313
+ const initialScope = context.sourceCode.getScope(node);
314
+ if (!core.isUseMemoCall(node, context) && !alias.some(tools.F.flip(core.isReactHookCallWithNameLoose)(node))) {
315
+ return;
316
+ }
317
+ const scope = context.sourceCode.getScope(node);
318
+ const component = scope.block;
319
+ if (!ast.isFunction(component)) return;
320
+ const [cb, deps] = node.arguments;
293
321
  if (!deps) {
294
322
  context.report({
295
323
  messageId: "ENSURE_USE_MEMO_HAS_NON_EMPTY_DEPS",
@@ -297,7 +325,7 @@ var ensure_use_memo_has_non_empty_deps_default = createRule({
297
325
  });
298
326
  return;
299
327
  }
300
- const maybeDescriptor = tools.F.pipe(
328
+ const hasEmptyDeps = tools.F.pipe(
301
329
  $(deps).with({ type: ast.NodeType.ArrayExpression }, tools.O.some).with({ type: ast.NodeType.Identifier }, (n2) => {
302
330
  return tools.F.pipe(
303
331
  _var.findVariable(n2.name, initialScope),
@@ -305,13 +333,33 @@ var ensure_use_memo_has_non_empty_deps_default = createRule({
305
333
  tools.O.filter(ast.is(ast.NodeType.ArrayExpression))
306
334
  );
307
335
  }).otherwise(tools.O.none),
308
- tools.O.filter((x2) => x2.elements.length === 0),
309
- tools.O.map(() => ({
336
+ tools.O.exists((x2) => x2.elements.length === 0)
337
+ );
338
+ if (!hasEmptyDeps) return;
339
+ if (!cb) {
340
+ context.report({
310
341
  messageId: "ENSURE_USE_MEMO_HAS_NON_EMPTY_DEPS",
311
342
  node
312
- }))
343
+ });
344
+ return;
345
+ }
346
+ const isReferencedToComponentScope = tools.F.pipe(
347
+ $(cb).with({ type: ast.NodeType.ArrowFunctionExpression }, tools.O.some).with({ type: ast.NodeType.FunctionExpression }, tools.O.some).with({ type: ast.NodeType.Identifier }, (n2) => {
348
+ return tools.F.pipe(
349
+ _var.findVariable(n2.name, initialScope),
350
+ tools.O.flatMap(_var.getVariableInit(0)),
351
+ tools.O.filter(ast.isFunction)
352
+ );
353
+ }).otherwise(tools.O.none),
354
+ tools.O.map((n2) => context.sourceCode.getScope(n2)),
355
+ tools.O.map((s2) => [...s2.childScopes, s2].flatMap((x2) => x2.references)),
356
+ tools.O.exists((refs) => refs.some((x2) => x2.resolved?.scope.block === component))
313
357
  );
314
- tools.O.map(maybeDescriptor, context.report);
358
+ if (isReferencedToComponentScope) return;
359
+ context.report({
360
+ messageId: "ENSURE_USE_MEMO_HAS_NON_EMPTY_DEPS",
361
+ node
362
+ });
315
363
  }
316
364
  };
317
365
  },
package/dist/index.mjs CHANGED
@@ -1,13 +1,13 @@
1
1
  import { useHookCollector, isReactHookCall, isUseCallbackCall, isReactHookCallWithNameLoose, isUseMemoCall, isUseStateCall, isUseEffectCall, isUseLayoutEffectCall } from '@eslint-react/core';
2
2
  import { createRuleForPlugin, getESLintReactSettings } from '@eslint-react/shared';
3
- import { NodeType, is, traverseUp, getNestedCallExpressions } from '@eslint-react/ast';
3
+ import { isFunction, NodeType, is, traverseUp, getNestedCallExpressions } from '@eslint-react/ast';
4
4
  import { F, O } from '@eslint-react/tools';
5
5
  import { findVariable, getVariableInit } from '@eslint-react/var';
6
6
  import { getStaticValue } from '@typescript-eslint/utils/ast-utils';
7
7
 
8
8
  // package.json
9
9
  var name = "eslint-plugin-react-hooks-extra";
10
- var version = "1.5.26-next.1";
10
+ var version = "1.5.26-next.3";
11
11
  var createRule = createRuleForPlugin("hooks-extra");
12
12
 
13
13
  // src/rules/ensure-custom-hooks-using-other-hooks.ts
@@ -225,7 +225,7 @@ var ensure_use_callback_has_non_empty_deps_default = createRule({
225
225
  description: "enforce 'useCallback' has non-empty dependencies array"
226
226
  },
227
227
  messages: {
228
- ENSURE_USE_CALLBACK_HAS_NON_EMPTY_DEPS: "An useCallback should have a non-empty dependencies array."
228
+ ENSURE_USE_CALLBACK_HAS_NON_EMPTY_DEPS: "An 'useCallback' should have a non-empty dependencies array."
229
229
  },
230
230
  schema: []
231
231
  },
@@ -234,12 +234,15 @@ var ensure_use_callback_has_non_empty_deps_default = createRule({
234
234
  const alias = getESLintReactSettings(context.settings).additionalHooks?.useCallback ?? [];
235
235
  return {
236
236
  CallExpression(node) {
237
- const initialScope = context.sourceCode.getScope(node);
238
237
  if (!isReactHookCall(node)) return;
238
+ const initialScope = context.sourceCode.getScope(node);
239
239
  if (!isUseCallbackCall(node, context) && !alias.some(F.flip(isReactHookCallWithNameLoose)(node))) {
240
240
  return;
241
241
  }
242
- const [_2, deps] = node.arguments;
242
+ const scope = context.sourceCode.getScope(node);
243
+ const component = scope.block;
244
+ if (!isFunction(component)) return;
245
+ const [cb, deps] = node.arguments;
243
246
  if (!deps) {
244
247
  context.report({
245
248
  messageId: "ENSURE_USE_CALLBACK_HAS_NON_EMPTY_DEPS",
@@ -247,7 +250,7 @@ var ensure_use_callback_has_non_empty_deps_default = createRule({
247
250
  });
248
251
  return;
249
252
  }
250
- const maybeDescriptor = F.pipe(
253
+ const hasEmptyDeps = F.pipe(
251
254
  $(deps).with({ type: NodeType.ArrayExpression }, O.some).with({ type: NodeType.Identifier }, (n2) => {
252
255
  return F.pipe(
253
256
  findVariable(n2.name, initialScope),
@@ -255,13 +258,33 @@ var ensure_use_callback_has_non_empty_deps_default = createRule({
255
258
  O.filter(is(NodeType.ArrayExpression))
256
259
  );
257
260
  }).otherwise(O.none),
258
- O.filter((x2) => x2.elements.length === 0),
259
- O.map(() => ({
261
+ O.exists((x2) => x2.elements.length === 0)
262
+ );
263
+ if (!hasEmptyDeps) return;
264
+ if (!cb) {
265
+ context.report({
260
266
  messageId: "ENSURE_USE_CALLBACK_HAS_NON_EMPTY_DEPS",
261
267
  node
262
- }))
268
+ });
269
+ return;
270
+ }
271
+ const isReferencedToComponentScope = F.pipe(
272
+ $(cb).with({ type: NodeType.ArrowFunctionExpression }, O.some).with({ type: NodeType.FunctionExpression }, O.some).with({ type: NodeType.Identifier }, (n2) => {
273
+ return F.pipe(
274
+ findVariable(n2.name, initialScope),
275
+ O.flatMap(getVariableInit(0)),
276
+ O.filter(isFunction)
277
+ );
278
+ }).otherwise(O.none),
279
+ O.map((n2) => context.sourceCode.getScope(n2)),
280
+ O.map((s2) => [...s2.childScopes, s2].flatMap((x2) => x2.references)),
281
+ O.exists((refs) => refs.some((x2) => x2.resolved?.scope.block === component))
263
282
  );
264
- O.map(maybeDescriptor, context.report);
283
+ if (isReferencedToComponentScope) return;
284
+ context.report({
285
+ messageId: "ENSURE_USE_CALLBACK_HAS_NON_EMPTY_DEPS",
286
+ node
287
+ });
265
288
  }
266
289
  };
267
290
  },
@@ -275,7 +298,7 @@ var ensure_use_memo_has_non_empty_deps_default = createRule({
275
298
  description: "enforce 'useMemo' has non-empty dependencies array"
276
299
  },
277
300
  messages: {
278
- ENSURE_USE_MEMO_HAS_NON_EMPTY_DEPS: "An useMemo should have a non-empty dependencies array."
301
+ ENSURE_USE_MEMO_HAS_NON_EMPTY_DEPS: "An 'useMemo' should have a non-empty dependencies array."
279
302
  },
280
303
  schema: []
281
304
  },
@@ -284,10 +307,15 @@ var ensure_use_memo_has_non_empty_deps_default = createRule({
284
307
  const alias = getESLintReactSettings(context.settings).additionalHooks?.useMemo ?? [];
285
308
  return {
286
309
  CallExpression(node) {
287
- const initialScope = context.sourceCode.getScope(node);
288
310
  if (!isReactHookCall(node)) return;
289
- if (!isUseMemoCall(node, context) && !alias.some(F.flip(isReactHookCallWithNameLoose)(node))) return;
290
- const [_2, deps] = node.arguments;
311
+ const initialScope = context.sourceCode.getScope(node);
312
+ if (!isUseMemoCall(node, context) && !alias.some(F.flip(isReactHookCallWithNameLoose)(node))) {
313
+ return;
314
+ }
315
+ const scope = context.sourceCode.getScope(node);
316
+ const component = scope.block;
317
+ if (!isFunction(component)) return;
318
+ const [cb, deps] = node.arguments;
291
319
  if (!deps) {
292
320
  context.report({
293
321
  messageId: "ENSURE_USE_MEMO_HAS_NON_EMPTY_DEPS",
@@ -295,7 +323,7 @@ var ensure_use_memo_has_non_empty_deps_default = createRule({
295
323
  });
296
324
  return;
297
325
  }
298
- const maybeDescriptor = F.pipe(
326
+ const hasEmptyDeps = F.pipe(
299
327
  $(deps).with({ type: NodeType.ArrayExpression }, O.some).with({ type: NodeType.Identifier }, (n2) => {
300
328
  return F.pipe(
301
329
  findVariable(n2.name, initialScope),
@@ -303,13 +331,33 @@ var ensure_use_memo_has_non_empty_deps_default = createRule({
303
331
  O.filter(is(NodeType.ArrayExpression))
304
332
  );
305
333
  }).otherwise(O.none),
306
- O.filter((x2) => x2.elements.length === 0),
307
- O.map(() => ({
334
+ O.exists((x2) => x2.elements.length === 0)
335
+ );
336
+ if (!hasEmptyDeps) return;
337
+ if (!cb) {
338
+ context.report({
308
339
  messageId: "ENSURE_USE_MEMO_HAS_NON_EMPTY_DEPS",
309
340
  node
310
- }))
341
+ });
342
+ return;
343
+ }
344
+ const isReferencedToComponentScope = F.pipe(
345
+ $(cb).with({ type: NodeType.ArrowFunctionExpression }, O.some).with({ type: NodeType.FunctionExpression }, O.some).with({ type: NodeType.Identifier }, (n2) => {
346
+ return F.pipe(
347
+ findVariable(n2.name, initialScope),
348
+ O.flatMap(getVariableInit(0)),
349
+ O.filter(isFunction)
350
+ );
351
+ }).otherwise(O.none),
352
+ O.map((n2) => context.sourceCode.getScope(n2)),
353
+ O.map((s2) => [...s2.childScopes, s2].flatMap((x2) => x2.references)),
354
+ O.exists((refs) => refs.some((x2) => x2.resolved?.scope.block === component))
311
355
  );
312
- O.map(maybeDescriptor, context.report);
356
+ if (isReferencedToComponentScope) return;
357
+ context.report({
358
+ messageId: "ENSURE_USE_MEMO_HAS_NON_EMPTY_DEPS",
359
+ node
360
+ });
313
361
  }
314
362
  };
315
363
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-react-hooks-extra",
3
- "version": "1.5.26-next.1",
3
+ "version": "1.5.26-next.3",
4
4
  "description": "ESLint React's ESLint plugin for React Hooks related rules.",
5
5
  "homepage": "https://github.com/rel1cx/eslint-react",
6
6
  "bugs": {
@@ -39,13 +39,13 @@
39
39
  "@typescript-eslint/type-utils": "^7.16.0",
40
40
  "@typescript-eslint/types": "^7.16.0",
41
41
  "@typescript-eslint/utils": "^7.16.0",
42
- "@eslint-react/ast": "1.5.26-next.1",
43
- "@eslint-react/jsx": "1.5.26-next.1",
44
- "@eslint-react/tools": "1.5.26-next.1",
45
- "@eslint-react/types": "1.5.26-next.1",
46
- "@eslint-react/var": "1.5.26-next.1",
47
- "@eslint-react/shared": "1.5.26-next.1",
48
- "@eslint-react/core": "1.5.26-next.1"
42
+ "@eslint-react/ast": "1.5.26-next.3",
43
+ "@eslint-react/core": "1.5.26-next.3",
44
+ "@eslint-react/jsx": "1.5.26-next.3",
45
+ "@eslint-react/shared": "1.5.26-next.3",
46
+ "@eslint-react/tools": "1.5.26-next.3",
47
+ "@eslint-react/var": "1.5.26-next.3",
48
+ "@eslint-react/types": "1.5.26-next.3"
49
49
  },
50
50
  "devDependencies": {
51
51
  "string-ts": "2.2.0",