eslint-plugin-react-hooks 4.0.4 → 4.0.8

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
@@ -48,6 +48,24 @@ If you want more fine-grained configuration, you can instead add a snippet like
48
48
  ```
49
49
 
50
50
 
51
+ ## Advanced Configuration
52
+
53
+ `exhaustive-deps` can be configured to validate dependencies of custom Hooks with the `additionalHooks` option.
54
+ This option accepts a regex to match the names of custom Hooks that have dependencies.
55
+
56
+ ```js
57
+ {
58
+ "rules": {
59
+ // ...
60
+ "react-hooks/exhaustive-deps": ["warn", {
61
+ "additionalHooks": "(useMyCustomHook|useMyOtherCustomHook)"
62
+ }]
63
+ }
64
+ }
65
+ ```
66
+
67
+ We suggest to use this option **very sparingly, if at all**. Generally saying, we recommend most custom Hooks to not use the dependencies argument, and instead provide a higher-level API that is more focused around a specific use case.
68
+
51
69
  ## Valid and Invalid Examples
52
70
 
53
71
  Please refer to the [Rules of Hooks](https://reactjs.org/docs/hooks-rules.html) documentation and the [Hooks FAQ](https://reactjs.org/docs/hooks-faq.html#what-exactly-do-the-lint-rules-enforce) to learn more about this rule.
@@ -9,8 +9,6 @@
9
9
 
10
10
  'use strict';
11
11
 
12
-
13
-
14
12
  if (process.env.NODE_ENV !== "production") {
15
13
  (function() {
16
14
  'use strict';
@@ -669,7 +667,7 @@ var RulesOfHooks = {
669
667
  * easy. For anonymous function expressions it is much harder. If you search for
670
668
  * `IsAnonymousFunctionDefinition()` in the ECMAScript spec you'll find places
671
669
  * where JS gives anonymous function expressions names. We roughly detect the
672
- * same AST nodes with some exceptions to better fit our usecase.
670
+ * same AST nodes with some exceptions to better fit our use case.
673
671
  */
674
672
 
675
673
  function getFunctionName(node) {
@@ -770,7 +768,7 @@ var ExhaustiveDeps = {
770
768
 
771
769
  var setStateCallSites = new WeakMap();
772
770
  var stateVariables = new WeakSet();
773
- var staticKnownValueCache = new WeakMap();
771
+ var stableKnownValueCache = new WeakMap();
774
772
  var functionWithoutCapturedValueCache = new WeakMap();
775
773
 
776
774
  function memoizeWithWeakMap(fn, map) {
@@ -836,7 +834,7 @@ var ExhaustiveDeps = {
836
834
 
837
835
 
838
836
  if (declaredDependenciesNode.elements && declaredDependenciesNode.elements.some(function (el) {
839
- return el.type === 'Identifier' && el.name === callback.name;
837
+ return el && el.type === 'Identifier' && el.name === callback.name;
840
838
  })) {
841
839
  // If it's already in the list of deps, we don't care because
842
840
  // this is valid regardless.
@@ -967,7 +965,7 @@ var ExhaustiveDeps = {
967
965
  componentScope = currentScope;
968
966
  } // Next we'll define a few helpers that helps us
969
967
  // tell if some values don't have to be declared as deps.
970
- // Some are known to be static based on Hook calls.
968
+ // Some are known to be stable based on Hook calls.
971
969
  // const [state, setState] = useState() / React.useState()
972
970
  // ^^^ true for this reference
973
971
  // const [state, dispatch] = useReducer() / React.useReducer()
@@ -976,7 +974,7 @@ var ExhaustiveDeps = {
976
974
  // ^^^ true for this reference
977
975
  // False for everything else.
978
976
 
979
- function isStaticKnownHookValue(resolved) {
977
+ function isStableKnownHookValue(resolved) {
980
978
  if (!Array.isArray(resolved.defs)) {
981
979
  return false;
982
980
  }
@@ -1019,7 +1017,7 @@ var ExhaustiveDeps = {
1019
1017
  }
1020
1018
 
1021
1019
  if (declaration.kind === 'const' && init.type === 'Literal' && (typeof init.value === 'string' || typeof init.value === 'number' || init.value === null)) {
1022
- // Definitely static
1020
+ // Definitely stable
1023
1021
  return true;
1024
1022
  } // Detect known Hook calls
1025
1023
  // const [_, setState] = useState()
@@ -1044,10 +1042,10 @@ var ExhaustiveDeps = {
1044
1042
  name = _callee.name;
1045
1043
 
1046
1044
  if (name === 'useRef' && id.type === 'Identifier') {
1047
- // useRef() return value is static.
1045
+ // useRef() return value is stable.
1048
1046
  return true;
1049
1047
  } else if (name === 'useState' || name === 'useReducer') {
1050
- // Only consider second value in initializing tuple static.
1048
+ // Only consider second value in initializing tuple stable.
1051
1049
  if (id.type === 'ArrayPattern' && id.elements.length === 2 && Array.isArray(resolved.identifiers)) {
1052
1050
  // Is second tuple value the same reference we're checking?
1053
1051
  if (id.elements[1] === resolved.identifiers[0]) {
@@ -1057,7 +1055,7 @@ var ExhaustiveDeps = {
1057
1055
  for (var i = 0; i < references.length; i++) {
1058
1056
  setStateCallSites.set(references[i].identifier, id.elements[0]);
1059
1057
  }
1060
- } // Setter is static.
1058
+ } // Setter is stable.
1061
1059
 
1062
1060
 
1063
1061
  return true;
@@ -1130,20 +1128,20 @@ var ExhaustiveDeps = {
1130
1128
  continue;
1131
1129
  }
1132
1130
 
1133
- if (pureScopes.has(ref.resolved.scope) && // Static values are fine though,
1131
+ if (pureScopes.has(ref.resolved.scope) && // Stable values are fine though,
1134
1132
  // although we won't check functions deeper.
1135
- !memoizedIsStaticKnownHookValue(ref.resolved)) {
1133
+ !memoizedIsStablecKnownHookValue(ref.resolved)) {
1136
1134
  return false;
1137
1135
  }
1138
1136
  } // If we got here, this function doesn't capture anything
1139
- // from render--or everything it captures is known static.
1137
+ // from render--or everything it captures is known stable.
1140
1138
 
1141
1139
 
1142
1140
  return true;
1143
1141
  } // Remember such values. Avoid re-running extra checks on them.
1144
1142
 
1145
1143
 
1146
- var memoizedIsStaticKnownHookValue = memoizeWithWeakMap(isStaticKnownHookValue, staticKnownValueCache);
1144
+ var memoizedIsStablecKnownHookValue = memoizeWithWeakMap(isStableKnownHookValue, stableKnownValueCache);
1147
1145
  var memoizedIsFunctionWithoutCapturedValues = memoizeWithWeakMap(isFunctionWithoutCapturedValues, functionWithoutCapturedValueCache); // These are usually mistaken. Collect them.
1148
1146
 
1149
1147
  var currentRefsInEffectCleanup = new Map(); // Is this reference inside a cleanup function for this effect node?
@@ -1164,10 +1162,11 @@ var ExhaustiveDeps = {
1164
1162
 
1165
1163
  return isInReturnedFunction;
1166
1164
  } // Get dependencies from all our resolved references in pure scopes.
1167
- // Key is dependency string, value is whether it's static.
1165
+ // Key is dependency string, value is whether it's stable.
1168
1166
 
1169
1167
 
1170
1168
  var dependencies = new Map();
1169
+ var optionalChains = new Map();
1171
1170
  gatherDependenciesRecursively(scope);
1172
1171
 
1173
1172
  function gatherDependenciesRecursively(currentScope) {
@@ -1193,11 +1192,11 @@ var ExhaustiveDeps = {
1193
1192
 
1194
1193
  var referenceNode = fastFindReferenceWithParent(node, reference.identifier);
1195
1194
  var dependencyNode = getDependency(referenceNode);
1196
- var dependency = toPropertyAccessString(dependencyNode); // Accessing ref.current inside effect cleanup is bad.
1195
+ var dependency = analyzePropertyChain(dependencyNode, optionalChains); // Accessing ref.current inside effect cleanup is bad.
1197
1196
 
1198
1197
  if ( // We're in an effect...
1199
1198
  isEffect && // ... and this look like accessing .current...
1200
- dependencyNode.type === 'Identifier' && dependencyNode.parent.type === 'MemberExpression' && !dependencyNode.parent.computed && dependencyNode.parent.property.type === 'Identifier' && dependencyNode.parent.property.name === 'current' && // ...in a cleanup function or below...
1199
+ dependencyNode.type === 'Identifier' && (dependencyNode.parent.type === 'MemberExpression' || dependencyNode.parent.type === 'OptionalMemberExpression') && !dependencyNode.parent.computed && dependencyNode.parent.property.type === 'Identifier' && dependencyNode.parent.property.name === 'current' && // ...in a cleanup function or below...
1201
1200
  isInsideEffectCleanup(reference)) {
1202
1201
  currentRefsInEffectCleanup.set(dependency, {
1203
1202
  reference: reference,
@@ -1205,6 +1204,10 @@ var ExhaustiveDeps = {
1205
1204
  });
1206
1205
  }
1207
1206
 
1207
+ if (dependencyNode.parent.type === 'TSTypeQuery') {
1208
+ continue;
1209
+ }
1210
+
1208
1211
  var def = reference.resolved.defs[0];
1209
1212
 
1210
1213
  if (def == null) {
@@ -1220,14 +1223,14 @@ var ExhaustiveDeps = {
1220
1223
  if (def.type === 'TypeParameter') {
1221
1224
  continue;
1222
1225
  } // Add the dependency to a map so we can make sure it is referenced
1223
- // again in our dependencies array. Remember whether it's static.
1226
+ // again in our dependencies array. Remember whether it's stable.
1224
1227
 
1225
1228
 
1226
1229
  if (!dependencies.has(dependency)) {
1227
1230
  var resolved = reference.resolved;
1228
- var isStatic = memoizedIsStaticKnownHookValue(resolved) || memoizedIsFunctionWithoutCapturedValues(resolved);
1231
+ var isStable = memoizedIsStablecKnownHookValue(resolved) || memoizedIsFunctionWithoutCapturedValues(resolved);
1229
1232
  dependencies.set(dependency, {
1230
- isStatic: isStatic,
1233
+ isStable: isStable,
1231
1234
  references: [reference]
1232
1235
  });
1233
1236
  } else {
@@ -1288,6 +1291,7 @@ var ExhaustiveDeps = {
1288
1291
  var parent = identifier.parent;
1289
1292
 
1290
1293
  if (parent != null && // ref.current
1294
+ // Note: no need to handle OptionalMemberExpression because it can't be LHS.
1291
1295
  parent.type === 'MemberExpression' && !parent.computed && parent.property.type === 'Identifier' && parent.property.name === 'current' && // ref.current = <something>
1292
1296
  parent.parent.type === 'AssignmentExpression' && parent.parent.left === parent) {
1293
1297
  foundCurrentAssignment = true;
@@ -1319,16 +1323,16 @@ var ExhaustiveDeps = {
1319
1323
  node: writeExpr,
1320
1324
  message: "Assignments to the '" + key + "' variable from inside React Hook " + (context.getSource(reactiveHook) + " will be lost after each ") + "render. To preserve the value over time, store it in a useRef " + "Hook and keep the mutable value in the '.current' property. " + "Otherwise, you can move this variable directly inside " + (context.getSource(reactiveHook) + ".")
1321
1325
  });
1322
- } // Remember which deps are optional and report bad usage first.
1326
+ } // Remember which deps are stable and report bad usage first.
1323
1327
 
1324
1328
 
1325
- var optionalDependencies = new Set();
1329
+ var stableDependencies = new Set();
1326
1330
  dependencies.forEach(function (_ref2, key) {
1327
- var isStatic = _ref2.isStatic,
1331
+ var isStable = _ref2.isStable,
1328
1332
  references = _ref2.references;
1329
1333
 
1330
- if (isStatic) {
1331
- optionalDependencies.add(key);
1334
+ if (isStable) {
1335
+ stableDependencies.add(key);
1332
1336
  }
1333
1337
 
1334
1338
  references.forEach(function (reference) {
@@ -1348,7 +1352,7 @@ var ExhaustiveDeps = {
1348
1352
  // Those tend to lead to infinite loops.
1349
1353
  var setStateInsideEffectWithoutDeps = null;
1350
1354
  dependencies.forEach(function (_ref3, key) {
1351
- var isStatic = _ref3.isStatic,
1355
+ var isStable = _ref3.isStable,
1352
1356
  references = _ref3.references;
1353
1357
 
1354
1358
  if (setStateInsideEffectWithoutDeps) {
@@ -1386,7 +1390,7 @@ var ExhaustiveDeps = {
1386
1390
  var _collectRecommendatio = collectRecommendations({
1387
1391
  dependencies: dependencies,
1388
1392
  declaredDependencies: [],
1389
- optionalDependencies: optionalDependencies,
1393
+ stableDependencies: stableDependencies,
1390
1394
  externalDependencies: new Set(),
1391
1395
  isEffect: true
1392
1396
  }),
@@ -1439,7 +1443,7 @@ var ExhaustiveDeps = {
1439
1443
  var declaredDependency;
1440
1444
 
1441
1445
  try {
1442
- declaredDependency = toPropertyAccessString(declaredDependencyNode);
1446
+ declaredDependency = analyzePropertyChain(declaredDependencyNode, null);
1443
1447
  } catch (error) {
1444
1448
  if (/Unsupported node type/.test(error.message)) {
1445
1449
  if (declaredDependencyNode.type === 'Literal') {
@@ -1469,7 +1473,7 @@ var ExhaustiveDeps = {
1469
1473
 
1470
1474
  var maybeID = declaredDependencyNode;
1471
1475
 
1472
- while (maybeID.type === 'MemberExpression') {
1476
+ while (maybeID.type === 'MemberExpression' || maybeID.type === 'OptionalMemberExpression') {
1473
1477
  maybeID = maybeID.object;
1474
1478
  }
1475
1479
 
@@ -1491,7 +1495,7 @@ var ExhaustiveDeps = {
1491
1495
  var _collectRecommendatio2 = collectRecommendations({
1492
1496
  dependencies: dependencies,
1493
1497
  declaredDependencies: declaredDependencies,
1494
- optionalDependencies: optionalDependencies,
1498
+ stableDependencies: stableDependencies,
1495
1499
  externalDependencies: externalDependencies,
1496
1500
  isEffect: isEffect
1497
1501
  }),
@@ -1563,7 +1567,7 @@ var ExhaustiveDeps = {
1563
1567
  dependencies: dependencies,
1564
1568
  declaredDependencies: [],
1565
1569
  // Pretend we don't know
1566
- optionalDependencies: optionalDependencies,
1570
+ stableDependencies: stableDependencies,
1567
1571
  externalDependencies: externalDependencies,
1568
1572
  isEffect: isEffect
1569
1573
  }).suggestedDependencies;
@@ -1584,6 +1588,27 @@ var ExhaustiveDeps = {
1584
1588
 
1585
1589
  if (areDeclaredDepsAlphabetized()) {
1586
1590
  suggestedDeps.sort();
1591
+ } // Most of our algorithm deals with dependency paths with optional chaining stripped.
1592
+ // This function is the last step before printing a dependency, so now is a good time to
1593
+ // check whether any members in our path are always used as optional-only. In that case,
1594
+ // we will use ?. instead of . to concatenate those parts of the path.
1595
+
1596
+
1597
+ function formatDependency(path) {
1598
+ var members = path.split('.');
1599
+ var finalPath = '';
1600
+
1601
+ for (var i = 0; i < members.length; i++) {
1602
+ if (i !== 0) {
1603
+ var pathSoFar = members.slice(0, i + 1).join('.');
1604
+ var isOptional = optionalChains.get(pathSoFar) === true;
1605
+ finalPath += isOptional ? '?.' : '.';
1606
+ }
1607
+
1608
+ finalPath += members[i];
1609
+ }
1610
+
1611
+ return finalPath;
1587
1612
  }
1588
1613
 
1589
1614
  function getWarningMessage(deps, singlePrefix, label, fixVerb) {
@@ -1592,7 +1617,7 @@ var ExhaustiveDeps = {
1592
1617
  }
1593
1618
 
1594
1619
  return (deps.size > 1 ? '' : singlePrefix + ' ') + label + ' ' + (deps.size > 1 ? 'dependencies' : 'dependency') + ': ' + joinEnglish(Array.from(deps).sort().map(function (name) {
1595
- return "'" + name + "'";
1620
+ return "'" + formatDependency(name) + "'";
1596
1621
  })) + (". Either " + fixVerb + " " + (deps.size > 1 ? 'them' : 'it') + " or remove the dependency array.");
1597
1622
  }
1598
1623
 
@@ -1656,7 +1681,7 @@ var ExhaustiveDeps = {
1656
1681
  break;
1657
1682
  }
1658
1683
 
1659
- if (parent.type !== 'MemberExpression') {
1684
+ if (parent.type !== 'MemberExpression' && parent.type !== 'OptionalMemberExpression') {
1660
1685
  isPropsOnlyUsedInMembers = false;
1661
1686
  break;
1662
1687
  }
@@ -1698,7 +1723,7 @@ var ExhaustiveDeps = {
1698
1723
  for (var _i2 = 0; _i2 < usedDep.references.length; _i2++) {
1699
1724
  id = usedDep.references[_i2].identifier;
1700
1725
 
1701
- if (id != null && id.parent != null && id.parent.type === 'CallExpression' && id.parent.callee === id) {
1726
+ if (id != null && id.parent != null && (id.parent.type === 'CallExpression' || id.parent.type === 'OptionalCallExpression') && id.parent.callee === id) {
1702
1727
  isFunctionCall = true;
1703
1728
  break;
1704
1729
  }
@@ -1811,10 +1836,10 @@ var ExhaustiveDeps = {
1811
1836
  message: "React Hook " + context.getSource(reactiveHook) + " has " + ( // To avoid a long message, show the next actionable item.
1812
1837
  getWarningMessage(missingDependencies, 'a', 'missing', 'include') || getWarningMessage(unnecessaryDependencies, 'an', 'unnecessary', 'exclude') || getWarningMessage(duplicateDependencies, 'a', 'duplicate', 'omit')) + extraWarning,
1813
1838
  suggest: [{
1814
- desc: "Update the dependencies array to be: [" + suggestedDeps.join(', ') + "]",
1839
+ desc: "Update the dependencies array to be: [" + suggestedDeps.map(formatDependency).join(', ') + "]",
1815
1840
  fix: function (fixer) {
1816
1841
  // TODO: consider preserving the comments or formatting?
1817
- return fixer.replaceText(declaredDependenciesNode, "[" + suggestedDeps.join(', ') + "]");
1842
+ return fixer.replaceText(declaredDependenciesNode, "[" + suggestedDeps.map(formatDependency).join(', ') + "]");
1818
1843
  }
1819
1844
  }]
1820
1845
  });
@@ -1825,7 +1850,7 @@ var ExhaustiveDeps = {
1825
1850
  function collectRecommendations(_ref5) {
1826
1851
  var dependencies = _ref5.dependencies,
1827
1852
  declaredDependencies = _ref5.declaredDependencies,
1828
- optionalDependencies = _ref5.optionalDependencies,
1853
+ stableDependencies = _ref5.stableDependencies,
1829
1854
  externalDependencies = _ref5.externalDependencies,
1830
1855
  isEffect = _ref5.isEffect;
1831
1856
  // Our primary data structure.
@@ -1841,11 +1866,11 @@ function collectRecommendations(_ref5) {
1841
1866
 
1842
1867
  function createDepTree() {
1843
1868
  return {
1844
- isRequired: false,
1869
+ isUsed: false,
1845
1870
  // True if used in code
1846
1871
  isSatisfiedRecursively: false,
1847
1872
  // True if specified in deps
1848
- hasRequiredNodesBelow: false,
1873
+ isSubtreeUsed: false,
1849
1874
  // True if something deeper is used by code
1850
1875
  children: new Map() // Nodes for properties
1851
1876
 
@@ -1856,9 +1881,9 @@ function collectRecommendations(_ref5) {
1856
1881
 
1857
1882
  dependencies.forEach(function (_, key) {
1858
1883
  var node = getOrCreateNodeByPath(depTree, key);
1859
- node.isRequired = true;
1884
+ node.isUsed = true;
1860
1885
  markAllParentsByPath(depTree, key, function (parent) {
1861
- parent.hasRequiredNodesBelow = true;
1886
+ parent.isSubtreeUsed = true;
1862
1887
  });
1863
1888
  }); // Mark all satisfied nodes.
1864
1889
  // Imagine checkmarks next to each declared dependency.
@@ -1868,7 +1893,7 @@ function collectRecommendations(_ref5) {
1868
1893
  var node = getOrCreateNodeByPath(depTree, key);
1869
1894
  node.isSatisfiedRecursively = true;
1870
1895
  });
1871
- optionalDependencies.forEach(function (key) {
1896
+ stableDependencies.forEach(function (key) {
1872
1897
  var node = getOrCreateNodeByPath(depTree, key);
1873
1898
  node.isSatisfiedRecursively = true;
1874
1899
  }); // Tree manipulation helpers.
@@ -1883,7 +1908,7 @@ function collectRecommendations(_ref5) {
1883
1908
  try {
1884
1909
  for (var _iterator3 = keys[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
1885
1910
  var key = _step3.value;
1886
- var child = getChildByKey(node, key);
1911
+ var child = node.children.get(key);
1887
1912
 
1888
1913
  if (!child) {
1889
1914
  child = createDepTree();
@@ -1920,7 +1945,7 @@ function collectRecommendations(_ref5) {
1920
1945
  try {
1921
1946
  for (var _iterator4 = keys[Symbol.iterator](), _step4; !(_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done); _iteratorNormalCompletion4 = true) {
1922
1947
  var key = _step4.value;
1923
- var child = getChildByKey(node, key);
1948
+ var child = node.children.get(key);
1924
1949
 
1925
1950
  if (!child) {
1926
1951
  return;
@@ -1943,18 +1968,6 @@ function collectRecommendations(_ref5) {
1943
1968
  }
1944
1969
  }
1945
1970
  }
1946
- }
1947
- /**
1948
- * Match key with optional chaining
1949
- * key -> key
1950
- * key? -> key
1951
- * key -> key?
1952
- * Otherwise undefined.
1953
- */
1954
-
1955
-
1956
- function getChildByKey(node, key) {
1957
- return node.children.get(key) || node.children.get(key.split('?')[0]) || node.children.get(key + '?');
1958
1971
  } // Now we can learn which dependencies are missing or necessary.
1959
1972
 
1960
1973
 
@@ -1969,7 +1982,7 @@ function collectRecommendations(_ref5) {
1969
1982
  var path = keyToPath(key);
1970
1983
 
1971
1984
  if (child.isSatisfiedRecursively) {
1972
- if (child.hasRequiredNodesBelow) {
1985
+ if (child.isSubtreeUsed) {
1973
1986
  // Remember this dep actually satisfied something.
1974
1987
  satisfyingPaths.add(path);
1975
1988
  } // It doesn't matter if there's something deeper.
@@ -1980,7 +1993,7 @@ function collectRecommendations(_ref5) {
1980
1993
  return;
1981
1994
  }
1982
1995
 
1983
- if (child.isRequired) {
1996
+ if (child.isUsed) {
1984
1997
  // Remember that no declared deps satisfied this node.
1985
1998
  missingPaths.add(path); // If we got here, nothing in its subtree was satisfied.
1986
1999
  // No need to search further.
@@ -2124,9 +2137,10 @@ function scanForDeclaredBareFunctions(_ref8) {
2124
2137
 
2125
2138
 
2126
2139
  function getDependency(node) {
2127
- if ((node.parent.type === 'MemberExpression' || node.parent.type === 'OptionalMemberExpression') && node.parent.object === node && node.parent.property.name !== 'current' && !node.parent.computed && !(node.parent.parent != null && node.parent.parent.type === 'CallExpression' && node.parent.parent.callee === node.parent)) {
2140
+ if ((node.parent.type === 'MemberExpression' || node.parent.type === 'OptionalMemberExpression') && node.parent.object === node && node.parent.property.name !== 'current' && !node.parent.computed && !(node.parent.parent != null && (node.parent.parent.type === 'CallExpression' || node.parent.parent.type === 'OptionalCallExpression') && node.parent.parent.callee === node.parent)) {
2128
2141
  return getDependency(node.parent);
2129
- } else if (node.type === 'MemberExpression' && node.parent && node.parent.type === 'AssignmentExpression') {
2142
+ } else if ( // Note: we don't check OptionalMemberExpression because it can't be LHS.
2143
+ node.type === 'MemberExpression' && node.parent && node.parent.type === 'AssignmentExpression' && node.parent.left === node) {
2130
2144
  return node.object;
2131
2145
  } else {
2132
2146
  return node;
@@ -2135,26 +2149,58 @@ function getDependency(node) {
2135
2149
  /**
2136
2150
  * Assuming () means the passed node.
2137
2151
  * (foo) -> 'foo'
2138
- * foo.(bar) -> 'foo.bar'
2139
- * foo.bar.(baz) -> 'foo.bar.baz'
2140
- * foo?.(bar) -> 'foo?.bar'
2152
+ * foo(.)bar -> 'foo.bar'
2153
+ * foo.bar(.)baz -> 'foo.bar.baz'
2141
2154
  * Otherwise throw.
2142
2155
  */
2143
2156
 
2144
2157
 
2145
- function toPropertyAccessString(node) {
2158
+ function analyzePropertyChain(node, optionalChains) {
2146
2159
  if (node.type === 'Identifier') {
2147
- return node.name;
2160
+ var result = node.name;
2161
+
2162
+ if (optionalChains) {
2163
+ // Mark as required.
2164
+ optionalChains.set(result, false);
2165
+ }
2166
+
2167
+ return result;
2148
2168
  } else if (node.type === 'MemberExpression' && !node.computed) {
2149
- var object = toPropertyAccessString(node.object);
2150
- var property = toPropertyAccessString(node.property);
2151
- return object + "." + property;
2169
+ var object = analyzePropertyChain(node.object, optionalChains);
2170
+ var property = analyzePropertyChain(node.property, null);
2171
+
2172
+ var _result = object + "." + property;
2173
+
2174
+ if (optionalChains) {
2175
+ // Mark as required.
2176
+ optionalChains.set(_result, false);
2177
+ }
2178
+
2179
+ return _result;
2152
2180
  } else if (node.type === 'OptionalMemberExpression' && !node.computed) {
2153
- var _object = toPropertyAccessString(node.object);
2181
+ var _object = analyzePropertyChain(node.object, optionalChains);
2182
+
2183
+ var _property = analyzePropertyChain(node.property, null);
2154
2184
 
2155
- var _property = toPropertyAccessString(node.property);
2185
+ var _result2 = _object + "." + _property;
2186
+
2187
+ if (optionalChains) {
2188
+ // Note: OptionalMemberExpression doesn't necessarily mean this node is optional.
2189
+ // It just means there is an optional member somewhere inside.
2190
+ // This particular node might still represent a required member, so check .optional field.
2191
+ if (node.optional) {
2192
+ // We only want to consider it optional if *all* usages were optional.
2193
+ if (!optionalChains.has(_result2)) {
2194
+ // Mark as (maybe) optional. If there's a required usage, this will be overridden.
2195
+ optionalChains.set(_result2, true);
2196
+ }
2197
+ } else {
2198
+ // Mark as required.
2199
+ optionalChains.set(_result2, false);
2200
+ }
2201
+ }
2156
2202
 
2157
- return _object + "?." + _property;
2203
+ return _result2;
2158
2204
  } else {
2159
2205
  throw new Error("Unsupported node type: " + node.type);
2160
2206
  }
@@ -2199,7 +2245,7 @@ function getReactiveHookCallbackIndex(calleeNode, options) {
2199
2245
  var name;
2200
2246
 
2201
2247
  try {
2202
- name = toPropertyAccessString(node);
2248
+ name = analyzePropertyChain(node, null);
2203
2249
  } catch (error) {
2204
2250
  if (/Unsupported node type/.test(error.message)) {
2205
2251
  return 0;
@@ -6,52 +6,52 @@
6
6
  * This source code is licensed under the MIT license found in the
7
7
  * LICENSE file in the root directory of this source tree.
8
8
  */
9
-
10
- 'use strict';function L(a){if("Identifier"===a.type)return/^use[A-Z0-9].*$/.test(a.name);if("MemberExpression"===a.type&&!a.computed&&L(a.property)){a=a.object;var c=/^[A-Z].*/;return"Identifier"===a.type&&c.test(a.name)}return!1}function ba(a){return"Identifier"===a.type?!/^[a-z]/.test(a.name):!1}function ca(a,c){return a.name===c||"MemberExpression"===a.type&&"React"===a.object.name&&a.property.name===c}function fa(a){return!!(a.parent&&a.parent.callee&&ca(a.parent.callee,"forwardRef"))}
11
- function ha(a){return!!(a.parent&&a.parent.callee&&ca(a.parent.callee,"memo"))}function ia(a){for(;a;){var c=ja(a);if(c&&(ba(c)||L(c))||fa(a)||ha(a))return!0;a=a.parent}return!1}
12
- function ja(a){if("FunctionDeclaration"===a.type||"FunctionExpression"===a.type&&a.id)return a.id;if("FunctionExpression"===a.type||"ArrowFunctionExpression"===a.type)return"VariableDeclarator"===a.parent.type&&a.parent.init===a?a.parent.id:"AssignmentExpression"===a.parent.type&&a.parent.right===a&&"="===a.parent.operator?a.parent.left:"Property"!==a.parent.type||a.parent.value!==a||a.parent.computed?"AssignmentPattern"!==a.parent.type||a.parent.right!==a||a.parent.computed?void 0:a.parent.left:
9
+ 'use strict';function O(a){if("Identifier"===a.type)return/^use[A-Z0-9].*$/.test(a.name);if("MemberExpression"===a.type&&!a.computed&&O(a.property)){a=a.object;var c=/^[A-Z].*/;return"Identifier"===a.type&&c.test(a.name)}return!1}function aa(a){return"Identifier"===a.type?!/^[a-z]/.test(a.name):!1}function ba(a,c){return a.name===c||"MemberExpression"===a.type&&"React"===a.object.name&&a.property.name===c}function ea(a){return!!(a.parent&&a.parent.callee&&ba(a.parent.callee,"forwardRef"))}
10
+ function fa(a){return!!(a.parent&&a.parent.callee&&ba(a.parent.callee,"memo"))}function ha(a){for(;a;){var c=ia(a);if(c&&(aa(c)||O(c))||ea(a)||fa(a))return!0;a=a.parent}return!1}
11
+ function ia(a){if("FunctionDeclaration"===a.type||"FunctionExpression"===a.type&&a.id)return a.id;if("FunctionExpression"===a.type||"ArrowFunctionExpression"===a.type)return"VariableDeclarator"===a.parent.type&&a.parent.init===a?a.parent.id:"AssignmentExpression"===a.parent.type&&a.parent.right===a&&"="===a.parent.operator?a.parent.left:"Property"!==a.parent.type||a.parent.value!==a||a.parent.computed?"AssignmentPattern"!==a.parent.type||a.parent.right!==a||a.parent.computed?void 0:a.parent.left:
13
12
  a.parent.key}
14
- function ka(a){function c(){return{isRequired:!1,isSatisfiedRecursively:!1,hasRequiredNodesBelow:!1,children:new Map}}function e(a,d){d=d.split(".");var l=!0,b=!1,h=void 0;try{for(var e=d[Symbol.iterator](),n;!(l=(n=e.next()).done);l=!0){var B=n.value,k=f(a,B);k||(k=c(),a.children.set(B,k));a=k}}catch(r){b=!0,h=r}finally{try{l||null==e.return||e.return()}finally{if(b)throw h;}}return a}function h(a,c,d){c=c.split(".");var l=!0,b=!1,e=void 0;try{for(var h=c[Symbol.iterator](),n;!(l=(n=h.next()).done);l=
15
- !0){var k=f(a,n.value);if(!k)break;d(k);a=k}}catch(r){b=!0,e=r}finally{try{l||null==h.return||h.return()}finally{if(b)throw e;}}}function f(a,c){return a.children.get(c)||a.children.get(c.split("?")[0])||a.children.get(c+"?")}function k(a,c,d,b){a.children.forEach(function(a,l){var f=b(l);a.isSatisfiedRecursively?a.hasRequiredNodesBelow&&d.add(f):a.isRequired?c.add(f):k(a,c,d,function(a){return f+"."+a})})}var q=a.dependencies,t=a.declaredDependencies,x=a.optionalDependencies,A=a.externalDependencies,
16
- V=a.isEffect,d=c();q.forEach(function(a,c){e(d,c).isRequired=!0;h(d,c,function(a){a.hasRequiredNodesBelow=!0})});t.forEach(function(a){e(d,a.key).isSatisfiedRecursively=!0});x.forEach(function(a){e(d,a).isSatisfiedRecursively=!0});a=new Set;var b=new Set;k(d,a,b,function(a){return a});var n=[],D=new Set,p=new Set;t.forEach(function(a){a=a.key;b.has(a)?-1===n.indexOf(a)?n.push(a):p.add(a):!V||a.endsWith(".current")||A.has(a)?D.add(a):-1===n.indexOf(a)&&n.push(a)});a.forEach(function(a){n.push(a)});
17
- return{suggestedDependencies:n,unnecessaryDependencies:D,duplicateDependencies:p,missingDependencies:a}}
18
- function la(a){var c=a.declaredDependenciesNode,e=a.componentScope,h=a.scope;return a.declaredDependencies.map(function(a){a=e.set.get(a.key);if(null==a)return null;var c=a.defs[0];return null==c?null:"Variable"===c.type&&"VariableDeclarator"===c.node.type&&null!=c.node.init&&("ArrowFunctionExpression"===c.node.init.type||"FunctionExpression"===c.node.init.type)||"FunctionName"===c.type&&"FunctionDeclaration"===c.node.type?a:null}).filter(Boolean).map(function(a){var e=a.defs[0];a:{for(var f=!1,t=
19
- 0;t<a.references.length;t++){var x=a.references[t];if(x.writeExpr)if(f){a=!0;break a}else{f=!0;continue}for(var A=x.from;A!==h&&null!=A;)A=A.upper;if(A!==h&&!ma(c,x.identifier)){a=!0;break a}}a=!1}return{fn:e,suggestUseCallback:a}})}
20
- function na(a){return"MemberExpression"!==a.parent.type&&"OptionalMemberExpression"!==a.parent.type||a.parent.object!==a||"current"===a.parent.property.name||a.parent.computed||null!=a.parent.parent&&"CallExpression"===a.parent.parent.type&&a.parent.parent.callee===a.parent?"MemberExpression"===a.type&&a.parent&&"AssignmentExpression"===a.parent.type?a.object:a:na(a.parent)}
21
- function Z(a){if("Identifier"===a.type)return a.name;if("MemberExpression"!==a.type||a.computed){if("OptionalMemberExpression"!==a.type||a.computed)throw Error("Unsupported node type: "+a.type);var c=Z(a.object);a=Z(a.property);return c+"?."+a}c=Z(a.object);a=Z(a.property);return c+"."+a}function oa(a){return"MemberExpression"!==a.type||"Identifier"!==a.object.type||"React"!==a.object.name||"Identifier"!==a.property.type||a.computed?a:a.property}
22
- function pa(a,c){var e=oa(a);if("Identifier"!==e.type)return-1;switch(e.name){case "useEffect":case "useLayoutEffect":case "useCallback":case "useMemo":return 0;case "useImperativeHandle":return 1;default:if(e===a&&c&&c.additionalHooks){try{var h=Z(e)}catch(f){if(/Unsupported node type/.test(f.message))return 0;throw f;}return c.additionalHooks.test(h)?0:-1}return-1}}
23
- function qa(a,c){for(var e=[a],h=null;e.length;){h=e.shift();if("Identifier"===h.type&&h.name===c.name&&h.range[0]===c.range[0]&&h.range[1]===c.range[1])return h;if(ma(h,c)){a=0;for(var f=Object.entries(h);a<f.length;a++){var k=f[a],q=k[1];"parent"!==k[0]&&(ra(q)?(q.parent=h,e.push(q)):Array.isArray(q)&&q.forEach(function(a){ra(a)&&(a.parent=h,e.push(a))}))}}}return null}
24
- function sa(a){for(var c="",e=0;e<a.length;e++)c+=a[e],0===e&&2===a.length?c+=" and ":e===a.length-2&&2<a.length?c+=", and ":e<a.length-1&&(c+=", ");return c}function ra(a){return"object"===typeof a&&null!==a&&!Array.isArray(a)&&"string"===typeof a.type}function ma(a,c){return a.range[0]<=c.range[0]&&a.range[1]>=c.range[1]}exports.configs={recommended:{plugins:["react-hooks"],rules:{"react-hooks/rules-of-hooks":"error","react-hooks/exhaustive-deps":"warn"}}};
25
- exports.rules={"rules-of-hooks":{meta:{type:"problem",docs:{description:"enforces the Rules of Hooks",category:"Possible Errors",recommended:!0,url:"https://reactjs.org/docs/hooks-rules.html"}},create:function(a){var c=[],e=[];return{onCodePathSegmentStart:function(a){return e.push(a)},onCodePathSegmentEnd:function(){return e.pop()},onCodePathStart:function(){return c.push(new Map)},onCodePathEnd:function(e,f){function h(a,c){var d=h.cache,b=d.get(a.id);c=new Set(c);if(c.has(a.id)){d=[].concat(c);
26
- a=d.slice(d.indexOf(a.id)+1);d=!0;b=!1;var g=void 0;try{for(var m=a[Symbol.iterator](),y;!(d=(y=m.next()).done);d=!0)A.add(y.value)}catch(G){b=!0,g=G}finally{try{d||null==m.return||m.return()}finally{if(b)throw g;}}return 0}c.add(a.id);if(void 0!==b)return b;if(e.thrownSegments.includes(a))b=0;else if(0===a.prevSegments.length)b=1;else{b=0;m=!0;y=!1;var I=void 0;try{g=a.prevSegments[Symbol.iterator]();for(var J;!(m=(J=g.next()).done);m=!0)b+=h(J.value,c)}catch(G){y=!0,I=G}finally{try{m||null==
27
- g.return||g.return()}finally{if(y)throw I;}}}a.reachable&&0===b?d.delete(a.id):d.set(a.id,b);return b}function q(a,c){var d=q.cache,b=d.get(a.id);c=new Set(c);if(c.has(a.id)){d=Array.from(c);a=d.slice(d.indexOf(a.id)+1);d=!0;b=!1;var g=void 0;try{for(var m=a[Symbol.iterator](),y;!(d=(y=m.next()).done);d=!0)A.add(y.value)}catch(G){b=!0,g=G}finally{try{d||null==m.return||m.return()}finally{if(b)throw g;}}return 0}c.add(a.id);if(void 0!==b)return b;if(e.thrownSegments.includes(a))b=0;else if(0===
28
- a.nextSegments.length)b=1;else{b=0;m=!0;y=!1;var I=void 0;try{g=a.nextSegments[Symbol.iterator]();for(var J;!(m=(J=g.next()).done);m=!0)b+=q(J.value,c)}catch(G){y=!0,I=G}finally{try{m||null==g.return||g.return()}finally{if(y)throw I;}}}d.set(a.id,b);return b}function t(a){var c=t.cache,b=c.get(a.id);if(null===b)return Infinity;if(void 0!==b)return b;c.set(a.id,null);if(0===a.prevSegments.length)b=1;else{b=Infinity;var d=!0,g=!1,m=void 0;try{for(var y=a.prevSegments[Symbol.iterator](),I;!(d=
29
- (I=y.next()).done);d=!0){var e=t(I.value);e<b&&(b=e)}}catch(G){g=!0,m=G}finally{try{d||null==y.return||y.return()}finally{if(g)throw m;}}b+=1}c.set(a.id,b);return b}var x=c.pop();if(0!==x.size){var A=new Set;h.cache=new Map;q.cache=new Map;t.cache=new Map;var V=q(e.initialSegment),d=ja(f),b=ia(f),n=d?ba(d)||L(d):fa(f)||ha(f),D=Infinity,p=!0,l=!1,B=void 0;try{for(var O=e.finalSegments[Symbol.iterator](),T;!(p=(T=O.next()).done);p=!0){var C=T.value;if(C.reachable){var v=t(C);v<D&&(D=v)}}}catch(F){l=
30
- !0,B=F}finally{try{p||null==O.return||O.return()}finally{if(l)throw B;}}p=!0;l=!1;B=void 0;try{for(var P=x[Symbol.iterator](),aa;!(p=(aa=P.next()).done);p=!0){var U=aa.value,r=U[0],X=U[1];if(r.reachable){var Q=0===r.nextSegments.length?D<=t(r):D<t(r),M=h(r)*q(r),K=A.has(r.id);x=!0;O=!1;T=void 0;try{for(var u=X[Symbol.iterator](),E;!(x=(E=u.next()).done);x=!0){var z=E.value;K&&a.report({node:z,message:'React Hook "'+a.getSource(z)+'" may be executed more than once. Possibly because it is called in a loop. React Hooks must be called in the exact same order in every component render.'});
31
- if(n){if(!K&&M!==V){var R='React Hook "'+a.getSource(z)+'" is called conditionally. React Hooks must be called in the exact same order in every component render.'+(Q?" Did you accidentally call a React Hook after an early return?":"");a.report({node:z,message:R})}}else if(f.parent&&("MethodDefinition"===f.parent.type||"ClassProperty"===f.parent.type)&&f.parent.value===f){var S='React Hook "'+a.getSource(z)+'" cannot be called in a class component. React Hooks must be called in a React function component or a custom React Hook function.';
32
- a.report({node:z,message:S})}else if(d){var H='React Hook "'+a.getSource(z)+'" is called in function "'+(a.getSource(d)+'" that is neither a React function component nor a custom React Hook function. React component names must start with an uppercase letter.');a.report({node:z,message:H})}else if("Program"===f.type){var Y='React Hook "'+a.getSource(z)+'" cannot be called at the top level. React Hooks must be called in a React function component or a custom React Hook function.';a.report({node:z,message:Y})}else if(b){var N=
33
- 'React Hook "'+a.getSource(z)+'" cannot be called inside a callback. React Hooks must be called in a React function component or a custom React Hook function.';a.report({node:z,message:N})}}}catch(F){O=!0,T=F}finally{try{x||null==u.return||u.return()}finally{if(O)throw T;}}}}}catch(F){l=!0,B=F}finally{try{p||null==P.return||P.return()}finally{if(l)throw B;}}}},CallExpression:function(a){if(L(a.callee)){var h=c[c.length-1],k=e[e.length-1],q=h.get(k);q||(q=[],h.set(k,q));q.push(a.callee)}}}}},"exhaustive-deps":{meta:{type:"suggestion",
13
+ function ja(a){function c(){return{isUsed:!1,isSatisfiedRecursively:!1,isSubtreeUsed:!1,children:new Map}}function e(a,b){b=b.split(".");var m=!0,e=!1,f=void 0;try{for(var d=b[Symbol.iterator](),h;!(m=(h=d.next()).done);m=!0){var y=h.value,l=a.children.get(y);l||(l=c(),a.children.set(y,l));a=l}}catch(K){e=!0,f=K}finally{try{m||null==d.return||d.return()}finally{if(e)throw f;}}return a}function h(a,c,b){c=c.split(".");var m=!0,e=!1,f=void 0;try{for(var d=c[Symbol.iterator](),h;!(m=(h=d.next()).done);m=
14
+ !0){var l=a.children.get(h.value);if(!l)break;b(l);a=l}}catch(K){e=!0,f=K}finally{try{m||null==d.return||d.return()}finally{if(e)throw f;}}}function f(a,c,b,e){a.children.forEach(function(a,m){var d=e(m);a.isSatisfiedRecursively?a.isSubtreeUsed&&b.add(d):a.isUsed?c.add(d):f(a,c,b,function(a){return d+"."+a})})}var B=a.dependencies,k=a.declaredDependencies,q=a.stableDependencies,u=a.externalDependencies,v=a.isEffect,E=c();B.forEach(function(a,c){e(E,c).isUsed=!0;h(E,c,function(a){a.isSubtreeUsed=!0})});
15
+ k.forEach(function(a){e(E,a.key).isSatisfiedRecursively=!0});q.forEach(function(a){e(E,a).isSatisfiedRecursively=!0});a=new Set;var d=new Set;f(E,a,d,function(a){return a});var b=[],l=new Set,w=new Set;k.forEach(function(a){a=a.key;d.has(a)?-1===b.indexOf(a)?b.push(a):w.add(a):!v||a.endsWith(".current")||u.has(a)?l.add(a):-1===b.indexOf(a)&&b.push(a)});a.forEach(function(a){b.push(a)});return{suggestedDependencies:b,unnecessaryDependencies:l,duplicateDependencies:w,missingDependencies:a}}
16
+ function la(a){var c=a.declaredDependenciesNode,e=a.componentScope,h=a.scope;return a.declaredDependencies.map(function(a){a=e.set.get(a.key);if(null==a)return null;var c=a.defs[0];return null==c?null:"Variable"===c.type&&"VariableDeclarator"===c.node.type&&null!=c.node.init&&("ArrowFunctionExpression"===c.node.init.type||"FunctionExpression"===c.node.init.type)||"FunctionName"===c.type&&"FunctionDeclaration"===c.node.type?a:null}).filter(Boolean).map(function(a){var e=a.defs[0];a:{for(var f=!1,q=
17
+ 0;q<a.references.length;q++){var u=a.references[q];if(u.writeExpr)if(f){a=!0;break a}else{f=!0;continue}for(var v=u.from;v!==h&&null!=v;)v=v.upper;if(v!==h&&!ma(c,u.identifier)){a=!0;break a}}a=!1}return{fn:e,suggestUseCallback:a}})}
18
+ function na(a){return"MemberExpression"!==a.parent.type&&"OptionalMemberExpression"!==a.parent.type||a.parent.object!==a||"current"===a.parent.property.name||a.parent.computed||null!=a.parent.parent&&("CallExpression"===a.parent.parent.type||"OptionalCallExpression"===a.parent.parent.type)&&a.parent.parent.callee===a.parent?"MemberExpression"===a.type&&a.parent&&"AssignmentExpression"===a.parent.type&&a.parent.left===a?a.object:a:na(a.parent)}
19
+ function W(a,c){if("Identifier"===a.type)return a=a.name,c&&c.set(a,!1),a;if("MemberExpression"!==a.type||a.computed){if("OptionalMemberExpression"!==a.type||a.computed)throw Error("Unsupported node type: "+a.type);var e=W(a.object,c),h=W(a.property,null);e=e+"."+h;c&&(a.optional?c.has(e)||c.set(e,!0):c.set(e,!1));return e}e=W(a.object,c);a=W(a.property,null);a=e+"."+a;c&&c.set(a,!1);return a}
20
+ function oa(a){return"MemberExpression"!==a.type||"Identifier"!==a.object.type||"React"!==a.object.name||"Identifier"!==a.property.type||a.computed?a:a.property}
21
+ function qa(a,c){var e=oa(a);if("Identifier"!==e.type)return-1;switch(e.name){case "useEffect":case "useLayoutEffect":case "useCallback":case "useMemo":return 0;case "useImperativeHandle":return 1;default:if(e===a&&c&&c.additionalHooks){try{var h=W(e,null)}catch(f){if(/Unsupported node type/.test(f.message))return 0;throw f;}return c.additionalHooks.test(h)?0:-1}return-1}}
22
+ function ra(a,c){for(var e=[a],h=null;e.length;){h=e.shift();if("Identifier"===h.type&&h.name===c.name&&h.range[0]===c.range[0]&&h.range[1]===c.range[1])return h;if(ma(h,c)){a=0;for(var f=Object.entries(h);a<f.length;a++){var B=f[a],k=B[1];"parent"!==B[0]&&(sa(k)?(k.parent=h,e.push(k)):Array.isArray(k)&&k.forEach(function(a){sa(a)&&(a.parent=h,e.push(a))}))}}}return null}
23
+ function ta(a){for(var c="",e=0;e<a.length;e++)c+=a[e],0===e&&2===a.length?c+=" and ":e===a.length-2&&2<a.length?c+=", and ":e<a.length-1&&(c+=", ");return c}function sa(a){return"object"===typeof a&&null!==a&&!Array.isArray(a)&&"string"===typeof a.type}function ma(a,c){return a.range[0]<=c.range[0]&&a.range[1]>=c.range[1]}exports.configs={recommended:{plugins:["react-hooks"],rules:{"react-hooks/rules-of-hooks":"error","react-hooks/exhaustive-deps":"warn"}}};
24
+ exports.rules={"rules-of-hooks":{meta:{type:"problem",docs:{description:"enforces the Rules of Hooks",category:"Possible Errors",recommended:!0,url:"https://reactjs.org/docs/hooks-rules.html"}},create:function(a){var c=[],e=[];return{onCodePathSegmentStart:function(a){return e.push(a)},onCodePathSegmentEnd:function(){return e.pop()},onCodePathStart:function(){return c.push(new Map)},onCodePathEnd:function(e,f){function h(a,c){var b=h.cache,d=b.get(a.id);c=new Set(c);if(c.has(a.id)){b=[].concat(c);
25
+ a=b.slice(b.indexOf(a.id)+1);b=!0;d=!1;var l=void 0;try{for(var f=a[Symbol.iterator](),g;!(b=(g=f.next()).done);b=!0)v.add(g.value)}catch(G){d=!0,l=G}finally{try{b||null==f.return||f.return()}finally{if(d)throw l;}}return 0}c.add(a.id);if(void 0!==d)return d;if(e.thrownSegments.includes(a))d=0;else if(0===a.prevSegments.length)d=1;else{d=0;f=!0;g=!1;var L=void 0;try{l=a.prevSegments[Symbol.iterator]();for(var M;!(f=(M=l.next()).done);f=!0)d+=h(M.value,c)}catch(G){g=!0,L=G}finally{try{f||null==l.return||
26
+ l.return()}finally{if(g)throw L;}}}a.reachable&&0===d?b.delete(a.id):b.set(a.id,d);return d}function k(a,c){var d=k.cache,b=d.get(a.id);c=new Set(c);if(c.has(a.id)){d=Array.from(c);a=d.slice(d.indexOf(a.id)+1);d=!0;b=!1;var f=void 0;try{for(var l=a[Symbol.iterator](),g;!(d=(g=l.next()).done);d=!0)v.add(g.value)}catch(G){b=!0,f=G}finally{try{d||null==l.return||l.return()}finally{if(b)throw f;}}return 0}c.add(a.id);if(void 0!==b)return b;if(e.thrownSegments.includes(a))b=0;else if(0===a.nextSegments.length)b=
27
+ 1;else{b=0;l=!0;g=!1;var L=void 0;try{f=a.nextSegments[Symbol.iterator]();for(var M;!(l=(M=f.next()).done);l=!0)b+=k(M.value,c)}catch(G){g=!0,L=G}finally{try{l||null==f.return||f.return()}finally{if(g)throw L;}}}d.set(a.id,b);return b}function q(a){var c=q.cache,b=c.get(a.id);if(null===b)return Infinity;if(void 0!==b)return b;c.set(a.id,null);if(0===a.prevSegments.length)b=1;else{b=Infinity;var d=!0,e=!1,l=void 0;try{for(var g=a.prevSegments[Symbol.iterator](),L;!(d=(L=g.next()).done);d=!0){var M=
28
+ q(L.value);M<b&&(b=M)}}catch(G){e=!0,l=G}finally{try{d||null==g.return||g.return()}finally{if(e)throw l;}}b+=1}c.set(a.id,b);return b}var u=c.pop();if(0!==u.size){var v=new Set;h.cache=new Map;k.cache=new Map;q.cache=new Map;var E=k(e.initialSegment),d=ia(f),b=ha(f),l=d?aa(d)||O(d):ea(f)||fa(f),w=Infinity,m=!0,y=!1,J=void 0;try{for(var A=e.finalSegments[Symbol.iterator](),p;!(m=(p=A.next()).done);m=!0){var ka=p.value;if(ka.reachable){var z=q(ka);z<w&&(w=z)}}}catch(F){y=!0,J=F}finally{try{m||null==
29
+ A.return||A.return()}finally{if(y)throw J;}}m=!0;y=!1;J=void 0;try{for(var r=u[Symbol.iterator](),T;!(m=(T=r.next()).done);m=!0){var K=T.value,C=K[0],x=K[1];if(C.reachable){var Y=0===C.nextSegments.length?w<=q(C):w<q(C),V=h(C)*k(C),P=v.has(C.id);u=!0;A=!1;p=void 0;try{for(var H=x[Symbol.iterator](),N;!(u=(N=H.next()).done);u=!0){var n=N.value;P&&a.report({node:n,message:'React Hook "'+a.getSource(n)+'" may be executed more than once. Possibly because it is called in a loop. React Hooks must be called in the exact same order in every component render.'});
30
+ if(l){if(!P&&V!==E){var D='React Hook "'+a.getSource(n)+'" is called conditionally. React Hooks must be called in the exact same order in every component render.'+(Y?" Did you accidentally call a React Hook after an early return?":"");a.report({node:n,message:D})}}else if(f.parent&&("MethodDefinition"===f.parent.type||"ClassProperty"===f.parent.type)&&f.parent.value===f){var Z='React Hook "'+a.getSource(n)+'" cannot be called in a class component. React Hooks must be called in a React function component or a custom React Hook function.';
31
+ a.report({node:n,message:Z})}else if(d){var R='React Hook "'+a.getSource(n)+'" is called in function "'+(a.getSource(d)+'" that is neither a React function component nor a custom React Hook function. React component names must start with an uppercase letter.');a.report({node:n,message:R})}else if("Program"===f.type){var S='React Hook "'+a.getSource(n)+'" cannot be called at the top level. React Hooks must be called in a React function component or a custom React Hook function.';a.report({node:n,message:S})}else if(b){var I=
32
+ 'React Hook "'+a.getSource(n)+'" cannot be called inside a callback. React Hooks must be called in a React function component or a custom React Hook function.';a.report({node:n,message:I})}}}catch(F){A=!0,p=F}finally{try{u||null==H.return||H.return()}finally{if(A)throw p;}}}}}catch(F){y=!0,J=F}finally{try{m||null==r.return||r.return()}finally{if(y)throw J;}}}},CallExpression:function(a){if(O(a.callee)){var f=c[c.length-1],h=e[e.length-1],k=f.get(h);k||(k=[],f.set(h,k));k.push(a.callee)}}}}},"exhaustive-deps":{meta:{type:"suggestion",
34
33
  docs:{description:"verifies the list of dependencies for Hooks like useEffect and similar",category:"Best Practices",recommended:!0,url:"https://github.com/facebook/react/issues/14920"},fixable:"code",schema:[{type:"object",additionalProperties:!1,enableDangerousAutofixThisMayCauseInfiniteLoops:!1,properties:{additionalHooks:{type:"string"},enableDangerousAutofixThisMayCauseInfiniteLoops:{type:"boolean"}}}]},create:function(a){function c(c){f&&Array.isArray(c.suggest)&&0<c.suggest.length&&(c.fix=
35
- c.suggest[0].fix);a.report(c)}function e(a,c){return function(b){if(c.has(b))return c.get(b);var d=a(b);c.set(b,d);return d}}function h(d,b,h,f,p){function l(a){var c=!0,g=!1,b=void 0;try{for(var e=a.references[Symbol.iterator](),h;!(c=(h=e.next()).done);c=!0){var f=h.value;if(f.resolved&&D.has(f.resolved.scope)){var k=qa(d,f.identifier),n=na(k),q=Z(n),u;if(u=p&&"Identifier"===n.type&&"MemberExpression"===n.parent.type&&!n.parent.computed&&"Identifier"===n.parent.property.type&&"current"===n.parent.property.name){for(var t=
36
- f.from,w=!1;t.block!==d;)"function"===t.type&&(w=null!=t.block.parent&&"ReturnStatement"===t.block.parent.type),t=t.upper;u=w}u&&U.set(q,{reference:f,dependencyNode:n});var v=f.resolved.defs[0];if(null!=v&&(null==v.node||v.node.init!==d.parent)&&"TypeParameter"!==v.type)if(r.has(q))r.get(q).references.push(f);else{var x=f.resolved,z=P(x)||aa(x);r.set(q,{isStatic:z,references:[f]})}}}}catch(ea){g=!0,b=ea}finally{try{c||null==e.return||e.return()}finally{if(g)throw b;}}c=!0;g=!1;b=void 0;try{for(var B=
37
- a.childScopes[Symbol.iterator](),A;!(c=(A=B.next()).done);c=!0)l(A.value)}catch(ea){g=!0,b=ea}finally{try{c||null==B.return||B.return()}finally{if(g)throw b;}}}function n(a,c,y,b){return 0===a.size?null:(1<a.size?"":c+" ")+y+" "+(1<a.size?"dependencies":"dependency")+": "+sa(Array.from(a).sort().map(function(a){return"'"+a+"'"}))+(". Either "+b+" "+(1<a.size?"them":"it")+" or remove the dependency array.")}p&&d.async&&c({node:d,message:"Effect callbacks are synchronous to prevent race conditions. Put the async function inside:\n\nuseEffect(() => {\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\nLearn more about data fetching with Hooks: https://fb.me/react-hooks-data-fetching"});
38
- for(var k=q.acquire(d),D=new Set,C=null,v=k.upper;v;){D.add(v);if("function"===v.type)break;v=v.upper}if(v){C=v;var P=e(function(a){if(!Array.isArray(a.defs))return!1;var c=a.defs[0];if(null==c||"VariableDeclarator"!==c.node.type)return!1;var b=c.node.init;if(null==b)return!1;for(;"TSAsExpression"===b.type;)b=b.expression;var g=c.node.parent;if(null==g&&(qa(C.block,c.node.id),g=c.node.parent,null==g))return!1;if("const"===g.kind&&"Literal"===b.type&&("string"===typeof b.value||"number"===typeof b.value||
39
- null===b.value))return!0;if("CallExpression"!==b.type)return!1;b=b.callee;"MemberExpression"!==b.type||"React"!==b.object.name||null==b.property||b.computed||(b=b.property);if("Identifier"!==b.type)return!1;c=c.node.id;b=b.name;if("useRef"===b&&"Identifier"===c.type)return!0;if(("useState"===b||"useReducer"===b)&&"ArrayPattern"===c.type&&2===c.elements.length&&Array.isArray(a.identifiers)){if(c.elements[1]===a.identifiers[0]){if("useState"===b)for(a=a.references,b=0;b<a.length;b++)t.set(a[b].identifier,
40
- c.elements[0]);return!0}if(c.elements[0]===a.identifiers[0]&&"useState"===b)for(a=a.references,c=0;c<a.length;c++)x.add(a[c].identifier)}return!1},A),aa=e(function(a){if(!Array.isArray(a.defs))return!1;a=a.defs[0];if(null==a||null==a.node||null==a.node.id)return!1;var c=a.node,b=C.childScopes;a=null;var g;for(g=0;g<b.length;g++){var d=b[g],e=d.block;if("FunctionDeclaration"===c.type&&e===c||"VariableDeclarator"===c.type&&e.parent===c){a=d;break}}if(null==a)return!1;for(g=0;g<a.through.length;g++)if(c=
41
- a.through[g],null!=c.resolved&&D.has(c.resolved.scope)&&!P(c.resolved))return!1;return!0},V),U=new Map,r=new Map;l(k);U.forEach(function(a,b){var g=a.dependencyNode;a=a.reference.resolved.references;for(var d=!1,e=0;e<a.length;e++){var m=a[e].identifier.parent;if(null!=m&&"MemberExpression"===m.type&&!m.computed&&"Identifier"===m.property.type&&"current"===m.property.name&&"AssignmentExpression"===m.parent.type&&m.parent.left===m){d=!0;break}}d||c({node:g.parent.property,message:"The ref value '"+
42
- b+".current' will likely have changed by the time this effect cleanup function runs. If this ref points to a node rendered by React, copy '"+(b+".current' to a variable inside the effect, and use that variable in the cleanup function.")})});var X=new Set,Q=new Set;r.forEach(function(b,d){var g=b.references;b.isStatic&&Q.add(d);g.forEach(function(b){b.writeExpr&&(b=b.writeExpr,X.has(d)||(X.add(d),c({node:b,message:"Assignments to the '"+d+"' variable from inside React Hook "+(a.getSource(h)+" will be lost after each render. To preserve the value over time, store it in a useRef Hook and keep the mutable value in the '.current' property. Otherwise, you can move this variable directly inside ")+
43
- (a.getSource(h)+".")})))})});if(!(0<X.size))if(b){var M=[],K=new Set;"ArrayExpression"!==b.type?c({node:b,message:"React Hook "+a.getSource(h)+" 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."}):b.elements.forEach(function(b){if(null!==b)if("SpreadElement"===b.type)c({node:b,message:"React Hook "+a.getSource(h)+" has a spread element in its dependency array. This means we can't statically verify whether you've passed the correct dependencies."});
44
- else{try{var d=Z(b)}catch(J){if(/Unsupported node type/.test(J.message)){"Literal"===b.type?r.has(b.value)?c({node:b,message:"The "+b.raw+" literal is not a valid dependency because it never changes. Did you mean to include "+(b.value+" in the array instead?")}):c({node:b,message:"The "+b.raw+" literal is not a valid dependency because it never changes. You can safely remove it."}):c({node:b,message:"React Hook "+a.getSource(h)+" has a complex expression in the dependency array. Extract it to a separate variable so it can be statically checked."});
45
- return}throw J;}for(var g=b;"MemberExpression"===g.type;)g=g.object;var e=!C.through.some(function(a){return a.identifier===g});M.push({key:d,node:b});e||K.add(d)}});var u=ka({dependencies:r,declaredDependencies:M,optionalDependencies:Q,externalDependencies:K,isEffect:p});v=u.unnecessaryDependencies;var E=u.missingDependencies,z=u.duplicateDependencies,R=u.suggestedDependencies;if(0===z.size+E.size+v.size)la({declaredDependencies:M,declaredDependenciesNode:b,componentScope:C,scope:k}).forEach(function(a){var d=
46
- a.fn;a=a.suggestUseCallback;var g="The '"+d.name.name+"' function makes the dependencies of "+(f+" Hook (at line "+b.loc.start.line+") change on every render.");g=a?g+(" To fix this, wrap the '"+(d.name.name+"' definition into its own useCallback() Hook.")):g+(" Move it inside the "+f+" callback. Alternatively, wrap the '"+(d.name.name+"' definition into its own useCallback() Hook."));var e;a&&"Variable"===d.type&&(e=[{desc:"Wrap the '"+d.name.name+"' definition into its own useCallback() Hook.",
47
- fix:function(a){return[a.insertTextBefore(d.node.init,"useCallback("),a.insertTextAfter(d.node.init,")")]}}]);c({node:d.node,message:g,suggest:e})});else{!p&&0<E.size&&(R=ka({dependencies:r,declaredDependencies:[],optionalDependencies:Q,externalDependencies:K,isEffect:p}).suggestedDependencies);(function(){if(0===M.length)return!0;var a=M.map(function(a){return a.key}),b=a.slice().sort();return a.join(",")===b.join(",")})()&&R.sort();u="";if(0<v.size){var S=null;Array.from(v.keys()).forEach(function(a){null===
48
- S&&a.endsWith(".current")&&(S=a)});if(null!==S)u=" Mutable values like '"+S+"' aren't valid dependencies because mutating them doesn't re-render the component.";else if(0<K.size){var H=Array.from(K)[0];k.set.has(H)||(u=" Outer scope values like '"+H+"' aren't valid dependencies because mutating them doesn't re-render the component.")}}if(!u&&E.has("props")){k=r.get("props");if(null==k)return;k=k.references;if(!Array.isArray(k))return;H=!0;for(var Y=0;Y<k.length;Y++){var N=qa(C.block,k[Y].identifier);
49
- if(!N){H=!1;break}N=N.parent;if(null==N){H=!1;break}if("MemberExpression"!==N.type){H=!1;break}}H&&(u=" However, 'props' will change when *any* prop changes, so the preferred fix is to destructure the 'props' object outside of the "+(f+" call and refer to those specific props inside ")+(a.getSource(h)+"."))}if(!u&&0<E.size){var F=null;E.forEach(function(a){if(!F){var b=C.set.get(a),c=r.get(a);if(c.references[0].resolved===b&&(b=b.defs[0],null!=b&&null!=b.name&&"Parameter"===b.type)){b=!1;for(var d,
50
- e=0;e<c.references.length;e++)if(d=c.references[e].identifier,null!=d&&null!=d.parent&&"CallExpression"===d.parent.type&&d.parent.callee===d){b=!0;break}b&&(F=a)}}});null!==F&&(u=" If '"+F+"' changes too often, find the parent component that defines it and wrap that definition in useCallback.")}if(!u&&0<E.size){var w=null;E.forEach(function(a){if(null===w)for(var b=r.get(a).references,c,d,e=0;e<b.length;e++){c=b[e].identifier;for(d=c.parent;null!=d&&d!==C.block;){if("CallExpression"===d.type){var g=
51
- t.get(d.callee);if(null!=g){g.name===a?w={missingDep:a,setter:d.callee.name,form:"updater"}:x.has(c)?w={missingDep:a,setter:d.callee.name,form:"reducer"}:(c=b[e].resolved,null!=c&&(c=c.defs[0],null!=c&&"Parameter"===c.type&&(w={missingDep:a,setter:d.callee.name,form:"inlineReducer"})));break}}d=d.parent}if(null!==w)break}});if(null!==w)switch(w.form){case "reducer":u=" You can also replace multiple useState variables with useReducer if '"+(w.setter+"' needs the current value of '")+(w.missingDep+
52
- "'.");break;case "inlineReducer":u=" If '"+w.setter+"' needs the current value of '"+(w.missingDep+"', you can also switch to useReducer instead of useState and read '")+(w.missingDep+"' in the reducer.");break;case "updater":u=" You can also do a functional update '"+w.setter+"("+w.missingDep.substring(0,1)+" => ...)' if you only need '"+w.missingDep+"' in the '"+(w.setter+"' call.");break;default:throw Error("Unknown case.");}}c({node:b,message:"React Hook "+a.getSource(h)+" has "+(n(E,"a","missing",
53
- "include")||n(v,"an","unnecessary","exclude")||n(z,"a","duplicate","omit"))+u,suggest:[{desc:"Update the dependencies array to be: ["+R.join(", ")+"]",fix:function(a){return a.replaceText(b,"["+R.join(", ")+"]")}}]})}}else{var W=null;r.forEach(function(a,b){W||a.references.forEach(function(a){if(!W&&t.has(a.identifier)){for(a=a.from;"function"!==a.type;)a=a.upper;a.block===d&&(W=b)}})});if(W){var da=ka({dependencies:r,declaredDependencies:[],optionalDependencies:Q,externalDependencies:new Set,isEffect:!0}).suggestedDependencies;
54
- c({node:h,message:"React Hook "+f+" contains a call to '"+W+"'. Without a list of dependencies, this can lead to an infinite chain of updates. To fix this, pass ["+da.join(", ")+("] as a second argument to the "+f+" Hook."),suggest:[{desc:"Add dependencies array: ["+da.join(", ")+"]",fix:function(a){return a.insertTextAfter(d,", ["+da.join(", ")+"]")}}]})}}}}var f=a.options&&a.options[0]&&a.options[0].enableDangerousAutofixThisMayCauseInfiniteLoops||!1,k={additionalHooks:a.options&&a.options[0]&&
55
- a.options[0].additionalHooks?new RegExp(a.options[0].additionalHooks):void 0,enableDangerousAutofixThisMayCauseInfiniteLoops:f},q=a.getSourceCode().scopeManager,t=new WeakMap,x=new WeakSet,A=new WeakMap,V=new WeakMap;return{CallExpression:function(d){var b=pa(d.callee,k);if(-1!==b){var e=d.arguments[b],f=d.callee,p=oa(f).name,l=d.arguments[b+1];d=/Effect($|[^a-z])/g.test(p);if(l||d){switch(e.type){case "FunctionExpression":case "ArrowFunctionExpression":h(e,l,f,p,d);return;case "Identifier":if(!l||
56
- l.elements&&l.elements.some(function(a){return"Identifier"===a.type&&a.name===e.name}))return;b=a.getScope().set.get(e.name);if(null==b||null==b.defs)return;b=b.defs[0];if(!b||!b.node)break;if("Variable"!==b.type&&"FunctionName"!==b.type)break;switch(b.node.type){case "FunctionDeclaration":h(b.node,l,f,p,d);return;case "VariableDeclarator":if(b=b.node.init)switch(b.type){case "ArrowFunctionExpression":case "FunctionExpression":h(b,l,f,p,d);return}}break;default:c({node:f,message:"React Hook "+p+" received a function whose dependencies are unknown. Pass an inline function instead."});
57
- return}c({node:f,message:"React Hook "+p+" has a missing dependency: '"+e.name+"'. Either include it or remove the dependency array.",suggest:[{desc:"Update the dependencies array to be: ["+e.name+"]",fix:function(a){return a.replaceText(l,"["+e.name+"]")}}]})}else"useMemo"!==p&&"useCallback"!==p||c({node:f,message:"React Hook "+p+" does nothing when called with only one argument. Did you forget to pass an array of dependencies?"})}}}}}};
34
+ c.suggest[0].fix);a.report(c)}function e(a,c){return function(b){if(c.has(b))return c.get(b);var d=a(b);c.set(b,d);return d}}function h(d,b,f,h,m){function l(a){var c=!0,g=!1,b=void 0;try{for(var f=a.references[Symbol.iterator](),e;!(c=(e=f.next()).done);c=!0){var h=e.value;if(h.resolved&&B.has(h.resolved.scope)){var n=ra(d,h.identifier),k=na(n),w=W(k,Y),y;if(y=m&&"Identifier"===k.type&&("MemberExpression"===k.parent.type||"OptionalMemberExpression"===k.parent.type)&&!k.parent.computed&&"Identifier"===
35
+ k.parent.property.type&&"current"===k.parent.property.name){for(var p=h.from,r=!1;p.block!==d;)"function"===p.type&&(r=null!=p.block.parent&&"ReturnStatement"===p.block.parent.type),p=p.upper;y=r}y&&C.set(w,{reference:h,dependencyNode:k});if("TSTypeQuery"!==k.parent.type){var q=h.resolved.defs[0];if(null!=q&&(null==q.node||q.node.init!==d.parent)&&"TypeParameter"!==q.type)if(x.has(w))x.get(w).references.push(h);else{var t=h.resolved,u=T(t)||K(t);x.set(w,{isStable:u,references:[h]})}}}}}catch(da){g=
36
+ !0,b=da}finally{try{c||null==f.return||f.return()}finally{if(g)throw b;}}c=!0;g=!1;b=void 0;try{for(var v=a.childScopes[Symbol.iterator](),J;!(c=(J=v.next()).done);c=!0)l(J.value)}catch(da){g=!0,b=da}finally{try{c||null==v.return||v.return()}finally{if(g)throw b;}}}function w(a){a=a.split(".");for(var c="",g=0;g<a.length;g++){if(0!==g){var b=a.slice(0,g+1).join(".");b=!0===Y.get(b);c+=b?"?.":"."}c+=a[g]}return c}function A(a,c,b,d){return 0===a.size?null:(1<a.size?"":c+" ")+b+" "+(1<a.size?"dependencies":
37
+ "dependency")+": "+ta(Array.from(a).sort().map(function(a){return"'"+w(a)+"'"}))+(". Either "+d+" "+(1<a.size?"them":"it")+" or remove the dependency array.")}m&&d.async&&c({node:d,message:"Effect callbacks are synchronous to prevent race conditions. Put the async function inside:\n\nuseEffect(() => {\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\nLearn more about data fetching with Hooks: https://fb.me/react-hooks-data-fetching"});
38
+ for(var p=k.acquire(d),B=new Set,z=null,r=p.upper;r;){B.add(r);if("function"===r.type)break;r=r.upper}if(r){z=r;var T=e(function(a){if(!Array.isArray(a.defs))return!1;var c=a.defs[0];if(null==c||"VariableDeclarator"!==c.node.type)return!1;var b=c.node.init;if(null==b)return!1;for(;"TSAsExpression"===b.type;)b=b.expression;var g=c.node.parent;if(null==g&&(ra(z.block,c.node.id),g=c.node.parent,null==g))return!1;if("const"===g.kind&&"Literal"===b.type&&("string"===typeof b.value||"number"===typeof b.value||
39
+ null===b.value))return!0;if("CallExpression"!==b.type)return!1;b=b.callee;"MemberExpression"!==b.type||"React"!==b.object.name||null==b.property||b.computed||(b=b.property);if("Identifier"!==b.type)return!1;c=c.node.id;b=b.name;if("useRef"===b&&"Identifier"===c.type)return!0;if(("useState"===b||"useReducer"===b)&&"ArrayPattern"===c.type&&2===c.elements.length&&Array.isArray(a.identifiers)){if(c.elements[1]===a.identifiers[0]){if("useState"===b)for(a=a.references,b=0;b<a.length;b++)q.set(a[b].identifier,
40
+ c.elements[0]);return!0}if(c.elements[0]===a.identifiers[0]&&"useState"===b)for(a=a.references,c=0;c<a.length;c++)u.add(a[c].identifier)}return!1},v),K=e(function(a){if(!Array.isArray(a.defs))return!1;a=a.defs[0];if(null==a||null==a.node||null==a.node.id)return!1;var c=a.node,b=z.childScopes;a=null;var g;for(g=0;g<b.length;g++){var d=b[g],f=d.block;if("FunctionDeclaration"===c.type&&f===c||"VariableDeclarator"===c.type&&f.parent===c){a=d;break}}if(null==a)return!1;for(g=0;g<a.through.length;g++)if(c=
41
+ a.through[g],null!=c.resolved&&B.has(c.resolved.scope)&&!T(c.resolved))return!1;return!0},E),C=new Map,x=new Map,Y=new Map;l(p);C.forEach(function(a,b){var g=a.dependencyNode;a=a.reference.resolved.references;for(var d=!1,f=0;f<a.length;f++){var e=a[f].identifier.parent;if(null!=e&&"MemberExpression"===e.type&&!e.computed&&"Identifier"===e.property.type&&"current"===e.property.name&&"AssignmentExpression"===e.parent.type&&e.parent.left===e){d=!0;break}}d||c({node:g.parent.property,message:"The ref value '"+
42
+ b+".current' will likely have changed by the time this effect cleanup function runs. If this ref points to a node rendered by React, copy '"+(b+".current' to a variable inside the effect, and use that variable in the cleanup function.")})});var V=new Set,P=new Set;x.forEach(function(b,d){var g=b.references;b.isStable&&P.add(d);g.forEach(function(b){b.writeExpr&&(b=b.writeExpr,V.has(d)||(V.add(d),c({node:b,message:"Assignments to the '"+d+"' variable from inside React Hook "+(a.getSource(f)+" will be lost after each render. To preserve the value over time, store it in a useRef Hook and keep the mutable value in the '.current' property. Otherwise, you can move this variable directly inside ")+
43
+ (a.getSource(f)+".")})))})});if(!(0<V.size))if(b){var H=[],N=new Set;"ArrayExpression"!==b.type?c({node:b,message:"React Hook "+a.getSource(f)+" 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."}):b.elements.forEach(function(b){if(null!==b)if("SpreadElement"===b.type)c({node:b,message:"React Hook "+a.getSource(f)+" has a spread element in its dependency array. This means we can't statically verify whether you've passed the correct dependencies."});
44
+ else{try{var d=W(b,null)}catch(pa){if(/Unsupported node type/.test(pa.message)){"Literal"===b.type?x.has(b.value)?c({node:b,message:"The "+b.raw+" literal is not a valid dependency because it never changes. Did you mean to include "+(b.value+" in the array instead?")}):c({node:b,message:"The "+b.raw+" literal is not a valid dependency because it never changes. You can safely remove it."}):c({node:b,message:"React Hook "+a.getSource(f)+" has a complex expression in the dependency array. Extract it to a separate variable so it can be statically checked."});
45
+ return}throw pa;}for(var g=b;"MemberExpression"===g.type||"OptionalMemberExpression"===g.type;)g=g.object;var e=!z.through.some(function(a){return a.identifier===g});H.push({key:d,node:b});e||N.add(d)}});var n=ja({dependencies:x,declaredDependencies:H,stableDependencies:P,externalDependencies:N,isEffect:m});r=n.unnecessaryDependencies;var D=n.missingDependencies,Z=n.duplicateDependencies,R=n.suggestedDependencies;if(0===Z.size+D.size+r.size)la({declaredDependencies:H,declaredDependenciesNode:b,componentScope:z,
46
+ scope:p}).forEach(function(a){var d=a.fn;a=a.suggestUseCallback;var g="The '"+d.name.name+"' function makes the dependencies of "+(h+" Hook (at line "+b.loc.start.line+") change on every render.");g=a?g+(" To fix this, wrap the '"+(d.name.name+"' definition into its own useCallback() Hook.")):g+(" Move it inside the "+h+" callback. Alternatively, wrap the '"+(d.name.name+"' definition into its own useCallback() Hook."));var e;a&&"Variable"===d.type&&(e=[{desc:"Wrap the '"+d.name.name+"' definition into its own useCallback() Hook.",
47
+ fix:function(a){return[a.insertTextBefore(d.node.init,"useCallback("),a.insertTextAfter(d.node.init,")")]}}]);c({node:d.node,message:g,suggest:e})});else{!m&&0<D.size&&(R=ja({dependencies:x,declaredDependencies:[],stableDependencies:P,externalDependencies:N,isEffect:m}).suggestedDependencies);(function(){if(0===H.length)return!0;var a=H.map(function(a){return a.key}),b=a.slice().sort();return a.join(",")===b.join(",")})()&&R.sort();n="";if(0<r.size){var S=null;Array.from(r.keys()).forEach(function(a){null===
48
+ S&&a.endsWith(".current")&&(S=a)});if(null!==S)n=" Mutable values like '"+S+"' aren't valid dependencies because mutating them doesn't re-render the component.";else if(0<N.size){var I=Array.from(N)[0];p.set.has(I)||(n=" Outer scope values like '"+I+"' aren't valid dependencies because mutating them doesn't re-render the component.")}}if(!n&&D.has("props")){p=x.get("props");if(null==p)return;p=p.references;if(!Array.isArray(p))return;I=!0;for(var F=0;F<p.length;F++){var Q=ra(z.block,p[F].identifier);
49
+ if(!Q){I=!1;break}Q=Q.parent;if(null==Q){I=!1;break}if("MemberExpression"!==Q.type&&"OptionalMemberExpression"!==Q.type){I=!1;break}}I&&(n=" However, 'props' will change when *any* prop changes, so the preferred fix is to destructure the 'props' object outside of the "+(h+" call and refer to those specific props inside ")+(a.getSource(f)+"."))}if(!n&&0<D.size){var X=null;D.forEach(function(a){if(!X){var b=z.set.get(a),c=x.get(a);if(c.references[0].resolved===b&&(b=b.defs[0],null!=b&&null!=b.name&&
50
+ "Parameter"===b.type)){b=!1;for(var d,e=0;e<c.references.length;e++)if(d=c.references[e].identifier,null!=d&&null!=d.parent&&("CallExpression"===d.parent.type||"OptionalCallExpression"===d.parent.type)&&d.parent.callee===d){b=!0;break}b&&(X=a)}}});null!==X&&(n=" If '"+X+"' changes too often, find the parent component that defines it and wrap that definition in useCallback.")}if(!n&&0<D.size){var t=null;D.forEach(function(a){if(null===t)for(var b=x.get(a).references,c,d,e=0;e<b.length;e++){c=b[e].identifier;
51
+ for(d=c.parent;null!=d&&d!==z.block;){if("CallExpression"===d.type){var f=q.get(d.callee);if(null!=f){f.name===a?t={missingDep:a,setter:d.callee.name,form:"updater"}:u.has(c)?t={missingDep:a,setter:d.callee.name,form:"reducer"}:(c=b[e].resolved,null!=c&&(c=c.defs[0],null!=c&&"Parameter"===c.type&&(t={missingDep:a,setter:d.callee.name,form:"inlineReducer"})));break}}d=d.parent}if(null!==t)break}});if(null!==t)switch(t.form){case "reducer":n=" You can also replace multiple useState variables with useReducer if '"+
52
+ (t.setter+"' needs the current value of '")+(t.missingDep+"'.");break;case "inlineReducer":n=" If '"+t.setter+"' needs the current value of '"+(t.missingDep+"', you can also switch to useReducer instead of useState and read '")+(t.missingDep+"' in the reducer.");break;case "updater":n=" You can also do a functional update '"+t.setter+"("+t.missingDep.substring(0,1)+" => ...)' if you only need '"+t.missingDep+"' in the '"+(t.setter+"' call.");break;default:throw Error("Unknown case.");}}c({node:b,
53
+ message:"React Hook "+a.getSource(f)+" has "+(A(D,"a","missing","include")||A(r,"an","unnecessary","exclude")||A(Z,"a","duplicate","omit"))+n,suggest:[{desc:"Update the dependencies array to be: ["+R.map(w).join(", ")+"]",fix:function(a){return a.replaceText(b,"["+R.map(w).join(", ")+"]")}}]})}}else{var U=null;x.forEach(function(a,b){U||a.references.forEach(function(a){if(!U&&q.has(a.identifier)){for(a=a.from;"function"!==a.type;)a=a.upper;a.block===d&&(U=b)}})});if(U){var ca=ja({dependencies:x,declaredDependencies:[],
54
+ stableDependencies:P,externalDependencies:new Set,isEffect:!0}).suggestedDependencies;c({node:f,message:"React Hook "+h+" contains a call to '"+U+"'. Without a list of dependencies, this can lead to an infinite chain of updates. To fix this, pass ["+ca.join(", ")+("] as a second argument to the "+h+" Hook."),suggest:[{desc:"Add dependencies array: ["+ca.join(", ")+"]",fix:function(a){return a.insertTextAfter(d,", ["+ca.join(", ")+"]")}}]})}}}}var f=a.options&&a.options[0]&&a.options[0].enableDangerousAutofixThisMayCauseInfiniteLoops||
55
+ !1,B={additionalHooks:a.options&&a.options[0]&&a.options[0].additionalHooks?new RegExp(a.options[0].additionalHooks):void 0,enableDangerousAutofixThisMayCauseInfiniteLoops:f},k=a.getSourceCode().scopeManager,q=new WeakMap,u=new WeakSet,v=new WeakMap,E=new WeakMap;return{CallExpression:function(d){var b=qa(d.callee,B);if(-1!==b){var e=d.arguments[b],f=d.callee,m=oa(f).name,k=d.arguments[b+1];d=/Effect($|[^a-z])/g.test(m);if(k||d){switch(e.type){case "FunctionExpression":case "ArrowFunctionExpression":h(e,
56
+ k,f,m,d);return;case "Identifier":if(!k||k.elements&&k.elements.some(function(a){return a&&"Identifier"===a.type&&a.name===e.name}))return;b=a.getScope().set.get(e.name);if(null==b||null==b.defs)return;b=b.defs[0];if(!b||!b.node)break;if("Variable"!==b.type&&"FunctionName"!==b.type)break;switch(b.node.type){case "FunctionDeclaration":h(b.node,k,f,m,d);return;case "VariableDeclarator":if(b=b.node.init)switch(b.type){case "ArrowFunctionExpression":case "FunctionExpression":h(b,k,f,m,d);return}}break;
57
+ default:c({node:f,message:"React Hook "+m+" received a function whose dependencies are unknown. Pass an inline function instead."});return}c({node:f,message:"React Hook "+m+" has a missing dependency: '"+e.name+"'. Either include it or remove the dependency array.",suggest:[{desc:"Update the dependencies array to be: ["+e.name+"]",fix:function(a){return a.replaceText(k,"["+e.name+"]")}}]})}else"useMemo"!==m&&"useCallback"!==m||c({node:f,message:"React Hook "+m+" does nothing when called with only one argument. Did you forget to pass an array of dependencies?"})}}}}}};
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "eslint-plugin-react-hooks",
3
3
  "description": "ESLint rules for React Hooks",
4
- "version": "4.0.4",
4
+ "version": "4.0.8",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "https://github.com/facebook/react.git",