eslint-plugin-react-hooks 1.5.0-beta.1 → 1.6.1
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.
|
@@ -760,17 +760,25 @@ var ExhaustiveDeps = {
|
|
|
760
760
|
// So no need to check for dependency inclusion.
|
|
761
761
|
var depsIndex = callbackIndex + 1;
|
|
762
762
|
var declaredDependenciesNode = node.parent.arguments[depsIndex];
|
|
763
|
-
if (!declaredDependenciesNode) {
|
|
763
|
+
if (!declaredDependenciesNode && !isEffect) {
|
|
764
764
|
// These are only used for optimization.
|
|
765
765
|
if (reactiveHookName === 'useMemo' || reactiveHookName === 'useCallback') {
|
|
766
|
+
// TODO: Can this have an autofix?
|
|
766
767
|
context.report({
|
|
767
768
|
node: node.parent.callee,
|
|
768
|
-
message: 'React Hook ' + reactiveHookName + '
|
|
769
|
+
message: 'React Hook ' + reactiveHookName + ' does nothing when called with ' + 'only one argument. Did you forget to pass an array of ' + 'dependencies?'
|
|
769
770
|
});
|
|
770
771
|
}
|
|
771
772
|
return;
|
|
772
773
|
}
|
|
773
774
|
|
|
775
|
+
if (isEffect && node.async) {
|
|
776
|
+
context.report({
|
|
777
|
+
node: node,
|
|
778
|
+
message: 'Effect callbacks are synchronous to prevent race conditions. ' + 'Put the async function inside:\n\n' + 'useEffect(() => {\n' + ' async function fetchData() {\n' + ' // You can await here\n' + ' const response = await MyAPI.getData(someId);\n' + ' // ...\n' + ' }\n' + ' fetchData();\n' + '}, [someId]); // Or [] if effect doesn\'t need props or state\n\n' + 'Learn more about data fetching with Hooks: https://fb.me/react-hooks-data-fetching'
|
|
779
|
+
});
|
|
780
|
+
}
|
|
781
|
+
|
|
774
782
|
// Get the current scope.
|
|
775
783
|
var scope = context.getScope();
|
|
776
784
|
|
|
@@ -1014,6 +1022,17 @@ var ExhaustiveDeps = {
|
|
|
1014
1022
|
});
|
|
1015
1023
|
}
|
|
1016
1024
|
|
|
1025
|
+
// Ignore references to the function itself as it's not defined yet.
|
|
1026
|
+
var def = reference.resolved.defs[0];
|
|
1027
|
+
if (def != null && def.node != null && def.node.init === node.parent) {
|
|
1028
|
+
continue;
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
// Ignore Flow type parameters
|
|
1032
|
+
if (def.type === 'TypeParameter') {
|
|
1033
|
+
continue;
|
|
1034
|
+
}
|
|
1035
|
+
|
|
1017
1036
|
// Add the dependency to a map so we can make sure it is referenced
|
|
1018
1037
|
// again in our dependencies array. Remember whether it's static.
|
|
1019
1038
|
if (!dependencies.has(dependency)) {
|
|
@@ -1096,10 +1115,99 @@ var ExhaustiveDeps = {
|
|
|
1096
1115
|
}
|
|
1097
1116
|
context.report({
|
|
1098
1117
|
node: dependencyNode.parent.property,
|
|
1099
|
-
message: '
|
|
1118
|
+
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.'
|
|
1100
1119
|
});
|
|
1101
1120
|
});
|
|
1102
1121
|
|
|
1122
|
+
// Warn about assigning to variables in the outer scope.
|
|
1123
|
+
// Those are usually bugs.
|
|
1124
|
+
var staleAssignments = new Set();
|
|
1125
|
+
function reportStaleAssignment(writeExpr, key) {
|
|
1126
|
+
if (staleAssignments.has(key)) {
|
|
1127
|
+
return;
|
|
1128
|
+
}
|
|
1129
|
+
staleAssignments.add(key);
|
|
1130
|
+
context.report({
|
|
1131
|
+
node: writeExpr,
|
|
1132
|
+
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) + '.')
|
|
1133
|
+
});
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1136
|
+
// Remember which deps are optional and report bad usage first.
|
|
1137
|
+
var optionalDependencies = new Set();
|
|
1138
|
+
dependencies.forEach(function (_ref2, key) {
|
|
1139
|
+
var isStatic = _ref2.isStatic,
|
|
1140
|
+
references = _ref2.references;
|
|
1141
|
+
|
|
1142
|
+
if (isStatic) {
|
|
1143
|
+
optionalDependencies.add(key);
|
|
1144
|
+
}
|
|
1145
|
+
references.forEach(function (reference) {
|
|
1146
|
+
if (reference.writeExpr) {
|
|
1147
|
+
reportStaleAssignment(reference.writeExpr, key);
|
|
1148
|
+
}
|
|
1149
|
+
});
|
|
1150
|
+
});
|
|
1151
|
+
|
|
1152
|
+
if (staleAssignments.size > 0) {
|
|
1153
|
+
// The intent isn't clear so we'll wait until you fix those first.
|
|
1154
|
+
return;
|
|
1155
|
+
}
|
|
1156
|
+
|
|
1157
|
+
if (!declaredDependenciesNode) {
|
|
1158
|
+
// Check if there are any top-level setState() calls.
|
|
1159
|
+
// Those tend to lead to infinite loops.
|
|
1160
|
+
var setStateInsideEffectWithoutDeps = null;
|
|
1161
|
+
dependencies.forEach(function (_ref3, key) {
|
|
1162
|
+
var isStatic = _ref3.isStatic,
|
|
1163
|
+
references = _ref3.references;
|
|
1164
|
+
|
|
1165
|
+
if (setStateInsideEffectWithoutDeps) {
|
|
1166
|
+
return;
|
|
1167
|
+
}
|
|
1168
|
+
references.forEach(function (reference) {
|
|
1169
|
+
if (setStateInsideEffectWithoutDeps) {
|
|
1170
|
+
return;
|
|
1171
|
+
}
|
|
1172
|
+
|
|
1173
|
+
var id = reference.identifier;
|
|
1174
|
+
var isSetState = setStateCallSites.has(id);
|
|
1175
|
+
if (!isSetState) {
|
|
1176
|
+
return;
|
|
1177
|
+
}
|
|
1178
|
+
|
|
1179
|
+
var fnScope = reference.from;
|
|
1180
|
+
while (fnScope.type !== 'function') {
|
|
1181
|
+
fnScope = fnScope.upper;
|
|
1182
|
+
}
|
|
1183
|
+
var isDirectlyInsideEffect = fnScope.block === node;
|
|
1184
|
+
if (isDirectlyInsideEffect) {
|
|
1185
|
+
// TODO: we could potentially ignore early returns.
|
|
1186
|
+
setStateInsideEffectWithoutDeps = key;
|
|
1187
|
+
}
|
|
1188
|
+
});
|
|
1189
|
+
});
|
|
1190
|
+
if (setStateInsideEffectWithoutDeps) {
|
|
1191
|
+
var _collectRecommendatio = collectRecommendations({
|
|
1192
|
+
dependencies: dependencies,
|
|
1193
|
+
declaredDependencies: [],
|
|
1194
|
+
optionalDependencies: optionalDependencies,
|
|
1195
|
+
externalDependencies: new Set(),
|
|
1196
|
+
isEffect: true
|
|
1197
|
+
}),
|
|
1198
|
+
_suggestedDependencies = _collectRecommendatio.suggestedDependencies;
|
|
1199
|
+
|
|
1200
|
+
context.report({
|
|
1201
|
+
node: node.parent.callee,
|
|
1202
|
+
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.'),
|
|
1203
|
+
fix: function (fixer) {
|
|
1204
|
+
return fixer.insertTextAfter(node, ', [' + _suggestedDependencies.join(', ') + ']');
|
|
1205
|
+
}
|
|
1206
|
+
});
|
|
1207
|
+
}
|
|
1208
|
+
return;
|
|
1209
|
+
}
|
|
1210
|
+
|
|
1103
1211
|
var declaredDependencies = [];
|
|
1104
1212
|
var externalDependencies = new Set();
|
|
1105
1213
|
if (declaredDependenciesNode.type !== 'ArrayExpression') {
|
|
@@ -1108,7 +1216,7 @@ var ExhaustiveDeps = {
|
|
|
1108
1216
|
// the user this in an error.
|
|
1109
1217
|
context.report({
|
|
1110
1218
|
node: declaredDependenciesNode,
|
|
1111
|
-
message: 'React Hook ' + context.getSource(reactiveHook) + '
|
|
1219
|
+
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.'
|
|
1112
1220
|
});
|
|
1113
1221
|
} else {
|
|
1114
1222
|
declaredDependenciesNode.elements.forEach(function (declaredDependencyNode) {
|
|
@@ -1132,15 +1240,15 @@ var ExhaustiveDeps = {
|
|
|
1132
1240
|
} catch (error) {
|
|
1133
1241
|
if (/Unsupported node type/.test(error.message)) {
|
|
1134
1242
|
if (declaredDependencyNode.type === 'Literal') {
|
|
1135
|
-
if (
|
|
1243
|
+
if (dependencies.has(declaredDependencyNode.value)) {
|
|
1136
1244
|
context.report({
|
|
1137
1245
|
node: declaredDependencyNode,
|
|
1138
|
-
message: 'The ' + declaredDependencyNode.raw + '
|
|
1246
|
+
message: 'The ' + declaredDependencyNode.raw + ' literal is not a valid dependency ' + 'because it never changes. ' + ('Did you mean to include ' + declaredDependencyNode.value + ' in the array instead?')
|
|
1139
1247
|
});
|
|
1140
1248
|
} else {
|
|
1141
1249
|
context.report({
|
|
1142
1250
|
node: declaredDependencyNode,
|
|
1143
|
-
message: 'The
|
|
1251
|
+
message: 'The ' + declaredDependencyNode.raw + ' literal is not a valid dependency ' + 'because it never changes. You can safely remove it.'
|
|
1144
1252
|
});
|
|
1145
1253
|
}
|
|
1146
1254
|
} else {
|
|
@@ -1176,51 +1284,17 @@ var ExhaustiveDeps = {
|
|
|
1176
1284
|
});
|
|
1177
1285
|
}
|
|
1178
1286
|
|
|
1179
|
-
|
|
1180
|
-
// Those are usually bugs.
|
|
1181
|
-
var staleAssignments = new Set();
|
|
1182
|
-
function reportStaleAssignment(writeExpr, key) {
|
|
1183
|
-
if (staleAssignments.has(key)) {
|
|
1184
|
-
return;
|
|
1185
|
-
}
|
|
1186
|
-
staleAssignments.add(key);
|
|
1187
|
-
context.report({
|
|
1188
|
-
node: writeExpr,
|
|
1189
|
-
message: 'Assignments to the \'' + key + '\' variable from inside a React ' + context.getSource(reactiveHook) + ' Hook ' + 'will not persist between re-renders. ' + 'If it\'s only needed by this Hook, move the variable inside it. ' + 'Alternatively, declare a ref with the useRef Hook, ' + 'and keep the mutable value in its \'current\' property.'
|
|
1190
|
-
});
|
|
1191
|
-
}
|
|
1192
|
-
|
|
1193
|
-
// Remember which deps are optional and report bad usage first.
|
|
1194
|
-
var optionalDependencies = new Set();
|
|
1195
|
-
dependencies.forEach(function (_ref2, key) {
|
|
1196
|
-
var isStatic = _ref2.isStatic,
|
|
1197
|
-
references = _ref2.references;
|
|
1198
|
-
|
|
1199
|
-
if (isStatic) {
|
|
1200
|
-
optionalDependencies.add(key);
|
|
1201
|
-
}
|
|
1202
|
-
references.forEach(function (reference) {
|
|
1203
|
-
if (reference.writeExpr) {
|
|
1204
|
-
reportStaleAssignment(reference.writeExpr, key);
|
|
1205
|
-
}
|
|
1206
|
-
});
|
|
1207
|
-
});
|
|
1208
|
-
if (staleAssignments.size > 0) {
|
|
1209
|
-
// The intent isn't clear so we'll wait until you fix those first.
|
|
1210
|
-
return;
|
|
1211
|
-
}
|
|
1212
|
-
|
|
1213
|
-
var _collectRecommendatio = collectRecommendations({
|
|
1287
|
+
var _collectRecommendatio2 = collectRecommendations({
|
|
1214
1288
|
dependencies: dependencies,
|
|
1215
1289
|
declaredDependencies: declaredDependencies,
|
|
1216
1290
|
optionalDependencies: optionalDependencies,
|
|
1217
1291
|
externalDependencies: externalDependencies,
|
|
1218
1292
|
isEffect: isEffect
|
|
1219
1293
|
}),
|
|
1220
|
-
suggestedDependencies =
|
|
1221
|
-
unnecessaryDependencies =
|
|
1222
|
-
missingDependencies =
|
|
1223
|
-
duplicateDependencies =
|
|
1294
|
+
suggestedDependencies = _collectRecommendatio2.suggestedDependencies,
|
|
1295
|
+
unnecessaryDependencies = _collectRecommendatio2.unnecessaryDependencies,
|
|
1296
|
+
missingDependencies = _collectRecommendatio2.missingDependencies,
|
|
1297
|
+
duplicateDependencies = _collectRecommendatio2.duplicateDependencies;
|
|
1224
1298
|
|
|
1225
1299
|
var problemCount = duplicateDependencies.size + missingDependencies.size + unnecessaryDependencies.size;
|
|
1226
1300
|
|
|
@@ -1233,9 +1307,9 @@ var ExhaustiveDeps = {
|
|
|
1233
1307
|
componentScope: componentScope,
|
|
1234
1308
|
scope: scope
|
|
1235
1309
|
});
|
|
1236
|
-
bareFunctions.forEach(function (
|
|
1237
|
-
var fn =
|
|
1238
|
-
suggestUseCallback =
|
|
1310
|
+
bareFunctions.forEach(function (_ref4) {
|
|
1311
|
+
var fn = _ref4.fn,
|
|
1312
|
+
suggestUseCallback = _ref4.suggestUseCallback;
|
|
1239
1313
|
|
|
1240
1314
|
var message = 'The \'' + fn.name.name + '\' function makes the dependencies of ' + (reactiveHookName + ' Hook (at line ' + declaredDependenciesNode.loc.start.line + ') ') + 'change on every render.';
|
|
1241
1315
|
if (suggestUseCallback) {
|
|
@@ -1243,7 +1317,10 @@ var ExhaustiveDeps = {
|
|
|
1243
1317
|
} else {
|
|
1244
1318
|
message += ' Move it inside the ' + reactiveHookName + ' callback. ' + ('Alternatively, wrap the \'' + fn.name.name + '\' definition into its own useCallback() Hook.');
|
|
1245
1319
|
}
|
|
1320
|
+
// TODO: What if the function needs to change on every render anyway?
|
|
1321
|
+
// Should we suggest removing effect deps as an appropriate fix too?
|
|
1246
1322
|
context.report({
|
|
1323
|
+
// TODO: Why not report this at the dependency site?
|
|
1247
1324
|
node: fn.node,
|
|
1248
1325
|
message: message,
|
|
1249
1326
|
fix: function (fixer) {
|
|
@@ -1253,10 +1330,10 @@ var ExhaustiveDeps = {
|
|
|
1253
1330
|
return [
|
|
1254
1331
|
// TODO: also add an import?
|
|
1255
1332
|
fixer.insertTextBefore(fn.node.init, 'useCallback('),
|
|
1256
|
-
// TODO: ideally we'd gather deps here but it would
|
|
1257
|
-
//
|
|
1258
|
-
//
|
|
1259
|
-
// [] because that changes semantics.
|
|
1333
|
+
// TODO: ideally we'd gather deps here but it would require
|
|
1334
|
+
// restructuring the rule code. This will cause a new lint
|
|
1335
|
+
// error to appear immediately for useCallback. Note we're
|
|
1336
|
+
// not adding [] because would that changes semantics.
|
|
1260
1337
|
fixer.insertTextAfter(fn.node.init, ')')];
|
|
1261
1338
|
}
|
|
1262
1339
|
}
|
|
@@ -1317,13 +1394,13 @@ var ExhaustiveDeps = {
|
|
|
1317
1394
|
}
|
|
1318
1395
|
});
|
|
1319
1396
|
if (badRef !== null) {
|
|
1320
|
-
extraWarning = ' Mutable values like \'' + badRef + '\' aren\'t valid dependencies ' + "because
|
|
1397
|
+
extraWarning = ' Mutable values like \'' + badRef + '\' aren\'t valid dependencies ' + "because mutating them doesn't re-render the component.";
|
|
1321
1398
|
} else if (externalDependencies.size > 0) {
|
|
1322
1399
|
var dep = Array.from(externalDependencies)[0];
|
|
1323
1400
|
// Don't show this warning for things that likely just got moved *inside* the callback
|
|
1324
1401
|
// because in that case they're clearly not referring to globals.
|
|
1325
1402
|
if (!scope.set.has(dep)) {
|
|
1326
|
-
extraWarning = ' Outer scope values like \'' + dep + '\' aren\'t valid dependencies ' + 'because
|
|
1403
|
+
extraWarning = ' Outer scope values like \'' + dep + '\' aren\'t valid dependencies ' + 'because mutating them doesn\'t re-render the component.';
|
|
1327
1404
|
}
|
|
1328
1405
|
}
|
|
1329
1406
|
}
|
|
@@ -1359,7 +1436,7 @@ var ExhaustiveDeps = {
|
|
|
1359
1436
|
}
|
|
1360
1437
|
}
|
|
1361
1438
|
if (isPropsOnlyUsedInMembers) {
|
|
1362
|
-
extraWarning = ' However, the preferred fix is to destructure the \'props\' ' + ('
|
|
1439
|
+
extraWarning = ' However, \'props\' will change when *any* prop changes, so the ' + 'preferred fix is to destructure the \'props\' object outside of ' + ('the ' + reactiveHookName + ' call and refer to those specific props ') + ('inside ' + context.getSource(reactiveHook) + '.');
|
|
1363
1440
|
}
|
|
1364
1441
|
}
|
|
1365
1442
|
|
|
@@ -1401,7 +1478,7 @@ var ExhaustiveDeps = {
|
|
|
1401
1478
|
missingCallbackDep = missingDep;
|
|
1402
1479
|
});
|
|
1403
1480
|
if (missingCallbackDep !== null) {
|
|
1404
|
-
extraWarning = ' If
|
|
1481
|
+
extraWarning = ' If \'' + missingCallbackDep + '\' changes too often, ' + 'find the parent component that defines it ' + 'and wrap that definition in useCallback.';
|
|
1405
1482
|
}
|
|
1406
1483
|
}
|
|
1407
1484
|
|
|
@@ -1469,10 +1546,10 @@ var ExhaustiveDeps = {
|
|
|
1469
1546
|
extraWarning = ' You can also replace multiple useState variables with useReducer ' + ('if \'' + setStateRecommendation.setter + '\' needs the ') + ('current value of \'' + setStateRecommendation.missingDep + '\'.');
|
|
1470
1547
|
break;
|
|
1471
1548
|
case 'inlineReducer':
|
|
1472
|
-
extraWarning = '
|
|
1549
|
+
extraWarning = ' If \'' + setStateRecommendation.setter + '\' needs the ' + ('current value of \'' + setStateRecommendation.missingDep + '\', ') + 'you can also switch to useReducer instead of useState and ' + ('read \'' + setStateRecommendation.missingDep + '\' in the reducer.');
|
|
1473
1550
|
break;
|
|
1474
1551
|
case 'updater':
|
|
1475
|
-
extraWarning = ' You can also
|
|
1552
|
+
extraWarning = ' You can also do a functional update \'' + setStateRecommendation.setter + '(' + setStateRecommendation.missingDep.substring(0, 1) + ' => ...)\' if you only need \'' + setStateRecommendation.missingDep + '\'' + (' in the \'' + setStateRecommendation.setter + '\' call.');
|
|
1476
1553
|
break;
|
|
1477
1554
|
default:
|
|
1478
1555
|
throw new Error('Unknown case.');
|
|
@@ -1495,12 +1572,12 @@ var ExhaustiveDeps = {
|
|
|
1495
1572
|
};
|
|
1496
1573
|
|
|
1497
1574
|
// The meat of the logic.
|
|
1498
|
-
function collectRecommendations(
|
|
1499
|
-
var dependencies =
|
|
1500
|
-
declaredDependencies =
|
|
1501
|
-
optionalDependencies =
|
|
1502
|
-
externalDependencies =
|
|
1503
|
-
isEffect =
|
|
1575
|
+
function collectRecommendations(_ref5) {
|
|
1576
|
+
var dependencies = _ref5.dependencies,
|
|
1577
|
+
declaredDependencies = _ref5.declaredDependencies,
|
|
1578
|
+
optionalDependencies = _ref5.optionalDependencies,
|
|
1579
|
+
externalDependencies = _ref5.externalDependencies,
|
|
1580
|
+
isEffect = _ref5.isEffect;
|
|
1504
1581
|
|
|
1505
1582
|
// Our primary data structure.
|
|
1506
1583
|
// It is a logical representation of property chains:
|
|
@@ -1533,8 +1610,8 @@ function collectRecommendations(_ref4) {
|
|
|
1533
1610
|
|
|
1534
1611
|
// Mark all satisfied nodes.
|
|
1535
1612
|
// Imagine checkmarks next to each declared dependency.
|
|
1536
|
-
declaredDependencies.forEach(function (
|
|
1537
|
-
var key =
|
|
1613
|
+
declaredDependencies.forEach(function (_ref6) {
|
|
1614
|
+
var key = _ref6.key;
|
|
1538
1615
|
|
|
1539
1616
|
var node = getOrCreateNodeByPath(depTree, key);
|
|
1540
1617
|
node.isSatisfiedRecursively = true;
|
|
@@ -1650,8 +1727,8 @@ function collectRecommendations(_ref4) {
|
|
|
1650
1727
|
var suggestedDependencies = [];
|
|
1651
1728
|
var unnecessaryDependencies = new Set();
|
|
1652
1729
|
var duplicateDependencies = new Set();
|
|
1653
|
-
declaredDependencies.forEach(function (
|
|
1654
|
-
var key =
|
|
1730
|
+
declaredDependencies.forEach(function (_ref7) {
|
|
1731
|
+
var key = _ref7.key;
|
|
1655
1732
|
|
|
1656
1733
|
// Does this declared dep satsify a real need?
|
|
1657
1734
|
if (satisfyingDependencies.has(key)) {
|
|
@@ -1693,14 +1770,14 @@ function collectRecommendations(_ref4) {
|
|
|
1693
1770
|
|
|
1694
1771
|
// Finds functions declared as dependencies
|
|
1695
1772
|
// that would invalidate on every render.
|
|
1696
|
-
function scanForDeclaredBareFunctions(
|
|
1697
|
-
var declaredDependencies =
|
|
1698
|
-
declaredDependenciesNode =
|
|
1699
|
-
componentScope =
|
|
1700
|
-
scope =
|
|
1701
|
-
|
|
1702
|
-
var bareFunctions = declaredDependencies.map(function (
|
|
1703
|
-
var key =
|
|
1773
|
+
function scanForDeclaredBareFunctions(_ref8) {
|
|
1774
|
+
var declaredDependencies = _ref8.declaredDependencies,
|
|
1775
|
+
declaredDependenciesNode = _ref8.declaredDependenciesNode,
|
|
1776
|
+
componentScope = _ref8.componentScope,
|
|
1777
|
+
scope = _ref8.scope;
|
|
1778
|
+
|
|
1779
|
+
var bareFunctions = declaredDependencies.map(function (_ref9) {
|
|
1780
|
+
var key = _ref9.key;
|
|
1704
1781
|
|
|
1705
1782
|
var fnRef = componentScope.set.get(key);
|
|
1706
1783
|
if (fnRef == null) {
|
|
@@ -7,41 +7,45 @@
|
|
|
7
7
|
* LICENSE file in the root directory of this source tree.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
'use strict';function
|
|
11
|
-
function
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
!0
|
|
15
|
-
function
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
function
|
|
19
|
-
function
|
|
20
|
-
function
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
g=!0
|
|
27
|
-
|
|
28
|
-
a.report({node:
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
if(!(0<
|
|
40
|
-
c=
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
0<
|
|
44
|
-
0<
|
|
45
|
-
|
|
46
|
-
"
|
|
47
|
-
|
|
10
|
+
'use strict';function R(a){return"Identifier"===a.type?/^use[A-Z0-9].*$/.test(a.name):"MemberExpression"===a.type&&!a.computed&&R(a.property)?(a=a.object,"Identifier"===a.type&&"React"===a.name):!1}function aa(a){return"Identifier"===a.type?!/^[a-z]/.test(a.name):!1}function ba(a){for(;a;){var b=ca(a);if(b&&(aa(b)||R(b)))return!0;a=a.parent}return!1}
|
|
11
|
+
function ca(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:
|
|
12
|
+
a.parent.key}
|
|
13
|
+
function V(a){function b(){return{isRequired:!1,isSatisfiedRecursively:!1,hasRequiredNodesBelow:!1,children:new Map}}function e(a,h){h=h.split(".");var l=!0,c=!1,e=void 0;try{for(var f=h[Symbol.iterator](),p;!(l=(p=f.next()).done);l=!0){var u=p.value,g=a.children.get(u);g||(g=b(),a.children.set(u,g));a=g}}catch(E){c=!0,e=E}finally{try{!l&&f.return&&f.return()}finally{if(c)throw e;}}return a}function f(a,h,c){h=h.split(".");var l=!0,b=!1,f=void 0;try{for(var e=h[Symbol.iterator](),g;!(l=(g=e.next()).done);l=
|
|
14
|
+
!0){var u=a.children.get(g.value);if(!u)break;c(u);a=u}}catch(E){b=!0,f=E}finally{try{!l&&e.return&&e.return()}finally{if(b)throw f;}}}function h(a,c,b,e){a.children.forEach(function(a,l){var f=e(l);a.isSatisfiedRecursively?a.hasRequiredNodesBelow&&b.add(f):a.isRequired?c.add(f):h(a,c,b,function(a){return f+"."+a})})}var u=a.dependencies,q=a.declaredDependencies,r=a.optionalDependencies,c=a.externalDependencies,n=a.isEffect,C=b();u.forEach(function(a,h){e(C,h).isRequired=!0;f(C,h,function(a){a.hasRequiredNodesBelow=
|
|
15
|
+
!0})});q.forEach(function(a){e(C,a.key).isSatisfiedRecursively=!0});r.forEach(function(a){e(C,a).isSatisfiedRecursively=!0});a=new Set;var g=new Set;h(C,a,g,function(a){return a});var v=[],A=new Set,B=new Set;q.forEach(function(a){a=a.key;g.has(a)?-1===v.indexOf(a)?v.push(a):B.add(a):!n||a.endsWith(".current")||c.has(a)?A.add(a):-1===v.indexOf(a)&&v.push(a)});a.forEach(function(a){v.push(a)});return{suggestedDependencies:v,unnecessaryDependencies:A,duplicateDependencies:B,missingDependencies:a}}
|
|
16
|
+
function da(a){var b=a.declaredDependenciesNode,e=a.componentScope,f=a.scope;return a.declaredDependencies.map(function(a){a=e.set.get(a.key);if(null==a)return null;var b=a.defs[0];return null==b?null:"Variable"===b.type&&"VariableDeclarator"===b.node.type&&null!=b.node.init&&("ArrowFunctionExpression"===b.node.init.type||"FunctionExpression"===b.node.init.type)||"FunctionName"===b.type&&"FunctionDeclaration"===b.node.type?a:null}).filter(Boolean).map(function(a){var e=a.defs[0];a:{for(var h=!1,r=
|
|
17
|
+
0;r<a.references.length;r++){var c=a.references[r];if(c.writeExpr)if(h){a=!0;break a}else{h=!0;continue}for(var n=c.from;n!==f&&null!=n;)n=n.upper;if(n!==f&&!ea(b,c.identifier)){a=!0;break a}}a=!1}return{fn:e,suggestUseCallback:a}})}function fa(a){return"MemberExpression"!==a.parent.type||a.parent.object!==a||"current"===a.parent.property.name||a.parent.computed||null!=a.parent.parent&&"CallExpression"===a.parent.parent.type&&a.parent.parent.callee===a.parent?a:fa(a.parent)}
|
|
18
|
+
function X(a){if("Identifier"===a.type)return a.name;if("MemberExpression"!==a.type||a.computed)throw Error("Unsupported node type: "+a.type);var b=X(a.object);a=X(a.property);return b+"."+a}function ja(a){return"MemberExpression"!==a.type||"Identifier"!==a.object.type||"React"!==a.object.name||"Identifier"!==a.property.type||a.computed?a:a.property}
|
|
19
|
+
function ka(a,b){var e=ja(a);if("Identifier"!==e.type)return null;switch(e.name){case "useEffect":case "useLayoutEffect":case "useCallback":case "useMemo":return 0;case "useImperativeHandle":return 1;default:if(e===a&&b&&b.additionalHooks){a=void 0;try{a=X(e)}catch(f){if(/Unsupported node type/.test(f.message))return 0;throw f;}return b.additionalHooks.test(a)?0:-1}return-1}}
|
|
20
|
+
function Y(a,b){for(var e=[a],f=null;e.length;){f=e.shift();if("Identifier"===f.type&&f.name===b.name&&f.range[0]===b.range[0]&&f.range[1]===b.range[1])return f;if(ea(f,b)){a=!0;var h=!1,u=void 0;try{for(var q=Object.entries(f)[Symbol.iterator](),r;!(a=(r=q.next()).done);a=!0){var c=r.value,n=c[1];"parent"!==c[0]&&(la(n)?(n.parent=f,e.push(n)):Array.isArray(n)&&n.forEach(function(a){la(a)&&(a.parent=f,e.push(a))}))}}catch(C){h=!0,u=C}finally{try{!a&&q.return&&q.return()}finally{if(h)throw u;}}}}return null}
|
|
21
|
+
function ma(a){for(var b="",e=0;e<a.length;e++)b+=a[e],0===e&&2===a.length?b+=" and ":e===a.length-2&&2<a.length?b+=", and ":e<a.length-1&&(b+=", ");return b}function la(a){return"object"===typeof a&&null!==a&&!Array.isArray(a)&&"string"===typeof a.type}function ea(a,b){return a.range[0]<=b.range[0]&&a.range[1]>=b.range[1]}
|
|
22
|
+
module.exports={rules:{"rules-of-hooks":{create:function(a){var b=[],e=[];return{onCodePathSegmentStart:function(a){return e.push(a)},onCodePathSegmentEnd:function(){return e.pop()},onCodePathStart:function(){return b.push(new Map)},onCodePathEnd:function(e,h){function f(a){var b=f.cache,c=b.get(a.id);if(null===c){if(!n.has(a.id)){n.add(a.id);b=!0;c=!1;var d=void 0;try{for(var m=a.prevSegments[Symbol.iterator](),t;!(b=(t=m.next()).done);b=!0)f(t.value)}catch(K){c=!0,d=K}finally{try{!b&&m.return&&
|
|
23
|
+
m.return()}finally{if(c)throw d;}}}return 0}if(void 0!==c)return c;b.set(a.id,null);if(e.thrownSegments.includes(a))c=0;else if(0===a.prevSegments.length)c=1;else{c=0;m=!0;t=!1;var H=void 0;try{d=a.prevSegments[Symbol.iterator]();for(var k;!(m=(k=d.next()).done);m=!0)c+=f(k.value)}catch(K){t=!0,H=K}finally{try{!m&&d.return&&d.return()}finally{if(t)throw H;}}}a.reachable&&0===c?b.delete(a.id):b.set(a.id,c);return c}function q(a){var b=q.cache,c=b.get(a.id);if(null===c){if(!n.has(a.id)){n.add(a.id);
|
|
24
|
+
b=!0;c=!1;var d=void 0;try{for(var m=a.nextSegments[Symbol.iterator](),t;!(b=(t=m.next()).done);b=!0)q(t.value)}catch(K){c=!0,d=K}finally{try{!b&&m.return&&m.return()}finally{if(c)throw d;}}}return 0}if(void 0!==c)return c;b.set(a.id,null);if(e.thrownSegments.includes(a))c=0;else if(0===a.nextSegments.length)c=1;else{c=0;m=!0;t=!1;var H=void 0;try{d=a.nextSegments[Symbol.iterator]();for(var f;!(m=(f=d.next()).done);m=!0)c+=q(f.value)}catch(K){t=!0,H=K}finally{try{!m&&d.return&&d.return()}finally{if(t)throw H;
|
|
25
|
+
}}}b.set(a.id,c);return c}function r(a){var c=r.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,m=!1,t=void 0;try{for(var H=a.prevSegments[Symbol.iterator](),e;!(d=(e=H.next()).done);d=!0){var f=r(e.value);f<b&&(b=f)}}catch(na){m=!0,t=na}finally{try{!d&&H.return&&H.return()}finally{if(m)throw t;}}b+=1}c.set(a.id,b);return b}var c=b.pop();if(0!==c.size){var n=new Set;f.cache=new Map;q.cache=new Map;r.cache=
|
|
26
|
+
new Map;var C=q(e.initialSegment),g=ca(h),v=ba(h),A=g?aa(g)||R(g):!1,B=Infinity,l=!0,p=!1,G=void 0;try{for(var w=e.finalSegments[Symbol.iterator](),J;!(l=(J=w.next()).done);l=!0){var W=J.value;if(W.reachable){var Q=r(W);Q<B&&(B=Q)}}}catch(k){p=!0,G=k}finally{try{!l&&w.return&&w.return()}finally{if(p)throw G;}}l=!0;p=!1;G=void 0;try{for(var z=c[Symbol.iterator](),O;!(l=(O=z.next()).done);l=!0){var E=O.value,D=E[0],L=E[1];if(D.reachable){var I=0===D.nextSegments.length?B<=r(D):B<r(D),T=f(D)*q(D),F=
|
|
27
|
+
n.has(D.id);c=!0;w=!1;J=void 0;try{for(var x=L[Symbol.iterator](),M;!(c=(M=x.next()).done);c=!0){var y=M.value;F&&a.report({node:y,message:'React Hook "'+a.getSource(y)+'" 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(A){if(!F&&T!==C){var U='React Hook "'+a.getSource(y)+'" is called conditionally. React Hooks must be called in the exact same order in every component render.'+(I?" Did you accidentally call a React Hook after an early return?":
|
|
28
|
+
"");a.report({node:y,message:U})}}else if(!h.parent||"MethodDefinition"!==h.parent.type&&"ClassProperty"!==h.parent.type||h.parent.value!==h)if(g){var N='React Hook "'+a.getSource(y)+'" is called in '+('function "'+a.getSource(g)+'" ')+"which is neither a React function component or a custom React Hook function.";a.report({node:y,message:N})}else if("Program"!==h.type&&v){var P='React Hook "'+a.getSource(y)+'" cannot be called inside a callback. React Hooks must be called in a React function component or a custom React Hook function.';
|
|
29
|
+
a.report({node:y,message:P})}}}catch(k){w=!0,J=k}finally{try{!c&&x.return&&x.return()}finally{if(w)throw J;}}}}}catch(k){p=!0,G=k}finally{try{!l&&z.return&&z.return()}finally{if(p)throw G;}}}},CallExpression:function(a){if(R(a.callee)){var f=b[b.length-1],u=e[e.length-1],q=f.get(u);q||(q=[],f.set(u,q));q.push(a.callee)}}}}},"exhaustive-deps":{meta:{fixable:"code",schema:[{type:"object",additionalProperties:!1,properties:{additionalHooks:{type:"string"}}}]},create:function(a){function b(a,b){return function(c){if(b.has(c))return b.get(c);
|
|
30
|
+
var e=a(c);b.set(c,e);return e}}function e(c){function e(a){var d=!0,t=!1,b=void 0;try{for(var f=a.references[Symbol.iterator](),h;!(d=(h=f.next()).done);d=!0){var g=h.value;if(g.resolved&&G.has(g.resolved.scope)){var l=Y(c,g.identifier),k=fa(l),p=X(k),q;if(q=B&&"Identifier"===k.type&&"MemberExpression"===k.parent.type&&!k.parent.computed&&"Identifier"===k.parent.property.type&&"current"===k.parent.property.name){for(var n=g.from,u=!1;n.block!==c;)"function"===n.type&&(u=null!=n.block.parent&&"ReturnStatement"===
|
|
31
|
+
n.block.parent.type),n=n.upper;q=u}q&&Q.set(p,{reference:g,dependencyNode:k});var r=g.resolved.defs[0];if((null==r||null==r.node||r.node.init!==c.parent)&&"TypeParameter"!==r.type)if(z.has(p))z.get(p).references.push(g);else{var v=g.resolved,y=J(v)||W(v);z.set(p,{isStatic:y,references:[g]})}}}}catch(Z){t=!0,b=Z}finally{try{!d&&f.return&&f.return()}finally{if(t)throw b;}}d=!0;t=!1;b=void 0;try{for(var w=a.childScopes[Symbol.iterator](),x;!(d=(x=w.next()).done);d=!0)e(x.value)}catch(Z){t=!0,b=Z}finally{try{!d&&
|
|
32
|
+
w.return&&w.return()}finally{if(t)throw b;}}}function C(a,b,t,c){return 0===a.size?null:(1<a.size?"":b+" ")+t+" "+(1<a.size?"dependencies":"dependency")+": "+ma(Array.from(a).sort().map(function(a){return"'"+a+"'"}))+(". Either "+c+" "+(1<a.size?"them":"it")+" or remove the dependency array.")}if(("FunctionExpression"===c.type||"ArrowFunctionExpression"===c.type)&&"CallExpression"===c.parent.type){var g=ka(c.parent.callee,f);if(c.parent.arguments[g]===c){var v=c.parent.callee,A=ja(v).name,B=A.endsWith("Effect"),
|
|
33
|
+
l=c.parent.arguments[g+1];if(l||B){B&&c.async&&a.report({node:c,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"});var p=a.getScope(),G=
|
|
34
|
+
new Set,w=null;for(g=p.upper;g;){G.add(g);if("function"===g.type)break;g=g.upper}if(g){w=g;var J=b(function(a){if(!Array.isArray(a.defs))return!1;var d=a.defs[0];if(null==d||"VariableDeclarator"!==d.node.type)return!1;var b=d.node.init;if(null==b)return!1;var c=d.node.parent;if(null==c&&(Y(w.block,d.node.id),c=d.node.parent,null==c))return!1;if("const"===c.kind&&"Literal"===b.type&&("string"===typeof b.value||"number"===typeof b.value||null===b.value))return!0;if("CallExpression"!==b.type)return!1;
|
|
35
|
+
b=b.callee;"MemberExpression"!==b.type||"React"!==b.object.name||null==b.property||b.computed||(b=b.property);if("Identifier"!==b.type)return!1;d=d.node.id;b=b.name;if("useRef"===b&&"Identifier"===d.type)return!0;if(("useState"===b||"useReducer"===b)&&"ArrayPattern"===d.type&&2===d.elements.length&&Array.isArray(a.identifiers)){if(d.elements[1]===a.identifiers[0]){if("useState"===b)for(a=a.references,b=0;b<a.length;b++)h.set(a[b].identifier,d.elements[0]);return!0}if(d.elements[0]===a.identifiers[0]&&
|
|
36
|
+
"useState"===b)for(a=a.references,d=0;d<a.length;d++)u.add(a[d].identifier)}return!1},q),W=b(function(a){if(!Array.isArray(a.defs))return!1;a=a.defs[0];if(null==a||null==a.node||null==a.node.id)return!1;var b=a.node,d=w.childScopes;a=null;var c;for(c=0;c<d.length;c++){var e=d[c],f=e.block;if("FunctionDeclaration"===b.type&&f===b||"VariableDeclarator"===b.type&&f.parent===b){a=e;break}}if(null==a)return!1;for(c=0;c<a.through.length;c++)if(b=a.through[c],null!=b.resolved&&G.has(b.resolved.scope)&&!J(b.resolved))return!1;
|
|
37
|
+
return!0},r),Q=new Map,z=new Map;e(p);Q.forEach(function(b,c){var d=b.dependencyNode;b=b.reference.resolved.references;for(var e=!1,f=0;f<b.length;f++){var m=b[f].identifier.parent;if(null!=m&&"MemberExpression"===m.type&&!m.computed&&"Identifier"===m.property.type&&"current"===m.property.name&&"AssignmentExpression"===m.parent.type&&m.parent.left===m){e=!0;break}}e||a.report({node:d.parent.property,message:"The ref value '"+c+".current' will likely have changed by the time this effect cleanup function runs. If this ref points to a node rendered by React, copy "+
|
|
38
|
+
("'"+c+".current' to a variable inside the effect, and ")+"use that variable in the cleanup function."})});var O=new Set,E=new Set;z.forEach(function(b,c){var d=b.references;b.isStatic&&E.add(c);d.forEach(function(b){b.writeExpr&&(b=b.writeExpr,O.has(c)||(O.add(c),a.report({node:b,message:"Assignments to the '"+c+"' variable from inside React Hook "+(a.getSource(v)+" 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 "+
|
|
39
|
+
(a.getSource(v)+".")})))})});if(!(0<O.size))if(l){var D=[],L=new Set;"ArrayExpression"!==l.type?a.report({node:l,message:"React Hook "+a.getSource(v)+" 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."}):l.elements.forEach(function(b){if(null!==b)if("SpreadElement"===b.type)a.report({node:b,message:"React Hook "+a.getSource(v)+" has a spread element in its dependency array. This means we can't statically verify whether you've passed the correct dependencies."});
|
|
40
|
+
else{var c=void 0;try{c=X(b)}catch(ia){if(/Unsupported node type/.test(ia.message)){"Literal"===b.type?z.has(b.value)?a.report({node:b,message:"The "+b.raw+" literal is not a valid dependency because it never changes. "+("Did you mean to include "+b.value+" in the array instead?")}):a.report({node:b,message:"The "+b.raw+" literal is not a valid dependency because it never changes. You can safely remove it."}):a.report({node:b,message:"React Hook "+a.getSource(v)+" has a complex expression in the dependency array. Extract it to a separate variable so it can be statically checked."});
|
|
41
|
+
return}throw ia;}for(var d=b;"MemberExpression"===d.type;)d=d.object;var e=!w.through.some(function(a){return a.identifier===d});D.push({key:c,node:b});e||L.add(c)}});var I=V({dependencies:z,declaredDependencies:D,optionalDependencies:E,externalDependencies:L,isEffect:B}),T=I.suggestedDependencies;g=I.unnecessaryDependencies;var F=I.missingDependencies;I=I.duplicateDependencies;if(0===I.size+F.size+g.size)da({declaredDependencies:D,declaredDependenciesNode:l,componentScope:w,scope:p}).forEach(function(b){var c=
|
|
42
|
+
b.fn,d=b.suggestUseCallback;b="The '"+c.name.name+"' function makes the dependencies of "+(A+" Hook (at line "+l.loc.start.line+") ")+"change on every render.";b=d?b+(" To fix this, "+("wrap the '"+c.name.name+"' definition into its own useCallback() Hook.")):b+(" Move it inside the "+A+" callback. "+("Alternatively, wrap the '"+c.name.name+"' definition into its own useCallback() Hook."));a.report({node:c.node,message:b,fix:function(a){if(d&&"Variable"===c.type)return[a.insertTextBefore(c.node.init,
|
|
43
|
+
"useCallback("),a.insertTextAfter(c.node.init,")")]}})});else{!B&&0<F.size&&(T=V({dependencies:z,declaredDependencies:[],optionalDependencies:E,externalDependencies:L,isEffect:B}).suggestedDependencies);(function(){if(0===D.length)return!0;var a=D.map(function(a){return a.key}),b=a.slice().sort();return a.join(",")===b.join(",")})()&&T.sort();var x="";if(0<g.size){var M=null;Array.from(g.keys()).forEach(function(a){null===M&&a.endsWith(".current")&&(M=a)});if(null!==M)x=" Mutable values like '"+M+
|
|
44
|
+
"' aren't valid dependencies because mutating them doesn't re-render the component.";else if(0<L.size){var y=Array.from(L)[0];p.set.has(y)||(x=" Outer scope values like '"+y+"' aren't valid dependencies because mutating them doesn't re-render the component.")}}if(!x&&F.has("props")){p=z.get("props");if(null==p)return;p=p.references;if(!Array.isArray(p))return;y=!0;for(var U=0;U<p.length;U++){var N=Y(w.block,p[U].identifier);if(!N){y=!1;break}N=N.parent;if(null==N){y=!1;break}if("MemberExpression"!==
|
|
45
|
+
N.type){y=!1;break}}y&&(x=" However, 'props' will change when *any* prop changes, so the preferred fix is to destructure the 'props' object outside of "+("the "+A+" call and refer to those specific props ")+("inside "+a.getSource(v)+"."))}if(!x&&0<F.size){var P=null;F.forEach(function(a){if(!P){var b=w.set.get(a),c=z.get(a);if(c.references[0].resolved===b&&(b=b.defs[0],null!=b&&null!=b.name&&"Parameter"===b.type)){b=!1;for(var d,e=0;e<c.references.length;e++)if(d=c.references[e].identifier,null!=
|
|
46
|
+
d&&null!=d.parent&&"CallExpression"===d.parent.type&&d.parent.callee===d){b=!0;break}b&&(P=a)}}});null!==P&&(x=" If '"+P+"' changes too often, find the parent component that defines it and wrap that definition in useCallback.")}if(!x&&0<F.size){var k=null;F.forEach(function(a){if(null===k)for(var b=z.get(a).references,c,d,e=0;e<b.length;e++){c=b[e].identifier;for(d=c.parent;null!=d&&d!==w.block;){if("CallExpression"===d.type){var f=h.get(d.callee);if(null!=f){f.name===a?k={missingDep:a,setter:d.callee.name,
|
|
47
|
+
form:"updater"}:u.has(c)?k={missingDep:a,setter:d.callee.name,form:"reducer"}:(c=b[e].resolved,null!=c&&(c=c.defs[0],null!=c&&"Parameter"===c.type&&(k={missingDep:a,setter:d.callee.name,form:"inlineReducer"})));break}}d=d.parent}if(null!==k)break}});if(null!==k)switch(k.form){case "reducer":x=" You can also replace multiple useState variables with useReducer "+("if '"+k.setter+"' needs the ")+("current value of '"+k.missingDep+"'.");break;case "inlineReducer":x=" If '"+k.setter+"' needs the "+("current value of '"+
|
|
48
|
+
k.missingDep+"', ")+"you can also switch to useReducer instead of useState and "+("read '"+k.missingDep+"' in the reducer.");break;case "updater":x=" You can also do a functional update '"+k.setter+"("+k.missingDep.substring(0,1)+" => ...)' if you only need '"+k.missingDep+"'"+(" in the '"+k.setter+"' call.");break;default:throw Error("Unknown case.");}}a.report({node:l,message:"React Hook "+a.getSource(v)+" has "+(C(F,"a","missing","include")||C(g,"an","unnecessary","exclude")||C(I,"a","duplicate",
|
|
49
|
+
"omit"))+x,fix:function(a){return a.replaceText(l,"["+T.join(", ")+"]")}})}}else{var S=null;z.forEach(function(a,b){S||a.references.forEach(function(a){if(!S&&h.has(a.identifier)){for(a=a.from;"function"!==a.type;)a=a.upper;a.block===c&&(S=b)}})});if(S){var ha=V({dependencies:z,declaredDependencies:[],optionalDependencies:E,externalDependencies:new Set,isEffect:!0}).suggestedDependencies;a.report({node:c.parent.callee,message:"React Hook "+A+" contains a call to '"+S+"'. Without a list of dependencies, this can lead to an infinite chain of updates. To fix this, pass ["+
|
|
50
|
+
ha.join(", ")+("] as a second argument to the "+A+" Hook."),fix:function(a){return a.insertTextAfter(c,", ["+ha.join(", ")+"]")}})}}}}else"useMemo"!==A&&"useCallback"!==A||a.report({node:c.parent.callee,message:"React Hook "+A+" does nothing when called with only one argument. Did you forget to pass an array of dependencies?"})}}}var f={additionalHooks:a.options&&a.options[0]&&a.options[0].additionalHooks?new RegExp(a.options[0].additionalHooks):void 0},h=new WeakMap,u=new WeakSet,q=new WeakMap,r=
|
|
51
|
+
new WeakMap;return{FunctionExpression:e,ArrowFunctionExpression:e}}}}};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eslint-plugin-react-hooks",
|
|
3
3
|
"description": "ESLint rules for React Hooks",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.6.1",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
7
7
|
"url": "https://github.com/facebook/react.git",
|
|
@@ -29,6 +29,6 @@
|
|
|
29
29
|
},
|
|
30
30
|
"homepage": "https://reactjs.org/",
|
|
31
31
|
"peerDependencies": {
|
|
32
|
-
"eslint": "^3.0.0 || ^4.0.0 || ^5.0.0"
|
|
32
|
+
"eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0"
|
|
33
33
|
}
|
|
34
34
|
}
|