@agilebot/eslint-plugin 0.5.3 → 0.5.4

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.
Files changed (2) hide show
  1. package/dist/index.js +40 -9
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license @agilebot/eslint-plugin v0.5.3
2
+ * @license @agilebot/eslint-plugin v0.5.4
3
3
  *
4
4
  * Copyright (c) Agilebot, Inc. and its affiliates.
5
5
  *
@@ -859,6 +859,7 @@ var reactBetterExhaustiveDeps = {
859
859
  const stateVariables = new WeakSet();
860
860
  const stableKnownValueCache = new WeakMap();
861
861
  const functionWithoutCapturedValueCache = new WeakMap();
862
+ const useEffectEventVariables = new WeakSet();
862
863
  function memoizeWithWeakMap(fn, map) {
863
864
  return function (arg) {
864
865
  if (map.has(arg)) {
@@ -882,7 +883,7 @@ var reactBetterExhaustiveDeps = {
882
883
  if (!isCustomHook) {
883
884
  reportProblem({
884
885
  node: node,
885
- message: "Effect callbacks are synchronous to prevent race conditions. " + "Put the async function inside:\n\n" + 'useEffect(() => {\n' + ' async function fetchData() {\n' + ' // You can await here\n' + ' const response = await MyAPI.getData(someId);\n' + ' // ...\n' + ' }\n' + ' fetchData();\n' + "}, [someId]); // Or [] if effect doesn't need props or state\n\n" + 'Learn more about data fetching with Hooks: https://reactjs.org/link/hooks-data-fetching'
886
+ message: "Effect callbacks are synchronous to prevent race conditions. " + "Put the async function inside:\n\n" + 'useEffect(() => {\n' + ' async function fetchData() {\n' + ' // You can await here\n' + ' const response = await MyAPI.getData(someId);\n' + ' // ...\n' + ' }\n' + ' fetchData();\n' + "}, [someId]); // Or [] if effect doesn't need props or state\n\n" + 'Learn more about data fetching with Hooks: https://react.dev/link/hooks-data-fetching'
886
887
  });
887
888
  }
888
889
  }
@@ -921,7 +922,7 @@ var reactBetterExhaustiveDeps = {
921
922
  if (init == null) {
922
923
  return false;
923
924
  }
924
- while (init.type === 'TSAsExpression') {
925
+ while (init.type === 'TSAsExpression' || init.type === 'AsExpression') {
925
926
  init = init.expression;
926
927
  }
927
928
  let declaration = def.node.parent;
@@ -951,7 +952,14 @@ var reactBetterExhaustiveDeps = {
951
952
  } = callee;
952
953
  if (name === 'useRef' && id.type === 'Identifier') {
953
954
  return true;
954
- } else if (name === 'useState' || name === 'useReducer') {
955
+ } else if (isUseEffectEventIdentifier() && id.type === 'Identifier') {
956
+ for (const ref of resolved.references) {
957
+ if (ref !== id) {
958
+ useEffectEventVariables.add(ref.identifier);
959
+ }
960
+ }
961
+ return true;
962
+ } else if (name === 'useState' || name === 'useReducer' || name === 'useActionState') {
955
963
  if (id.type === 'ArrayPattern' && id.elements.length === 2 && isArray(resolved.identifiers)) {
956
964
  if (id.elements[1] === resolved.identifiers[0]) {
957
965
  if (name === 'useState') {
@@ -1238,13 +1246,16 @@ var reactBetterExhaustiveDeps = {
1238
1246
  }
1239
1247
  const declaredDependencies = [];
1240
1248
  const externalDependencies = new Set();
1241
- if (declaredDependenciesNode.type !== 'ArrayExpression') {
1249
+ const isArrayExpression = declaredDependenciesNode.type === 'ArrayExpression';
1250
+ const isTSAsArrayExpression = declaredDependenciesNode.type === 'TSAsExpression' && declaredDependenciesNode.expression.type === 'ArrayExpression';
1251
+ if (!isArrayExpression && !isTSAsArrayExpression) {
1242
1252
  reportProblem({
1243
1253
  node: declaredDependenciesNode,
1244
1254
  message: "React Hook ".concat(getSource(reactiveHook), " was passed a ") + 'dependency list that is not an array literal. This means we ' + "can't statically verify whether you've passed the correct " + 'dependencies.'
1245
1255
  });
1246
1256
  } else {
1247
- declaredDependenciesNode.elements.forEach(declaredDependencyNode => {
1257
+ const arrayExpression = isTSAsArrayExpression ? declaredDependenciesNode.expression : declaredDependenciesNode;
1258
+ arrayExpression.elements.forEach(declaredDependencyNode => {
1248
1259
  if (declaredDependencyNode == null) {
1249
1260
  return;
1250
1261
  }
@@ -1255,6 +1266,18 @@ var reactBetterExhaustiveDeps = {
1255
1266
  });
1256
1267
  return;
1257
1268
  }
1269
+ if (useEffectEventVariables.has(declaredDependencyNode)) {
1270
+ reportProblem({
1271
+ node: declaredDependencyNode,
1272
+ message: 'Functions returned from `useEffectEvent` must not be included in the dependency array. ' + "Remove `".concat(getSource(declaredDependencyNode), "` from the list."),
1273
+ suggest: [{
1274
+ desc: "Remove the dependency `".concat(getSource(declaredDependencyNode), "`"),
1275
+ fix(fixer) {
1276
+ return fixer.removeRange(declaredDependencyNode.range);
1277
+ }
1278
+ }]
1279
+ });
1280
+ }
1258
1281
  let declaredDependency;
1259
1282
  try {
1260
1283
  declaredDependency = analyzePropertyChain(declaredDependencyNode, null);
@@ -1532,7 +1555,8 @@ var reactBetterExhaustiveDeps = {
1532
1555
  extraWarning = " If '".concat(setStateRecommendation.setter, "' needs the ") + "current value of '".concat(setStateRecommendation.missingDep, "', ") + "you can also switch to useReducer instead of useState and " + "read '".concat(setStateRecommendation.missingDep, "' in the reducer.");
1533
1556
  break;
1534
1557
  case 'updater':
1535
- extraWarning = " You can also do a functional update '".concat(setStateRecommendation.setter, "(").concat(setStateRecommendation.missingDep.slice(0, 1), " => ...)' if you only need '").concat(setStateRecommendation.missingDep, "' in the '").concat(setStateRecommendation.setter, "' call.");
1558
+ extraWarning = " You can also do a functional update '".concat(setStateRecommendation.setter, "(").concat(setStateRecommendation.missingDep.slice(0, 1), " => ...)' if you only need '").concat(setStateRecommendation.missingDep
1559
+ , "'") + " in the '".concat(setStateRecommendation.setter, "' call.");
1536
1560
  break;
1537
1561
  default:
1538
1562
  throw new Error('Unknown case.');
@@ -1559,7 +1583,8 @@ var reactBetterExhaustiveDeps = {
1559
1583
  const callback = node.arguments[callbackIndex];
1560
1584
  const reactiveHook = node.callee;
1561
1585
  const reactiveHookName = getNodeWithoutReactNamespace(reactiveHook).name;
1562
- const declaredDependenciesNode = node.arguments[callbackIndex + 1];
1586
+ const maybeNode = node.arguments[callbackIndex + 1];
1587
+ const declaredDependenciesNode = maybeNode && !(maybeNode.type === 'Identifier' && maybeNode.name === 'undefined') ? maybeNode : undefined;
1563
1588
  const isEffect = /Effect($|[^a-z])/g.test(reactiveHookName);
1564
1589
  if (!callback) {
1565
1590
  reportProblem({
@@ -1582,6 +1607,9 @@ var reactBetterExhaustiveDeps = {
1582
1607
  case 'ArrowFunctionExpression':
1583
1608
  visitFunctionWithDependencies(callback, declaredDependenciesNode, reactiveHook, reactiveHookName, isEffect);
1584
1609
  return;
1610
+ case 'TSAsExpression':
1611
+ visitFunctionWithDependencies(callback.expression, declaredDependenciesNode, reactiveHook, reactiveHookName, isEffect);
1612
+ return;
1585
1613
  case 'Identifier':
1586
1614
  if (!declaredDependenciesNode) {
1587
1615
  return;
@@ -1786,7 +1814,7 @@ function getConstructionExpressionType(node) {
1786
1814
  }
1787
1815
  return null;
1788
1816
  case 'TypeCastExpression':
1789
- return getConstructionExpressionType(node.expression);
1817
+ case 'AsExpression':
1790
1818
  case 'TSAsExpression':
1791
1819
  return getConstructionExpressionType(node.expression);
1792
1820
  }
@@ -2008,6 +2036,9 @@ function isSameIdentifier(a, b) {
2008
2036
  function isAncestorNodeOf(a, b) {
2009
2037
  return a.range[0] <= b.range[0] && a.range[1] >= b.range[1];
2010
2038
  }
2039
+ function isUseEffectEventIdentifier(node) {
2040
+ return false;
2041
+ }
2011
2042
 
2012
2043
  const Components = require('eslint-plugin-react/lib/util/Components');
2013
2044
  const RULE_NAME$7 = 'react-hook-use-ref';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agilebot/eslint-plugin",
3
- "version": "0.5.3",
3
+ "version": "0.5.4",
4
4
  "description": "Agilebot's ESLint plugin",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -23,7 +23,7 @@
23
23
  "eslint-plugin-deprecation": "^3.0.0",
24
24
  "eslint-plugin-react": "^7.35.0",
25
25
  "eslint-plugin-react-hooks": "^4.6.2",
26
- "@agilebot/eslint-utils": "0.5.3"
26
+ "@agilebot/eslint-utils": "0.5.4"
27
27
  },
28
28
  "peerDependencies": {
29
29
  "eslint": "^7.0.0 || ^8.0.0 || ^9.0.0"