-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
build(deps): bump eslint-plugin-react-hooks from 4.6.2 to 5.0.0 #1360
Open
dependabot
wants to merge
1
commit into
main
Choose a base branch
from
dependabot/npm_and_yarn/eslint-plugin-react-hooks-5.0.0
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
build(deps): bump eslint-plugin-react-hooks from 4.6.2 to 5.0.0 #1360
dependabot
wants to merge
1
commit into
main
from
dependabot/npm_and_yarn/eslint-plugin-react-hooks-5.0.0
Conversation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
dependabot
bot
added
dependencies
Pull requests that update a dependency file
javascript
Pull requests that update Javascript code
labels
Nov 1, 2024
Diff between eslint-plugin-react-hooks 4.6.2 and 5.0.0diff --git a/LICENSE b/LICENSE
index v4.6.2..v5.0.0 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,5 +1,5 @@
MIT License
-Copyright (c) Facebook, Inc. and its affiliates.
+Copyright (c) Meta Platforms, Inc. and affiliates.
Permission is hereby granted, free of charge, to any person obtaining a copy
diff --git a/cjs/eslint-plugin-react-hooks.development.js b/cjs/eslint-plugin-react-hooks.development.js
index v4.6.2..v5.0.0 100644
--- a/cjs/eslint-plugin-react-hooks.development.js
+++ b/cjs/eslint-plugin-react-hooks.development.js
@@ -3,5 +3,5 @@
* eslint-plugin-react-hooks.development.js
*
- * Copyright (c) Facebook, Inc. and its affiliates.
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
@@ -9,2450 +9,2030 @@
*/
-'use strict';
-
-if (process.env.NODE_ENV !== "production") {
- (function() {
-'use strict';
-
-function _unsupportedIterableToArray(o, minLen) {
- if (!o) return;
- if (typeof o === "string") return _arrayLikeToArray(o, minLen);
- var n = Object.prototype.toString.call(o).slice(8, -1);
- if (n === "Object" && o.constructor) n = o.constructor.name;
- if (n === "Map" || n === "Set") return Array.from(o);
- if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen);
-}
-
-function _arrayLikeToArray(arr, len) {
- if (len == null || len > arr.length) len = arr.length;
-
- for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i];
-
- return arr2;
-}
-
-function _createForOfIteratorHelper(o, allowArrayLike) {
- var it;
-
- if (typeof Symbol === "undefined" || o[Symbol.iterator] == null) {
- if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") {
- if (it) o = it;
- var i = 0;
-
- var F = function () {};
-
- return {
- s: F,
- n: function () {
- if (i >= o.length) return {
- done: true
- };
+"use strict";
+"production" !== process.env.NODE_ENV &&
+ (function () {
+ function _unsupportedIterableToArray(o, minLen) {
+ if (o) {
+ if ("string" === typeof o) return _arrayLikeToArray(o, minLen);
+ var n = Object.prototype.toString.call(o).slice(8, -1);
+ "Object" === n && o.constructor && (n = o.constructor.name);
+ if ("Map" === n || "Set" === n) return Array.from(o);
+ if (
+ "Arguments" === n ||
+ /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)
+ )
+ return _arrayLikeToArray(o, minLen);
+ }
+ }
+ function _arrayLikeToArray(arr, len) {
+ if (null == len || len > arr.length) len = arr.length;
+ for (var i = 0, arr2 = Array(len); i < len; i++) arr2[i] = arr[i];
+ return arr2;
+ }
+ function _createForOfIteratorHelper(o, allowArrayLike) {
+ var it;
+ if ("undefined" === typeof Symbol || null == o[Symbol.iterator]) {
+ if (
+ Array.isArray(o) ||
+ (it = _unsupportedIterableToArray(o)) ||
+ (allowArrayLike && o && "number" === typeof o.length)
+ ) {
+ it && (o = it);
+ var i = 0;
+ allowArrayLike = function () {};
return {
- done: false,
- value: o[i++]
+ s: allowArrayLike,
+ n: function () {
+ return i >= o.length ? { done: !0 } : { done: !1, value: o[i++] };
+ },
+ e: function (e) {
+ throw e;
+ },
+ f: allowArrayLike
};
+ }
+ throw new TypeError(
+ "Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."
+ );
+ }
+ var normalCompletion = !0,
+ didErr = !1,
+ err;
+ return {
+ s: function () {
+ it = o[Symbol.iterator]();
},
+ n: function () {
+ var step = it.next();
+ normalCompletion = step.done;
+ return step;
+ },
e: function (e) {
- throw e;
+ didErr = !0;
+ err = e;
},
- f: F
+ f: function () {
+ try {
+ normalCompletion || null == it.return || it.return();
+ } finally {
+ if (didErr) throw err;
+ }
+ }
};
}
-
- throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
- }
-
- var normalCompletion = true,
- didErr = false,
- err;
- return {
- s: function () {
- it = o[Symbol.iterator]();
- },
- n: function () {
- var step = it.next();
- normalCompletion = step.done;
- return step;
- },
- e: function (e) {
- didErr = true;
- err = e;
- },
- f: function () {
- try {
- if (!normalCompletion && it.return != null) it.return();
- } finally {
- if (didErr) throw err;
+ function isHook(node) {
+ if ("Identifier" === node.type)
+ return (node = node.name), "use" === node || /^use[A-Z0-9]/.test(node);
+ if (
+ "MemberExpression" === node.type &&
+ !node.computed &&
+ isHook(node.property)
+ ) {
+ node = node.object;
+ var isPascalCaseNameSpace = /^[A-Z].*/;
+ return (
+ "Identifier" === node.type && isPascalCaseNameSpace.test(node.name)
+ );
}
+ return !1;
}
- };
-}
-
-/* global BigInt */
-
-function isHookName(s) {
- return /^use[A-Z0-9].*$/.test(s);
-}
-/**
- * We consider hooks to be a hook name identifier or a member expression
- * containing a hook name.
- */
-
-
-function isHook(node) {
- if (node.type === 'Identifier') {
- return isHookName(node.name);
- } else if (node.type === 'MemberExpression' && !node.computed && isHook(node.property)) {
- var obj = node.object;
- var isPascalCaseNameSpace = /^[A-Z].*/;
- return obj.type === 'Identifier' && isPascalCaseNameSpace.test(obj.name);
- } else {
- return false;
- }
-}
-/**
- * Checks if the node is a React component name. React component names must
- * always start with a non-lowercase letter. So `MyComponent` or `_MyComponent`
- * are valid component names for instance.
- */
-
-
-function isComponentName(node) {
- if (node.type === 'Identifier') {
- return !/^[a-z]/.test(node.name);
- } else {
- return false;
- }
-}
-
-function isReactFunction(node, functionName) {
- return node.name === functionName || node.type === 'MemberExpression' && node.object.name === 'React' && node.property.name === functionName;
-}
-/**
- * Checks if the node is a callback argument of forwardRef. This render function
- * should follow the rules of hooks.
- */
-
-
-function isForwardRefCallback(node) {
- return !!(node.parent && node.parent.callee && isReactFunction(node.parent.callee, 'forwardRef'));
-}
-/**
- * Checks if the node is a callback argument of React.memo. This anonymous
- * functional component should follow the rules of hooks.
- */
-
-
-function isMemoCallback(node) {
- return !!(node.parent && node.parent.callee && isReactFunction(node.parent.callee, 'memo'));
-}
-
-function isInsideComponentOrHook(node) {
- while (node) {
- var functionName = getFunctionName(node);
-
- if (functionName) {
- if (isComponentName(functionName) || isHook(functionName)) {
- return true;
- }
+ function isComponentName(node) {
+ return "Identifier" === node.type && /^[A-Z]/.test(node.name);
}
-
- if (isForwardRefCallback(node) || isMemoCallback(node)) {
- return true;
+ function isReactFunction(node, functionName) {
+ return (
+ node.name === functionName ||
+ ("MemberExpression" === node.type &&
+ "React" === node.object.name &&
+ node.property.name === functionName)
+ );
}
-
- node = node.parent;
- }
-
- return false;
-}
-
-var RulesOfHooks = {
- meta: {
- type: 'problem',
- docs: {
- description: 'enforces the Rules of Hooks',
- recommended: true,
- url: 'https://reactjs.org/docs/hooks-rules.html'
+ function isForwardRefCallback(node) {
+ return !!(
+ node.parent &&
+ node.parent.callee &&
+ isReactFunction(node.parent.callee, "forwardRef")
+ );
}
- },
- create: function (context) {
- var codePathReactHooksMapStack = [];
- var codePathSegmentStack = [];
- return {
- // Maintain code segment path stack as we traverse.
- onCodePathSegmentStart: function (segment) {
- return codePathSegmentStack.push(segment);
- },
- onCodePathSegmentEnd: function () {
- return codePathSegmentStack.pop();
- },
- // Maintain code path stack as we traverse.
- onCodePathStart: function () {
- return codePathReactHooksMapStack.push(new Map());
- },
- // Process our code path.
- //
- // Everything is ok if all React Hooks are both reachable from the initial
- // segment and reachable from every final segment.
- onCodePathEnd: function (codePath, codePathNode) {
- var reactHooksMap = codePathReactHooksMapStack.pop();
-
- if (reactHooksMap.size === 0) {
- return;
- } // All of the segments which are cyclic are recorded in this set.
-
-
- var cyclic = new Set();
- /**
- * Count the number of code paths from the start of the function to this
- * segment. For example:
- *
- * ```js
- * function MyComponent() {
- * if (condition) {
- * // Segment 1
- * } else {
- * // Segment 2
- * }
- * // Segment 3
- * }
- * ```
- *
- * Segments 1 and 2 have one path to the beginning of `MyComponent` and
- * segment 3 has two paths to the beginning of `MyComponent` since we
- * could have either taken the path of segment 1 or segment 2.
- *
- * Populates `cyclic` with cyclic segments.
- */
-
- function countPathsFromStart(segment, pathHistory) {
- var cache = countPathsFromStart.cache;
- var paths = cache.get(segment.id);
- var pathList = new Set(pathHistory); // If `pathList` includes the current segment then we've found a cycle!
- // We need to fill `cyclic` with all segments inside cycle
-
- if (pathList.has(segment.id)) {
- var pathArray = [].concat(pathList);
- var cyclicSegments = pathArray.slice(pathArray.indexOf(segment.id) + 1);
-
- var _iterator = _createForOfIteratorHelper(cyclicSegments),
- _step;
-
- try {
- for (_iterator.s(); !(_step = _iterator.n()).done;) {
- var cyclicSegment = _step.value;
- cyclic.add(cyclicSegment);
- }
- } catch (err) {
- _iterator.e(err);
- } finally {
- _iterator.f();
- }
-
- return BigInt('0');
- } // add the current segment to pathList
-
-
- pathList.add(segment.id); // We have a cached `paths`. Return it.
-
- if (paths !== undefined) {
- return paths;
- }
-
- if (codePath.thrownSegments.includes(segment)) {
- paths = BigInt('0');
- } else if (segment.prevSegments.length === 0) {
- paths = BigInt('1');
- } else {
- paths = BigInt('0');
-
- var _iterator2 = _createForOfIteratorHelper(segment.prevSegments),
- _step2;
-
- try {
- for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
- var prevSegment = _step2.value;
- paths += countPathsFromStart(prevSegment, pathList);
- }
- } catch (err) {
- _iterator2.e(err);
- } finally {
- _iterator2.f();
- }
- } // If our segment is reachable then there should be at least one path
- // to it from the start of our code path.
-
-
- if (segment.reachable && paths === BigInt('0')) {
- cache.delete(segment.id);
- } else {
- cache.set(segment.id, paths);
- }
-
- return paths;
- }
- /**
- * Count the number of code paths from this segment to the end of the
- * function. For example:
- *
- * ```js
- * function MyComponent() {
- * // Segment 1
- * if (condition) {
- * // Segment 2
- * } else {
- * // Segment 3
- * }
- * }
- * ```
- *
- * Segments 2 and 3 have one path to the end of `MyComponent` and
- * segment 1 has two paths to the end of `MyComponent` since we could
- * either take the path of segment 1 or segment 2.
- *
- * Populates `cyclic` with cyclic segments.
- */
-
-
- function countPathsToEnd(segment, pathHistory) {
- var cache = countPathsToEnd.cache;
- var paths = cache.get(segment.id);
- var pathList = new Set(pathHistory); // If `pathList` includes the current segment then we've found a cycle!
- // We need to fill `cyclic` with all segments inside cycle
-
- if (pathList.has(segment.id)) {
- var pathArray = Array.from(pathList);
- var cyclicSegments = pathArray.slice(pathArray.indexOf(segment.id) + 1);
-
- var _iterator3 = _createForOfIteratorHelper(cyclicSegments),
- _step3;
-
- try {
- for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
- var cyclicSegment = _step3.value;
- cyclic.add(cyclicSegment);
- }
- } catch (err) {
- _iterator3.e(err);
- } finally {
- _iterator3.f();
- }
-
- return BigInt('0');
- } // add the current segment to pathList
-
-
- pathList.add(segment.id); // We have a cached `paths`. Return it.
-
- if (paths !== undefined) {
- return paths;
- }
-
- if (codePath.thrownSegments.includes(segment)) {
- paths = BigInt('0');
- } else if (segment.nextSegments.length === 0) {
- paths = BigInt('1');
- } else {
- paths = BigInt('0');
-
- var _iterator4 = _createForOfIteratorHelper(segment.nextSegments),
- _step4;
-
- try {
- for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
- var nextSegment = _step4.value;
- paths += countPathsToEnd(nextSegment, pathList);
- }
- } catch (err) {
- _iterator4.e(err);
- } finally {
- _iterator4.f();
- }
- }
-
- cache.set(segment.id, paths);
- return paths;
- }
- /**
- * Gets the shortest path length to the start of a code path.
- * For example:
- *
- * ```js
- * function MyComponent() {
- * if (condition) {
- * // Segment 1
- * }
- * // Segment 2
- * }
- * ```
- *
- * There is only one path from segment 1 to the code path start. Its
- * length is one so that is the shortest path.
- *
- * There are two paths from segment 2 to the code path start. One
- * through segment 1 with a length of two and another directly to the
- * start with a length of one. The shortest path has a length of one
- * so we would return that.
- */
-
-
- function shortestPathLengthToStart(segment) {
- var cache = shortestPathLengthToStart.cache;
- var length = cache.get(segment.id); // If `length` is null then we found a cycle! Return infinity since
- // the shortest path is definitely not the one where we looped.
-
- if (length === null) {
- return Infinity;
- } // We have a cached `length`. Return it.
-
-
- if (length !== undefined) {
- return length;
- } // Compute `length` and cache it. Guarding against cycles.
-
-
- cache.set(segment.id, null);
-
- if (segment.prevSegments.length === 0) {
- length = 1;
- } else {
- length = Infinity;
-
- var _iterator5 = _createForOfIteratorHelper(segment.prevSegments),
- _step5;
-
- try {
- for (_iterator5.s(); !(_step5 = _iterator5.n()).done;) {
- var prevSegment = _step5.value;
- var prevLength = shortestPathLengthToStart(prevSegment);
-
- if (prevLength < length) {
- length = prevLength;
- }
- }
- } catch (err) {
- _iterator5.e(err);
- } finally {
- _iterator5.f();
- }
-
- length += 1;
- }
-
- cache.set(segment.id, length);
- return length;
- }
-
- countPathsFromStart.cache = new Map();
- countPathsToEnd.cache = new Map();
- shortestPathLengthToStart.cache = new Map(); // Count all code paths to the end of our component/hook. Also primes
- // the `countPathsToEnd` cache.
-
- var allPathsFromStartToEnd = countPathsToEnd(codePath.initialSegment); // Gets the function name for our code path. If the function name is
- // `undefined` then we know either that we have an anonymous function
- // expression or our code path is not in a function. In both cases we
- // will want to error since neither are React function components or
- // hook functions - unless it is an anonymous function argument to
- // forwardRef or memo.
-
- var codePathFunctionName = getFunctionName(codePathNode); // This is a valid code path for React hooks if we are directly in a React
- // function component or we are in a hook function.
-
- var isSomewhereInsideComponentOrHook = isInsideComponentOrHook(codePathNode);
- var isDirectlyInsideComponentOrHook = codePathFunctionName ? isComponentName(codePathFunctionName) || isHook(codePathFunctionName) : isForwardRefCallback(codePathNode) || isMemoCallback(codePathNode); // Compute the earliest finalizer level using information from the
- // cache. We expect all reachable final segments to have a cache entry
- // after calling `visitSegment()`.
-
- var shortestFinalPathLength = Infinity;
-
- var _iterator6 = _createForOfIteratorHelper(codePath.finalSegments),
- _step6;
-
- try {
- for (_iterator6.s(); !(_step6 = _iterator6.n()).done;) {
- var finalSegment = _step6.value;
-
- if (!finalSegment.reachable) {
- continue;
- }
-
- var length = shortestPathLengthToStart(finalSegment);
-
- if (length < shortestFinalPathLength) {
- shortestFinalPathLength = length;
- }
- } // Make sure all React Hooks pass our lint invariants. Log warnings
- // if not.
-
- } catch (err) {
- _iterator6.e(err);
- } finally {
- _iterator6.f();
- }
-
- var _iterator7 = _createForOfIteratorHelper(reactHooksMap),
- _step7;
-
- try {
- for (_iterator7.s(); !(_step7 = _iterator7.n()).done;) {
- var _step7$value = _step7.value,
- segment = _step7$value[0],
- reactHooks = _step7$value[1];
-
- // NOTE: We could report here that the hook is not reachable, but
- // that would be redundant with more general "no unreachable"
- // lint rules.
- if (!segment.reachable) {
- continue;
- } // If there are any final segments with a shorter path to start then
- // we possibly have an early return.
- //
- // If our segment is a final segment itself then siblings could
- // possibly be early returns.
-
-
- var possiblyHasEarlyReturn = segment.nextSegments.length === 0 ? shortestFinalPathLength <= shortestPathLengthToStart(segment) : shortestFinalPathLength < shortestPathLengthToStart(segment); // Count all the paths from the start of our code path to the end of
- // our code path that go _through_ this segment. The critical piece
- // of this is _through_. If we just call `countPathsToEnd(segment)`
- // then we neglect that we may have gone through multiple paths to get
- // to this point! Consider:
- //
- // ```js
- // function MyComponent() {
- // if (a) {
- // // Segment 1
- // } else {
- // // Segment 2
- // }
- // // Segment 3
- // if (b) {
- // // Segment 4
- // } else {
- // // Segment 5
- // }
- // }
- // ```
- //
- // In this component we have four code paths:
- //
- // 1. `a = true; b = true`
- // 2. `a = true; b = false`
- // 3. `a = false; b = true`
- // 4. `a = false; b = false`
- //
- // From segment 3 there are two code paths to the end through segment
- // 4 and segment 5. However, we took two paths to get here through
- // segment 1 and segment 2.
- //
- // If we multiply the paths from start (two) by the paths to end (two)
- // for segment 3 we get four. Which is our desired count.
-
- var pathsFromStartToEnd = countPathsFromStart(segment) * countPathsToEnd(segment); // Is this hook a part of a cyclic segment?
-
- var cycled = cyclic.has(segment.id);
-
- var _iterator8 = _createForOfIteratorHelper(reactHooks),
- _step8;
-
- try {
- for (_iterator8.s(); !(_step8 = _iterator8.n()).done;) {
- var hook = _step8.value;
-
- // Report an error if a hook may be called more then once.
- if (cycled) {
- context.report({
- node: hook,
- message: "React Hook \"" + context.getSource(hook) + "\" 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 this is not a valid code path for React hooks then we need to
- // log a warning for every hook in this code path.
- //
- // Pick a special message depending on the scope this hook was
- // called in.
-
-
- if (isDirectlyInsideComponentOrHook) {
- // Report an error if a hook does not reach all finalizing code
- // path segments.
- //
- // Special case when we think there might be an early return.
- if (!cycled && pathsFromStartToEnd !== allPathsFromStartToEnd) {
- var message = "React Hook \"" + context.getSource(hook) + "\" is called " + 'conditionally. React Hooks must be called in the exact ' + 'same order in every component render.' + (possiblyHasEarlyReturn ? ' Did you accidentally call a React Hook after an' + ' early return?' : '');
- context.report({
- node: hook,
- message: message
- });
- }
- } else if (codePathNode.parent && (codePathNode.parent.type === 'MethodDefinition' || codePathNode.parent.type === 'ClassProperty') && codePathNode.parent.value === codePathNode) {
- // Custom message for hooks inside a class
- 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.';
-
- context.report({
- node: hook,
- message: _message
- });
- } else if (codePathFunctionName) {
- // Custom message if we found an invalid function name.
- 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.' + ' React component names must start with an uppercase letter.' + ' React Hook names must start with the word "use".';
-
- context.report({
- node: hook,
- message: _message2
- });
- } else if (codePathNode.type === 'Program') {
- // These are dangerous if you have inline requires enabled.
- 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.';
-
- context.report({
- node: hook,
- message: _message3
- });
- } else {
- // Assume in all other cases the user called a hook in some
- // random function callback. This should usually be true for
- // anonymous function expressions. Hopefully this is clarifying
- // enough in the common case that the incorrect message in
- // uncommon cases doesn't matter.
- if (isSomewhereInsideComponentOrHook) {
- 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.';
-
- context.report({
- node: hook,
- message: _message4
- });
- }
- }
- }
- } catch (err) {
- _iterator8.e(err);
- } finally {
- _iterator8.f();
- }
- }
- } catch (err) {
- _iterator7.e(err);
- } finally {
- _iterator7.f();
- }
- },
- // Missed opportunity...We could visit all `Identifier`s instead of all
- // `CallExpression`s and check that _every use_ of a hook name is valid.
- // But that gets complicated and enters type-system territory, so we're
- // only being strict about hook calls for now.
- CallExpression: function (node) {
- if (isHook(node.callee)) {
- // Add the hook node to a map keyed by the code path segment. We will
- // do full code path analysis at the end of our code path.
- var reactHooksMap = last(codePathReactHooksMapStack);
- var codePathSegment = last(codePathSegmentStack);
- var reactHooks = reactHooksMap.get(codePathSegment);
-
- if (!reactHooks) {
- reactHooks = [];
- reactHooksMap.set(codePathSegment, reactHooks);
- }
-
- reactHooks.push(node.callee);
- }
- }
- };
- }
-};
-/**
- * Gets the static name of a function AST node. For function declarations it is
- * easy. For anonymous function expressions it is much harder. If you search for
- * `IsAnonymousFunctionDefinition()` in the ECMAScript spec you'll find places
- * where JS gives anonymous function expressions names. We roughly detect the
- * same AST nodes with some exceptions to better fit our use case.
- */
-
-function getFunctionName(node) {
- if (node.type === 'FunctionDeclaration' || node.type === 'FunctionExpression' && node.id) {
- // function useHook() {}
- // const whatever = function useHook() {};
- //
- // Function declaration or function expression names win over any
- // assignment statements or other renames.
- return node.id;
- } else if (node.type === 'FunctionExpression' || node.type === 'ArrowFunctionExpression') {
- if (node.parent.type === 'VariableDeclarator' && node.parent.init === node) {
- // const useHook = () => {};
- return node.parent.id;
- } else if (node.parent.type === 'AssignmentExpression' && node.parent.right === node && node.parent.operator === '=') {
- // useHook = () => {};
- return node.parent.left;
- } else if (node.parent.type === 'Property' && node.parent.value === node && !node.parent.computed) {
- // {useHook: () => {}}
- // {useHook() {}}
- return node.parent.key; // NOTE: We could also support `ClassProperty` and `MethodDefinition`
- // here to be pedantic. However, hooks in a class are an anti-pattern. So
- // we don't allow it to error early.
- //
- // class {useHook = () => {}}
- // class {useHook() {}}
- } else if (node.parent.type === 'AssignmentPattern' && node.parent.right === node && !node.parent.computed) {
- // const {useHook = () => {}} = {};
- // ({useHook = () => {}} = {});
- //
- // Kinda clowny, but we'd said we'd follow spec convention for
- // `IsAnonymousFunctionDefinition()` usage.
- return node.parent.left;
- } else {
- return undefined;
+ function isMemoCallback(node) {
+ return !!(
+ node.parent &&
+ node.parent.callee &&
+ isReactFunction(node.parent.callee, "memo")
+ );
}
- } else {
- return undefined;
- }
-}
-/**
- * Convenience function for peeking the last item in a stack.
- */
-
-
-function last(array) {
- return array[array.length - 1];
-}
-
-/* eslint-disable no-for-of-loops/no-for-of-loops */
-var ExhaustiveDeps = {
- meta: {
- type: 'suggestion',
- docs: {
- description: 'verifies the list of dependencies for Hooks like useEffect and similar',
- recommended: true,
- url: 'https://github.com/facebook/react/issues/14920'
- },
- fixable: 'code',
- hasSuggestions: true,
- schema: [{
- type: 'object',
- additionalProperties: false,
- enableDangerousAutofixThisMayCauseInfiniteLoops: false,
- properties: {
- additionalHooks: {
- type: 'string'
- },
- enableDangerousAutofixThisMayCauseInfiniteLoops: {
- type: 'boolean'
- }
+ function isInsideComponentOrHook(node) {
+ for (; node; ) {
+ var functionName = getFunctionName(node);
+ if (
+ (functionName &&
+ (isComponentName(functionName) || isHook(functionName))) ||
+ isForwardRefCallback(node) ||
+ isMemoCallback(node)
+ )
+ return !0;
+ node = node.parent;
}
- }]
- },
- create: function (context) {
- // Parse the `additionalHooks` regex.
- var additionalHooks = context.options && context.options[0] && context.options[0].additionalHooks ? new RegExp(context.options[0].additionalHooks) : undefined;
- var enableDangerousAutofixThisMayCauseInfiniteLoops = context.options && context.options[0] && context.options[0].enableDangerousAutofixThisMayCauseInfiniteLoops || false;
- var options = {
- additionalHooks: additionalHooks,
- enableDangerousAutofixThisMayCauseInfiniteLoops: enableDangerousAutofixThisMayCauseInfiniteLoops
- };
-
- function reportProblem(problem) {
- if (enableDangerousAutofixThisMayCauseInfiniteLoops) {
- // Used to enable legacy behavior. Dangerous.
- // Keep this as an option until major IDEs upgrade (including VSCode FB ESLint extension).
- if (Array.isArray(problem.suggest) && problem.suggest.length > 0) {
- problem.fix = problem.suggest[0].fix;
- }
- }
-
- context.report(problem);
+ return !1;
}
-
- var scopeManager = context.getSourceCode().scopeManager; // Should be shared between visitors.
-
- var setStateCallSites = new WeakMap();
- var stateVariables = new WeakSet();
- var stableKnownValueCache = new WeakMap();
- var functionWithoutCapturedValueCache = new WeakMap();
-
- function memoizeWithWeakMap(fn, map) {
- return function (arg) {
- if (map.has(arg)) {
- // to verify cache hits:
- // console.log(arg.name)
- return map.get(arg);
- }
-
- var result = fn(arg);
- map.set(arg, result);
- return result;
- };
+ function getFunctionName(node) {
+ if (
+ "FunctionDeclaration" === node.type ||
+ ("FunctionExpression" === node.type && node.id)
+ )
+ return node.id;
+ if (
+ "FunctionExpression" === node.type ||
+ "ArrowFunctionExpression" === node.type
+ )
+ return "VariableDeclarator" === node.parent.type &&
+ node.parent.init === node
+ ? node.parent.id
+ : "AssignmentExpression" === node.parent.type &&
+ node.parent.right === node &&
+ "=" === node.parent.operator
+ ? node.parent.left
+ : "Property" !== node.parent.type ||
+ node.parent.value !== node ||
+ node.parent.computed
+ ? "AssignmentPattern" !== node.parent.type ||
+ node.parent.right !== node ||
+ node.parent.computed
+ ? void 0
+ : node.parent.left
+ : node.parent.key;
}
- /**
- * Visitor for both function expressions and arrow function expressions.
- */
-
-
- function visitFunctionWithDependencies(node, declaredDependenciesNode, reactiveHook, reactiveHookName, isEffect) {
- if (isEffect && node.async) {
- reportProblem({
- node: node,
- message: "Effect callbacks are synchronous to prevent race conditions. " + "Put the async function inside:\n\n" + 'useEffect(() => {\n' + ' async function fetchData() {\n' + ' // You can await here\n' + ' const response = await MyAPI.getData(someId);\n' + ' // ...\n' + ' }\n' + ' fetchData();\n' + "}, [someId]); // Or [] if effect doesn't need props or state\n\n" + 'Learn more about data fetching with Hooks: https://reactjs.org/link/hooks-data-fetching'
- });
- } // Get the current scope.
-
-
- var scope = scopeManager.acquire(node); // Find all our "pure scopes". On every re-render of a component these
- // pure scopes may have changes to the variables declared within. So all
- // variables used in our reactive hook callback but declared in a pure
- // scope need to be listed as dependencies of our reactive hook callback.
- //
- // According to the rules of React you can't read a mutable value in pure
- // scope. We can't enforce this in a lint so we trust that all variables
- // declared outside of pure scope are indeed frozen.
-
- var pureScopes = new Set();
- var componentScope = null;
- {
- var currentScope = scope.upper;
-
- while (currentScope) {
- pureScopes.add(currentScope);
-
- if (currentScope.type === 'function') {
- break;
- }
-
- currentScope = currentScope.upper;
- } // If there is no parent function scope then there are no pure scopes.
- // The ones we've collected so far are incorrect. So don't continue with
- // the lint.
-
-
- if (!currentScope) {
- return;
- }
-
- componentScope = currentScope;
+ function collectRecommendations(_ref6) {
+ function createDepTree() {
+ return {
+ isUsed: !1,
+ isSatisfiedRecursively: !1,
+ isSubtreeUsed: !1,
+ children: new Map()
+ };
}
- var isArray = Array.isArray; // Next we'll define a few helpers that helps us
- // tell if some values don't have to be declared as deps.
- // Some are known to be stable based on Hook calls.
- // const [state, setState] = useState() / React.useState()
- // ^^^ true for this reference
- // const [state, dispatch] = useReducer() / React.useReducer()
- // ^^^ true for this reference
- // const ref = useRef()
- // ^^^ true for this reference
- // False for everything else.
-
- function isStableKnownHookValue(resolved) {
- if (!isArray(resolved.defs)) {
- return false;
- }
-
- var def = resolved.defs[0];
-
- if (def == null) {
- return false;
- } // Look for `let stuff = ...`
-
-
- if (def.node.type !== 'VariableDeclarator') {
- return false;
- }
-
- var init = def.node.init;
-
- if (init == null) {
- return false;
- }
-
- while (init.type === 'TSAsExpression') {
- init = init.expression;
- } // Detect primitive constants
- // const foo = 42
-
-
- var declaration = def.node.parent;
-
- if (declaration == null) {
- // This might happen if variable is declared after the callback.
- // In that case ESLint won't set up .parent refs.
- // So we'll set them up manually.
- fastFindReferenceWithParent(componentScope.block, def.node.id);
- declaration = def.node.parent;
-
- if (declaration == null) {
- return false;
- }
- }
-
- if (declaration.kind === 'const' && init.type === 'Literal' && (typeof init.value === 'string' || typeof init.value === 'number' || init.value === null)) {
- // Definitely stable
- return true;
- } // Detect known Hook calls
- // const [_, setState] = useState()
-
-
- if (init.type !== 'CallExpression') {
- return false;
- }
-
- var callee = init.callee; // Step into `= React.something` initializer.
-
- if (callee.type === 'MemberExpression' && callee.object.name === 'React' && callee.property != null && !callee.computed) {
- callee = callee.property;
- }
-
- if (callee.type !== 'Identifier') {
- return false;
- }
-
- var id = def.node.id;
- var _callee = callee,
- name = _callee.name;
-
- if (name === 'useRef' && id.type === 'Identifier') {
- // useRef() return value is stable.
- return true;
- } else if (name === 'useState' || name === 'useReducer') {
- // Only consider second value in initializing tuple stable.
- if (id.type === 'ArrayPattern' && id.elements.length === 2 && isArray(resolved.identifiers)) {
- // Is second tuple value the same reference we're checking?
- if (id.elements[1] === resolved.identifiers[0]) {
- if (name === 'useState') {
- var references = resolved.references;
- var writeCount = 0;
-
- for (var i = 0; i < references.length; i++) {
- if (references[i].isWrite()) {
- writeCount++;
- }
-
- if (writeCount > 1) {
- return false;
- }
-
- setStateCallSites.set(references[i].identifier, id.elements[0]);
- }
- } // Setter is stable.
-
-
- return true;
- } else if (id.elements[0] === resolved.identifiers[0]) {
- if (name === 'useState') {
- var _references = resolved.references;
-
- for (var _i = 0; _i < _references.length; _i++) {
- stateVariables.add(_references[_i].identifier);
- }
- } // State variable itself is dynamic.
-
-
- return false;
- }
- }
- } else if (name === 'useTransition') {
- // Only consider second value in initializing tuple stable.
- if (id.type === 'ArrayPattern' && id.elements.length === 2 && Array.isArray(resolved.identifiers)) {
- // Is second tuple value the same reference we're checking?
- if (id.elements[1] === resolved.identifiers[0]) {
- // Setter is stable.
- return true;
- }
- }
- } // By default assume it's dynamic.
-
-
- return false;
- } // Some are just functions that don't reference anything dynamic.
-
-
- function isFunctionWithoutCapturedValues(resolved) {
- if (!isArray(resolved.defs)) {
- return false;
- }
-
- var def = resolved.defs[0];
-
- if (def == null) {
- return false;
- }
-
- if (def.node == null || def.node.id == null) {
- return false;
- } // Search the direct component subscopes for
- // top-level function definitions matching this reference.
-
-
- var fnNode = def.node;
- var childScopes = componentScope.childScopes;
- var fnScope = null;
- var i;
-
- for (i = 0; i < childScopes.length; i++) {
- var childScope = childScopes[i];
- var childScopeBlock = childScope.block;
-
- if ( // function handleChange() {}
- fnNode.type === 'FunctionDeclaration' && childScopeBlock === fnNode || // const handleChange = () => {}
- // const handleChange = function() {}
- fnNode.type === 'VariableDeclarator' && childScopeBlock.parent === fnNode) {
- // Found it!
- fnScope = childScope;
- break;
- }
- }
-
- if (fnScope == null) {
- return false;
- } // Does this function capture any values
- // that are in pure scopes (aka render)?
-
-
- for (i = 0; i < fnScope.through.length; i++) {
- var ref = fnScope.through[i];
-
- if (ref.resolved == null) {
- continue;
- }
-
- if (pureScopes.has(ref.resolved.scope) && // Stable values are fine though,
- // although we won't check functions deeper.
- !memoizedIsStableKnownHookValue(ref.resolved)) {
- return false;
- }
- } // If we got here, this function doesn't capture anything
- // from render--or everything it captures is known stable.
-
-
- return true;
- } // Remember such values. Avoid re-running extra checks on them.
-
-
- var memoizedIsStableKnownHookValue = memoizeWithWeakMap(isStableKnownHookValue, stableKnownValueCache);
- var memoizedIsFunctionWithoutCapturedValues = memoizeWithWeakMap(isFunctionWithoutCapturedValues, functionWithoutCapturedValueCache); // These are usually mistaken. Collect them.
-
- var currentRefsInEffectCleanup = new Map(); // Is this reference inside a cleanup function for this effect node?
- // We can check by traversing scopes upwards from the reference, and checking
- // if the last "return () => " we encounter is located directly inside the effect.
-
- function isInsideEffectCleanup(reference) {
- var curScope = reference.from;
- var isInReturnedFunction = false;
-
- while (curScope.block !== node) {
- if (curScope.type === 'function') {
- isInReturnedFunction = curScope.block.parent != null && curScope.block.parent.type === 'ReturnStatement';
- }
-
- curScope = curScope.upper;
- }
-
- return isInReturnedFunction;
- } // Get dependencies from all our resolved references in pure scopes.
- // Key is dependency string, value is whether it's stable.
-
-
- var dependencies = new Map();
- var optionalChains = new Map();
- gatherDependenciesRecursively(scope);
-
- function gatherDependenciesRecursively(currentScope) {
- var _iterator = _createForOfIteratorHelper(currentScope.references),
- _step;
-
+ function getOrCreateNodeByPath(rootNode, path) {
+ path = path.split(".");
+ path = _createForOfIteratorHelper(path);
+ var _step4;
try {
- for (_iterator.s(); !(_step = _iterator.n()).done;) {
- var reference = _step.value;
-
- // If this reference is not resolved or it is not declared in a pure
- // scope then we don't care about this reference.
- if (!reference.resolved) {
- continue;
- }
-
- if (!pureScopes.has(reference.resolved.scope)) {
- continue;
- } // Narrow the scope of a dependency if it is, say, a member expression.
- // Then normalize the narrowed dependency.
-
-
- var referenceNode = fastFindReferenceWithParent(node, reference.identifier);
- var dependencyNode = getDependency(referenceNode);
- var dependency = analyzePropertyChain(dependencyNode, optionalChains); // Accessing ref.current inside effect cleanup is bad.
-
- if ( // We're in an effect...
- isEffect && // ... and this look like accessing .current...
- 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...
- isInsideEffectCleanup(reference)) {
- currentRefsInEffectCleanup.set(dependency, {
- reference: reference,
- dependencyNode: dependencyNode
- });
- }
-
- if (dependencyNode.parent.type === 'TSTypeQuery' || dependencyNode.parent.type === 'TSTypeReference') {
- continue;
- }
-
- var def = reference.resolved.defs[0];
-
- if (def == null) {
- continue;
- } // Ignore references to the function itself as it's not defined yet.
-
-
- if (def.node != null && def.node.init === node.parent) {
- continue;
- } // Ignore Flow type parameters
-
-
- if (def.type === 'TypeParameter') {
- continue;
- } // Add the dependency to a map so we can make sure it is referenced
- // again in our dependencies array. Remember whether it's stable.
-
-
- if (!dependencies.has(dependency)) {
- var resolved = reference.resolved;
- var isStable = memoizedIsStableKnownHookValue(resolved) || memoizedIsFunctionWithoutCapturedValues(resolved);
- dependencies.set(dependency, {
- isStable: isStable,
- references: [reference]
- });
- } else {
- dependencies.get(dependency).references.push(reference);
- }
+ for (path.s(); !(_step4 = path.n()).done; ) {
+ var key = _step4.value,
+ child = rootNode.children.get(key);
+ child ||
+ ((child = createDepTree()), rootNode.children.set(key, child));
+ rootNode = child;
}
} catch (err) {
- _iterator.e(err);
+ path.e(err);
} finally {
- _iterator.f();
+ path.f();
}
-
- var _iterator2 = _createForOfIteratorHelper(currentScope.childScopes),
- _step2;
-
+ return rootNode;
+ }
+ function markAllParentsByPath(rootNode, path, fn) {
+ path = path.split(".");
+ path = _createForOfIteratorHelper(path);
+ var _step5;
try {
- for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
- var childScope = _step2.value;
- gatherDependenciesRecursively(childScope);
+ for (path.s(); !(_step5 = path.n()).done; ) {
+ var child = rootNode.children.get(_step5.value);
+ if (!child) break;
+ fn(child);
+ rootNode = child;
}
} catch (err) {
- _iterator2.e(err);
+ path.e(err);
} finally {
- _iterator2.f();
+ path.f();
}
- } // Warn about accessing .current in cleanup effects.
-
-
- currentRefsInEffectCleanup.forEach(function (_ref, dependency) {
- var reference = _ref.reference,
- dependencyNode = _ref.dependencyNode;
- var references = reference.resolved.references; // Is React managing this ref or us?
- // Let's see if we can find a .current assignment.
-
- var foundCurrentAssignment = false;
-
- for (var i = 0; i < references.length; i++) {
- var identifier = references[i].identifier;
- var parent = identifier.parent;
-
- if (parent != null && // ref.current
- // Note: no need to handle OptionalMemberExpression because it can't be LHS.
- parent.type === 'MemberExpression' && !parent.computed && parent.property.type === 'Identifier' && parent.property.name === 'current' && // ref.current = <something>
- parent.parent.type === 'AssignmentExpression' && parent.parent.left === parent) {
- foundCurrentAssignment = true;
- break;
- }
- } // We only want to warn about React-managed refs.
-
-
- if (foundCurrentAssignment) {
- return;
- }
-
- reportProblem({
- node: dependencyNode.parent.property,
- 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."
+ }
+ function scanTreeRecursively(
+ node,
+ missingPaths,
+ satisfyingPaths,
+ keyToPath
+ ) {
+ node.children.forEach(function (child, key) {
+ var path = keyToPath(key);
+ child.isSatisfiedRecursively
+ ? child.isSubtreeUsed && satisfyingPaths.add(path)
+ : child.isUsed
+ ? missingPaths.add(path)
+ : scanTreeRecursively(
+ child,
+ missingPaths,
+ satisfyingPaths,
+ function (childKey) {
+ return path + "." + childKey;
+ }
+ );
});
- }); // Warn about assigning to variables in the outer scope.
- // Those are usually bugs.
-
- var staleAssignments = new Set();
-
- function reportStaleAssignment(writeExpr, key) {
- if (staleAssignments.has(key)) {
- return;
- }
-
- staleAssignments.add(key);
- reportProblem({
- node: writeExpr,
- 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) + ".")
+ }
+ var dependencies = _ref6.dependencies,
+ declaredDependencies = _ref6.declaredDependencies,
+ stableDependencies = _ref6.stableDependencies,
+ externalDependencies = _ref6.externalDependencies,
+ isEffect = _ref6.isEffect,
+ depTree = createDepTree();
+ dependencies.forEach(function (_, key) {
+ getOrCreateNodeByPath(depTree, key).isUsed = !0;
+ markAllParentsByPath(depTree, key, function (parent) {
+ parent.isSubtreeUsed = !0;
});
- } // Remember which deps are stable and report bad usage first.
-
-
- var stableDependencies = new Set();
- dependencies.forEach(function (_ref2, key) {
- var isStable = _ref2.isStable,
- references = _ref2.references;
-
- if (isStable) {
- stableDependencies.add(key);
+ });
+ declaredDependencies.forEach(function (_ref7) {
+ getOrCreateNodeByPath(depTree, _ref7.key).isSatisfiedRecursively = !0;
+ });
+ stableDependencies.forEach(function (key) {
+ getOrCreateNodeByPath(depTree, key).isSatisfiedRecursively = !0;
+ });
+ _ref6 = new Set();
+ var satisfyingDependencies = new Set();
+ scanTreeRecursively(
+ depTree,
+ _ref6,
+ satisfyingDependencies,
+ function (key) {
+ return key;
}
-
- references.forEach(function (reference) {
- if (reference.writeExpr) {
- reportStaleAssignment(reference.writeExpr, key);
- }
- });
+ );
+ var suggestedDependencies = [],
+ unnecessaryDependencies = new Set(),
+ duplicateDependencies = new Set();
+ declaredDependencies.forEach(function (_ref8) {
+ _ref8 = _ref8.key;
+ satisfyingDependencies.has(_ref8)
+ ? -1 === suggestedDependencies.indexOf(_ref8)
+ ? suggestedDependencies.push(_ref8)
+ : duplicateDependencies.add(_ref8)
+ : !isEffect ||
+ _ref8.endsWith(".current") ||
+ externalDependencies.has(_ref8)
+ ? unnecessaryDependencies.add(_ref8)
+ : -1 === suggestedDependencies.indexOf(_ref8) &&
+ suggestedDependencies.push(_ref8);
});
-
- if (staleAssignments.size > 0) {
- // The intent isn't clear so we'll wait until you fix those first.
- return;
+ _ref6.forEach(function (key) {
+ suggestedDependencies.push(key);
+ });
+ return {
+ suggestedDependencies: suggestedDependencies,
+ unnecessaryDependencies: unnecessaryDependencies,
+ duplicateDependencies: duplicateDependencies,
+ missingDependencies: _ref6
+ };
+ }
+ function getConstructionExpressionType(node) {
+ switch (node.type) {
+ case "ObjectExpression":
+ return "object";
+ case "ArrayExpression":
+ return "array";
+ case "ArrowFunctionExpression":
+ case "FunctionExpression":
+ return "function";
+ case "ClassExpression":
+ return "class";
+ case "ConditionalExpression":
+ if (
+ null != getConstructionExpressionType(node.consequent) ||
+ null != getConstructionExpressionType(node.alternate)
+ )
+ return "conditional";
+ break;
+ case "LogicalExpression":
+ if (
+ null != getConstructionExpressionType(node.left) ||
+ null != getConstructionExpressionType(node.right)
+ )
+ return "logical expression";
+ break;
+ case "JSXFragment":
+ return "JSX fragment";
+ case "JSXElement":
+ return "JSX element";
+ case "AssignmentExpression":
+ if (null != getConstructionExpressionType(node.right))
+ return "assignment expression";
+ break;
+ case "NewExpression":
+ return "object construction";
+ case "Literal":
+ if (node.value instanceof RegExp) return "regular expression";
+ break;
+ case "TypeCastExpression":
+ case "AsExpression":
+ case "TSAsExpression":
+ return getConstructionExpressionType(node.expression);
}
-
- if (!declaredDependenciesNode) {
- // Check if there are any top-level setState() calls.
- // Those tend to lead to infinite loops.
- var setStateInsideEffectWithoutDeps = null;
- dependencies.forEach(function (_ref3, key) {
- var isStable = _ref3.isStable,
- references = _ref3.references;
-
- if (setStateInsideEffectWithoutDeps) {
- return;
+ return null;
+ }
+ function scanForConstructions(_ref9) {
+ var declaredDependenciesNode = _ref9.declaredDependenciesNode,
+ componentScope = _ref9.componentScope,
+ scope = _ref9.scope;
+ return _ref9.declaredDependencies
+ .map(function (_ref10) {
+ var key = _ref10.key;
+ _ref10 = componentScope.variables.find(function (v) {
+ return v.name === key;
+ });
+ if (null == _ref10) return null;
+ var node = _ref10.defs[0];
+ if (null == node) return null;
+ if (
+ "Variable" === node.type &&
+ "VariableDeclarator" === node.node.type &&
+ "Identifier" === node.node.id.type &&
+ null != node.node.init
+ ) {
+ var constantExpressionType = getConstructionExpressionType(
+ node.node.init
+ );
+ if (null != constantExpressionType)
+ return [_ref10, constantExpressionType];
}
+ return "FunctionName" === node.type &&
+ "FunctionDeclaration" === node.node.type
+ ? [_ref10, "function"]
+ : "ClassName" === node.type && "ClassDeclaration" === node.node.type
+ ? [_ref10, "class"]
+ : null;
+ })
+ .filter(Boolean)
+ .map(function (_ref11) {
+ var ref = _ref11[0];
+ _ref11 = _ref11[1];
+ var JSCompiler_temp_const = ref.defs[0];
+ a: {
+ for (
+ var foundWriteExpr = !1, i = 0;
+ i < ref.references.length;
+ i++
+ ) {
+ var reference = ref.references[i];
+ if (reference.writeExpr)
+ if (foundWriteExpr) {
+ ref = !0;
+ break a;
+ } else {
+ foundWriteExpr = !0;
+ continue;
+ }
+ for (
+ var currentScope = reference.from;
+ currentScope !== scope && null != currentScope;
- references.forEach(function (reference) {
- if (setStateInsideEffectWithoutDeps) {
- return;
+ )
+ currentScope = currentScope.upper;
+ if (
+ currentScope !== scope &&
+ !isAncestorNodeOf(
+ declaredDependenciesNode,
+ reference.identifier
+ )
+ ) {
+ ref = !0;
+ break a;
+ }
}
-
- var id = reference.identifier;
- var isSetState = setStateCallSites.has(id);
-
- if (!isSetState) {
- return;
- }
-
- var fnScope = reference.from;
-
- while (fnScope.type !== 'function') {
- fnScope = fnScope.upper;
- }
-
- var isDirectlyInsideEffect = fnScope.block === node;
-
- if (isDirectlyInsideEffect) {
- // TODO: we could potentially ignore early returns.
- setStateInsideEffectWithoutDeps = key;
- }
- });
+ ref = !1;
+ }
+ return {
+ construction: JSCompiler_temp_const,
+ depType: _ref11,
+ isUsedOutsideOfHook: ref
+ };
});
-
- if (setStateInsideEffectWithoutDeps) {
- var _collectRecommendatio = collectRecommendations({
- dependencies: dependencies,
- declaredDependencies: [],
- stableDependencies: stableDependencies,
- externalDependencies: new Set(),
- isEffect: true
- }),
- _suggestedDependencies = _collectRecommendatio.suggestedDependencies;
-
- reportProblem({
- node: reactiveHook,
- 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."),
- suggest: [{
- desc: "Add dependencies array: [" + _suggestedDependencies.join(', ') + "]",
- fix: function (fixer) {
- return fixer.insertTextAfter(node, ", [" + _suggestedDependencies.join(', ') + "]");
- }
- }]
- });
+ }
+ function getDependency(node) {
+ return ("MemberExpression" !== node.parent.type &&
+ "OptionalMemberExpression" !== node.parent.type) ||
+ node.parent.object !== node ||
+ "current" === node.parent.property.name ||
+ node.parent.computed ||
+ (null != node.parent.parent &&
+ ("CallExpression" === node.parent.parent.type ||
+ "OptionalCallExpression" === node.parent.parent.type) &&
+ node.parent.parent.callee === node.parent)
+ ? "MemberExpression" === node.type &&
+ node.parent &&
+ "AssignmentExpression" === node.parent.type &&
+ node.parent.left === node
+ ? node.object
+ : node
+ : getDependency(node.parent);
+ }
+ function markNode(node, optionalChains, result) {
+ optionalChains &&
+ (node.optional
+ ? optionalChains.has(result) || optionalChains.set(result, !0)
+ : optionalChains.set(result, !1));
+ }
+ function analyzePropertyChain(node, optionalChains) {
+ if ("Identifier" === node.type || "JSXIdentifier" === node.type)
+ return (
+ (node = node.name),
+ optionalChains && optionalChains.set(node, !1),
+ node
+ );
+ if ("MemberExpression" !== node.type || node.computed) {
+ if ("OptionalMemberExpression" !== node.type || node.computed) {
+ if ("ChainExpression" !== node.type || node.computed)
+ throw Error("Unsupported node type: " + node.type);
+ node = node.expression;
+ if ("CallExpression" === node.type)
+ throw Error("Unsupported node type: " + node.type);
+ var _object2 = analyzePropertyChain(node.object, optionalChains),
+ _property2 = analyzePropertyChain(node.property, null);
+ _object2 = _object2 + "." + _property2;
+ markNode(node, optionalChains, _object2);
+ return _object2;
}
-
- return;
+ _object2 = analyzePropertyChain(node.object, optionalChains);
+ _property2 = analyzePropertyChain(node.property, null);
+ _object2 = _object2 + "." + _property2;
+ markNode(node, optionalChains, _object2);
+ return _object2;
}
-
- var declaredDependencies = [];
- var externalDependencies = new Set();
-
- if (declaredDependenciesNode.type !== 'ArrayExpression') {
- // If the declared dependencies are not an array expression then we
- // can't verify that the user provided the correct dependencies. Tell
- // the user this in an error.
- reportProblem({
- node: declaredDependenciesNode,
- 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.'
- });
- } else {
- declaredDependenciesNode.elements.forEach(function (declaredDependencyNode) {
- // Skip elided elements.
- if (declaredDependencyNode === null) {
- return;
- } // If we see a spread element then add a special warning.
-
-
- if (declaredDependencyNode.type === 'SpreadElement') {
- reportProblem({
- node: declaredDependencyNode,
- 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.'
- });
- return;
- } // Try to normalize the declared depe (too long so truncated)
Command detailsnpm diff --diff=eslint-plugin-react-hooks@4.6.2 --diff=eslint-plugin-react-hooks@5.0.0 --diff-unified=2 See also the Reported by ybiquitous/npm-diff-action@v1.6.0 (Node.js 22.11.0 and npm 10.9.0) |
dependabot
bot
force-pushed
the
dependabot/npm_and_yarn/eslint-plugin-react-hooks-5.0.0
branch
2 times, most recently
from
November 1, 2024 03:08
e588d72
to
78d1fda
Compare
Bumps [eslint-plugin-react-hooks](https://github.com/facebook/react/tree/HEAD/packages/eslint-plugin-react-hooks) from 4.6.2 to 5.0.0. - [Release notes](https://github.com/facebook/react/releases) - [Changelog](https://github.com/facebook/react/blob/main/packages/eslint-plugin-react-hooks/CHANGELOG.md) - [Commits](https://github.com/facebook/react/commits/eslint-plugin-react-hooks@5.0.0/packages/eslint-plugin-react-hooks) --- updated-dependencies: - dependency-name: eslint-plugin-react-hooks dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com>
dependabot
bot
force-pushed
the
dependabot/npm_and_yarn/eslint-plugin-react-hooks-5.0.0
branch
from
November 5, 2024 05:56
78d1fda
to
096b74b
Compare
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Labels
dependencies
Pull requests that update a dependency file
javascript
Pull requests that update Javascript code
0 participants
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Bumps eslint-plugin-react-hooks from 4.6.2 to 5.0.0.
Release notes
Sourced from eslint-plugin-react-hooks's releases.
Changelog
Sourced from eslint-plugin-react-hooks's changelog.
... (truncated)
Commits
Maintainer changes
This version was pushed to npm by eps1lon, a new releaser for eslint-plugin-react-hooks since your current version.
You can trigger a rebase of this PR by commenting
@dependabot rebase
.Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR:
@dependabot rebase
will rebase this PR@dependabot recreate
will recreate this PR, overwriting any edits that have been made to it@dependabot merge
will merge this PR after your CI passes on it@dependabot squash and merge
will squash and merge this PR after your CI passes on it@dependabot cancel merge
will cancel a previously requested merge and block automerging@dependabot reopen
will reopen this PR if it is closed@dependabot close
will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually@dependabot show <dependency name> ignore conditions
will show all of the ignore conditions of the specified dependency@dependabot ignore this major version
will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)@dependabot ignore this minor version
will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)@dependabot ignore this dependency
will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)