eslint-plugin-react-hooks 2.4.0 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -34,6 +34,17 @@ Then add it to your ESLint configuration:
34
34
  }
35
35
  ```
36
36
 
37
+ Or use the recommended config:
38
+
39
+ ```js
40
+ {
41
+ "extends": [
42
+ // ...
43
+ "plugin:react-hooks/recommended"
44
+ ]
45
+ }
46
+ ```
47
+
37
48
  ## Valid and Invalid Examples
38
49
 
39
50
  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.
@@ -16,7 +16,6 @@ if (process.env.NODE_ENV !== "production") {
16
16
  'use strict';
17
17
 
18
18
  /* eslint-disable no-for-of-loops/no-for-of-loops */
19
-
20
19
  /**
21
20
  * Catch all identifiers that begin with "use" followed by an uppercase Latin
22
21
  * character to exclude identifiers like "user".
@@ -35,10 +34,9 @@ function isHook(node) {
35
34
  if (node.type === 'Identifier') {
36
35
  return isHookName(node.name);
37
36
  } else if (node.type === 'MemberExpression' && !node.computed && isHook(node.property)) {
38
- // Only consider React.useFoo() to be namespace hooks for now to avoid false positives.
39
- // We can expand this check later.
40
37
  var obj = node.object;
41
- return obj.type === 'Identifier' && obj.name === 'React';
38
+ var isPascalCaseNameSpace = /^[A-Z].*/;
39
+ return obj.type === 'Identifier' && isPascalCaseNameSpace.test(obj.name);
42
40
  } else {
43
41
  return false;
44
42
  }
@@ -100,7 +98,16 @@ function isInsideComponentOrHook(node) {
100
98
  return false;
101
99
  }
102
100
 
103
- var RuleOfHooks = {
101
+ var RulesOfHooks = {
102
+ meta: {
103
+ type: 'problem',
104
+ docs: {
105
+ description: 'enforces the Rules of Hooks',
106
+ category: 'Possible Errors',
107
+ recommended: true,
108
+ url: 'https://reactjs.org/docs/hooks-rules.html'
109
+ }
110
+ },
104
111
  create: function (context) {
105
112
  var codePathReactHooksMapStack = [];
106
113
  var codePathSegmentStack = [];
@@ -151,51 +158,48 @@ var RuleOfHooks = {
151
158
  * Populates `cyclic` with cyclic segments.
152
159
  */
153
160
 
154
- function countPathsFromStart(segment) {
161
+ function countPathsFromStart(segment, pathHistory) {
155
162
  var cache = countPathsFromStart.cache;
156
- var paths = cache.get(segment.id); // If `paths` is null then we've found a cycle! Add it to `cyclic` and
157
- // any other segments which are a part of this cycle.
163
+ var paths = cache.get(segment.id);
164
+ var pathList = new Set(pathHistory); // If `pathList` includes the current segment then we've found a cycle!
165
+ // We need to fill `cyclic` with all segments inside cycle
158
166
 
159
- if (paths === null) {
160
- if (cyclic.has(segment.id)) {
161
- return 0;
162
- } else {
163
- cyclic.add(segment.id);
164
- var _iteratorNormalCompletion = true;
165
- var _didIteratorError = false;
166
- var _iteratorError = undefined;
167
+ if (pathList.has(segment.id)) {
168
+ var pathArray = [].concat(pathList);
169
+ var cyclicSegments = pathArray.slice(pathArray.indexOf(segment.id) + 1);
170
+ var _iteratorNormalCompletion = true;
171
+ var _didIteratorError = false;
172
+ var _iteratorError = undefined;
167
173
 
174
+ try {
175
+ for (var _iterator = cyclicSegments[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
176
+ var cyclicSegment = _step.value;
177
+ cyclic.add(cyclicSegment);
178
+ }
179
+ } catch (err) {
180
+ _didIteratorError = true;
181
+ _iteratorError = err;
182
+ } finally {
168
183
  try {
169
- for (var _iterator = segment.prevSegments[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
170
- var prevSegment = _step.value;
171
- countPathsFromStart(prevSegment);
184
+ if (!_iteratorNormalCompletion && _iterator.return != null) {
185
+ _iterator.return();
172
186
  }
173
- } catch (err) {
174
- _didIteratorError = true;
175
- _iteratorError = err;
176
187
  } finally {
177
- try {
178
- if (!_iteratorNormalCompletion && _iterator.return != null) {
179
- _iterator.return();
180
- }
181
- } finally {
182
- if (_didIteratorError) {
183
- throw _iteratorError;
184
- }
188
+ if (_didIteratorError) {
189
+ throw _iteratorError;
185
190
  }
186
191
  }
187
-
188
- return 0;
189
192
  }
190
- } // We have a cached `paths`. Return it.
191
193
 
194
+ return 0;
195
+ } // add the current segment to pathList
192
196
 
193
- if (paths !== undefined) {
194
- return paths;
195
- } // Compute `paths` and cache it. Guarding against cycles.
196
197
 
198
+ pathList.add(segment.id); // We have a cached `paths`. Return it.
197
199
 
198
- cache.set(segment.id, null);
200
+ if (paths !== undefined) {
201
+ return paths;
202
+ }
199
203
 
200
204
  if (codePath.thrownSegments.includes(segment)) {
201
205
  paths = 0;
@@ -209,8 +213,8 @@ var RuleOfHooks = {
209
213
 
210
214
  try {
211
215
  for (var _iterator2 = segment.prevSegments[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
212
- var _prevSegment = _step2.value;
213
- paths += countPathsFromStart(_prevSegment);
216
+ var prevSegment = _step2.value;
217
+ paths += countPathsFromStart(prevSegment, pathList);
214
218
  }
215
219
  } catch (err) {
216
220
  _didIteratorError2 = true;
@@ -261,51 +265,48 @@ var RuleOfHooks = {
261
265
  */
262
266
 
263
267
 
264
- function countPathsToEnd(segment) {
268
+ function countPathsToEnd(segment, pathHistory) {
265
269
  var cache = countPathsToEnd.cache;
266
- var paths = cache.get(segment.id); // If `paths` is null then we've found a cycle! Add it to `cyclic` and
267
- // any other segments which are a part of this cycle.
270
+ var paths = cache.get(segment.id);
271
+ var pathList = new Set(pathHistory); // If `pathList` includes the current segment then we've found a cycle!
272
+ // We need to fill `cyclic` with all segments inside cycle
268
273
 
269
- if (paths === null) {
270
- if (cyclic.has(segment.id)) {
271
- return 0;
272
- } else {
273
- cyclic.add(segment.id);
274
- var _iteratorNormalCompletion3 = true;
275
- var _didIteratorError3 = false;
276
- var _iteratorError3 = undefined;
274
+ if (pathList.has(segment.id)) {
275
+ var pathArray = Array.from(pathList);
276
+ var cyclicSegments = pathArray.slice(pathArray.indexOf(segment.id) + 1);
277
+ var _iteratorNormalCompletion3 = true;
278
+ var _didIteratorError3 = false;
279
+ var _iteratorError3 = undefined;
277
280
 
281
+ try {
282
+ for (var _iterator3 = cyclicSegments[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
283
+ var cyclicSegment = _step3.value;
284
+ cyclic.add(cyclicSegment);
285
+ }
286
+ } catch (err) {
287
+ _didIteratorError3 = true;
288
+ _iteratorError3 = err;
289
+ } finally {
278
290
  try {
279
- for (var _iterator3 = segment.nextSegments[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
280
- var nextSegment = _step3.value;
281
- countPathsToEnd(nextSegment);
291
+ if (!_iteratorNormalCompletion3 && _iterator3.return != null) {
292
+ _iterator3.return();
282
293
  }
283
- } catch (err) {
284
- _didIteratorError3 = true;
285
- _iteratorError3 = err;
286
294
  } finally {
287
- try {
288
- if (!_iteratorNormalCompletion3 && _iterator3.return != null) {
289
- _iterator3.return();
290
- }
291
- } finally {
292
- if (_didIteratorError3) {
293
- throw _iteratorError3;
294
- }
295
+ if (_didIteratorError3) {
296
+ throw _iteratorError3;
295
297
  }
296
298
  }
297
-
298
- return 0;
299
299
  }
300
- } // We have a cached `paths`. Return it.
301
300
 
301
+ return 0;
302
+ } // add the current segment to pathList
302
303
 
303
- if (paths !== undefined) {
304
- return paths;
305
- } // Compute `paths` and cache it. Guarding against cycles.
306
304
 
305
+ pathList.add(segment.id); // We have a cached `paths`. Return it.
307
306
 
308
- cache.set(segment.id, null);
307
+ if (paths !== undefined) {
308
+ return paths;
309
+ }
309
310
 
310
311
  if (codePath.thrownSegments.includes(segment)) {
311
312
  paths = 0;
@@ -319,8 +320,8 @@ var RuleOfHooks = {
319
320
 
320
321
  try {
321
322
  for (var _iterator4 = segment.nextSegments[Symbol.iterator](), _step4; !(_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done); _iteratorNormalCompletion4 = true) {
322
- var _nextSegment = _step4.value;
323
- paths += countPathsToEnd(_nextSegment);
323
+ var nextSegment = _step4.value;
324
+ paths += countPathsToEnd(nextSegment, pathList);
324
325
  }
325
326
  } catch (err) {
326
327
  _didIteratorError4 = true;
@@ -570,26 +571,29 @@ var RuleOfHooks = {
570
571
  message: message
571
572
  });
572
573
  }
573
- } else if (codePathNode.parent && (codePathNode.parent.type === 'MethodDefinition' || codePathNode.parent.type === 'ClassProperty') && codePathNode.parent.value === codePathNode) {// Ignore class methods for now because they produce too many
574
- // false positives due to feature flag checks. We're less
575
- // sensitive to them in classes because hooks would produce
576
- // runtime errors in classes anyway, and because a use*()
577
- // call in a class, if it works, is unambiguously *not* a hook.
574
+ } else if (codePathNode.parent && (codePathNode.parent.type === 'MethodDefinition' || codePathNode.parent.type === 'ClassProperty') && codePathNode.parent.value === codePathNode) {
575
+ // Custom message for hooks inside a class
576
+ var _message = "React Hook \"" + context.getSource(hook) + "\" cannot be called " + 'in a class component. React Hooks must be called in a ' + 'React function component or a custom React Hook function.';
577
+
578
+ context.report({
579
+ node: hook,
580
+ message: _message
581
+ });
578
582
  } else if (codePathFunctionName) {
579
583
  // Custom message if we found an invalid function name.
580
- var _message = "React Hook \"" + context.getSource(hook) + "\" is called in " + ("function \"" + context.getSource(codePathFunctionName) + "\" ") + 'that is neither a React function component nor a custom ' + 'React Hook function.';
584
+ var _message2 = "React Hook \"" + context.getSource(hook) + "\" is called in " + ("function \"" + context.getSource(codePathFunctionName) + "\" ") + 'that is neither a React function component nor a custom ' + 'React Hook function.';
581
585
 
582
586
  context.report({
583
587
  node: hook,
584
- message: _message
588
+ message: _message2
585
589
  });
586
590
  } else if (codePathNode.type === 'Program') {
587
591
  // These are dangerous if you have inline requires enabled.
588
- var _message2 = "React Hook \"" + context.getSource(hook) + "\" cannot be called " + 'at the top level. React Hooks must be called in a ' + 'React function component or a custom React Hook function.';
592
+ var _message3 = "React Hook \"" + context.getSource(hook) + "\" cannot be called " + 'at the top level. React Hooks must be called in a ' + 'React function component or a custom React Hook function.';
589
593
 
590
594
  context.report({
591
595
  node: hook,
592
- message: _message2
596
+ message: _message3
593
597
  });
594
598
  } else {
595
599
  // Assume in all other cases the user called a hook in some
@@ -598,11 +602,11 @@ var RuleOfHooks = {
598
602
  // enough in the common case that the incorrect message in
599
603
  // uncommon cases doesn't matter.
600
604
  if (isSomewhereInsideComponentOrHook) {
601
- var _message3 = "React Hook \"" + context.getSource(hook) + "\" cannot be called " + 'inside a callback. React Hooks must be called in a ' + 'React function component or a custom React Hook function.';
605
+ var _message4 = "React Hook \"" + context.getSource(hook) + "\" cannot be called " + 'inside a callback. React Hooks must be called in a ' + 'React function component or a custom React Hook function.';
602
606
 
603
607
  context.report({
604
608
  node: hook,
605
- message: _message3
609
+ message: _message4
606
610
  });
607
611
  }
608
612
  }
@@ -717,15 +721,26 @@ function last(array) {
717
721
 
718
722
  /* eslint-disable no-for-of-loops/no-for-of-loops */
719
723
 
720
-
721
724
  var ExhaustiveDeps = {
722
725
  meta: {
726
+ type: 'suggestion',
727
+ docs: {
728
+ description: 'verifies the list of dependencies for Hooks like useEffect and similar',
729
+ category: 'Best Practices',
730
+ recommended: true,
731
+ url: 'https://github.com/facebook/react/issues/14920'
732
+ },
733
+ fixable: 'code',
723
734
  schema: [{
724
735
  type: 'object',
725
736
  additionalProperties: false,
737
+ enableDangerousAutofixThisMayCauseInfiniteLoops: false,
726
738
  properties: {
727
739
  additionalHooks: {
728
740
  type: 'string'
741
+ },
742
+ enableDangerousAutofixThisMayCauseInfiniteLoops: {
743
+ type: 'boolean'
729
744
  }
730
745
  }
731
746
  }]
@@ -733,9 +748,25 @@ var ExhaustiveDeps = {
733
748
  create: function (context) {
734
749
  // Parse the `additionalHooks` regex.
735
750
  var additionalHooks = context.options && context.options[0] && context.options[0].additionalHooks ? new RegExp(context.options[0].additionalHooks) : undefined;
751
+ var enableDangerousAutofixThisMayCauseInfiniteLoops = context.options && context.options[0] && context.options[0].enableDangerousAutofixThisMayCauseInfiniteLoops || false;
736
752
  var options = {
737
- additionalHooks: additionalHooks
738
- }; // Should be shared between visitors.
753
+ additionalHooks: additionalHooks,
754
+ enableDangerousAutofixThisMayCauseInfiniteLoops: enableDangerousAutofixThisMayCauseInfiniteLoops
755
+ };
756
+
757
+ function reportProblem(problem) {
758
+ if (enableDangerousAutofixThisMayCauseInfiniteLoops) {
759
+ // Used to enable legacy behavior. Dangerous.
760
+ // Keep this as an option until major IDEs upgrade (including VSCode FB ESLint extension).
761
+ if (Array.isArray(problem.suggest) && problem.suggest.length > 0) {
762
+ problem.fix = problem.suggest[0].fix;
763
+ }
764
+ }
765
+
766
+ context.report(problem);
767
+ }
768
+
769
+ var scopeManager = context.getSourceCode().scopeManager; // Should be shared between visitors.
739
770
 
740
771
  var setStateCallSites = new WeakMap();
741
772
  var stateVariables = new WeakSet();
@@ -757,41 +788,31 @@ var ExhaustiveDeps = {
757
788
  }
758
789
 
759
790
  return {
760
- FunctionExpression: visitFunctionExpression,
761
- ArrowFunctionExpression: visitFunctionExpression
791
+ CallExpression: visitCallExpression
762
792
  };
763
- /**
764
- * Visitor for both function expressions and arrow function expressions.
765
- */
766
793
 
767
- function visitFunctionExpression(node) {
768
- // We only want to lint nodes which are reactive hook callbacks.
769
- if (node.type !== 'FunctionExpression' && node.type !== 'ArrowFunctionExpression' || node.parent.type !== 'CallExpression') {
770
- return;
771
- }
794
+ function visitCallExpression(node) {
795
+ var callbackIndex = getReactiveHookCallbackIndex(node.callee, options);
772
796
 
773
- var callbackIndex = getReactiveHookCallbackIndex(node.parent.callee, options);
774
-
775
- if (node.parent.arguments[callbackIndex] !== node) {
797
+ if (callbackIndex === -1) {
798
+ // Not a React Hook call that needs deps.
776
799
  return;
777
- } // Get the reactive hook node.
778
-
800
+ }
779
801
 
780
- var reactiveHook = node.parent.callee;
802
+ var callback = node.arguments[callbackIndex];
803
+ var reactiveHook = node.callee;
781
804
  var reactiveHookName = getNodeWithoutReactNamespace(reactiveHook).name;
782
- var isEffect = /Effect($|[^a-z])/g.test(reactiveHookName); // Get the declared dependencies for this reactive hook. If there is no
805
+ var declaredDependenciesNode = node.arguments[callbackIndex + 1];
806
+ var isEffect = /Effect($|[^a-z])/g.test(reactiveHookName); // Check the declared dependencies for this reactive hook. If there is no
783
807
  // second argument then the reactive callback will re-run on every render.
784
808
  // So no need to check for dependency inclusion.
785
809
 
786
- var depsIndex = callbackIndex + 1;
787
- var declaredDependenciesNode = node.parent.arguments[depsIndex];
788
-
789
810
  if (!declaredDependenciesNode && !isEffect) {
790
811
  // These are only used for optimization.
791
812
  if (reactiveHookName === 'useMemo' || reactiveHookName === 'useCallback') {
792
813
  // TODO: Can this have a suggestion?
793
- context.report({
794
- node: node.parent.callee,
814
+ reportProblem({
815
+ node: reactiveHook,
795
816
  message: "React Hook " + reactiveHookName + " does nothing when called with " + "only one argument. Did you forget to pass an array of " + "dependencies?"
796
817
  });
797
818
  }
@@ -799,15 +820,120 @@ var ExhaustiveDeps = {
799
820
  return;
800
821
  }
801
822
 
823
+ switch (callback.type) {
824
+ case 'FunctionExpression':
825
+ case 'ArrowFunctionExpression':
826
+ visitFunctionWithDependencies(callback, declaredDependenciesNode, reactiveHook, reactiveHookName, isEffect);
827
+ return;
828
+ // Handled
829
+
830
+ case 'Identifier':
831
+ if (!declaredDependenciesNode) {
832
+ // No deps, no problems.
833
+ return; // Handled
834
+ } // The function passed as a callback is not written inline.
835
+ // But perhaps it's in the dependencies array?
836
+
837
+
838
+ if (declaredDependenciesNode.elements && declaredDependenciesNode.elements.some(function (el) {
839
+ return el.type === 'Identifier' && el.name === callback.name;
840
+ })) {
841
+ // If it's already in the list of deps, we don't care because
842
+ // this is valid regardless.
843
+ return; // Handled
844
+ } // We'll do our best effort to find it, complain otherwise.
845
+
846
+
847
+ var variable = context.getScope().set.get(callback.name);
848
+
849
+ if (variable == null || variable.defs == null) {
850
+ // If it's not in scope, we don't care.
851
+ return; // Handled
852
+ } // The function passed as a callback is not written inline.
853
+ // But it's defined somewhere in the render scope.
854
+ // We'll do our best effort to find and check it, complain otherwise.
855
+
856
+
857
+ var def = variable.defs[0];
858
+
859
+ if (!def || !def.node) {
860
+ break; // Unhandled
861
+ }
862
+
863
+ if (def.type !== 'Variable' && def.type !== 'FunctionName') {
864
+ // Parameter or an unusual pattern. Bail out.
865
+ break; // Unhandled
866
+ }
867
+
868
+ switch (def.node.type) {
869
+ case 'FunctionDeclaration':
870
+ // useEffect(() => { ... }, []);
871
+ visitFunctionWithDependencies(def.node, declaredDependenciesNode, reactiveHook, reactiveHookName, isEffect);
872
+ return;
873
+ // Handled
874
+
875
+ case 'VariableDeclarator':
876
+ var init = def.node.init;
877
+
878
+ if (!init) {
879
+ break; // Unhandled
880
+ }
881
+
882
+ switch (init.type) {
883
+ // const effectBody = () => {...};
884
+ // useEffect(effectBody, []);
885
+ case 'ArrowFunctionExpression':
886
+ case 'FunctionExpression':
887
+ // We can inspect this function as if it were inline.
888
+ visitFunctionWithDependencies(init, declaredDependenciesNode, reactiveHook, reactiveHookName, isEffect);
889
+ return;
890
+ // Handled
891
+ }
892
+
893
+ break;
894
+ // Unhandled
895
+ }
896
+
897
+ break;
898
+ // Unhandled
899
+
900
+ default:
901
+ // useEffect(generateEffectBody(), []);
902
+ reportProblem({
903
+ node: reactiveHook,
904
+ message: "React Hook " + reactiveHookName + " received a function whose dependencies " + "are unknown. Pass an inline function instead."
905
+ });
906
+ return;
907
+ // Handled
908
+ } // Something unusual. Fall back to suggesting to add the body itself as a dep.
909
+
910
+
911
+ reportProblem({
912
+ node: reactiveHook,
913
+ message: "React Hook " + reactiveHookName + " has a missing dependency: '" + callback.name + "'. " + "Either include it or remove the dependency array.",
914
+ suggest: [{
915
+ desc: "Update the dependencies array to be: [" + callback.name + "]",
916
+ fix: function (fixer) {
917
+ return fixer.replaceText(declaredDependenciesNode, "[" + callback.name + "]");
918
+ }
919
+ }]
920
+ });
921
+ }
922
+ /**
923
+ * Visitor for both function expressions and arrow function expressions.
924
+ */
925
+
926
+
927
+ function visitFunctionWithDependencies(node, declaredDependenciesNode, reactiveHook, reactiveHookName, isEffect) {
802
928
  if (isEffect && node.async) {
803
- context.report({
929
+ reportProblem({
804
930
  node: node,
805
931
  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://fb.me/react-hooks-data-fetching'
806
932
  });
807
933
  } // Get the current scope.
808
934
 
809
935
 
810
- var scope = context.getScope(); // Find all our "pure scopes". On every re-render of a component these
936
+ var scope = scopeManager.acquire(node); // Find all our "pure scopes". On every re-render of a component these
811
937
  // pure scopes may have changes to the variables declared within. So all
812
938
  // variables used in our reactive hook callback but declared in a pure
813
939
  // scope need to be listed as dependencies of our reactive hook callback.
@@ -870,6 +996,10 @@ var ExhaustiveDeps = {
870
996
 
871
997
  if (init == null) {
872
998
  return false;
999
+ }
1000
+
1001
+ while (init.type === 'TSAsExpression') {
1002
+ init = init.expression;
873
1003
  } // Detect primitive constants
874
1004
  // const foo = 42
875
1005
 
@@ -1170,7 +1300,7 @@ var ExhaustiveDeps = {
1170
1300
  return;
1171
1301
  }
1172
1302
 
1173
- context.report({
1303
+ reportProblem({
1174
1304
  node: dependencyNode.parent.property,
1175
1305
  message: "The ref value '" + dependency + ".current' will likely have " + "changed by the time this effect cleanup function runs. If " + "this ref points to a node rendered by React, copy " + ("'" + dependency + ".current' to a variable inside the effect, and ") + "use that variable in the cleanup function."
1176
1306
  });
@@ -1185,7 +1315,7 @@ var ExhaustiveDeps = {
1185
1315
  }
1186
1316
 
1187
1317
  staleAssignments.add(key);
1188
- context.report({
1318
+ reportProblem({
1189
1319
  node: writeExpr,
1190
1320
  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) + ".")
1191
1321
  });
@@ -1262,8 +1392,8 @@ var ExhaustiveDeps = {
1262
1392
  }),
1263
1393
  _suggestedDependencies = _collectRecommendatio.suggestedDependencies;
1264
1394
 
1265
- context.report({
1266
- node: node.parent.callee,
1395
+ reportProblem({
1396
+ node: reactiveHook,
1267
1397
  message: "React Hook " + reactiveHookName + " contains a call to '" + setStateInsideEffectWithoutDeps + "'. " + "Without a list of dependencies, this can lead to an infinite chain of updates. " + "To fix this, pass [" + _suggestedDependencies.join(', ') + ("] as a second argument to the " + reactiveHookName + " Hook."),
1268
1398
  suggest: [{
1269
1399
  desc: "Add dependencies array: [" + _suggestedDependencies.join(', ') + "]",
@@ -1284,7 +1414,7 @@ var ExhaustiveDeps = {
1284
1414
  // If the declared dependencies are not an array expression then we
1285
1415
  // can't verify that the user provided the correct dependencies. Tell
1286
1416
  // the user this in an error.
1287
- context.report({
1417
+ reportProblem({
1288
1418
  node: declaredDependenciesNode,
1289
1419
  message: "React Hook " + context.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.'
1290
1420
  });
@@ -1297,7 +1427,7 @@ var ExhaustiveDeps = {
1297
1427
 
1298
1428
 
1299
1429
  if (declaredDependencyNode.type === 'SpreadElement') {
1300
- context.report({
1430
+ reportProblem({
1301
1431
  node: declaredDependencyNode,
1302
1432
  message: "React Hook " + context.getSource(reactiveHook) + " has a spread " + "element in its dependency array. This means we can't " + "statically verify whether you've passed the " + 'correct dependencies.'
1303
1433
  });
@@ -1314,18 +1444,18 @@ var ExhaustiveDeps = {
1314
1444
  if (/Unsupported node type/.test(error.message)) {
1315
1445
  if (declaredDependencyNode.type === 'Literal') {
1316
1446
  if (dependencies.has(declaredDependencyNode.value)) {
1317
- context.report({
1447
+ reportProblem({
1318
1448
  node: declaredDependencyNode,
1319
1449
  message: "The " + declaredDependencyNode.raw + " literal is not a valid dependency " + "because it never changes. " + ("Did you mean to include " + declaredDependencyNode.value + " in the array instead?")
1320
1450
  });
1321
1451
  } else {
1322
- context.report({
1452
+ reportProblem({
1323
1453
  node: declaredDependencyNode,
1324
1454
  message: "The " + declaredDependencyNode.raw + " literal is not a valid dependency " + 'because it never changes. You can safely remove it.'
1325
1455
  });
1326
1456
  }
1327
1457
  } else {
1328
- context.report({
1458
+ reportProblem({
1329
1459
  node: declaredDependencyNode,
1330
1460
  message: "React Hook " + context.getSource(reactiveHook) + " has a " + "complex expression in the dependency array. " + 'Extract it to a separate variable so it can be statically checked.'
1331
1461
  });
@@ -1370,6 +1500,7 @@ var ExhaustiveDeps = {
1370
1500
  missingDependencies = _collectRecommendatio2.missingDependencies,
1371
1501
  duplicateDependencies = _collectRecommendatio2.duplicateDependencies;
1372
1502
 
1503
+ var suggestedDeps = suggestedDependencies;
1373
1504
  var problemCount = duplicateDependencies.size + missingDependencies.size + unnecessaryDependencies.size;
1374
1505
 
1375
1506
  if (problemCount === 0) {
@@ -1411,7 +1542,7 @@ var ExhaustiveDeps = {
1411
1542
  // Should we suggest removing effect deps as an appropriate fix too?
1412
1543
 
1413
1544
 
1414
- context.report({
1545
+ reportProblem({
1415
1546
  // TODO: Why not report this at the dependency site?
1416
1547
  node: fn.node,
1417
1548
  message: message,
@@ -1428,7 +1559,7 @@ var ExhaustiveDeps = {
1428
1559
 
1429
1560
 
1430
1561
  if (!isEffect && missingDependencies.size > 0) {
1431
- suggestedDependencies = collectRecommendations({
1562
+ suggestedDeps = collectRecommendations({
1432
1563
  dependencies: dependencies,
1433
1564
  declaredDependencies: [],
1434
1565
  // Pretend we don't know
@@ -1452,7 +1583,7 @@ var ExhaustiveDeps = {
1452
1583
  }
1453
1584
 
1454
1585
  if (areDeclaredDepsAlphabetized()) {
1455
- suggestedDependencies.sort();
1586
+ suggestedDeps.sort();
1456
1587
  }
1457
1588
 
1458
1589
  function getWarningMessage(deps, singlePrefix, label, fixVerb) {
@@ -1675,15 +1806,15 @@ var ExhaustiveDeps = {
1675
1806
  }
1676
1807
  }
1677
1808
 
1678
- context.report({
1809
+ reportProblem({
1679
1810
  node: declaredDependenciesNode,
1680
1811
  message: "React Hook " + context.getSource(reactiveHook) + " has " + ( // To avoid a long message, show the next actionable item.
1681
1812
  getWarningMessage(missingDependencies, 'a', 'missing', 'include') || getWarningMessage(unnecessaryDependencies, 'an', 'unnecessary', 'exclude') || getWarningMessage(duplicateDependencies, 'a', 'duplicate', 'omit')) + extraWarning,
1682
1813
  suggest: [{
1683
- desc: "Update the dependencies array to be: [" + suggestedDependencies.join(', ') + "]",
1814
+ desc: "Update the dependencies array to be: [" + suggestedDeps.join(', ') + "]",
1684
1815
  fix: function (fixer) {
1685
1816
  // TODO: consider preserving the comments or formatting?
1686
- return fixer.replaceText(declaredDependenciesNode, "[" + suggestedDependencies.join(', ') + "]");
1817
+ return fixer.replaceText(declaredDependenciesNode, "[" + suggestedDeps.join(', ') + "]");
1687
1818
  }
1688
1819
  }]
1689
1820
  });
@@ -1983,6 +2114,8 @@ function scanForDeclaredBareFunctions(_ref8) {
1983
2114
  function getDependency(node) {
1984
2115
  if (node.parent.type === 'MemberExpression' && 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)) {
1985
2116
  return getDependency(node.parent);
2117
+ } else if (node.type === 'MemberExpression' && node.parent && node.parent.type === 'AssignmentExpression') {
2118
+ return node.object;
1986
2119
  } else {
1987
2120
  return node;
1988
2121
  }
@@ -2025,7 +2158,7 @@ function getReactiveHookCallbackIndex(calleeNode, options) {
2025
2158
  var node = getNodeWithoutReactNamespace(calleeNode);
2026
2159
 
2027
2160
  if (node.type !== 'Identifier') {
2028
- return null;
2161
+ return -1;
2029
2162
  }
2030
2163
 
2031
2164
  switch (node.name) {
@@ -2041,7 +2174,9 @@ function getReactiveHookCallbackIndex(calleeNode, options) {
2041
2174
  return 1;
2042
2175
 
2043
2176
  default:
2044
- if (node === calleeNode && options && options.additionalHooks) {
2177
+ if (node === calleeNode && node.name.match(/use.+Effect/)) {
2178
+ return 0;
2179
+ } else if (node === calleeNode && options && options.additionalHooks) {
2045
2180
  // Allow the user to provide a regular expression which enables the lint to
2046
2181
  // target custom reactive hooks.
2047
2182
  var name;
@@ -2146,17 +2281,21 @@ function isAncestorNodeOf(a, b) {
2146
2281
  return a.range[0] <= b.range[0] && a.range[1] >= b.range[1];
2147
2282
  }
2148
2283
 
2284
+ var configs = {
2285
+ recommended: {
2286
+ plugins: ['react-hooks'],
2287
+ rules: {
2288
+ 'react-hooks/rules-of-hooks': 'error',
2289
+ 'react-hooks/exhaustive-deps': 'warn'
2290
+ }
2291
+ }
2292
+ };
2149
2293
  var rules = {
2150
- 'rules-of-hooks': RuleOfHooks,
2294
+ 'rules-of-hooks': RulesOfHooks,
2151
2295
  'exhaustive-deps': ExhaustiveDeps
2152
2296
  };
2153
2297
 
2154
- var src = Object.freeze({
2155
- rules: rules
2156
- });
2157
-
2158
- var eslintPluginReactHooks = src;
2159
-
2160
- module.exports = eslintPluginReactHooks;
2298
+ exports.configs = configs;
2299
+ exports.rules = rules;
2161
2300
  })();
2162
2301
  }
@@ -7,46 +7,51 @@
7
7
  * LICENSE file in the root directory of this source tree.
8
8
  */
9
9
 
10
- 'use strict';function T(a){return"Identifier"===a.type?/^use[A-Z0-9].*$/.test(a.name):"MemberExpression"===a.type&&!a.computed&&T(a.property)?(a=a.object,"Identifier"===a.type&&"React"===a.name):!1}function ba(a){return"Identifier"===a.type?!/^[a-z]/.test(a.name):!1}function ca(a,e){return a.name===e||"MemberExpression"===a.type&&"React"===a.object.name&&a.property.name===e}function da(a){return!!(a.parent&&a.parent.callee&&ca(a.parent.callee,"forwardRef"))}
11
- function ea(a){return!!(a.parent&&a.parent.callee&&ca(a.parent.callee,"memo"))}function fa(a){for(;a;){var e=ha(a);if(e&&(ba(e)||T(e))||da(a)||ea(a))return!0;a=a.parent}return!1}
12
- function ha(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:
10
+ 'use strict';function U(a){if("Identifier"===a.type)return/^use[A-Z0-9].*$/.test(a.name);if("MemberExpression"===a.type&&!a.computed&&U(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 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&&(aa(c)||U(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:
13
13
  a.parent.key}
14
- function V(a){function e(){return{isRequired:!1,isSatisfiedRecursively:!1,hasRequiredNodesBelow:!1,children:new Map}}function d(a,c){c=c.split(".");var m=!0,k=!1,d=void 0;try{for(var h=c[Symbol.iterator](),p;!(m=(p=h.next()).done);m=!0){var z=p.value,g=a.children.get(z);g||(g=e(),a.children.set(z,g));a=g}}catch(E){k=!0,d=E}finally{try{m||null==h.return||h.return()}finally{if(k)throw d;}}return a}function h(a,c,e){c=c.split(".");var m=!0,k=!1,d=void 0;try{for(var h=c[Symbol.iterator](),g;!(m=(g=h.next()).done);m=
15
- !0){var z=a.children.get(g.value);if(!z)break;e(z);a=z}}catch(E){k=!0,d=E}finally{try{m||null==h.return||h.return()}finally{if(k)throw d;}}}function c(a,e,k,h){a.children.forEach(function(a,m){var d=h(m);a.isSatisfiedRecursively?a.hasRequiredNodesBelow&&k.add(d):a.isRequired?e.add(d):c(a,e,k,function(a){return d+"."+a})})}var z=a.dependencies,l=a.declaredDependencies,v=a.optionalDependencies,k=a.externalDependencies,C=a.isEffect,F=e();z.forEach(function(a,c){d(F,c).isRequired=!0;h(F,c,function(a){a.hasRequiredNodesBelow=
16
- !0})});l.forEach(function(a){d(F,a.key).isSatisfiedRecursively=!0});v.forEach(function(a){d(F,a).isSatisfiedRecursively=!0});a=new Set;var g=new Set;c(F,a,g,function(a){return a});var u=[],x=new Set,A=new Set;l.forEach(function(a){a=a.key;g.has(a)?-1===u.indexOf(a)?u.push(a):A.add(a):!C||a.endsWith(".current")||k.has(a)?x.add(a):-1===u.indexOf(a)&&u.push(a)});a.forEach(function(a){u.push(a)});return{suggestedDependencies:u,unnecessaryDependencies:x,duplicateDependencies:A,missingDependencies:a}}
17
- function ia(a){var e=a.declaredDependenciesNode,d=a.componentScope,h=a.scope;return a.declaredDependencies.map(function(a){a=d.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 c=a.defs[0];a:{for(var d=!1,v=
18
- 0;v<a.references.length;v++){var k=a.references[v];if(k.writeExpr)if(d){a=!0;break a}else{d=!0;continue}for(var C=k.from;C!==h&&null!=C;)C=C.upper;if(C!==h&&!ja(e,k.identifier)){a=!0;break a}}a=!1}return{fn:c,suggestUseCallback:a}})}function ka(a){return"MemberExpression"!==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?a:ka(a.parent)}
19
- function X(a){if("Identifier"===a.type)return a.name;if("MemberExpression"!==a.type||a.computed)throw Error("Unsupported node type: "+a.type);var e=X(a.object);a=X(a.property);return e+"."+a}function ma(a){return"MemberExpression"!==a.type||"Identifier"!==a.object.type||"React"!==a.object.name||"Identifier"!==a.property.type||a.computed?a:a.property}
20
- function na(a,e){var d=ma(a);if("Identifier"!==d.type)return null;switch(d.name){case "useEffect":case "useLayoutEffect":case "useCallback":case "useMemo":return 0;case "useImperativeHandle":return 1;default:if(d===a&&e&&e.additionalHooks){try{var h=X(d)}catch(c){if(/Unsupported node type/.test(c.message))return 0;throw c;}return e.additionalHooks.test(h)?0:-1}return-1}}
21
- function Y(a,e){for(var d=[a],h=null;d.length;){h=d.shift();if("Identifier"===h.type&&h.name===e.name&&h.range[0]===e.range[0]&&h.range[1]===e.range[1])return h;if(ja(h,e)){a=0;for(var c=Object.entries(h);a<c.length;a++){var z=c[a],l=z[1];"parent"!==z[0]&&(oa(l)?(l.parent=h,d.push(l)):Array.isArray(l)&&l.forEach(function(a){oa(a)&&(a.parent=h,d.push(a))}))}}}return null}
22
- function pa(a){for(var e="",d=0;d<a.length;d++)e+=a[d],0===d&&2===a.length?e+=" and ":d===a.length-2&&2<a.length?e+=", and ":d<a.length-1&&(e+=", ");return e}function oa(a){return"object"===typeof a&&null!==a&&!Array.isArray(a)&&"string"===typeof a.type}function ja(a,e){return a.range[0]<=e.range[0]&&a.range[1]>=e.range[1]}
23
- module.exports={rules:{"rules-of-hooks":{create:function(a){var e=[],d=[];return{onCodePathSegmentStart:function(a){return d.push(a)},onCodePathSegmentEnd:function(){return d.pop()},onCodePathStart:function(){return e.push(new Map)},onCodePathEnd:function(d,c){function h(a){var c=h.cache,b=c.get(a.id);if(null===b){if(!C.has(a.id)){C.add(a.id);c=!0;b=!1;var n=void 0;try{for(var f=a.prevSegments[Symbol.iterator](),B;!(c=(B=f.next()).done);c=!0)h(B.value)}catch(L){b=!0,n=L}finally{try{c||null==f.return||
24
- f.return()}finally{if(b)throw n;}}}return 0}if(void 0!==b)return b;c.set(a.id,null);if(d.thrownSegments.includes(a))b=0;else if(0===a.prevSegments.length)b=1;else{b=0;f=!0;B=!1;var e=void 0;try{n=a.prevSegments[Symbol.iterator]();for(var k;!(f=(k=n.next()).done);f=!0)b+=h(k.value)}catch(L){B=!0,e=L}finally{try{f||null==n.return||n.return()}finally{if(B)throw e;}}}a.reachable&&0===b?c.delete(a.id):c.set(a.id,b);return b}function l(a){var c=l.cache,b=c.get(a.id);if(null===b){if(!C.has(a.id)){C.add(a.id);
25
- c=!0;b=!1;var n=void 0;try{for(var f=a.nextSegments[Symbol.iterator](),B;!(c=(B=f.next()).done);c=!0)l(B.value)}catch(L){b=!0,n=L}finally{try{c||null==f.return||f.return()}finally{if(b)throw n;}}}return 0}if(void 0!==b)return b;c.set(a.id,null);if(d.thrownSegments.includes(a))b=0;else if(0===a.nextSegments.length)b=1;else{b=0;f=!0;B=!1;var e=void 0;try{n=a.nextSegments[Symbol.iterator]();for(var k;!(f=(k=n.next()).done);f=!0)b+=l(k.value)}catch(L){B=!0,e=L}finally{try{f||null==n.return||n.return()}finally{if(B)throw e;
26
- }}}c.set(a.id,b);return b}function v(a){var c=v.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 n=!0,f=!1,B=void 0;try{for(var e=a.prevSegments[Symbol.iterator](),d;!(n=(d=e.next()).done);n=!0){var k=v(d.value);k<b&&(b=k)}}catch(qa){f=!0,B=qa}finally{try{n||null==e.return||e.return()}finally{if(f)throw B;}}b+=1}c.set(a.id,b);return b}var k=e.pop();if(0!==k.size){var C=new Set;h.cache=new Map;l.cache=new Map;
27
- v.cache=new Map;var F=l(d.initialSegment),g=ha(c),u=fa(c),x=g?ba(g)||T(g):da(c)||ea(c),A=Infinity,m=!0,p=!1,I=void 0;try{for(var t=d.finalSegments[Symbol.iterator](),K;!(m=(K=t.next()).done);m=!0){var W=K.value;if(W.reachable){var S=v(W);S<A&&(A=S)}}}catch(G){p=!0,I=G}finally{try{m||null==t.return||t.return()}finally{if(p)throw I;}}m=!0;p=!1;I=void 0;try{for(var y=k[Symbol.iterator](),P;!(m=(P=y.next()).done);m=!0){var E=P.value,D=E[0],M=E[1];if(D.reachable){var J=0===D.nextSegments.length?A<=v(D):
28
- A<v(D),Q=h(D)*l(D),H=C.has(D.id);k=!0;t=!1;K=void 0;try{for(var w=M[Symbol.iterator](),N;!(k=(N=w.next()).done);k=!0){var r=N.value;H&&a.report({node:r,message:'React Hook "'+a.getSource(r)+'" 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.'});if(x){if(!H&&Q!==F){var U='React Hook "'+a.getSource(r)+'" is called conditionally. React Hooks must be called in the exact same order in every component render.'+
29
- (J?" Did you accidentally call a React Hook after an early return?":"");a.report({node:r,message:U})}}else if(!c.parent||"MethodDefinition"!==c.parent.type&&"ClassProperty"!==c.parent.type||c.parent.value!==c)if(g){var O='React Hook "'+a.getSource(r)+'" is called in function "'+(a.getSource(g)+'" that is neither a React function component nor a custom React Hook function.');a.report({node:r,message:O})}else if("Program"===c.type){var R='React Hook "'+a.getSource(r)+'" cannot be called at the top level. React Hooks must be called in a React function component or a custom React Hook function.';
30
- a.report({node:r,message:R})}else if(u){var q='React Hook "'+a.getSource(r)+'" 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:r,message:q})}}}catch(G){t=!0,K=G}finally{try{k||null==w.return||w.return()}finally{if(t)throw K;}}}}}catch(G){p=!0,I=G}finally{try{m||null==y.return||y.return()}finally{if(p)throw I;}}}},CallExpression:function(a){if(T(a.callee)){var c=e[e.length-1],h=d[d.length-1],l=c.get(h);l||
31
- (l=[],c.set(h,l));l.push(a.callee)}}}}},"exhaustive-deps":{meta:{schema:[{type:"object",additionalProperties:!1,properties:{additionalHooks:{type:"string"}}}]},create:function(a){function e(a,c){return function(e){if(c.has(e))return c.get(e);var d=a(e);c.set(e,d);return d}}function d(d){function k(a){var b=!0,f=!1,c=void 0;try{for(var e=a.references[Symbol.iterator](),h;!(b=(h=e.next()).done);b=!0){var g=h.value;if(g.resolved&&I.has(g.resolved.scope)){var m=Y(d,g.identifier),l=ka(m),p=X(l),r;if(r=
32
- A&&"Identifier"===l.type&&"MemberExpression"===l.parent.type&&!l.parent.computed&&"Identifier"===l.parent.property.type&&"current"===l.parent.property.name){for(var q=g.from,u=!1;q.block!==d;)"function"===q.type&&(u=null!=q.block.parent&&"ReturnStatement"===q.block.parent.type),q=q.upper;r=u}r&&S.set(p,{reference:g,dependencyNode:l});var t=g.resolved.defs[0];if(null!=t&&(null==t.node||t.node.init!==d.parent)&&"TypeParameter"!==t.type)if(y.has(p))y.get(p).references.push(g);else{var w=g.resolved,z=
33
- K(w)||W(w);y.set(p,{isStatic:z,references:[g]})}}}}catch(aa){f=!0,c=aa}finally{try{b||null==e.return||e.return()}finally{if(f)throw c;}}b=!0;f=!1;c=void 0;try{for(var v=a.childScopes[Symbol.iterator](),x;!(b=(x=v.next()).done);b=!0)k(x.value)}catch(aa){f=!0,c=aa}finally{try{b||null==v.return||v.return()}finally{if(f)throw c;}}}function F(a,n,f,c){return 0===a.size?null:(1<a.size?"":n+" ")+f+" "+(1<a.size?"dependencies":"dependency")+": "+pa(Array.from(a).sort().map(function(a){return"'"+a+"'"}))+
34
- (". Either "+c+" "+(1<a.size?"them":"it")+" or remove the dependency array.")}if(("FunctionExpression"===d.type||"ArrowFunctionExpression"===d.type)&&"CallExpression"===d.parent.type){var g=na(d.parent.callee,h);if(d.parent.arguments[g]===d){var u=d.parent.callee,x=ma(u).name,A=/Effect($|[^a-z])/g.test(x),m=d.parent.arguments[g+1];if(m||A){A&&d.async&&a.report({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"});
35
- var p=a.getScope(),I=new Set,t=null;for(g=p.upper;g;){I.add(g);if("function"===g.type)break;g=g.upper}if(g){t=g;var K=e(function(a){if(!Array.isArray(a.defs))return!1;var b=a.defs[0];if(null==b||"VariableDeclarator"!==b.node.type)return!1;var f=b.node.init;if(null==f)return!1;var d=b.node.parent;if(null==d&&(Y(t.block,b.node.id),d=b.node.parent,null==d))return!1;if("const"===d.kind&&"Literal"===f.type&&("string"===typeof f.value||"number"===typeof f.value||null===f.value))return!0;if("CallExpression"!==
36
- f.type)return!1;f=f.callee;"MemberExpression"!==f.type||"React"!==f.object.name||null==f.property||f.computed||(f=f.property);if("Identifier"!==f.type)return!1;b=b.node.id;f=f.name;if("useRef"===f&&"Identifier"===b.type)return!0;if(("useState"===f||"useReducer"===f)&&"ArrayPattern"===b.type&&2===b.elements.length&&Array.isArray(a.identifiers)){if(b.elements[1]===a.identifiers[0]){if("useState"===f)for(a=a.references,f=0;f<a.length;f++)c.set(a[f].identifier,b.elements[0]);return!0}if(b.elements[0]===
37
- a.identifiers[0]&&"useState"===f)for(a=a.references,b=0;b<a.length;b++)z.add(a[b].identifier)}return!1},l),W=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 b=a.node,f=t.childScopes;a=null;var c;for(c=0;c<f.length;c++){var d=f[c],e=d.block;if("FunctionDeclaration"===b.type&&e===b||"VariableDeclarator"===b.type&&e.parent===b){a=d;break}}if(null==a)return!1;for(c=0;c<a.through.length;c++)if(b=a.through[c],null!=b.resolved&&I.has(b.resolved.scope)&&
38
- !K(b.resolved))return!1;return!0},v),S=new Map,y=new Map;k(p);S.forEach(function(b,c){var f=b.dependencyNode;b=b.reference.resolved.references;for(var d=!1,e=0;e<b.length;e++){var n=b[e].identifier.parent;if(null!=n&&"MemberExpression"===n.type&&!n.computed&&"Identifier"===n.property.type&&"current"===n.property.name&&"AssignmentExpression"===n.parent.type&&n.parent.left===n){d=!0;break}}d||a.report({node:f.parent.property,message:"The ref value '"+c+".current' will likely have changed by the time this effect cleanup function runs. If this ref points to a node rendered by React, copy '"+
39
- (c+".current' to a variable inside the effect, and use that variable in the cleanup function.")})});var P=new Set,E=new Set;y.forEach(function(b,c){var f=b.references;b.isStatic&&E.add(c);f.forEach(function(b){b.writeExpr&&(b=b.writeExpr,P.has(c)||(P.add(c),a.report({node:b,message:"Assignments to the '"+c+"' variable from inside React Hook "+(a.getSource(u)+" 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 ")+
40
- (a.getSource(u)+".")})))})});if(!(0<P.size))if(m){var D=[],M=new Set;"ArrayExpression"!==m.type?a.report({node:m,message:"React Hook "+a.getSource(u)+" 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."}):m.elements.forEach(function(b){if(null!==b)if("SpreadElement"===b.type)a.report({node:b,message:"React Hook "+a.getSource(u)+" has a spread element in its dependency array. This means we can't statically verify whether you've passed the correct dependencies."});
41
- else{try{var c=X(b)}catch(la){if(/Unsupported node type/.test(la.message)){"Literal"===b.type?y.has(b.value)?a.report({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?")}):a.report({node:b,message:"The "+b.raw+" literal is not a valid dependency because it never changes. You can safely remove it."}):a.report({node:b,message:"React Hook "+a.getSource(u)+" has a complex expression in the dependency array. Extract it to a separate variable so it can be statically checked."});
42
- return}throw la;}for(var f=b;"MemberExpression"===f.type;)f=f.object;var d=!t.through.some(function(a){return a.identifier===f});D.push({key:c,node:b});d||M.add(c)}});var J=V({dependencies:y,declaredDependencies:D,optionalDependencies:E,externalDependencies:M,isEffect:A}),Q=J.suggestedDependencies;g=J.unnecessaryDependencies;var H=J.missingDependencies;J=J.duplicateDependencies;if(0===J.size+H.size+g.size)ia({declaredDependencies:D,declaredDependenciesNode:m,componentScope:t,scope:p}).forEach(function(b){var c=
43
- b.fn;b=b.suggestUseCallback;var f="The '"+c.name.name+"' function makes the dependencies of "+(x+" Hook (at line "+m.loc.start.line+") change on every render.");f=b?f+(" To fix this, wrap the '"+(c.name.name+"' definition into its own useCallback() Hook.")):f+(" Move it inside the "+x+" callback. Alternatively, wrap the '"+(c.name.name+"' definition into its own useCallback() Hook."));var d;b&&"Variable"===c.type&&(d=[{desc:"Wrap the '"+c.name.name+"' definition into its own useCallback() Hook.",
44
- fix:function(a){return[a.insertTextBefore(c.node.init,"useCallback("),a.insertTextAfter(c.node.init,")")]}}]);a.report({node:c.node,message:f,suggest:d})});else{!A&&0<H.size&&(Q=V({dependencies:y,declaredDependencies:[],optionalDependencies:E,externalDependencies:M,isEffect:A}).suggestedDependencies);(function(){if(0===D.length)return!0;var a=D.map(function(a){return a.key}),c=a.slice().sort();return a.join(",")===c.join(",")})()&&Q.sort();var w="";if(0<g.size){var N=null;Array.from(g.keys()).forEach(function(a){null===
45
- N&&a.endsWith(".current")&&(N=a)});if(null!==N)w=" Mutable values like '"+N+"' aren't valid dependencies because mutating them doesn't re-render the component.";else if(0<M.size){var r=Array.from(M)[0];p.set.has(r)||(w=" Outer scope values like '"+r+"' aren't valid dependencies because mutating them doesn't re-render the component.")}}if(!w&&H.has("props")){p=y.get("props");if(null==p)return;p=p.references;if(!Array.isArray(p))return;r=!0;for(var U=0;U<p.length;U++){var O=Y(t.block,p[U].identifier);
46
- if(!O){r=!1;break}O=O.parent;if(null==O){r=!1;break}if("MemberExpression"!==O.type){r=!1;break}}r&&(w=" However, 'props' will change when *any* prop changes, so the preferred fix is to destructure the 'props' object outside of the "+(x+" call and refer to those specific props inside ")+(a.getSource(u)+"."))}if(!w&&0<H.size){var R=null;H.forEach(function(a){if(!R){var b=t.set.get(a),c=y.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,
47
- 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&&(R=a)}}});null!==R&&(w=" If '"+R+"' changes too often, find the parent component that defines it and wrap that definition in useCallback.")}if(!w&&0<H.size){var q=null;H.forEach(function(a){if(null===q)for(var b=y.get(a).references,d,e,g=0;g<b.length;g++){d=b[g].identifier;for(e=d.parent;null!=e&&e!==t.block;){if("CallExpression"===e.type){var h=
48
- c.get(e.callee);if(null!=h){h.name===a?q={missingDep:a,setter:e.callee.name,form:"updater"}:z.has(d)?q={missingDep:a,setter:e.callee.name,form:"reducer"}:(d=b[g].resolved,null!=d&&(d=d.defs[0],null!=d&&"Parameter"===d.type&&(q={missingDep:a,setter:e.callee.name,form:"inlineReducer"})));break}}e=e.parent}if(null!==q)break}});if(null!==q)switch(q.form){case "reducer":w=" You can also replace multiple useState variables with useReducer if '"+(q.setter+"' needs the current value of '")+(q.missingDep+
49
- "'.");break;case "inlineReducer":w=" If '"+q.setter+"' needs the current value of '"+(q.missingDep+"', you can also switch to useReducer instead of useState and read '")+(q.missingDep+"' in the reducer.");break;case "updater":w=" You can also do a functional update '"+q.setter+"("+q.missingDep.substring(0,1)+" => ...)' if you only need '"+q.missingDep+"' in the '"+(q.setter+"' call.");break;default:throw Error("Unknown case.");}}a.report({node:m,message:"React Hook "+a.getSource(u)+" has "+(F(H,"a",
50
- "missing","include")||F(g,"an","unnecessary","exclude")||F(J,"a","duplicate","omit"))+w,suggest:[{desc:"Update the dependencies array to be: ["+Q.join(", ")+"]",fix:function(a){return a.replaceText(m,"["+Q.join(", ")+"]")}}]})}}else{var G=null;y.forEach(function(a,e){G||a.references.forEach(function(a){if(!G&&c.has(a.identifier)){for(a=a.from;"function"!==a.type;)a=a.upper;a.block===d&&(G=e)}})});if(G){var Z=V({dependencies:y,declaredDependencies:[],optionalDependencies:E,externalDependencies:new Set,
51
- isEffect:!0}).suggestedDependencies;a.report({node:d.parent.callee,message:"React Hook "+x+" contains a call to '"+G+"'. Without a list of dependencies, this can lead to an infinite chain of updates. To fix this, pass ["+Z.join(", ")+("] as a second argument to the "+x+" Hook."),suggest:[{desc:"Add dependencies array: ["+Z.join(", ")+"]",fix:function(a){return a.insertTextAfter(d,", ["+Z.join(", ")+"]")}}]})}}}}else"useMemo"!==x&&"useCallback"!==x||a.report({node:d.parent.callee,message:"React Hook "+
52
- x+" does nothing when called with only one argument. Did you forget to pass an array of dependencies?"})}}}var h={additionalHooks:a.options&&a.options[0]&&a.options[0].additionalHooks?new RegExp(a.options[0].additionalHooks):void 0},c=new WeakMap,z=new WeakSet,l=new WeakMap,v=new WeakMap;return{FunctionExpression:d,ArrowFunctionExpression:d}}}}};
14
+ function ka(a){function c(){return{isRequired:!1,isSatisfiedRecursively:!1,hasRequiredNodesBelow:!1,children:new Map}}function e(a,b){b=b.split(".");var k=!0,f=!1,d=void 0;try{for(var e=b[Symbol.iterator](),h;!(k=(h=e.next()).done);k=!0){var n=h.value,z=a.children.get(n);z||(z=c(),a.children.set(n,z));a=z}}catch(G){f=!0,d=G}finally{try{k||null==e.return||e.return()}finally{if(f)throw d;}}return a}function h(a,c,b){c=c.split(".");var k=!0,f=!1,d=void 0;try{for(var e=c[Symbol.iterator](),h;!(k=
15
+ (h=e.next()).done);k=!0){var z=a.children.get(h.value);if(!z)break;b(z);a=z}}catch(G){f=!0,d=G}finally{try{k||null==e.return||e.return()}finally{if(f)throw d;}}}function f(a,c,b,d){a.children.forEach(function(a,k){var e=d(k);a.isSatisfiedRecursively?a.hasRequiredNodesBelow&&b.add(e):a.isRequired?c.add(e):f(a,c,b,function(a){return e+"."+a})})}var H=a.dependencies,p=a.declaredDependencies,u=a.optionalDependencies,x=a.externalDependencies,y=a.isEffect,I=c();H.forEach(function(a,c){e(I,c).isRequired=
16
+ !0;h(I,c,function(a){a.hasRequiredNodesBelow=!0})});p.forEach(function(a){e(I,a.key).isSatisfiedRecursively=!0});u.forEach(function(a){e(I,a).isSatisfiedRecursively=!0});a=new Set;var d=new Set;f(I,a,d,function(a){return a});var b=[],z=new Set,A=new Set;p.forEach(function(a){a=a.key;d.has(a)?-1===b.indexOf(a)?b.push(a):A.add(a):!y||a.endsWith(".current")||x.has(a)?z.add(a):-1===b.indexOf(a)&&b.push(a)});a.forEach(function(a){b.push(a)});return{suggestedDependencies:b,unnecessaryDependencies:z,duplicateDependencies:A,
17
+ 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,u=
19
+ 0;u<a.references.length;u++){var x=a.references[u];if(x.writeExpr)if(f){a=!0;break a}else{f=!0;continue}for(var y=x.from;y!==h&&null!=y;)y=y.upper;if(y!==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||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 Y(a){if("Identifier"===a.type)return a.name;if("MemberExpression"!==a.type||a.computed)throw Error("Unsupported node type: "+a.type);var c=Y(a.object);a=Y(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&&e.name.match(/use.+Effect/))return 0;if(e===a&&c&&c.additionalHooks){try{var h=Y(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 H=f[a],p=H[1];"parent"!==H[0]&&(ra(p)?(p.parent=h,e.push(p)):Array.isArray(p)&&p.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 b=h.cache,d=b.get(a.id);c=new Set(c);if(c.has(a.id)){b=[].concat(c);
26
+ a=b.slice(b.indexOf(a.id)+1);b=!0;d=!1;var g=void 0;try{for(var m=a[Symbol.iterator](),B;!(b=(B=m.next()).done);b=!0)y.add(B.value)}catch(J){d=!0,g=J}finally{try{b||null==m.return||m.return()}finally{if(d)throw g;}}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;m=!0;B=!1;var L=void 0;try{g=a.prevSegments[Symbol.iterator]();for(var M;!(m=(M=g.next()).done);m=!0)d+=h(M.value,c)}catch(J){B=!0,L=J}finally{try{m||null==
27
+ g.return||g.return()}finally{if(B)throw L;}}}a.reachable&&0===d?b.delete(a.id):b.set(a.id,d);return d}function p(a,c){var b=p.cache,d=b.get(a.id);c=new Set(c);if(c.has(a.id)){b=Array.from(c);a=b.slice(b.indexOf(a.id)+1);b=!0;d=!1;var g=void 0;try{for(var m=a[Symbol.iterator](),B;!(b=(B=m.next()).done);b=!0)y.add(B.value)}catch(J){d=!0,g=J}finally{try{b||null==m.return||m.return()}finally{if(d)throw g;}}return 0}c.add(a.id);if(void 0!==d)return d;if(e.thrownSegments.includes(a))d=0;else if(0===
28
+ a.nextSegments.length)d=1;else{d=0;m=!0;B=!1;var L=void 0;try{g=a.nextSegments[Symbol.iterator]();for(var M;!(m=(M=g.next()).done);m=!0)d+=p(M.value,c)}catch(J){B=!0,L=J}finally{try{m||null==g.return||g.return()}finally{if(B)throw L;}}}b.set(a.id,d);return d}function u(a){var c=u.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 B=a.prevSegments[Symbol.iterator](),L;!(d=
29
+ (L=B.next()).done);d=!0){var e=u(L.value);e<b&&(b=e)}}catch(J){g=!0,m=J}finally{try{d||null==B.return||B.return()}finally{if(g)throw m;}}b+=1}c.set(a.id,b);return b}var x=c.pop();if(0!==x.size){var y=new Set;h.cache=new Map;p.cache=new Map;u.cache=new Map;var I=p(e.initialSegment),d=ja(f),b=ia(f),z=d?aa(d)||U(d):fa(f)||ha(f),A=Infinity,k=!0,n=!1,T=void 0;try{for(var q=e.finalSegments[Symbol.iterator](),F;!(k=(F=q.next()).done);k=!0){var C=F.value;if(C.reachable){var w=u(C);w<A&&(A=w)}}}catch(E){n=
30
+ !0,T=E}finally{try{k||null==q.return||q.return()}finally{if(n)throw T;}}k=!0;n=!1;T=void 0;try{for(var ba=x[Symbol.iterator](),Z;!(k=(Z=ba.next()).done);k=!0){var G=Z.value,t=G[0],W=G[1];if(t.reachable){var Q=0===t.nextSegments.length?A<=u(t):A<u(t),O=h(t)*p(t),N=y.has(t.id);x=!0;q=!1;F=void 0;try{for(var l=W[Symbol.iterator](),D;!(x=(D=l.next()).done);x=!0){var v=D.value;N&&a.report({node:v,message:'React Hook "'+a.getSource(v)+'" 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(z){if(!N&&O!==I){var R='React Hook "'+a.getSource(v)+'" 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:v,message:R})}}else if(f.parent&&("MethodDefinition"===f.parent.type||"ClassProperty"===f.parent.type)&&f.parent.value===f){var S='React Hook "'+a.getSource(v)+'" 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:v,message:S})}else if(d){var K='React Hook "'+a.getSource(v)+'" is called in function "'+(a.getSource(d)+'" that is neither a React function component nor a custom React Hook function.');a.report({node:v,message:K})}else if("Program"===f.type){var X='React Hook "'+a.getSource(v)+'" 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:v,message:X})}else if(b){var P='React Hook "'+a.getSource(v)+'" cannot be called inside a callback. React Hooks must be called in a React function component or a custom React Hook function.';
33
+ a.report({node:v,message:P})}}}catch(E){q=!0,F=E}finally{try{x||null==l.return||l.return()}finally{if(q)throw F;}}}}}catch(E){n=!0,T=E}finally{try{k||null==ba.return||ba.return()}finally{if(n)throw T;}}}},CallExpression:function(a){if(U(a.callee)){var f=c[c.length-1],h=e[e.length-1],p=f.get(h);p||(p=[],f.set(h,p));p.push(a.callee)}}}}},"exhaustive-deps":{meta:{type:"suggestion",docs:{description:"verifies the list of dependencies for Hooks like useEffect and similar",category:"Best Practices",recommended:!0,
34
+ 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=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,
35
+ b,h,f,k){function z(a){var g=!0,c=!1,b=void 0;try{for(var e=a.references[Symbol.iterator](),h;!(g=(h=e.next()).done);g=!0){var f=h.value;if(f.resolved&&F.has(f.resolved.scope)){var p=qa(d,f.identifier),l=na(p),A=Y(l),q;if(q=k&&"Identifier"===l.type&&"MemberExpression"===l.parent.type&&!l.parent.computed&&"Identifier"===l.parent.property.type&&"current"===l.parent.property.name){for(var n=f.from,u=!1;n.block!==d;)"function"===n.type&&(u=null!=n.block.parent&&"ReturnStatement"===n.block.parent.type),
36
+ n=n.upper;q=u}q&&G.set(A,{reference:f,dependencyNode:l});var r=f.resolved.defs[0];if(null!=r&&(null==r.node||r.node.init!==d.parent)&&"TypeParameter"!==r.type)if(t.has(A))t.get(A).references.push(f);else{var w=f.resolved,x=H(w)||Z(w);t.set(A,{isStatic:x,references:[f]})}}}}catch(ea){c=!0,b=ea}finally{try{g||null==e.return||e.return()}finally{if(c)throw b;}}g=!0;c=!1;b=void 0;try{for(var v=a.childScopes[Symbol.iterator](),y;!(g=(y=v.next()).done);g=!0)z(y.value)}catch(ea){c=!0,b=ea}finally{try{g||
37
+ null==v.return||v.return()}finally{if(c)throw b;}}}function A(a,c,b,d){return 0===a.size?null:(1<a.size?"":c+" ")+b+" "+(1<a.size?"dependencies":"dependency")+": "+sa(Array.from(a).sort().map(function(a){return"'"+a+"'"}))+(". Either "+d+" "+(1<a.size?"them":"it")+" or remove the dependency array.")}k&&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 q=p.acquire(d),F=new Set,C=null,w=q.upper;w;){F.add(w);if("function"===w.type)break;w=w.upper}if(w){C=w;var H=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++)u.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},y),Z=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&&F.has(c.resolved.scope)&&!H(c.resolved))return!1;return!0},I),G=new Map,t=new Map;z(q);G.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 W=new Set,Q=new Set;t.forEach(function(b,d){var g=b.references;b.isStatic&&Q.add(d);g.forEach(function(b){b.writeExpr&&(b=b.writeExpr,W.has(d)||(W.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<W.size))if(b){var O=[],N=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=Y(b)}catch(M){if(/Unsupported node type/.test(M.message)){"Literal"===b.type?t.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 M;}for(var g=b;"MemberExpression"===g.type;)g=g.object;var e=!C.through.some(function(a){return a.identifier===g});O.push({key:d,node:b});e||N.add(d)}});var l=ka({dependencies:t,declaredDependencies:O,optionalDependencies:Q,externalDependencies:N,isEffect:k});w=l.unnecessaryDependencies;var D=l.missingDependencies,v=l.duplicateDependencies,R=l.suggestedDependencies;if(0===v.size+D.size+w.size)la({declaredDependencies:O,declaredDependenciesNode:b,componentScope:C,scope:q}).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{!k&&0<D.size&&(R=ka({dependencies:t,declaredDependencies:[],optionalDependencies:Q,externalDependencies:N,isEffect:k}).suggestedDependencies);(function(){if(0===O.length)return!0;var a=O.map(function(a){return a.key}),b=a.slice().sort();return a.join(",")===b.join(",")})()&&R.sort();l="";if(0<w.size){var S=null;Array.from(w.keys()).forEach(function(a){null===
48
+ S&&a.endsWith(".current")&&(S=a)});if(null!==S)l=" Mutable values like '"+S+"' aren't valid dependencies because mutating them doesn't re-render the component.";else if(0<N.size){var K=Array.from(N)[0];q.set.has(K)||(l=" Outer scope values like '"+K+"' aren't valid dependencies because mutating them doesn't re-render the component.")}}if(!l&&D.has("props")){q=t.get("props");if(null==q)return;q=q.references;if(!Array.isArray(q))return;K=!0;for(var X=0;X<q.length;X++){var P=qa(C.block,q[X].identifier);
49
+ if(!P){K=!1;break}P=P.parent;if(null==P){K=!1;break}if("MemberExpression"!==P.type){K=!1;break}}K&&(l=" 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(!l&&0<D.size){var E=null;D.forEach(function(a){if(!E){var b=C.set.get(a),c=t.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&&(E=a)}}});null!==E&&(l=" If '"+E+"' changes too often, find the parent component that defines it and wrap that definition in useCallback.")}if(!l&&0<D.size){var r=null;D.forEach(function(a){if(null===r)for(var b=t.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
+ u.get(d.callee);if(null!=g){g.name===a?r={missingDep:a,setter:d.callee.name,form:"updater"}:x.has(c)?r={missingDep:a,setter:d.callee.name,form:"reducer"}:(c=b[e].resolved,null!=c&&(c=c.defs[0],null!=c&&"Parameter"===c.type&&(r={missingDep:a,setter:d.callee.name,form:"inlineReducer"})));break}}d=d.parent}if(null!==r)break}});if(null!==r)switch(r.form){case "reducer":l=" You can also replace multiple useState variables with useReducer if '"+(r.setter+"' needs the current value of '")+(r.missingDep+
52
+ "'.");break;case "inlineReducer":l=" If '"+r.setter+"' needs the current value of '"+(r.missingDep+"', you can also switch to useReducer instead of useState and read '")+(r.missingDep+"' in the reducer.");break;case "updater":l=" You can also do a functional update '"+r.setter+"("+r.missingDep.substring(0,1)+" => ...)' if you only need '"+r.missingDep+"' in the '"+(r.setter+"' call.");break;default:throw Error("Unknown case.");}}c({node:b,message:"React Hook "+a.getSource(h)+" has "+(A(D,"a","missing",
53
+ "include")||A(w,"an","unnecessary","exclude")||A(v,"a","duplicate","omit"))+l,suggest:[{desc:"Update the dependencies array to be: ["+R.join(", ")+"]",fix:function(a){return a.replaceText(b,"["+R.join(", ")+"]")}}]})}}else{var V=null;t.forEach(function(a,b){V||a.references.forEach(function(a){if(!V&&u.has(a.identifier)){for(a=a.from;"function"!==a.type;)a=a.upper;a.block===d&&(V=b)}})});if(V){var da=ka({dependencies:t,declaredDependencies:[],optionalDependencies:Q,externalDependencies:new Set,isEffect:!0}).suggestedDependencies;
54
+ c({node:h,message:"React Hook "+f+" contains a call to '"+V+"'. 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,H={additionalHooks:a.options&&a.options[0]&&
55
+ a.options[0].additionalHooks?new RegExp(a.options[0].additionalHooks):void 0,enableDangerousAutofixThisMayCauseInfiniteLoops:f},p=a.getSourceCode().scopeManager,u=new WeakMap,x=new WeakSet,y=new WeakMap,I=new WeakMap;return{CallExpression:function(d){var b=pa(d.callee,H);if(-1!==b){var e=d.arguments[b],f=d.callee,k=oa(f).name,n=d.arguments[b+1];d=/Effect($|[^a-z])/g.test(k);if(n||d){switch(e.type){case "FunctionExpression":case "ArrowFunctionExpression":h(e,n,f,k,d);return;case "Identifier":if(!n||
56
+ n.elements&&n.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,n,f,k,d);return;case "VariableDeclarator":if(b=b.node.init)switch(b.type){case "ArrowFunctionExpression":case "FunctionExpression":h(b,n,f,k,d);return}}break;default:c({node:f,message:"React Hook "+k+" received a function whose dependencies are unknown. Pass an inline function instead."});
57
+ return}c({node:f,message:"React Hook "+k+" 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(n,"["+e.name+"]")}}]})}else"useMemo"!==k&&"useCallback"!==k||c({node:f,message:"React Hook "+k+" 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": "2.4.0",
4
+ "version": "4.0.0",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "https://github.com/facebook/react.git",
@@ -25,10 +25,13 @@
25
25
  "url": "https://github.com/facebook/react/issues"
26
26
  },
27
27
  "engines": {
28
- "node": ">=7"
28
+ "node": ">=10"
29
29
  },
30
30
  "homepage": "https://reactjs.org/",
31
31
  "peerDependencies": {
32
32
  "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0"
33
+ },
34
+ "devDependencies": {
35
+ "@typescript-eslint/parser": "^2.26.0"
33
36
  }
34
37
  }