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