eslint-plugin-react-hooks 7.1.0-canary-66ae640b-20251204 → 7.1.0-canary-ec9cc003-20251208
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.
|
@@ -18033,7 +18033,8 @@ function printErrorSummary(category, message) {
|
|
|
18033
18033
|
case ErrorCategory.Syntax:
|
|
18034
18034
|
case ErrorCategory.UseMemo:
|
|
18035
18035
|
case ErrorCategory.VoidUseMemo:
|
|
18036
|
-
case ErrorCategory.MemoDependencies:
|
|
18036
|
+
case ErrorCategory.MemoDependencies:
|
|
18037
|
+
case ErrorCategory.EffectExhaustiveDependencies: {
|
|
18037
18038
|
heading = 'Error';
|
|
18038
18039
|
break;
|
|
18039
18040
|
}
|
|
@@ -18073,6 +18074,7 @@ var ErrorCategory;
|
|
|
18073
18074
|
ErrorCategory["Globals"] = "Globals";
|
|
18074
18075
|
ErrorCategory["Refs"] = "Refs";
|
|
18075
18076
|
ErrorCategory["EffectDependencies"] = "EffectDependencies";
|
|
18077
|
+
ErrorCategory["EffectExhaustiveDependencies"] = "EffectExhaustiveDependencies";
|
|
18076
18078
|
ErrorCategory["EffectSetState"] = "EffectSetState";
|
|
18077
18079
|
ErrorCategory["EffectDerivationsOfState"] = "EffectDerivationsOfState";
|
|
18078
18080
|
ErrorCategory["ErrorBoundaries"] = "ErrorBoundaries";
|
|
@@ -18139,6 +18141,15 @@ function getRuleForCategoryImpl(category) {
|
|
|
18139
18141
|
preset: LintRulePreset.Off,
|
|
18140
18142
|
};
|
|
18141
18143
|
}
|
|
18144
|
+
case ErrorCategory.EffectExhaustiveDependencies: {
|
|
18145
|
+
return {
|
|
18146
|
+
category,
|
|
18147
|
+
severity: ErrorSeverity.Error,
|
|
18148
|
+
name: 'exhaustive-effect-dependencies',
|
|
18149
|
+
description: 'Validates that effect dependencies are exhaustive and without extraneous values',
|
|
18150
|
+
preset: LintRulePreset.Off,
|
|
18151
|
+
};
|
|
18152
|
+
}
|
|
18142
18153
|
case ErrorCategory.EffectDerivationsOfState: {
|
|
18143
18154
|
return {
|
|
18144
18155
|
category,
|
|
@@ -32206,6 +32217,7 @@ const EnvironmentConfigSchema = v4.z.object({
|
|
|
32206
32217
|
enablePreserveExistingMemoizationGuarantees: v4.z.boolean().default(true),
|
|
32207
32218
|
validatePreserveExistingMemoizationGuarantees: v4.z.boolean().default(true),
|
|
32208
32219
|
validateExhaustiveMemoizationDependencies: v4.z.boolean().default(true),
|
|
32220
|
+
validateExhaustiveEffectDependencies: v4.z.boolean().default(false),
|
|
32209
32221
|
enablePreserveExistingManualUseMemo: v4.z.boolean().default(false),
|
|
32210
32222
|
enableForest: v4.z.boolean().default(false),
|
|
32211
32223
|
enableUseTypeAnnotations: v4.z.boolean().default(false),
|
|
@@ -32223,6 +32235,7 @@ const EnvironmentConfigSchema = v4.z.object({
|
|
|
32223
32235
|
validateHooksUsage: v4.z.boolean().default(true),
|
|
32224
32236
|
validateRefAccessDuringRender: v4.z.boolean().default(true),
|
|
32225
32237
|
validateNoSetStateInRender: v4.z.boolean().default(true),
|
|
32238
|
+
enableUseKeyedState: v4.z.boolean().default(false),
|
|
32226
32239
|
validateNoSetStateInEffects: v4.z.boolean().default(false),
|
|
32227
32240
|
validateNoDerivedComputationsInEffects: v4.z.boolean().default(false),
|
|
32228
32241
|
validateNoDerivedComputationsInEffects_exp: v4.z.boolean().default(false),
|
|
@@ -33138,12 +33151,6 @@ function findDisjointMutableValues(fn) {
|
|
|
33138
33151
|
for (const operand of eachInstructionOperand(instr)) {
|
|
33139
33152
|
if (isMutable(instr, operand) &&
|
|
33140
33153
|
operand.identifier.mutableRange.start > 0) {
|
|
33141
|
-
if (instr.value.kind === 'FunctionExpression' ||
|
|
33142
|
-
instr.value.kind === 'ObjectMethod') {
|
|
33143
|
-
if (operand.identifier.type.kind === 'Primitive') {
|
|
33144
|
-
continue;
|
|
33145
|
-
}
|
|
33146
|
-
}
|
|
33147
33154
|
operands.push(operand.identifier);
|
|
33148
33155
|
}
|
|
33149
33156
|
}
|
|
@@ -50098,16 +50105,35 @@ function validateNoSetStateInRenderImpl(fn, unconditionalSetStateFunctions) {
|
|
|
50098
50105
|
}));
|
|
50099
50106
|
}
|
|
50100
50107
|
else if (unconditionalBlocks.has(block.id)) {
|
|
50101
|
-
|
|
50102
|
-
|
|
50103
|
-
|
|
50104
|
-
|
|
50105
|
-
|
|
50106
|
-
|
|
50107
|
-
|
|
50108
|
-
|
|
50109
|
-
|
|
50110
|
-
|
|
50108
|
+
const enableUseKeyedState = fn.env.config.enableUseKeyedState;
|
|
50109
|
+
if (enableUseKeyedState) {
|
|
50110
|
+
errors.pushDiagnostic(CompilerDiagnostic.create({
|
|
50111
|
+
category: ErrorCategory.RenderSetState,
|
|
50112
|
+
reason: 'Cannot call setState during render',
|
|
50113
|
+
description: 'Calling setState during render may trigger an infinite loop.\n' +
|
|
50114
|
+
'* To reset state when other state/props change, use `const [state, setState] = useKeyedState(initialState, key)` to reset `state` when `key` changes.\n' +
|
|
50115
|
+
'* To derive data from other state/props, compute the derived data during render without using state',
|
|
50116
|
+
suggestions: null,
|
|
50117
|
+
}).withDetails({
|
|
50118
|
+
kind: 'error',
|
|
50119
|
+
loc: callee.loc,
|
|
50120
|
+
message: 'Found setState() in render',
|
|
50121
|
+
}));
|
|
50122
|
+
}
|
|
50123
|
+
else {
|
|
50124
|
+
errors.pushDiagnostic(CompilerDiagnostic.create({
|
|
50125
|
+
category: ErrorCategory.RenderSetState,
|
|
50126
|
+
reason: 'Cannot call setState during render',
|
|
50127
|
+
description: 'Calling setState during render may trigger an infinite loop.\n' +
|
|
50128
|
+
'* To reset state when other state/props change, store the previous value in state and update conditionally: https://react.dev/reference/react/useState#storing-information-from-previous-renders\n' +
|
|
50129
|
+
'* To derive data from other state/props, compute the derived data during render without using state',
|
|
50130
|
+
suggestions: null,
|
|
50131
|
+
}).withDetails({
|
|
50132
|
+
kind: 'error',
|
|
50133
|
+
loc: callee.loc,
|
|
50134
|
+
message: 'Found setState() in render',
|
|
50135
|
+
}));
|
|
50136
|
+
}
|
|
50111
50137
|
}
|
|
50112
50138
|
}
|
|
50113
50139
|
break;
|
|
@@ -53463,6 +53489,7 @@ function isKnownEventHandler(_tag, prop) {
|
|
|
53463
53489
|
}
|
|
53464
53490
|
|
|
53465
53491
|
function validateExhaustiveDependencies(fn) {
|
|
53492
|
+
const env = fn.env;
|
|
53466
53493
|
const reactive = collectReactiveIdentifiersHIR(fn);
|
|
53467
53494
|
const temporaries = new Map();
|
|
53468
53495
|
for (const param of fn.params) {
|
|
@@ -53487,215 +53514,259 @@ function validateExhaustiveDependencies(fn) {
|
|
|
53487
53514
|
locals.clear();
|
|
53488
53515
|
}
|
|
53489
53516
|
function onFinishMemoize(value, dependencies, locals) {
|
|
53490
|
-
var
|
|
53517
|
+
var _a;
|
|
53491
53518
|
CompilerError.simpleInvariant(startMemo != null && startMemo.manualMemoId === value.manualMemoId, {
|
|
53492
53519
|
reason: 'Found FinishMemoize without corresponding StartMemoize',
|
|
53493
53520
|
loc: value.loc,
|
|
53494
53521
|
});
|
|
53495
|
-
|
|
53496
|
-
|
|
53497
|
-
|
|
53498
|
-
|
|
53499
|
-
if (
|
|
53500
|
-
|
|
53501
|
-
}
|
|
53502
|
-
|
|
53503
|
-
|
|
53504
|
-
|
|
53505
|
-
|
|
53506
|
-
|
|
53507
|
-
|
|
53508
|
-
|
|
53509
|
-
|
|
53510
|
-
|
|
53511
|
-
|
|
53512
|
-
|
|
53513
|
-
|
|
53514
|
-
|
|
53522
|
+
if (env.config.validateExhaustiveMemoizationDependencies) {
|
|
53523
|
+
visitCandidateDependency(value.decl, temporaries, dependencies, locals);
|
|
53524
|
+
const inferred = Array.from(dependencies);
|
|
53525
|
+
const diagnostic = validateDependencies(inferred, (_a = startMemo.deps) !== null && _a !== void 0 ? _a : [], reactive, startMemo.depsLoc, ErrorCategory.MemoDependencies);
|
|
53526
|
+
if (diagnostic != null) {
|
|
53527
|
+
error.pushDiagnostic(diagnostic);
|
|
53528
|
+
}
|
|
53529
|
+
}
|
|
53530
|
+
dependencies.clear();
|
|
53531
|
+
locals.clear();
|
|
53532
|
+
startMemo = null;
|
|
53533
|
+
}
|
|
53534
|
+
collectDependencies(fn, temporaries, {
|
|
53535
|
+
onStartMemoize,
|
|
53536
|
+
onFinishMemoize,
|
|
53537
|
+
onEffect: (inferred, manual, manualMemoLoc) => {
|
|
53538
|
+
if (env.config.validateExhaustiveEffectDependencies === false) {
|
|
53539
|
+
return;
|
|
53540
|
+
}
|
|
53541
|
+
const manualDeps = [];
|
|
53542
|
+
for (const dep of manual) {
|
|
53543
|
+
if (dep.kind === 'Local') {
|
|
53544
|
+
manualDeps.push({
|
|
53545
|
+
root: {
|
|
53546
|
+
kind: 'NamedLocal',
|
|
53547
|
+
constant: false,
|
|
53548
|
+
value: {
|
|
53549
|
+
effect: Effect.Read,
|
|
53550
|
+
identifier: dep.identifier,
|
|
53551
|
+
kind: 'Identifier',
|
|
53552
|
+
loc: dep.loc,
|
|
53553
|
+
reactive: reactive.has(dep.identifier.id),
|
|
53554
|
+
},
|
|
53555
|
+
},
|
|
53556
|
+
path: dep.path,
|
|
53557
|
+
loc: dep.loc,
|
|
53558
|
+
});
|
|
53515
53559
|
}
|
|
53516
|
-
|
|
53517
|
-
|
|
53518
|
-
|
|
53519
|
-
|
|
53520
|
-
|
|
53521
|
-
|
|
53522
|
-
|
|
53523
|
-
|
|
53524
|
-
|
|
53525
|
-
return String(aProperty.property).localeCompare(String(bProperty.property));
|
|
53526
|
-
}
|
|
53560
|
+
else {
|
|
53561
|
+
manualDeps.push({
|
|
53562
|
+
root: {
|
|
53563
|
+
kind: 'Global',
|
|
53564
|
+
identifierName: dep.binding.name,
|
|
53565
|
+
},
|
|
53566
|
+
path: [],
|
|
53567
|
+
loc: GeneratedSource,
|
|
53568
|
+
});
|
|
53527
53569
|
}
|
|
53528
|
-
return 0;
|
|
53529
53570
|
}
|
|
53530
|
-
|
|
53531
|
-
|
|
53532
|
-
|
|
53533
|
-
if (aName != null && bName != null) {
|
|
53534
|
-
return aName.localeCompare(bName);
|
|
53535
|
-
}
|
|
53536
|
-
return 0;
|
|
53571
|
+
const diagnostic = validateDependencies(Array.from(inferred), manualDeps, reactive, manualMemoLoc, ErrorCategory.EffectExhaustiveDependencies);
|
|
53572
|
+
if (diagnostic != null) {
|
|
53573
|
+
error.pushDiagnostic(diagnostic);
|
|
53537
53574
|
}
|
|
53538
|
-
}
|
|
53539
|
-
|
|
53540
|
-
|
|
53541
|
-
|
|
53542
|
-
|
|
53543
|
-
|
|
53544
|
-
|
|
53545
|
-
|
|
53575
|
+
},
|
|
53576
|
+
}, false);
|
|
53577
|
+
return error.asResult();
|
|
53578
|
+
}
|
|
53579
|
+
function validateDependencies(inferred, manualDependencies, reactive, manualMemoLoc, category) {
|
|
53580
|
+
var _a, _b, _c, _d;
|
|
53581
|
+
inferred.sort((a, b) => {
|
|
53582
|
+
var _a, _b;
|
|
53583
|
+
if (a.kind === 'Global' && b.kind == 'Global') {
|
|
53584
|
+
return a.binding.name.localeCompare(b.binding.name);
|
|
53585
|
+
}
|
|
53586
|
+
else if (a.kind == 'Local' && b.kind == 'Local') {
|
|
53587
|
+
CompilerError.simpleInvariant(a.identifier.name != null &&
|
|
53588
|
+
a.identifier.name.kind === 'named' &&
|
|
53589
|
+
b.identifier.name != null &&
|
|
53590
|
+
b.identifier.name.kind === 'named', {
|
|
53591
|
+
reason: 'Expected dependencies to be named variables',
|
|
53592
|
+
loc: a.loc,
|
|
53546
53593
|
});
|
|
53547
|
-
|
|
53548
|
-
|
|
53549
|
-
|
|
53550
|
-
|
|
53551
|
-
|
|
53552
|
-
|
|
53553
|
-
|
|
53554
|
-
|
|
53555
|
-
|
|
53556
|
-
|
|
53557
|
-
|
|
53558
|
-
|
|
53559
|
-
|
|
53560
|
-
|
|
53561
|
-
|
|
53594
|
+
if (a.identifier.id !== b.identifier.id) {
|
|
53595
|
+
return a.identifier.name.value.localeCompare(b.identifier.name.value);
|
|
53596
|
+
}
|
|
53597
|
+
if (a.path.length !== b.path.length) {
|
|
53598
|
+
return a.path.length - b.path.length;
|
|
53599
|
+
}
|
|
53600
|
+
for (let i = 0; i < a.path.length; i++) {
|
|
53601
|
+
const aProperty = a.path[i];
|
|
53602
|
+
const bProperty = b.path[i];
|
|
53603
|
+
const aOptional = aProperty.optional ? 0 : 1;
|
|
53604
|
+
const bOptional = bProperty.optional ? 0 : 1;
|
|
53605
|
+
if (aOptional !== bOptional) {
|
|
53606
|
+
return aOptional - bOptional;
|
|
53607
|
+
}
|
|
53608
|
+
else if (aProperty.property !== bProperty.property) {
|
|
53609
|
+
return String(aProperty.property).localeCompare(String(bProperty.property));
|
|
53562
53610
|
}
|
|
53563
|
-
continue;
|
|
53564
53611
|
}
|
|
53565
|
-
|
|
53566
|
-
|
|
53567
|
-
|
|
53568
|
-
|
|
53569
|
-
|
|
53612
|
+
return 0;
|
|
53613
|
+
}
|
|
53614
|
+
else {
|
|
53615
|
+
const aName = a.kind === 'Global' ? a.binding.name : (_a = a.identifier.name) === null || _a === void 0 ? void 0 : _a.value;
|
|
53616
|
+
const bName = b.kind === 'Global' ? b.binding.name : (_b = b.identifier.name) === null || _b === void 0 ? void 0 : _b.value;
|
|
53617
|
+
if (aName != null && bName != null) {
|
|
53618
|
+
return aName.localeCompare(bName);
|
|
53619
|
+
}
|
|
53620
|
+
return 0;
|
|
53621
|
+
}
|
|
53622
|
+
});
|
|
53623
|
+
retainWhere(inferred, (dep, ix) => {
|
|
53624
|
+
const match = inferred.findIndex(prevDep => {
|
|
53625
|
+
return (isEqualTemporary(prevDep, dep) ||
|
|
53626
|
+
(prevDep.kind === 'Local' &&
|
|
53627
|
+
dep.kind === 'Local' &&
|
|
53628
|
+
prevDep.identifier.id === dep.identifier.id &&
|
|
53629
|
+
isSubPath(prevDep.path, dep.path)));
|
|
53630
|
+
});
|
|
53631
|
+
return match === -1 || match >= ix;
|
|
53632
|
+
});
|
|
53633
|
+
const matched = new Set();
|
|
53634
|
+
const missing = [];
|
|
53635
|
+
const extra = [];
|
|
53636
|
+
for (const inferredDependency of inferred) {
|
|
53637
|
+
if (inferredDependency.kind === 'Global') {
|
|
53570
53638
|
for (const manualDependency of manualDependencies) {
|
|
53571
|
-
if (manualDependency.root.kind === '
|
|
53572
|
-
manualDependency.root.
|
|
53573
|
-
inferredDependency.
|
|
53574
|
-
(areEqualPaths(manualDependency.path, inferredDependency.path) ||
|
|
53575
|
-
isSubPathIgnoringOptionals(manualDependency.path, inferredDependency.path))) {
|
|
53576
|
-
hasMatchingManualDependency = true;
|
|
53639
|
+
if (manualDependency.root.kind === 'Global' &&
|
|
53640
|
+
manualDependency.root.identifierName ===
|
|
53641
|
+
inferredDependency.binding.name) {
|
|
53577
53642
|
matched.add(manualDependency);
|
|
53643
|
+
extra.push(manualDependency);
|
|
53578
53644
|
}
|
|
53579
53645
|
}
|
|
53580
|
-
|
|
53581
|
-
isOptionalDependency(inferredDependency, reactive)) {
|
|
53582
|
-
continue;
|
|
53583
|
-
}
|
|
53584
|
-
missing.push(inferredDependency);
|
|
53646
|
+
continue;
|
|
53585
53647
|
}
|
|
53586
|
-
|
|
53587
|
-
|
|
53588
|
-
|
|
53589
|
-
|
|
53590
|
-
|
|
53591
|
-
|
|
53592
|
-
|
|
53593
|
-
|
|
53594
|
-
|
|
53595
|
-
|
|
53596
|
-
|
|
53648
|
+
CompilerError.simpleInvariant(inferredDependency.kind === 'Local', {
|
|
53649
|
+
reason: 'Unexpected function dependency',
|
|
53650
|
+
loc: inferredDependency.loc,
|
|
53651
|
+
});
|
|
53652
|
+
if (isEffectEventFunctionType(inferredDependency.identifier)) {
|
|
53653
|
+
continue;
|
|
53654
|
+
}
|
|
53655
|
+
let hasMatchingManualDependency = false;
|
|
53656
|
+
for (const manualDependency of manualDependencies) {
|
|
53657
|
+
if (manualDependency.root.kind === 'NamedLocal' &&
|
|
53658
|
+
manualDependency.root.value.identifier.id ===
|
|
53659
|
+
inferredDependency.identifier.id &&
|
|
53660
|
+
(areEqualPaths(manualDependency.path, inferredDependency.path) ||
|
|
53661
|
+
isSubPathIgnoringOptionals(manualDependency.path, inferredDependency.path))) {
|
|
53662
|
+
hasMatchingManualDependency = true;
|
|
53663
|
+
matched.add(manualDependency);
|
|
53597
53664
|
}
|
|
53598
|
-
|
|
53599
|
-
|
|
53600
|
-
|
|
53601
|
-
|
|
53602
|
-
|
|
53603
|
-
|
|
53604
|
-
|
|
53605
|
-
|
|
53606
|
-
|
|
53607
|
-
|
|
53608
|
-
|
|
53609
|
-
|
|
53610
|
-
|
|
53611
|
-
|
|
53665
|
+
}
|
|
53666
|
+
if (hasMatchingManualDependency ||
|
|
53667
|
+
isOptionalDependency(inferredDependency, reactive)) {
|
|
53668
|
+
continue;
|
|
53669
|
+
}
|
|
53670
|
+
missing.push(inferredDependency);
|
|
53671
|
+
}
|
|
53672
|
+
for (const dep of manualDependencies) {
|
|
53673
|
+
if (matched.has(dep)) {
|
|
53674
|
+
continue;
|
|
53675
|
+
}
|
|
53676
|
+
if (dep.root.kind === 'NamedLocal' && dep.root.constant) {
|
|
53677
|
+
CompilerError.simpleInvariant(!dep.root.value.reactive && isPrimitiveType(dep.root.value.identifier), {
|
|
53678
|
+
reason: 'Expected constant-folded dependency to be non-reactive',
|
|
53679
|
+
loc: dep.root.value.loc,
|
|
53680
|
+
});
|
|
53681
|
+
continue;
|
|
53682
|
+
}
|
|
53683
|
+
extra.push(dep);
|
|
53684
|
+
}
|
|
53685
|
+
if (missing.length !== 0 || extra.length !== 0) {
|
|
53686
|
+
let suggestion = null;
|
|
53687
|
+
if (manualMemoLoc != null && typeof manualMemoLoc !== 'symbol') {
|
|
53688
|
+
suggestion = {
|
|
53689
|
+
description: 'Update dependencies',
|
|
53690
|
+
range: [manualMemoLoc.start.index, manualMemoLoc.end.index],
|
|
53691
|
+
op: CompilerSuggestionOperation.Replace,
|
|
53692
|
+
text: `[${inferred
|
|
53693
|
+
.filter(dep => dep.kind === 'Local' &&
|
|
53694
|
+
!isOptionalDependency(dep, reactive) &&
|
|
53695
|
+
!isEffectEventFunctionType(dep.identifier))
|
|
53696
|
+
.map(printInferredDependency)
|
|
53697
|
+
.join(', ')}]`,
|
|
53698
|
+
};
|
|
53699
|
+
}
|
|
53700
|
+
const diagnostic = createDiagnostic(category, missing, extra, suggestion);
|
|
53701
|
+
for (const dep of missing) {
|
|
53702
|
+
let reactiveStableValueHint = '';
|
|
53703
|
+
if (isStableType(dep.identifier)) {
|
|
53704
|
+
reactiveStableValueHint =
|
|
53705
|
+
'. Refs, setState functions, and other "stable" values generally do not need to be added ' +
|
|
53706
|
+
'as dependencies, but this variable may change over time to point to different values';
|
|
53612
53707
|
}
|
|
53613
|
-
|
|
53614
|
-
|
|
53615
|
-
|
|
53616
|
-
|
|
53617
|
-
missing.length !== 0
|
|
53618
|
-
? 'Missing dependencies can cause a value to update less often than it should, ' +
|
|
53619
|
-
'resulting in stale UI'
|
|
53620
|
-
: null,
|
|
53621
|
-
extra.length !== 0
|
|
53622
|
-
? 'Extra dependencies can cause a value to update more often than it should, ' +
|
|
53623
|
-
'resulting in performance problems such as excessive renders or effects firing too often'
|
|
53624
|
-
: null,
|
|
53625
|
-
]
|
|
53626
|
-
.filter(Boolean)
|
|
53627
|
-
.join('. '),
|
|
53628
|
-
suggestions: suggestion != null ? [suggestion] : null,
|
|
53708
|
+
diagnostic.withDetails({
|
|
53709
|
+
kind: 'error',
|
|
53710
|
+
message: `Missing dependency \`${printInferredDependency(dep)}\`${reactiveStableValueHint}`,
|
|
53711
|
+
loc: dep.loc,
|
|
53629
53712
|
});
|
|
53630
|
-
|
|
53631
|
-
|
|
53632
|
-
|
|
53633
|
-
reactiveStableValueHint =
|
|
53634
|
-
'. Refs, setState functions, and other "stable" values generally do not need to be added ' +
|
|
53635
|
-
'as dependencies, but this variable may change over time to point to different values';
|
|
53636
|
-
}
|
|
53713
|
+
}
|
|
53714
|
+
for (const dep of extra) {
|
|
53715
|
+
if (dep.root.kind === 'Global') {
|
|
53637
53716
|
diagnostic.withDetails({
|
|
53638
53717
|
kind: 'error',
|
|
53639
|
-
message: `
|
|
53640
|
-
|
|
53718
|
+
message: `Unnecessary dependency \`${printManualMemoDependency(dep)}\`. ` +
|
|
53719
|
+
'Values declared outside of a component/hook should not be listed as ' +
|
|
53720
|
+
'dependencies as the component will not re-render if they change',
|
|
53721
|
+
loc: (_a = dep.loc) !== null && _a !== void 0 ? _a : manualMemoLoc,
|
|
53641
53722
|
});
|
|
53642
53723
|
}
|
|
53643
|
-
|
|
53644
|
-
|
|
53724
|
+
else {
|
|
53725
|
+
const root = dep.root.value;
|
|
53726
|
+
const matchingInferred = inferred.find((inferredDep) => {
|
|
53727
|
+
return (inferredDep.kind === 'Local' &&
|
|
53728
|
+
inferredDep.identifier.id === root.identifier.id &&
|
|
53729
|
+
isSubPathIgnoringOptionals(inferredDep.path, dep.path));
|
|
53730
|
+
});
|
|
53731
|
+
if (matchingInferred != null &&
|
|
53732
|
+
isEffectEventFunctionType(matchingInferred.identifier)) {
|
|
53733
|
+
diagnostic.withDetails({
|
|
53734
|
+
kind: 'error',
|
|
53735
|
+
message: `Functions returned from \`useEffectEvent\` must not be included in the dependency array. ` +
|
|
53736
|
+
`Remove \`${printManualMemoDependency(dep)}\` from the dependencies.`,
|
|
53737
|
+
loc: (_b = dep.loc) !== null && _b !== void 0 ? _b : manualMemoLoc,
|
|
53738
|
+
});
|
|
53739
|
+
}
|
|
53740
|
+
else if (matchingInferred != null &&
|
|
53741
|
+
!isOptionalDependency(matchingInferred, reactive)) {
|
|
53645
53742
|
diagnostic.withDetails({
|
|
53646
53743
|
kind: 'error',
|
|
53647
|
-
message: `
|
|
53648
|
-
|
|
53649
|
-
|
|
53650
|
-
loc: (_e = (_d = dep.loc) !== null && _d !== void 0 ? _d : startMemo.depsLoc) !== null && _e !== void 0 ? _e : value.loc,
|
|
53744
|
+
message: `Overly precise dependency \`${printManualMemoDependency(dep)}\`, ` +
|
|
53745
|
+
`use \`${printInferredDependency(matchingInferred)}\` instead`,
|
|
53746
|
+
loc: (_c = dep.loc) !== null && _c !== void 0 ? _c : manualMemoLoc,
|
|
53651
53747
|
});
|
|
53652
|
-
error.pushDiagnostic(diagnostic);
|
|
53653
53748
|
}
|
|
53654
53749
|
else {
|
|
53655
|
-
|
|
53656
|
-
|
|
53657
|
-
|
|
53658
|
-
|
|
53659
|
-
isSubPathIgnoringOptionals(inferredDep.path, dep.path));
|
|
53750
|
+
diagnostic.withDetails({
|
|
53751
|
+
kind: 'error',
|
|
53752
|
+
message: `Unnecessary dependency \`${printManualMemoDependency(dep)}\``,
|
|
53753
|
+
loc: (_d = dep.loc) !== null && _d !== void 0 ? _d : manualMemoLoc,
|
|
53660
53754
|
});
|
|
53661
|
-
if (matchingInferred != null &&
|
|
53662
|
-
!isOptionalDependency(matchingInferred, reactive)) {
|
|
53663
|
-
diagnostic.withDetails({
|
|
53664
|
-
kind: 'error',
|
|
53665
|
-
message: `Overly precise dependency \`${printManualMemoDependency(dep)}\`, ` +
|
|
53666
|
-
`use \`${printInferredDependency(matchingInferred)}\` instead`,
|
|
53667
|
-
loc: (_g = (_f = dep.loc) !== null && _f !== void 0 ? _f : startMemo.depsLoc) !== null && _g !== void 0 ? _g : value.loc,
|
|
53668
|
-
});
|
|
53669
|
-
}
|
|
53670
|
-
else {
|
|
53671
|
-
diagnostic.withDetails({
|
|
53672
|
-
kind: 'error',
|
|
53673
|
-
message: `Unnecessary dependency \`${printManualMemoDependency(dep)}\``,
|
|
53674
|
-
loc: (_j = (_h = dep.loc) !== null && _h !== void 0 ? _h : startMemo.depsLoc) !== null && _j !== void 0 ? _j : value.loc,
|
|
53675
|
-
});
|
|
53676
|
-
}
|
|
53677
53755
|
}
|
|
53678
53756
|
}
|
|
53679
|
-
if (suggestion != null) {
|
|
53680
|
-
diagnostic.withDetails({
|
|
53681
|
-
kind: 'hint',
|
|
53682
|
-
message: `Inferred dependencies: \`${suggestion.text}\``,
|
|
53683
|
-
});
|
|
53684
|
-
}
|
|
53685
|
-
error.pushDiagnostic(diagnostic);
|
|
53686
53757
|
}
|
|
53687
|
-
|
|
53688
|
-
|
|
53689
|
-
|
|
53758
|
+
if (suggestion != null) {
|
|
53759
|
+
diagnostic.withDetails({
|
|
53760
|
+
kind: 'hint',
|
|
53761
|
+
message: `Inferred dependencies: \`${suggestion.text}\``,
|
|
53762
|
+
});
|
|
53763
|
+
}
|
|
53764
|
+
return diagnostic;
|
|
53690
53765
|
}
|
|
53691
|
-
|
|
53692
|
-
onStartMemoize,
|
|
53693
|
-
onFinishMemoize,
|
|
53694
|
-
}, false);
|
|
53695
|
-
return error.asResult();
|
|
53766
|
+
return null;
|
|
53696
53767
|
}
|
|
53697
53768
|
function addDependency(dep, dependencies, locals) {
|
|
53698
|
-
if (dep.kind === '
|
|
53769
|
+
if (dep.kind === 'Aggregate') {
|
|
53699
53770
|
for (const x of dep.dependencies) {
|
|
53700
53771
|
addDependency(x, dependencies, locals);
|
|
53701
53772
|
}
|
|
@@ -53714,7 +53785,7 @@ function visitCandidateDependency(place, temporaries, dependencies, locals) {
|
|
|
53714
53785
|
}
|
|
53715
53786
|
}
|
|
53716
53787
|
function collectDependencies(fn, temporaries, callbacks, isFunctionExpression) {
|
|
53717
|
-
var _a;
|
|
53788
|
+
var _a, _b;
|
|
53718
53789
|
const optionals = findOptionalPlaces(fn);
|
|
53719
53790
|
const locals = new Set();
|
|
53720
53791
|
if (isFunctionExpression) {
|
|
@@ -53729,20 +53800,20 @@ function collectDependencies(fn, temporaries, callbacks, isFunctionExpression) {
|
|
|
53729
53800
|
}
|
|
53730
53801
|
for (const block of fn.body.blocks.values()) {
|
|
53731
53802
|
for (const phi of block.phis) {
|
|
53732
|
-
|
|
53803
|
+
const deps = [];
|
|
53733
53804
|
for (const operand of phi.operands.values()) {
|
|
53734
53805
|
const dep = temporaries.get(operand.identifier.id);
|
|
53735
53806
|
if (dep == null) {
|
|
53736
53807
|
continue;
|
|
53737
53808
|
}
|
|
53738
|
-
if (
|
|
53739
|
-
deps
|
|
53809
|
+
if (dep.kind === 'Aggregate') {
|
|
53810
|
+
deps.push(...dep.dependencies);
|
|
53740
53811
|
}
|
|
53741
53812
|
else {
|
|
53742
53813
|
deps.push(dep);
|
|
53743
53814
|
}
|
|
53744
53815
|
}
|
|
53745
|
-
if (deps
|
|
53816
|
+
if (deps.length === 0) {
|
|
53746
53817
|
continue;
|
|
53747
53818
|
}
|
|
53748
53819
|
else if (deps.length === 1) {
|
|
@@ -53750,7 +53821,7 @@ function collectDependencies(fn, temporaries, callbacks, isFunctionExpression) {
|
|
|
53750
53821
|
}
|
|
53751
53822
|
else {
|
|
53752
53823
|
temporaries.set(phi.place.identifier.id, {
|
|
53753
|
-
kind: '
|
|
53824
|
+
kind: 'Aggregate',
|
|
53754
53825
|
dependencies: new Set(deps),
|
|
53755
53826
|
});
|
|
53756
53827
|
}
|
|
@@ -53767,9 +53838,6 @@ function collectDependencies(fn, temporaries, callbacks, isFunctionExpression) {
|
|
|
53767
53838
|
}
|
|
53768
53839
|
case 'LoadContext':
|
|
53769
53840
|
case 'LoadLocal': {
|
|
53770
|
-
if (locals.has(value.place.identifier.id)) {
|
|
53771
|
-
break;
|
|
53772
|
-
}
|
|
53773
53841
|
const temp = temporaries.get(value.place.identifier.id);
|
|
53774
53842
|
if (temp != null) {
|
|
53775
53843
|
if (temp.kind === 'Local') {
|
|
@@ -53779,6 +53847,9 @@ function collectDependencies(fn, temporaries, callbacks, isFunctionExpression) {
|
|
|
53779
53847
|
else {
|
|
53780
53848
|
temporaries.set(lvalue.identifier.id, temp);
|
|
53781
53849
|
}
|
|
53850
|
+
if (locals.has(value.place.identifier.id)) {
|
|
53851
|
+
locals.add(lvalue.identifier.id);
|
|
53852
|
+
}
|
|
53782
53853
|
}
|
|
53783
53854
|
break;
|
|
53784
53855
|
}
|
|
@@ -53907,9 +53978,41 @@ function collectDependencies(fn, temporaries, callbacks, isFunctionExpression) {
|
|
|
53907
53978
|
}
|
|
53908
53979
|
break;
|
|
53909
53980
|
}
|
|
53981
|
+
case 'ArrayExpression': {
|
|
53982
|
+
const arrayDeps = new Set();
|
|
53983
|
+
for (const item of value.elements) {
|
|
53984
|
+
if (item.kind === 'Hole') {
|
|
53985
|
+
continue;
|
|
53986
|
+
}
|
|
53987
|
+
const place = item.kind === 'Identifier' ? item : item.place;
|
|
53988
|
+
visitCandidateDependency(place, temporaries, arrayDeps, new Set());
|
|
53989
|
+
visit(place);
|
|
53990
|
+
}
|
|
53991
|
+
temporaries.set(lvalue.identifier.id, {
|
|
53992
|
+
kind: 'Aggregate',
|
|
53993
|
+
dependencies: arrayDeps,
|
|
53994
|
+
loc: value.loc,
|
|
53995
|
+
});
|
|
53996
|
+
break;
|
|
53997
|
+
}
|
|
53998
|
+
case 'CallExpression':
|
|
53910
53999
|
case 'MethodCall': {
|
|
54000
|
+
const receiver = value.kind === 'CallExpression' ? value.callee : value.property;
|
|
54001
|
+
const onEffect = callbacks === null || callbacks === void 0 ? void 0 : callbacks.onEffect;
|
|
54002
|
+
if (onEffect != null && isEffectHook(receiver.identifier)) {
|
|
54003
|
+
const [fn, deps] = value.args;
|
|
54004
|
+
if ((fn === null || fn === void 0 ? void 0 : fn.kind) === 'Identifier' && (deps === null || deps === void 0 ? void 0 : deps.kind) === 'Identifier') {
|
|
54005
|
+
const fnDeps = temporaries.get(fn.identifier.id);
|
|
54006
|
+
const manualDeps = temporaries.get(deps.identifier.id);
|
|
54007
|
+
if ((fnDeps === null || fnDeps === void 0 ? void 0 : fnDeps.kind) === 'Aggregate' &&
|
|
54008
|
+
(manualDeps === null || manualDeps === void 0 ? void 0 : manualDeps.kind) === 'Aggregate') {
|
|
54009
|
+
onEffect(fnDeps.dependencies, manualDeps.dependencies, (_b = manualDeps.loc) !== null && _b !== void 0 ? _b : null);
|
|
54010
|
+
}
|
|
54011
|
+
}
|
|
54012
|
+
}
|
|
53911
54013
|
for (const operand of eachInstructionValueOperand(value)) {
|
|
53912
|
-
if (
|
|
54014
|
+
if (value.kind === 'MethodCall' &&
|
|
54015
|
+
operand.identifier.id === value.property.identifier.id) {
|
|
53913
54016
|
continue;
|
|
53914
54017
|
}
|
|
53915
54018
|
visit(operand);
|
|
@@ -53933,7 +54036,7 @@ function collectDependencies(fn, temporaries, callbacks, isFunctionExpression) {
|
|
|
53933
54036
|
visit(operand);
|
|
53934
54037
|
}
|
|
53935
54038
|
}
|
|
53936
|
-
return { kind: '
|
|
54039
|
+
return { kind: 'Aggregate', dependencies };
|
|
53937
54040
|
}
|
|
53938
54041
|
function printInferredDependency(dep) {
|
|
53939
54042
|
switch (dep.kind) {
|
|
@@ -53966,7 +54069,7 @@ function printManualMemoDependency(dep) {
|
|
|
53966
54069
|
}
|
|
53967
54070
|
function isEqualTemporary(a, b) {
|
|
53968
54071
|
switch (a.kind) {
|
|
53969
|
-
case '
|
|
54072
|
+
case 'Aggregate': {
|
|
53970
54073
|
return false;
|
|
53971
54074
|
}
|
|
53972
54075
|
case 'Global': {
|
|
@@ -54078,6 +54181,44 @@ function isOptionalDependency(inferredDependency, reactive) {
|
|
|
54078
54181
|
(isStableType(inferredDependency.identifier) ||
|
|
54079
54182
|
isPrimitiveType(inferredDependency.identifier)));
|
|
54080
54183
|
}
|
|
54184
|
+
function createDiagnostic(category, missing, extra, suggestion) {
|
|
54185
|
+
let reason;
|
|
54186
|
+
let description;
|
|
54187
|
+
function joinMissingExtraDetail(missingString, extraString, joinStr) {
|
|
54188
|
+
return [
|
|
54189
|
+
missing.length !== 0 ? missingString : null,
|
|
54190
|
+
extra.length !== 0 ? extraString : null,
|
|
54191
|
+
]
|
|
54192
|
+
.filter(Boolean)
|
|
54193
|
+
.join(joinStr);
|
|
54194
|
+
}
|
|
54195
|
+
switch (category) {
|
|
54196
|
+
case ErrorCategory.MemoDependencies: {
|
|
54197
|
+
reason = `Found ${joinMissingExtraDetail('missing', 'extra', '/')} memoization dependencies`;
|
|
54198
|
+
description = joinMissingExtraDetail('Missing dependencies can cause a value to update less often than it should, resulting in stale UI', 'Extra dependencies can cause a value to update more often than it should, resulting in performance' +
|
|
54199
|
+
' problems such as excessive renders or effects firing too often', '. ');
|
|
54200
|
+
break;
|
|
54201
|
+
}
|
|
54202
|
+
case ErrorCategory.EffectExhaustiveDependencies: {
|
|
54203
|
+
reason = `Found ${joinMissingExtraDetail('missing', 'extra', '/')} effect dependencies`;
|
|
54204
|
+
description = joinMissingExtraDetail('Missing dependencies can cause an effect to fire less often than it should', 'Extra dependencies can cause an effect to fire more often than it should, resulting' +
|
|
54205
|
+
' in performance problems such as excessive renders and side effects', '. ');
|
|
54206
|
+
break;
|
|
54207
|
+
}
|
|
54208
|
+
default: {
|
|
54209
|
+
CompilerError.simpleInvariant(false, {
|
|
54210
|
+
reason: `Unexpected error category: ${category}`,
|
|
54211
|
+
loc: GeneratedSource,
|
|
54212
|
+
});
|
|
54213
|
+
}
|
|
54214
|
+
}
|
|
54215
|
+
return CompilerDiagnostic.create({
|
|
54216
|
+
category,
|
|
54217
|
+
reason,
|
|
54218
|
+
description,
|
|
54219
|
+
suggestions: suggestion != null ? [suggestion] : null,
|
|
54220
|
+
});
|
|
54221
|
+
}
|
|
54081
54222
|
|
|
54082
54223
|
function run(func, config, fnType, mode, programContext, logger, filename, code) {
|
|
54083
54224
|
var _a, _b;
|
|
@@ -54205,7 +54346,8 @@ function runWithEnvironment(func, env) {
|
|
|
54205
54346
|
inferReactivePlaces(hir);
|
|
54206
54347
|
log({ kind: 'hir', name: 'InferReactivePlaces', value: hir });
|
|
54207
54348
|
if (env.enableValidations) {
|
|
54208
|
-
if (env.config.validateExhaustiveMemoizationDependencies
|
|
54349
|
+
if (env.config.validateExhaustiveMemoizationDependencies ||
|
|
54350
|
+
env.config.validateExhaustiveEffectDependencies) {
|
|
54209
54351
|
validateExhaustiveDependencies(hir).unwrap();
|
|
54210
54352
|
}
|
|
54211
54353
|
}
|