oxlint-plugin-react-doctor 0.7.4-dev.11333b9 → 0.7.4-dev.118f806
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/dist/index.d.ts +0 -138
- package/dist/index.js +361 -1411
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -9191,24 +9191,6 @@ const fileContainsReleaseForUsage = (usage, context) => {
|
|
|
9191
9191
|
});
|
|
9192
9192
|
return didFindRelease;
|
|
9193
9193
|
};
|
|
9194
|
-
const isUseSyncExternalStoreSubscribeFunction = (functionNode, context) => {
|
|
9195
|
-
const bindingIdentifier = getFunctionBindingIdentifier(functionNode);
|
|
9196
|
-
if (!bindingIdentifier) return false;
|
|
9197
|
-
const visitedSymbolIds = /* @__PURE__ */ new Set();
|
|
9198
|
-
const isSubscribeBinding = (candidateBinding) => {
|
|
9199
|
-
const symbol = context.scopes.symbolFor(candidateBinding);
|
|
9200
|
-
if (!symbol || visitedSymbolIds.has(symbol.id) || symbol.references.length === 0) return false;
|
|
9201
|
-
visitedSymbolIds.add(symbol.id);
|
|
9202
|
-
return symbol.references.every((reference) => {
|
|
9203
|
-
const referenceRoot = findTransparentExpressionRoot(reference.identifier);
|
|
9204
|
-
const referenceParent = referenceRoot.parent;
|
|
9205
|
-
if (isNodeOfType(referenceParent, "CallExpression") && referenceParent.arguments?.[0] === referenceRoot) return isReactApiCall(referenceParent, "useSyncExternalStore", context.scopes);
|
|
9206
|
-
const aliasDeclaration = referenceParent?.parent;
|
|
9207
|
-
return Boolean(isNodeOfType(referenceParent, "VariableDeclarator") && referenceParent.init === referenceRoot && isNodeOfType(referenceParent.id, "Identifier") && isNodeOfType(aliasDeclaration, "VariableDeclaration") && aliasDeclaration.kind === "const" && isSubscribeBinding(referenceParent.id));
|
|
9208
|
-
});
|
|
9209
|
-
};
|
|
9210
|
-
return isSubscribeBinding(bindingIdentifier);
|
|
9211
|
-
};
|
|
9212
9194
|
const doesResourceResultEscape = (resourceNode, allowConciseReturnEscape) => {
|
|
9213
9195
|
let currentNode = resourceNode;
|
|
9214
9196
|
let parentNode = currentNode.parent;
|
|
@@ -9230,8 +9212,6 @@ const findRetainedFunctionLeak = (retainedFunction, context) => {
|
|
|
9230
9212
|
if (!body) return null;
|
|
9231
9213
|
let leak = null;
|
|
9232
9214
|
const allowConciseReturnEscape = !isInlineRetainedHandlerFunction(retainedFunction, context);
|
|
9233
|
-
const isExternalStoreSubscribeFunction = isUseSyncExternalStoreSubscribeFunction(retainedFunction, context);
|
|
9234
|
-
const hasReleaseForUsage = (usage) => isExternalStoreSubscribeFunction ? effectHasCleanupForUsage(retainedFunction, usage, context) : fileContainsReleaseForUsage(usage, context);
|
|
9235
9215
|
walkAst(body, (child) => {
|
|
9236
9216
|
if (leak !== null) return false;
|
|
9237
9217
|
if (isFunctionLike$1(child)) return false;
|
|
@@ -9246,7 +9226,7 @@ const findRetainedFunctionLeak = (retainedFunction, context) => {
|
|
|
9246
9226
|
eventKey: null,
|
|
9247
9227
|
handlerKey: null
|
|
9248
9228
|
};
|
|
9249
|
-
if (!
|
|
9229
|
+
if (!fileContainsReleaseForUsage(socketUsage, context)) {
|
|
9250
9230
|
leak = socketUsage;
|
|
9251
9231
|
return false;
|
|
9252
9232
|
}
|
|
@@ -9263,7 +9243,7 @@ const findRetainedFunctionLeak = (retainedFunction, context) => {
|
|
|
9263
9243
|
eventKey: null,
|
|
9264
9244
|
handlerKey: null
|
|
9265
9245
|
};
|
|
9266
|
-
if (!
|
|
9246
|
+
if (!fileContainsReleaseForUsage(timerUsage, context)) {
|
|
9267
9247
|
leak = timerUsage;
|
|
9268
9248
|
return false;
|
|
9269
9249
|
}
|
|
@@ -9277,7 +9257,7 @@ const findRetainedFunctionLeak = (retainedFunction, context) => {
|
|
|
9277
9257
|
handleKey: findAssignedResourceKey(child, context),
|
|
9278
9258
|
...registrationDetails
|
|
9279
9259
|
};
|
|
9280
|
-
if (!hasSelfReleasingListenerOptions(child, context) && !
|
|
9260
|
+
if (!hasSelfReleasingListenerOptions(child, context) && !fileContainsReleaseForUsage(subscriptionUsage, context)) leak = subscriptionUsage;
|
|
9281
9261
|
return false;
|
|
9282
9262
|
}
|
|
9283
9263
|
});
|
|
@@ -9968,18 +9948,13 @@ const unwrapExpression$3 = (node) => {
|
|
|
9968
9948
|
return current;
|
|
9969
9949
|
};
|
|
9970
9950
|
/**
|
|
9971
|
-
* Get the hook name from a
|
|
9972
|
-
*
|
|
9951
|
+
* Get the hook name from a call expression's callee, regardless of
|
|
9952
|
+
* whether the hook is called as `useFoo()` (Identifier) or
|
|
9953
|
+
* `React.useFoo()` (MemberExpression).
|
|
9973
9954
|
*/
|
|
9974
|
-
const getHookName = (callee
|
|
9975
|
-
|
|
9976
|
-
if (isNodeOfType(
|
|
9977
|
-
const resolvedSymbol = scopes ? resolveConstIdentifierAlias(strippedCallee, scopes) : null;
|
|
9978
|
-
const importDeclaration = resolvedSymbol?.declarationNode.parent;
|
|
9979
|
-
if (resolvedSymbol?.kind === "import" && importDeclaration && isNodeOfType(importDeclaration, "ImportDeclaration") && importDeclaration.source.value === "react") return getImportedName(resolvedSymbol.declarationNode) ?? strippedCallee.name;
|
|
9980
|
-
return strippedCallee.name;
|
|
9981
|
-
}
|
|
9982
|
-
if (isNodeOfType(strippedCallee, "MemberExpression") && !strippedCallee.computed && isNodeOfType(strippedCallee.property, "Identifier")) return strippedCallee.property.name;
|
|
9955
|
+
const getHookName = (callee) => {
|
|
9956
|
+
if (isNodeOfType(callee, "Identifier")) return callee.name;
|
|
9957
|
+
if (isNodeOfType(callee, "MemberExpression") && !callee.computed && isNodeOfType(callee.property, "Identifier")) return callee.property.name;
|
|
9983
9958
|
return null;
|
|
9984
9959
|
};
|
|
9985
9960
|
const FUNCTION_SCOPE_KINDS = new Set([
|
|
@@ -10164,7 +10139,7 @@ const STABLE_IDENTITY_WRAPPER_HOOK_NAMES = new Set([
|
|
|
10164
10139
|
* - primitive-literal local consts (the value never changes
|
|
10165
10140
|
* between renders unless the literal does)
|
|
10166
10141
|
*/
|
|
10167
|
-
const symbolHasStableHookOrigin = (symbol
|
|
10142
|
+
const symbolHasStableHookOrigin = (symbol) => {
|
|
10168
10143
|
if (symbol.references.some((reference) => reference.flag !== "read")) return false;
|
|
10169
10144
|
let declarator = symbol.declarationNode;
|
|
10170
10145
|
while (declarator && declarator.type !== "VariableDeclarator") declarator = declarator.parent ?? null;
|
|
@@ -10177,7 +10152,7 @@ const symbolHasStableHookOrigin = (symbol, scopes) => {
|
|
|
10177
10152
|
if (isNodeOfType(initializer, "TemplateLiteral") && getStaticTemplateLiteralValue(initializer) !== null) return true;
|
|
10178
10153
|
}
|
|
10179
10154
|
if (!isNodeOfType(initializer, "CallExpression")) return false;
|
|
10180
|
-
const initializerHookName = getHookName(initializer.callee
|
|
10155
|
+
const initializerHookName = getHookName(initializer.callee);
|
|
10181
10156
|
if (!initializerHookName) return false;
|
|
10182
10157
|
if (initializerHookName === "useRef") return true;
|
|
10183
10158
|
if (initializerHookName === "useEffectEvent") return true;
|
|
@@ -10212,7 +10187,7 @@ const symbolHasStableMemoizedOrigin = (symbol, scopes, visitedSymbolIds) => {
|
|
|
10212
10187
|
if (!declarator.init) return false;
|
|
10213
10188
|
const initializer = unwrapExpression$3(declarator.init);
|
|
10214
10189
|
if (!isNodeOfType(initializer, "CallExpression")) return false;
|
|
10215
|
-
const initializerHookName = getHookName(initializer.callee
|
|
10190
|
+
const initializerHookName = getHookName(initializer.callee);
|
|
10216
10191
|
if (!initializerHookName || !MEMOIZING_HOOK_NAMES.has(initializerHookName)) return false;
|
|
10217
10192
|
const depsArgument = initializer.arguments[1];
|
|
10218
10193
|
if (!depsArgument || !isAstNode(depsArgument)) return false;
|
|
@@ -10242,7 +10217,7 @@ const getObjectPropertyValue = (objectExpression, propertyName) => {
|
|
|
10242
10217
|
}
|
|
10243
10218
|
return null;
|
|
10244
10219
|
};
|
|
10245
|
-
const isStableRefContainerCapture = (symbol, depKey
|
|
10220
|
+
const isStableRefContainerCapture = (symbol, depKey) => {
|
|
10246
10221
|
if (symbol.kind !== "const") return false;
|
|
10247
10222
|
if (!depKey.startsWith(`${symbol.name}.`)) return false;
|
|
10248
10223
|
if (symbol.references.some((reference) => reference.flag !== "read")) return false;
|
|
@@ -10256,7 +10231,7 @@ const isStableRefContainerCapture = (symbol, depKey, scopes) => {
|
|
|
10256
10231
|
if (!propertyValue) return false;
|
|
10257
10232
|
currentValue = propertyValue;
|
|
10258
10233
|
}
|
|
10259
|
-
return isNodeOfType(currentValue, "CallExpression") && getHookName(currentValue.callee
|
|
10234
|
+
return isNodeOfType(currentValue, "CallExpression") && getHookName(currentValue.callee) === "useRef";
|
|
10260
10235
|
};
|
|
10261
10236
|
const symbolHasStableFunctionOrigin = (symbol, scopes, visitedSymbolIds) => {
|
|
10262
10237
|
if (visitedSymbolIds.has(symbol.id)) return true;
|
|
@@ -10274,13 +10249,7 @@ const symbolHasStableFunctionOrigin = (symbol, scopes, visitedSymbolIds) => {
|
|
|
10274
10249
|
}
|
|
10275
10250
|
return true;
|
|
10276
10251
|
};
|
|
10277
|
-
const
|
|
10278
|
-
if (symbol.kind !== "const") return false;
|
|
10279
|
-
if (symbol.references.some((reference) => reference.flag !== "read")) return false;
|
|
10280
|
-
const resolvedSymbol = resolveConstIdentifierAlias(symbol.bindingIdentifier, scopes);
|
|
10281
|
-
return resolvedSymbol !== null && resolvedSymbol !== symbol && resolvedSymbol.kind === "import";
|
|
10282
|
-
};
|
|
10283
|
-
const symbolHasStableValue = (symbol, scopes, visitedSymbolIds = /* @__PURE__ */ new Set()) => symbolHasStableHookOrigin(symbol, scopes) || symbolHasStableImportedAlias(symbol, scopes) || symbolHasStableFunctionOrigin(symbol, scopes, visitedSymbolIds) || symbolHasStableMemoizedOrigin(symbol, scopes, visitedSymbolIds);
|
|
10252
|
+
const symbolHasStableValue = (symbol, scopes, visitedSymbolIds = /* @__PURE__ */ new Set()) => symbolHasStableHookOrigin(symbol) || symbolHasStableFunctionOrigin(symbol, scopes, visitedSymbolIds) || symbolHasStableMemoizedOrigin(symbol, scopes, visitedSymbolIds);
|
|
10284
10253
|
//#endregion
|
|
10285
10254
|
//#region src/plugin/utils/symbol-has-react-use-effect-event-origin.ts
|
|
10286
10255
|
const symbolHasReactUseEffectEventOrigin = (symbol, scopes) => {
|
|
@@ -10468,18 +10437,10 @@ const collectCaptureDepKeys = (callback, scopes) => {
|
|
|
10468
10437
|
}
|
|
10469
10438
|
const depKey = computeDepKey(reference);
|
|
10470
10439
|
if (!depKey) continue;
|
|
10471
|
-
if (isStableRefContainerCapture(symbol, depKey
|
|
10440
|
+
if (isStableRefContainerCapture(symbol, depKey)) {
|
|
10472
10441
|
stableCapturedNames.add(depKey);
|
|
10473
10442
|
continue;
|
|
10474
10443
|
}
|
|
10475
|
-
if (depKey === symbol.name) {
|
|
10476
|
-
const identitySourceKeys = resolveReactiveIdentitySourceKeys(symbol, scopes);
|
|
10477
|
-
if (identitySourceKeys) {
|
|
10478
|
-
if (identitySourceKeys.size === 0) stableCapturedNames.add(depKey);
|
|
10479
|
-
for (const identitySourceKey of identitySourceKeys) keys.add(identitySourceKey);
|
|
10480
|
-
continue;
|
|
10481
|
-
}
|
|
10482
|
-
}
|
|
10483
10444
|
keys.add(depKey);
|
|
10484
10445
|
}
|
|
10485
10446
|
return {
|
|
@@ -10508,60 +10469,12 @@ const hasComputedMemberExpression = (node) => {
|
|
|
10508
10469
|
if (stripped.computed) return true;
|
|
10509
10470
|
return hasComputedMemberExpression(stripped.object);
|
|
10510
10471
|
};
|
|
10511
|
-
const mergeIdentitySourceKeys = (expressions, scopes, visitedSymbolIds) => {
|
|
10512
|
-
const identitySourceKeys = /* @__PURE__ */ new Set();
|
|
10513
|
-
for (const expression of expressions) {
|
|
10514
|
-
const expressionSourceKeys = resolveIdentitySourceKeysFromExpression(expression, scopes, visitedSymbolIds);
|
|
10515
|
-
if (!expressionSourceKeys) return null;
|
|
10516
|
-
for (const expressionSourceKey of expressionSourceKeys) identitySourceKeys.add(expressionSourceKey);
|
|
10517
|
-
}
|
|
10518
|
-
return identitySourceKeys;
|
|
10519
|
-
};
|
|
10520
|
-
const resolveIdentitySourceKeysFromExpression = (expression, scopes, visitedSymbolIds) => {
|
|
10521
|
-
const stripped = unwrapExpression$3(expression);
|
|
10522
|
-
if (isNodeOfType(stripped, "Literal") && (stripped.value === null || typeof stripped.value === "string" || typeof stripped.value === "number" || typeof stripped.value === "boolean") || isNodeOfType(stripped, "TemplateLiteral") && getStaticTemplateLiteralValue(stripped) !== null) return /* @__PURE__ */ new Set();
|
|
10523
|
-
if (isNodeOfType(stripped, "Identifier")) {
|
|
10524
|
-
const sourceSymbol = scopes.symbolFor(stripped);
|
|
10525
|
-
if (!sourceSymbol) return null;
|
|
10526
|
-
if (isOutsideAllFunctions(sourceSymbol) || symbolHasStableValue(sourceSymbol, scopes)) return /* @__PURE__ */ new Set();
|
|
10527
|
-
if (sourceSymbol.kind === "const" && sourceSymbol.initializer && isNodeOfType(sourceSymbol.declarationNode, "VariableDeclarator") && sourceSymbol.declarationNode.id === sourceSymbol.bindingIdentifier && sourceSymbol.references.every((reference) => reference.flag === "read")) {
|
|
10528
|
-
if (visitedSymbolIds.has(sourceSymbol.id)) return null;
|
|
10529
|
-
visitedSymbolIds.add(sourceSymbol.id);
|
|
10530
|
-
const sourceKeys = resolveIdentitySourceKeysFromExpression(sourceSymbol.initializer, scopes, visitedSymbolIds);
|
|
10531
|
-
visitedSymbolIds.delete(sourceSymbol.id);
|
|
10532
|
-
if (sourceKeys) return sourceKeys;
|
|
10533
|
-
}
|
|
10534
|
-
return new Set([sourceSymbol.name]);
|
|
10535
|
-
}
|
|
10536
|
-
if (isNodeOfType(stripped, "MemberExpression")) {
|
|
10537
|
-
if (hasComputedMemberExpression(stripped)) return null;
|
|
10538
|
-
const sourceKey = stringifyMemberChain(stripped);
|
|
10539
|
-
const rootIdentifier = getMemberRootIdentifier(stripped);
|
|
10540
|
-
const rootSymbol = rootIdentifier ? scopes.symbolFor(rootIdentifier) : null;
|
|
10541
|
-
if (!sourceKey || !rootSymbol) return null;
|
|
10542
|
-
if (isOutsideAllFunctions(rootSymbol)) return /* @__PURE__ */ new Set();
|
|
10543
|
-
if (isStableRefContainerCapture(rootSymbol, sourceKey, scopes)) return /* @__PURE__ */ new Set();
|
|
10544
|
-
if (symbolHasStableValue(rootSymbol, scopes)) return /* @__PURE__ */ new Set();
|
|
10545
|
-
return new Set([sourceKey]);
|
|
10546
|
-
}
|
|
10547
|
-
if (isNodeOfType(stripped, "LogicalExpression")) return mergeIdentitySourceKeys([stripped.left, stripped.right], scopes, visitedSymbolIds);
|
|
10548
|
-
if (isNodeOfType(stripped, "ConditionalExpression")) return mergeIdentitySourceKeys([
|
|
10549
|
-
stripped.test,
|
|
10550
|
-
stripped.consequent,
|
|
10551
|
-
stripped.alternate
|
|
10552
|
-
], scopes, visitedSymbolIds);
|
|
10553
|
-
return null;
|
|
10554
|
-
};
|
|
10555
|
-
const resolveReactiveIdentitySourceKeys = (symbol, scopes) => {
|
|
10556
|
-
if (symbol.kind !== "const" || !symbol.initializer || !isNodeOfType(symbol.declarationNode, "VariableDeclarator") || symbol.declarationNode.id !== symbol.bindingIdentifier || symbol.references.some((reference) => reference.flag !== "read")) return null;
|
|
10557
|
-
return resolveIdentitySourceKeysFromExpression(symbol.initializer, scopes, new Set([symbol.id]));
|
|
10558
|
-
};
|
|
10559
10472
|
const isUseCallbackResultDep = (node, scopes) => {
|
|
10560
10473
|
const rootSymbol = getRootSymbol(node, scopes);
|
|
10561
10474
|
const initializer = rootSymbol?.initializer ? unwrapExpression$3(rootSymbol.initializer) : null;
|
|
10562
|
-
return Boolean(initializer && isNodeOfType(initializer, "CallExpression") && getHookName(initializer.callee
|
|
10475
|
+
return Boolean(initializer && isNodeOfType(initializer, "CallExpression") && getHookName(initializer.callee) === "useCallback");
|
|
10563
10476
|
};
|
|
10564
|
-
const
|
|
10477
|
+
const isExtraEffectDepAllowed = (node, scopes) => {
|
|
10565
10478
|
const rootIdentifier = getMemberRootIdentifier(node);
|
|
10566
10479
|
if (!rootIdentifier) return false;
|
|
10567
10480
|
const symbol = scopes.symbolFor(rootIdentifier);
|
|
@@ -10589,17 +10502,10 @@ const isUnstableInitializer = (node) => {
|
|
|
10589
10502
|
if (isNodeOfType(stripped, "LogicalExpression")) return isUnstableInitializer(stripped.left) || isUnstableInitializer(stripped.right);
|
|
10590
10503
|
return isNodeOfType(stripped, "ObjectExpression") || isNodeOfType(stripped, "ArrayExpression") || isNodeOfType(stripped, "ClassExpression") || isNodeOfType(stripped, "ClassDeclaration") || isNodeOfType(stripped, "JSXElement") || isNodeOfType(stripped, "JSXFragment") || isNodeOfType(stripped, "AssignmentExpression") || isNodeOfType(stripped, "NewExpression");
|
|
10591
10504
|
};
|
|
10592
|
-
const isExtraDepAllowedForHook = (hookName, node, scopes) => {
|
|
10593
|
-
if (!isExtraReactiveDepAllowed(node, scopes)) return false;
|
|
10594
|
-
if (EFFECT_HOOKS_ALLOWING_EXTRA_REACTIVE_DEPS.has(hookName)) return true;
|
|
10595
|
-
if (hookName !== "useMemo") return false;
|
|
10596
|
-
const rootSymbol = getRootSymbol(node, scopes);
|
|
10597
|
-
return Boolean(rootSymbol && !symbolHasStableValue(rootSymbol, scopes) && !isUnstableInitializer(rootSymbol.initializer));
|
|
10598
|
-
};
|
|
10599
10505
|
const hasDirectIdentifierDeclarator = (symbol) => isNodeOfType(symbol.declarationNode, "VariableDeclarator") && isNodeOfType(symbol.declarationNode.id, "Identifier") || isNodeOfType(symbol.declarationNode, "ClassDeclaration");
|
|
10600
10506
|
const isFunctionValueSymbol = (symbol) => getFunctionValueNode(symbol) !== null;
|
|
10601
|
-
const isStableSetterLikeSymbol = (symbol
|
|
10602
|
-
if (!symbolHasStableHookOrigin(symbol
|
|
10507
|
+
const isStableSetterLikeSymbol = (symbol) => {
|
|
10508
|
+
if (!symbolHasStableHookOrigin(symbol)) return false;
|
|
10603
10509
|
return symbol.name.startsWith("set") || symbol.name.startsWith("dispatch") || symbol.name.startsWith("startTransition");
|
|
10604
10510
|
};
|
|
10605
10511
|
const findStableSetterReference = (node, scopes) => {
|
|
@@ -10609,7 +10515,7 @@ const findStableSetterReference = (node, scopes) => {
|
|
|
10609
10515
|
if (current !== node && (isNodeOfType(current, "FunctionDeclaration") || isNodeOfType(current, "FunctionExpression") || isNodeOfType(current, "ArrowFunctionExpression"))) return;
|
|
10610
10516
|
if (isNodeOfType(current, "Identifier")) {
|
|
10611
10517
|
const symbol = scopes.referenceFor(current)?.resolvedSymbol;
|
|
10612
|
-
if (symbol && isStableSetterLikeSymbol(symbol
|
|
10518
|
+
if (symbol && isStableSetterLikeSymbol(symbol)) {
|
|
10613
10519
|
setterName = symbol.name;
|
|
10614
10520
|
return;
|
|
10615
10521
|
}
|
|
@@ -10725,11 +10631,11 @@ const hasRefCurrentAssignmentInComponent = (refSymbol) => {
|
|
|
10725
10631
|
}
|
|
10726
10632
|
return false;
|
|
10727
10633
|
};
|
|
10728
|
-
const isSeededDataRefSymbol = (refSymbol
|
|
10634
|
+
const isSeededDataRefSymbol = (refSymbol) => {
|
|
10729
10635
|
if (!refSymbol) return false;
|
|
10730
10636
|
const initializer = refSymbol.initializer ? unwrapExpression$3(refSymbol.initializer) : null;
|
|
10731
10637
|
if (!initializer || !isNodeOfType(initializer, "CallExpression")) return false;
|
|
10732
|
-
if (getHookName(initializer.callee
|
|
10638
|
+
if (getHookName(initializer.callee) !== "useRef") return false;
|
|
10733
10639
|
const firstArgument = initializer.arguments[0];
|
|
10734
10640
|
if (!firstArgument || !isAstNode(firstArgument)) return false;
|
|
10735
10641
|
const strippedArgument = unwrapExpression$3(firstArgument);
|
|
@@ -10769,9 +10675,7 @@ const isOuterFunctionScopeDep = (node, callback, scopes) => {
|
|
|
10769
10675
|
const componentOrHookScope = componentOrHookFunction ? scopes.ownScopeFor(componentOrHookFunction) : null;
|
|
10770
10676
|
return Boolean(componentOrHookScope && !isDescendantScope(symbol.scope, componentOrHookScope));
|
|
10771
10677
|
};
|
|
10772
|
-
const hasMemberCallForRoot = (node, rootName
|
|
10773
|
-
const rootSymbol = closureCaptures(node, scopes).find((reference) => reference.resolvedSymbol?.name === rootName)?.resolvedSymbol ?? null;
|
|
10774
|
-
if (!rootSymbol) return false;
|
|
10678
|
+
const hasMemberCallForRoot = (node, rootName) => {
|
|
10775
10679
|
let didFindMemberCall = false;
|
|
10776
10680
|
const visit = (current) => {
|
|
10777
10681
|
if (didFindMemberCall) return;
|
|
@@ -10787,7 +10691,7 @@ const hasMemberCallForRoot = (node, rootName, scopes) => {
|
|
|
10787
10691
|
}
|
|
10788
10692
|
chainObject = unwrapExpression$3(chainObject.object);
|
|
10789
10693
|
}
|
|
10790
|
-
if (!doesChainPassThroughCurrent && chainObject && isNodeOfType(chainObject, "Identifier") && chainObject.name === rootName
|
|
10694
|
+
if (!doesChainPassThroughCurrent && chainObject && isNodeOfType(chainObject, "Identifier") && chainObject.name === rootName) {
|
|
10791
10695
|
didFindMemberCall = true;
|
|
10792
10696
|
return;
|
|
10793
10697
|
}
|
|
@@ -10805,11 +10709,9 @@ const hasMemberCallForRoot = (node, rootName, scopes) => {
|
|
|
10805
10709
|
visit(node);
|
|
10806
10710
|
return didFindMemberCall;
|
|
10807
10711
|
};
|
|
10808
|
-
const addAggregatePropsDependency = (captureKeys, declaredKeys, callback
|
|
10809
|
-
|
|
10810
|
-
if (
|
|
10811
|
-
if (propsCaptureKeys.every((captureKey) => [...declaredKeys].some((declaredKey) => isMatchingDepOrPrefix(declaredKey, captureKey)))) return;
|
|
10812
|
-
if (hasMemberCallForRoot(callback, "props", scopes)) captureKeys.add("props");
|
|
10712
|
+
const addAggregatePropsDependency = (captureKeys, declaredKeys, callback) => {
|
|
10713
|
+
if ([...captureKeys].filter((captureKey) => captureKey.startsWith("props.")).length < 2 || declaredKeys.has("props")) return;
|
|
10714
|
+
if (hasMemberCallForRoot(callback, "props")) captureKeys.add("props");
|
|
10813
10715
|
};
|
|
10814
10716
|
const exhaustiveDeps = defineRule({
|
|
10815
10717
|
id: "exhaustive-deps",
|
|
@@ -10864,7 +10766,7 @@ If the missing value is recreated every render, move it inside the hook or stabi
|
|
|
10864
10766
|
};
|
|
10865
10767
|
const isAutoDependenciesHook = (hookName) => settings.experimental_autoDependenciesHooks.includes(hookName);
|
|
10866
10768
|
return { CallExpression(node) {
|
|
10867
|
-
const hookName = getHookName(node.callee
|
|
10769
|
+
const hookName = getHookName(node.callee);
|
|
10868
10770
|
if (!hookName || !isHookOfInterest(hookName, node.callee)) return;
|
|
10869
10771
|
const callbackArgumentIndex = getCallbackArgumentIndex(hookName);
|
|
10870
10772
|
const depsArgumentIndex = getDepsArgumentIndex(hookName);
|
|
@@ -10921,7 +10823,7 @@ If the missing value is recreated every render, move it inside the hook or stabi
|
|
|
10921
10823
|
if (outerAssignments.length > 0) return;
|
|
10922
10824
|
const refCurrentInCleanup = findRefCurrentInCleanup(callbackToAnalyze, context.scopes);
|
|
10923
10825
|
const shouldCheckRefCleanup = EFFECT_HOOKS_ALLOWING_EXTRA_REACTIVE_DEPS.has(hookName) || Boolean(additionalHooksRegex && additionalHooksRegex.test(hookName));
|
|
10924
|
-
if (refCurrentInCleanup && shouldCheckRefCleanup && !hasRefCurrentAssignment(callbackToAnalyze, refCurrentInCleanup.refCurrentName) && !hasRefCurrentAssignmentInComponent(refCurrentInCleanup.refSymbol) && !isSeededDataRefSymbol(refCurrentInCleanup.refSymbol
|
|
10826
|
+
if (refCurrentInCleanup && shouldCheckRefCleanup && !hasRefCurrentAssignment(callbackToAnalyze, refCurrentInCleanup.refCurrentName) && !hasRefCurrentAssignmentInComponent(refCurrentInCleanup.refSymbol) && !isSeededDataRefSymbol(refCurrentInCleanup.refSymbol)) context.report({
|
|
10925
10827
|
node: callbackToAnalyze,
|
|
10926
10828
|
message: buildRefCleanupMessage(refCurrentInCleanup.refCurrentName)
|
|
10927
10829
|
});
|
|
@@ -11020,7 +10922,7 @@ If the missing value is recreated every render, move it inside the hook or stabi
|
|
|
11020
10922
|
const fullChain = stringifyMemberChain(stripped);
|
|
11021
10923
|
if (fullChain && fullChain.endsWith(".current") && isNodeOfType(stripped, "MemberExpression") && isNodeOfType(stripped.object, "Identifier")) {
|
|
11022
10924
|
const refSymbol = context.scopes.symbolFor(stripped.object);
|
|
11023
|
-
if (refSymbol && symbolHasStableHookOrigin(refSymbol
|
|
10925
|
+
if (refSymbol && symbolHasStableHookOrigin(refSymbol)) {
|
|
11024
10926
|
if (!didReportRefCurrentDep) {
|
|
11025
10927
|
context.report({
|
|
11026
10928
|
node: elementNode,
|
|
@@ -11058,7 +10960,7 @@ If the missing value is recreated every render, move it inside the hook or stabi
|
|
|
11058
10960
|
declaredKeys.add(key);
|
|
11059
10961
|
declaredKeyToReportNode.set(key, elementNode);
|
|
11060
10962
|
}
|
|
11061
|
-
addAggregatePropsDependency(captureKeys, declaredKeys, callbackToAnalyze ?? callbackArgument
|
|
10963
|
+
addAggregatePropsDependency(captureKeys, declaredKeys, callbackToAnalyze ?? callbackArgument);
|
|
11062
10964
|
const missingCaptureKeys = [];
|
|
11063
10965
|
for (const captureKey of captureKeys) {
|
|
11064
10966
|
let isCoveredByDeclared = false;
|
|
@@ -11144,7 +11046,7 @@ If the missing value is recreated every render, move it inside the hook or stabi
|
|
|
11144
11046
|
if (stableCapturedNames.has(rootName) || stableCapturedNames.has(declaredKey)) continue;
|
|
11145
11047
|
if (outerFunctionCapturedNames.has(rootName)) continue;
|
|
11146
11048
|
const reportNode = declaredKeyToReportNode.get(declaredKey) ?? depsArgument;
|
|
11147
|
-
if (
|
|
11049
|
+
if (EFFECT_HOOKS_ALLOWING_EXTRA_REACTIVE_DEPS.has(hookName) && isExtraEffectDepAllowed(reportNode, context.scopes)) continue;
|
|
11148
11050
|
if (missingCaptureKeys.length > 0 && isOuterFunctionScopeDep(reportNode, callbackToAnalyze ?? callbackArgument, context.scopes)) continue;
|
|
11149
11051
|
const rootSymbol = getRootSymbol(reportNode, context.scopes);
|
|
11150
11052
|
if (rootSymbol && missingCaptureKeys.length > 0 && isRecursiveInitializerCapture(rootSymbol, callbackToAnalyze ?? callbackArgument)) continue;
|
|
@@ -21086,7 +20988,6 @@ const findExportedFunctionBody = (programRoot, exportedName) => {
|
|
|
21086
20988
|
continue;
|
|
21087
20989
|
}
|
|
21088
20990
|
if (isNodeOfType(statement, "ExportNamedDeclaration")) {
|
|
21089
|
-
if (statement.exportKind === "type") continue;
|
|
21090
20991
|
const declaration = statement.declaration;
|
|
21091
20992
|
if (declaration && isNodeOfType(declaration, "VariableDeclaration")) {
|
|
21092
20993
|
recordVariableDeclaration(declaration);
|
|
@@ -21101,7 +21002,6 @@ const findExportedFunctionBody = (programRoot, exportedName) => {
|
|
|
21101
21002
|
}
|
|
21102
21003
|
for (const specifier of statement.specifiers ?? []) {
|
|
21103
21004
|
if (!isNodeOfType(specifier, "ExportSpecifier")) continue;
|
|
21104
|
-
if (specifier.exportKind === "type") continue;
|
|
21105
21005
|
const local = specifier.local;
|
|
21106
21006
|
const exported = specifier.exported;
|
|
21107
21007
|
if (!isNodeOfType(local, "Identifier")) continue;
|
|
@@ -21150,37 +21050,25 @@ const resolveImportedExportName = (importSpecifier) => {
|
|
|
21150
21050
|
if (isNodeOfType(importSpecifier, "ImportDefaultSpecifier")) return "default";
|
|
21151
21051
|
return null;
|
|
21152
21052
|
};
|
|
21153
|
-
const
|
|
21053
|
+
const findReExportSourcesForName = (programRoot, exportedName) => {
|
|
21154
21054
|
if (!isNodeOfType(programRoot, "Program")) return [];
|
|
21155
|
-
const
|
|
21055
|
+
const exportAllSources = [];
|
|
21156
21056
|
for (const statement of programRoot.body ?? []) {
|
|
21157
21057
|
if (isNodeOfType(statement, "ExportNamedDeclaration") && statement.source) {
|
|
21158
|
-
if (statement.exportKind === "type") continue;
|
|
21159
21058
|
const sourceValue = statement.source.value;
|
|
21160
21059
|
if (typeof sourceValue !== "string") continue;
|
|
21161
21060
|
for (const specifier of statement.specifiers ?? []) {
|
|
21162
21061
|
if (!isNodeOfType(specifier, "ExportSpecifier")) continue;
|
|
21163
|
-
if (specifier.exportKind === "type") continue;
|
|
21164
21062
|
const exported = specifier.exported;
|
|
21165
|
-
if ((isNodeOfType(exported, "Identifier") ? exported.name : isNodeOfType(exported, "Literal") && typeof exported.value === "string" ? exported.value : null)
|
|
21166
|
-
const local = specifier.local;
|
|
21167
|
-
const importedName = isNodeOfType(local, "Identifier") ? local.name : isNodeOfType(local, "Literal") && typeof local.value === "string" ? local.value : null;
|
|
21168
|
-
if (importedName) return [{
|
|
21169
|
-
importedName,
|
|
21170
|
-
source: sourceValue
|
|
21171
|
-
}];
|
|
21063
|
+
if ((isNodeOfType(exported, "Identifier") ? exported.name : isNodeOfType(exported, "Literal") && typeof exported.value === "string" ? exported.value : null) === exportedName) return [sourceValue];
|
|
21172
21064
|
}
|
|
21173
21065
|
}
|
|
21174
21066
|
if (isNodeOfType(statement, "ExportAllDeclaration") && statement.source) {
|
|
21175
|
-
if (statement.exportKind === "type" || statement.exported) continue;
|
|
21176
21067
|
const sourceValue = statement.source.value;
|
|
21177
|
-
if (typeof sourceValue === "string")
|
|
21178
|
-
importedName: exportedName,
|
|
21179
|
-
source: sourceValue
|
|
21180
|
-
});
|
|
21068
|
+
if (typeof sourceValue === "string") exportAllSources.push(sourceValue);
|
|
21181
21069
|
}
|
|
21182
21070
|
}
|
|
21183
|
-
return
|
|
21071
|
+
return exportAllSources;
|
|
21184
21072
|
};
|
|
21185
21073
|
//#endregion
|
|
21186
21074
|
//#region src/plugin/utils/attach-parent-references.ts
|
|
@@ -21269,6 +21157,139 @@ const parseSourceFile = (absoluteFilePath) => {
|
|
|
21269
21157
|
return parsedProgram;
|
|
21270
21158
|
};
|
|
21271
21159
|
//#endregion
|
|
21160
|
+
//#region src/plugin/utils/is-barrel-index-module.ts
|
|
21161
|
+
const INDEX_MODULE_FILE_PATTERN = /^index\.(?:[cm]?[jt]sx?|mjs)$/;
|
|
21162
|
+
const BINDING_IMPORT_DECLARATION_PATTERN = /^\s*import\s+(type\s+)?(?!["'])([^;]*?)\s+from\s+["']([^"']+)["']\s*;?\s*(?:(?:\/\/[^\n]*)?\s*)/gm;
|
|
21163
|
+
const BARREL_REEXPORT_DECLARATION_PATTERN = /^\s*export\s+(type\s+)?(?:\*(?:\s+as\s+([\w$]+))?|\{([\s\S]*?)\})\s+from\s+["']([^"']+)["']\s*;?\s*(?:(?:\/\/[^\n]*)?\s*)/gm;
|
|
21164
|
+
const LOCAL_EXPORT_SPECIFIER_DECLARATION_PATTERN = /^\s*export\s+(type\s+)?\{([\s\S]*?)\}\s*;?\s*(?:(?:\/\/[^\n]*)?\s*)/gm;
|
|
21165
|
+
const barrelIndexModuleInfoCache = /* @__PURE__ */ new Map();
|
|
21166
|
+
const isIndexModuleFilePath = (filePath) => INDEX_MODULE_FILE_PATTERN.test(path.basename(filePath));
|
|
21167
|
+
const createNonBarrelInfo = () => ({
|
|
21168
|
+
isBarrel: false,
|
|
21169
|
+
exportsByName: /* @__PURE__ */ new Map(),
|
|
21170
|
+
starExportSources: []
|
|
21171
|
+
});
|
|
21172
|
+
const addImportedBinding = (importedBindings, binding) => {
|
|
21173
|
+
importedBindings.set(binding.localName, {
|
|
21174
|
+
...binding,
|
|
21175
|
+
didExport: false
|
|
21176
|
+
});
|
|
21177
|
+
};
|
|
21178
|
+
const collectNamedImportBindings = (namedSpecifiersText, source, declarationIsTypeOnly, importedBindings) => {
|
|
21179
|
+
for (const specifier of parseExportSpecifiers(namedSpecifiersText, declarationIsTypeOnly)) addImportedBinding(importedBindings, {
|
|
21180
|
+
localName: specifier.exportedName,
|
|
21181
|
+
importedName: specifier.localName,
|
|
21182
|
+
source,
|
|
21183
|
+
isTypeOnly: specifier.isTypeOnly
|
|
21184
|
+
});
|
|
21185
|
+
};
|
|
21186
|
+
const collectImportBindings = (importClause, source, declarationIsTypeOnly, importedBindings) => {
|
|
21187
|
+
const trimmedImportClause = importClause.trim();
|
|
21188
|
+
const namespaceMatch = trimmedImportClause.match(/(?:^|,\s*)\*\s+as\s+([\w$]+)/);
|
|
21189
|
+
if (namespaceMatch?.[1]) addImportedBinding(importedBindings, {
|
|
21190
|
+
localName: namespaceMatch[1],
|
|
21191
|
+
importedName: "*",
|
|
21192
|
+
source,
|
|
21193
|
+
isTypeOnly: declarationIsTypeOnly
|
|
21194
|
+
});
|
|
21195
|
+
const namedImportMatch = trimmedImportClause.match(/\{([\s\S]*?)\}/);
|
|
21196
|
+
if (namedImportMatch?.[1]) collectNamedImportBindings(namedImportMatch[1], source, declarationIsTypeOnly, importedBindings);
|
|
21197
|
+
const defaultImportName = trimmedImportClause.split(",")[0]?.trim();
|
|
21198
|
+
if (defaultImportName && !defaultImportName.startsWith("{") && !defaultImportName.startsWith("*")) addImportedBinding(importedBindings, {
|
|
21199
|
+
localName: defaultImportName,
|
|
21200
|
+
importedName: "default",
|
|
21201
|
+
source,
|
|
21202
|
+
isTypeOnly: declarationIsTypeOnly
|
|
21203
|
+
});
|
|
21204
|
+
};
|
|
21205
|
+
const replaceKnownDeclarations = (sourceText, importedBindings, exportsByName, starExportSources) => {
|
|
21206
|
+
let withoutKnownDeclarations = sourceText.replace(BINDING_IMPORT_DECLARATION_PATTERN, (_match, typeKeyword, importClause, source) => {
|
|
21207
|
+
collectImportBindings(importClause, source, Boolean(typeKeyword), importedBindings);
|
|
21208
|
+
return "";
|
|
21209
|
+
});
|
|
21210
|
+
withoutKnownDeclarations = withoutKnownDeclarations.replace(BARREL_REEXPORT_DECLARATION_PATTERN, (_match, typeKeyword, namespaceExportName, specifiersText, source) => {
|
|
21211
|
+
const isTypeOnly = Boolean(typeKeyword);
|
|
21212
|
+
if (namespaceExportName) {
|
|
21213
|
+
exportsByName.set(namespaceExportName, {
|
|
21214
|
+
exportedName: namespaceExportName,
|
|
21215
|
+
importedName: "*",
|
|
21216
|
+
source,
|
|
21217
|
+
isTypeOnly
|
|
21218
|
+
});
|
|
21219
|
+
return "";
|
|
21220
|
+
}
|
|
21221
|
+
if (specifiersText) {
|
|
21222
|
+
for (const specifier of parseExportSpecifiers(specifiersText, isTypeOnly)) exportsByName.set(specifier.exportedName, {
|
|
21223
|
+
exportedName: specifier.exportedName,
|
|
21224
|
+
importedName: specifier.localName,
|
|
21225
|
+
source,
|
|
21226
|
+
isTypeOnly: specifier.isTypeOnly
|
|
21227
|
+
});
|
|
21228
|
+
return "";
|
|
21229
|
+
}
|
|
21230
|
+
starExportSources.push(source);
|
|
21231
|
+
return "";
|
|
21232
|
+
});
|
|
21233
|
+
withoutKnownDeclarations = withoutKnownDeclarations.replace(LOCAL_EXPORT_SPECIFIER_DECLARATION_PATTERN, (_match, typeKeyword, specifiersText) => {
|
|
21234
|
+
for (const specifier of parseExportSpecifiers(specifiersText, Boolean(typeKeyword))) {
|
|
21235
|
+
const importedBinding = importedBindings.get(specifier.localName);
|
|
21236
|
+
if (!importedBinding) return _match;
|
|
21237
|
+
importedBinding.didExport = true;
|
|
21238
|
+
exportsByName.set(specifier.exportedName, {
|
|
21239
|
+
exportedName: specifier.exportedName,
|
|
21240
|
+
importedName: importedBinding.importedName,
|
|
21241
|
+
source: importedBinding.source,
|
|
21242
|
+
isTypeOnly: specifier.isTypeOnly || importedBinding.isTypeOnly
|
|
21243
|
+
});
|
|
21244
|
+
}
|
|
21245
|
+
return "";
|
|
21246
|
+
});
|
|
21247
|
+
return withoutKnownDeclarations;
|
|
21248
|
+
};
|
|
21249
|
+
const hasUnexportedRuntimeImport = (importedBindings) => {
|
|
21250
|
+
for (const binding of importedBindings.values()) if (!binding.isTypeOnly && !binding.didExport) return true;
|
|
21251
|
+
return false;
|
|
21252
|
+
};
|
|
21253
|
+
const classifyBarrelModule = (sourceText) => {
|
|
21254
|
+
const strippedSource = stripJsComments(sourceText).trim();
|
|
21255
|
+
if (!strippedSource) return createNonBarrelInfo();
|
|
21256
|
+
const importedBindings = /* @__PURE__ */ new Map();
|
|
21257
|
+
const exportsByName = /* @__PURE__ */ new Map();
|
|
21258
|
+
const starExportSources = [];
|
|
21259
|
+
if (replaceKnownDeclarations(strippedSource, importedBindings, exportsByName, starExportSources).trim() || hasUnexportedRuntimeImport(importedBindings)) return createNonBarrelInfo();
|
|
21260
|
+
return {
|
|
21261
|
+
isBarrel: exportsByName.size > 0 || starExportSources.length > 0,
|
|
21262
|
+
exportsByName,
|
|
21263
|
+
starExportSources
|
|
21264
|
+
};
|
|
21265
|
+
};
|
|
21266
|
+
const getBarrelIndexModuleInfo = (filePath) => {
|
|
21267
|
+
if (!isIndexModuleFilePath(filePath)) return createNonBarrelInfo();
|
|
21268
|
+
recordContentProbe(filePath);
|
|
21269
|
+
let fileStat;
|
|
21270
|
+
try {
|
|
21271
|
+
fileStat = fs.statSync(filePath);
|
|
21272
|
+
} catch {
|
|
21273
|
+
fileStat = null;
|
|
21274
|
+
}
|
|
21275
|
+
const cachedResult = barrelIndexModuleInfoCache.get(filePath);
|
|
21276
|
+
if (cachedResult !== void 0 && fileStat !== null && cachedResult.mtimeMs === fileStat.mtimeMs && cachedResult.size === fileStat.size) return cachedResult.moduleInfo;
|
|
21277
|
+
if (fileStat === null) return createNonBarrelInfo();
|
|
21278
|
+
let moduleInfo = createNonBarrelInfo();
|
|
21279
|
+
try {
|
|
21280
|
+
moduleInfo = classifyBarrelModule(fs.readFileSync(filePath, "utf8"));
|
|
21281
|
+
} catch {
|
|
21282
|
+
moduleInfo = createNonBarrelInfo();
|
|
21283
|
+
}
|
|
21284
|
+
barrelIndexModuleInfoCache.set(filePath, {
|
|
21285
|
+
mtimeMs: fileStat.mtimeMs,
|
|
21286
|
+
size: fileStat.size,
|
|
21287
|
+
moduleInfo
|
|
21288
|
+
});
|
|
21289
|
+
return moduleInfo;
|
|
21290
|
+
};
|
|
21291
|
+
const isBarrelIndexModule = (filePath) => getBarrelIndexModuleInfo(filePath).isBarrel;
|
|
21292
|
+
//#endregion
|
|
21272
21293
|
//#region src/plugin/utils/resolve-relative-import-path.ts
|
|
21273
21294
|
const MODULE_FILE_EXTENSIONS = [
|
|
21274
21295
|
".ts",
|
|
@@ -21385,6 +21406,35 @@ const resolveModuleFileFromAbsolutePath = (importPath) => {
|
|
|
21385
21406
|
};
|
|
21386
21407
|
const resolveRelativeImportPath = (filename, source) => resolveModuleFileFromAbsolutePath(path.resolve(path.dirname(filename), source));
|
|
21387
21408
|
//#endregion
|
|
21409
|
+
//#region src/plugin/utils/resolve-barrel-export-file-path.ts
|
|
21410
|
+
const getUniqueFilePath = (filePaths) => {
|
|
21411
|
+
const uniqueFilePaths = new Set(filePaths);
|
|
21412
|
+
if (uniqueFilePaths.size !== 1) return null;
|
|
21413
|
+
const [filePath] = uniqueFilePaths;
|
|
21414
|
+
return filePath ?? null;
|
|
21415
|
+
};
|
|
21416
|
+
const resolveStarExportFilePath = (barrelFilePath, exportedName, source, visitedFilePaths) => {
|
|
21417
|
+
const resolvedTargetPath = resolveRelativeImportPath(barrelFilePath, source);
|
|
21418
|
+
if (!resolvedTargetPath) return null;
|
|
21419
|
+
const nestedTargetPath = resolveBarrelExportFilePath(resolvedTargetPath, exportedName, new Set(visitedFilePaths));
|
|
21420
|
+
if (nestedTargetPath) return nestedTargetPath;
|
|
21421
|
+
return doesModuleExportName(resolvedTargetPath, exportedName) ? resolvedTargetPath : null;
|
|
21422
|
+
};
|
|
21423
|
+
const resolveBarrelExportFilePath = (barrelFilePath, exportedName, visitedFilePaths = /* @__PURE__ */ new Set()) => {
|
|
21424
|
+
if (visitedFilePaths.has(barrelFilePath)) return null;
|
|
21425
|
+
visitedFilePaths.add(barrelFilePath);
|
|
21426
|
+
const moduleInfo = getBarrelIndexModuleInfo(barrelFilePath);
|
|
21427
|
+
if (!moduleInfo.isBarrel) return null;
|
|
21428
|
+
const target = moduleInfo.exportsByName.get(exportedName);
|
|
21429
|
+
if (target) {
|
|
21430
|
+
const resolvedTargetPath = resolveRelativeImportPath(barrelFilePath, target.source);
|
|
21431
|
+
if (!resolvedTargetPath) return null;
|
|
21432
|
+
return resolveBarrelExportFilePath(resolvedTargetPath, target.importedName, visitedFilePaths) ?? resolvedTargetPath;
|
|
21433
|
+
}
|
|
21434
|
+
if (exportedName === "default") return null;
|
|
21435
|
+
return getUniqueFilePath(moduleInfo.starExportSources.map((source) => resolveStarExportFilePath(barrelFilePath, exportedName, source, visitedFilePaths)).filter((filePath) => Boolean(filePath)));
|
|
21436
|
+
};
|
|
21437
|
+
//#endregion
|
|
21388
21438
|
//#region src/plugin/utils/resolve-tsconfig-alias.ts
|
|
21389
21439
|
const TSCONFIG_FILE_NAMES = ["tsconfig.json", "jsconfig.json"];
|
|
21390
21440
|
const isObjectRecord = (value) => typeof value === "object" && value !== null;
|
|
@@ -21563,29 +21613,25 @@ const resolveTsconfigAliasPath = (fromFilename, source) => {
|
|
|
21563
21613
|
};
|
|
21564
21614
|
//#endregion
|
|
21565
21615
|
//#region src/plugin/utils/resolve-module-path.ts
|
|
21566
|
-
const resolveModulePath = (fromFilename, source) =>
|
|
21567
|
-
if (path.isAbsolute(source)) return null;
|
|
21568
|
-
return source.startsWith(".") ? resolveRelativeImportPath(fromFilename, source) : resolveTsconfigAliasPath(fromFilename, source);
|
|
21569
|
-
};
|
|
21616
|
+
const resolveModulePath = (fromFilename, source) => resolveRelativeImportPath(fromFilename, source) ?? resolveTsconfigAliasPath(fromFilename, source);
|
|
21570
21617
|
//#endregion
|
|
21571
21618
|
//#region src/plugin/utils/resolve-cross-file-function-export.ts
|
|
21572
21619
|
const resolveFunctionExportInFile = (filePath, exportedName, visitedFilePaths) => {
|
|
21573
21620
|
if (visitedFilePaths.size >= 4) return null;
|
|
21574
21621
|
if (visitedFilePaths.has(filePath)) return null;
|
|
21575
21622
|
visitedFilePaths.add(filePath);
|
|
21576
|
-
const
|
|
21623
|
+
const actualFilePath = resolveBarrelExportFilePath(filePath, exportedName) ?? filePath;
|
|
21624
|
+
const programRoot = parseSourceFile(actualFilePath);
|
|
21577
21625
|
if (!programRoot) return null;
|
|
21578
21626
|
const exported = findExportedFunctionBody(programRoot, exportedName);
|
|
21579
21627
|
if (exported) return exported;
|
|
21580
|
-
const
|
|
21581
|
-
|
|
21582
|
-
const nextFilePath = resolveModulePath(filePath, target.source);
|
|
21628
|
+
for (const reExportSource of findReExportSourcesForName(programRoot, exportedName)) {
|
|
21629
|
+
const nextFilePath = resolveModulePath(actualFilePath, reExportSource);
|
|
21583
21630
|
if (!nextFilePath) continue;
|
|
21584
|
-
const resolved = resolveFunctionExportInFile(nextFilePath,
|
|
21585
|
-
if (resolved)
|
|
21631
|
+
const resolved = resolveFunctionExportInFile(nextFilePath, exportedName, visitedFilePaths);
|
|
21632
|
+
if (resolved) return resolved;
|
|
21586
21633
|
}
|
|
21587
|
-
|
|
21588
|
-
return resolvedCandidates.values().next().value ?? null;
|
|
21634
|
+
return null;
|
|
21589
21635
|
};
|
|
21590
21636
|
const resolveCrossFileFunctionExport = (fromFilename, source, exportedName) => {
|
|
21591
21637
|
const resolvedFilePath = resolveModulePath(fromFilename, source);
|
|
@@ -22370,26 +22416,6 @@ const isReactNamedImportReference = (ref, importedName) => Boolean(ref?.resolved
|
|
|
22370
22416
|
const importDeclaration = declarationNode.parent;
|
|
22371
22417
|
return Boolean(importDeclaration && isNodeOfType(importDeclaration, "ImportDeclaration") && isNodeOfType(importDeclaration.source, "Literal") && importDeclaration.source.value === "react");
|
|
22372
22418
|
}));
|
|
22373
|
-
const isReactNamespaceImportReference = (ref) => Boolean(ref?.resolved?.defs.some((def) => {
|
|
22374
|
-
if (def.type !== "ImportBinding") return false;
|
|
22375
|
-
const declarationNode = def.node;
|
|
22376
|
-
if (!isNodeOfType(declarationNode, "ImportNamespaceSpecifier") && !isNodeOfType(declarationNode, "ImportDefaultSpecifier")) return false;
|
|
22377
|
-
const importDeclaration = declarationNode.parent;
|
|
22378
|
-
return Boolean(importDeclaration && isNodeOfType(importDeclaration, "ImportDeclaration") && isNodeOfType(importDeclaration.source, "Literal") && importDeclaration.source.value === "react");
|
|
22379
|
-
}));
|
|
22380
|
-
const isGenuineReactHookDeclarator = (analysis, declarator, hookName) => {
|
|
22381
|
-
if (!isNodeOfType(declarator, "VariableDeclarator") || !isNodeOfType(declarator.init, "CallExpression")) return false;
|
|
22382
|
-
const callee = stripParenExpression(declarator.init.callee);
|
|
22383
|
-
if (isNodeOfType(callee, "Identifier")) {
|
|
22384
|
-
const reference = getRef(analysis, callee);
|
|
22385
|
-
if (!reference?.resolved) return callee.name === hookName;
|
|
22386
|
-
return isReactNamedImportReference(reference, hookName);
|
|
22387
|
-
}
|
|
22388
|
-
if (!isNodeOfType(callee, "MemberExpression") || callee.computed || !isNodeOfType(callee.object, "Identifier") || !isNodeOfType(callee.property, "Identifier") || callee.property.name !== hookName) return false;
|
|
22389
|
-
const namespaceReference = getRef(analysis, callee.object);
|
|
22390
|
-
if (!namespaceReference?.resolved) return callee.object.name === "React";
|
|
22391
|
-
return isReactNamespaceImportReference(namespaceReference);
|
|
22392
|
-
};
|
|
22393
22419
|
const isHookCallee$1 = (analysis, node, hookName) => {
|
|
22394
22420
|
if (!node) return false;
|
|
22395
22421
|
if (isNodeOfType(node, "Identifier")) {
|
|
@@ -22788,46 +22814,6 @@ const PURE_GLOBAL_CALLEE_NAMES = new Set([
|
|
|
22788
22814
|
"parseInt"
|
|
22789
22815
|
]);
|
|
22790
22816
|
const PURE_GLOBAL_NAMESPACE_NAMES = new Set(["JSON", "Math"]);
|
|
22791
|
-
const PURE_HELPER_NAMESPACE_MEMBER_NAMES = new Map([["JSON", new Set([
|
|
22792
|
-
"isRawJSON",
|
|
22793
|
-
"parse",
|
|
22794
|
-
"rawJSON",
|
|
22795
|
-
"stringify"
|
|
22796
|
-
])], ["Math", new Set([
|
|
22797
|
-
"abs",
|
|
22798
|
-
"acos",
|
|
22799
|
-
"acosh",
|
|
22800
|
-
"asin",
|
|
22801
|
-
"asinh",
|
|
22802
|
-
"atan",
|
|
22803
|
-
"atan2",
|
|
22804
|
-
"atanh",
|
|
22805
|
-
"cbrt",
|
|
22806
|
-
"ceil",
|
|
22807
|
-
"clz32",
|
|
22808
|
-
"cos",
|
|
22809
|
-
"cosh",
|
|
22810
|
-
"exp",
|
|
22811
|
-
"floor",
|
|
22812
|
-
"fround",
|
|
22813
|
-
"hypot",
|
|
22814
|
-
"imul",
|
|
22815
|
-
"log",
|
|
22816
|
-
"log10",
|
|
22817
|
-
"log1p",
|
|
22818
|
-
"log2",
|
|
22819
|
-
"max",
|
|
22820
|
-
"min",
|
|
22821
|
-
"pow",
|
|
22822
|
-
"round",
|
|
22823
|
-
"sign",
|
|
22824
|
-
"sin",
|
|
22825
|
-
"sinh",
|
|
22826
|
-
"sqrt",
|
|
22827
|
-
"tan",
|
|
22828
|
-
"tanh",
|
|
22829
|
-
"trunc"
|
|
22830
|
-
])]]);
|
|
22831
22817
|
const PURE_MEMBER_TRANSFORM_NAMES = new Set([
|
|
22832
22818
|
"concat",
|
|
22833
22819
|
"filter",
|
|
@@ -22874,42 +22860,6 @@ const getParameterBindingIdentity = (analysis, functionNode, parameter) => {
|
|
|
22874
22860
|
}
|
|
22875
22861
|
return parameter;
|
|
22876
22862
|
};
|
|
22877
|
-
const isAsyncOrGeneratorFunction = (functionNode) => Boolean(functionNode.async === true || functionNode.generator === true);
|
|
22878
|
-
const isModuleFunction = (functionNode) => {
|
|
22879
|
-
let ancestor = functionNode.parent;
|
|
22880
|
-
while (ancestor) {
|
|
22881
|
-
if (isFunctionLike$1(ancestor)) return false;
|
|
22882
|
-
if (isNodeOfType(ancestor, "Program")) return true;
|
|
22883
|
-
ancestor = ancestor.parent;
|
|
22884
|
-
}
|
|
22885
|
-
return false;
|
|
22886
|
-
};
|
|
22887
|
-
const getFunctionBindingNames = (functionNode) => {
|
|
22888
|
-
const names = /* @__PURE__ */ new Set();
|
|
22889
|
-
if ((isNodeOfType(functionNode, "FunctionDeclaration") || isNodeOfType(functionNode, "FunctionExpression")) && functionNode.id) names.add(functionNode.id.name);
|
|
22890
|
-
const parent = functionNode.parent;
|
|
22891
|
-
if (parent && isNodeOfType(parent, "VariableDeclarator") && isNodeOfType(parent.id, "Identifier")) names.add(parent.id.name);
|
|
22892
|
-
return names;
|
|
22893
|
-
};
|
|
22894
|
-
const collectModuleBindingNames = (functionNode) => {
|
|
22895
|
-
let program = functionNode.parent;
|
|
22896
|
-
while (program && !isNodeOfType(program, "Program")) program = program.parent;
|
|
22897
|
-
const bindingNames = /* @__PURE__ */ new Set();
|
|
22898
|
-
if (!program || !isNodeOfType(program, "Program")) return bindingNames;
|
|
22899
|
-
for (const statement of program.body ?? []) {
|
|
22900
|
-
if (isNodeOfType(statement, "ImportDeclaration")) {
|
|
22901
|
-
for (const specifier of statement.specifiers ?? []) if (isNodeOfType(specifier.local, "Identifier")) bindingNames.add(specifier.local.name);
|
|
22902
|
-
continue;
|
|
22903
|
-
}
|
|
22904
|
-
const declaration = isNodeOfType(statement, "ExportNamedDeclaration") || isNodeOfType(statement, "ExportDefaultDeclaration") ? statement.declaration : statement;
|
|
22905
|
-
if (isNodeOfType(declaration, "VariableDeclaration")) {
|
|
22906
|
-
for (const declarator of declaration.declarations ?? []) collectPatternNames(declarator.id, bindingNames);
|
|
22907
|
-
continue;
|
|
22908
|
-
}
|
|
22909
|
-
if ((isNodeOfType(declaration, "FunctionDeclaration") || isNodeOfType(declaration, "ClassDeclaration")) && isNodeOfType(declaration.id, "Identifier")) bindingNames.add(declaration.id.name);
|
|
22910
|
-
}
|
|
22911
|
-
return bindingNames;
|
|
22912
|
-
};
|
|
22913
22863
|
const buildSubstitutions = (analysis, functionNode, argumentExpressions, parentFrame) => {
|
|
22914
22864
|
const substitutions = /* @__PURE__ */ new Map();
|
|
22915
22865
|
const parameters = getFunctionParameters(functionNode);
|
|
@@ -23040,7 +22990,7 @@ const collectIntroducedBindings = (analysis, functionNode) => {
|
|
|
23040
22990
|
for (const parameter of getFunctionParameters(functionNode)) if (isNodeOfType(parameter, "Identifier")) introducedBindings.add(getParameterBindingIdentity(analysis, functionNode, parameter));
|
|
23041
22991
|
return introducedBindings;
|
|
23042
22992
|
};
|
|
23043
|
-
const collectBoundedEffectExecutionFrames = (analysis, effectNode
|
|
22993
|
+
const collectBoundedEffectExecutionFrames = (analysis, effectNode) => {
|
|
23044
22994
|
const effectFunction = getEffectFn(analysis, effectNode);
|
|
23045
22995
|
if (!effectFunction || !isFunctionLike$1(effectFunction) || effectFunction.async === true) return [];
|
|
23046
22996
|
const invokedFunctionEvidence = collectEffectInvokedFunctions(effectFunction);
|
|
@@ -23049,8 +22999,7 @@ const collectBoundedEffectExecutionFrames = (analysis, effectNode, currentFilena
|
|
|
23049
22999
|
invocation: null,
|
|
23050
23000
|
isDeferred: false,
|
|
23051
23001
|
introducedBindings: /* @__PURE__ */ new Set(),
|
|
23052
|
-
substitutions: /* @__PURE__ */ new Map()
|
|
23053
|
-
currentFilename
|
|
23002
|
+
substitutions: /* @__PURE__ */ new Map()
|
|
23054
23003
|
};
|
|
23055
23004
|
const frames = [rootFrame];
|
|
23056
23005
|
walkAst(effectFunction, (child) => {
|
|
@@ -23071,8 +23020,7 @@ const collectBoundedEffectExecutionFrames = (analysis, effectNode, currentFilena
|
|
|
23071
23020
|
invocation: child,
|
|
23072
23021
|
isDeferred,
|
|
23073
23022
|
introducedBindings,
|
|
23074
|
-
substitutions: buildSubstitutions(analysis, callable, argumentsForCallable, rootFrame)
|
|
23075
|
-
currentFilename
|
|
23023
|
+
substitutions: buildSubstitutions(analysis, callable, argumentsForCallable, rootFrame)
|
|
23076
23024
|
});
|
|
23077
23025
|
};
|
|
23078
23026
|
if (isFunctionLike$1(callee)) {
|
|
@@ -23125,174 +23073,6 @@ const isOpaqueHookCall = (callExpression) => {
|
|
|
23125
23073
|
const calleeName = getCallCalleeName$1(callExpression);
|
|
23126
23074
|
return Boolean(calleeName && /^use[A-Z0-9]/.test(calleeName) && calleeName !== "useMemo" && calleeName !== "useCallback" && calleeName !== "useEffectEvent");
|
|
23127
23075
|
};
|
|
23128
|
-
const analyzeHelperStatements = (statements, environment, usedParameterIndices, analyzeExpression) => {
|
|
23129
|
-
let canContinue = true;
|
|
23130
|
-
for (const statement of statements) {
|
|
23131
|
-
if (!canContinue) {
|
|
23132
|
-
if (!isNodeOfType(statement, "EmptyStatement")) return {
|
|
23133
|
-
canContinue: false,
|
|
23134
|
-
isValid: false
|
|
23135
|
-
};
|
|
23136
|
-
continue;
|
|
23137
|
-
}
|
|
23138
|
-
if (isNodeOfType(statement, "EmptyStatement")) continue;
|
|
23139
|
-
if (isNodeOfType(statement, "ReturnStatement")) {
|
|
23140
|
-
if (!statement.argument || !analyzeExpression(statement.argument, environment, usedParameterIndices)) return {
|
|
23141
|
-
canContinue: false,
|
|
23142
|
-
isValid: false
|
|
23143
|
-
};
|
|
23144
|
-
canContinue = false;
|
|
23145
|
-
continue;
|
|
23146
|
-
}
|
|
23147
|
-
if (isNodeOfType(statement, "BlockStatement")) {
|
|
23148
|
-
const blockSummary = analyzeHelperStatements(statement.body ?? [], environment, usedParameterIndices, analyzeExpression);
|
|
23149
|
-
if (!blockSummary.isValid) return blockSummary;
|
|
23150
|
-
canContinue = blockSummary.canContinue;
|
|
23151
|
-
continue;
|
|
23152
|
-
}
|
|
23153
|
-
if (isNodeOfType(statement, "IfStatement")) {
|
|
23154
|
-
if (!analyzeExpression(statement.test, environment, usedParameterIndices)) return {
|
|
23155
|
-
canContinue: false,
|
|
23156
|
-
isValid: false
|
|
23157
|
-
};
|
|
23158
|
-
const consequentSummary = analyzeHelperStatements([statement.consequent], environment, usedParameterIndices, analyzeExpression);
|
|
23159
|
-
if (!consequentSummary.isValid) return consequentSummary;
|
|
23160
|
-
const alternateSummary = statement.alternate ? analyzeHelperStatements([statement.alternate], environment, usedParameterIndices, analyzeExpression) : {
|
|
23161
|
-
canContinue: true,
|
|
23162
|
-
isValid: true
|
|
23163
|
-
};
|
|
23164
|
-
if (!alternateSummary.isValid) return alternateSummary;
|
|
23165
|
-
canContinue = consequentSummary.canContinue || alternateSummary.canContinue;
|
|
23166
|
-
continue;
|
|
23167
|
-
}
|
|
23168
|
-
return {
|
|
23169
|
-
canContinue: false,
|
|
23170
|
-
isValid: false
|
|
23171
|
-
};
|
|
23172
|
-
}
|
|
23173
|
-
return {
|
|
23174
|
-
canContinue,
|
|
23175
|
-
isValid: true
|
|
23176
|
-
};
|
|
23177
|
-
};
|
|
23178
|
-
const analyzeHelperExpression = (expression, environment, usedParameterIndices) => {
|
|
23179
|
-
const node = stripParenExpression(expression);
|
|
23180
|
-
if (isNodeOfType(node, "Literal") || isNodeOfType(node, "TemplateElement")) return true;
|
|
23181
|
-
if (isNodeOfType(node, "Identifier")) {
|
|
23182
|
-
const parameterIndex = environment.parameterIndices.get(node.name);
|
|
23183
|
-
if (parameterIndex !== void 0) {
|
|
23184
|
-
if (parameterIndex !== null) usedParameterIndices.add(parameterIndex);
|
|
23185
|
-
return true;
|
|
23186
|
-
}
|
|
23187
|
-
return node.name === "undefined" || node.name === "NaN" || node.name === "Infinity";
|
|
23188
|
-
}
|
|
23189
|
-
if (isNodeOfType(node, "ArrayExpression")) return (node.elements ?? []).every((element) => !element || analyzeHelperExpression(element, environment, usedParameterIndices));
|
|
23190
|
-
if (isNodeOfType(node, "ObjectExpression")) return (node.properties ?? []).every((property) => {
|
|
23191
|
-
if (isNodeOfType(property, "SpreadElement")) return analyzeHelperExpression(property.argument, environment, usedParameterIndices);
|
|
23192
|
-
if (!isNodeOfType(property, "Property") || property.kind !== "init" || property.method === true) return false;
|
|
23193
|
-
if (property.computed && !analyzeHelperExpression(property.key, environment, usedParameterIndices)) return false;
|
|
23194
|
-
return analyzeHelperExpression(property.value, environment, usedParameterIndices);
|
|
23195
|
-
});
|
|
23196
|
-
if (isNodeOfType(node, "TemplateLiteral")) return (node.expressions ?? []).every((templateExpression) => analyzeHelperExpression(templateExpression, environment, usedParameterIndices));
|
|
23197
|
-
if (isNodeOfType(node, "UnaryExpression")) return node.operator !== "delete" && analyzeHelperExpression(node.argument, environment, usedParameterIndices);
|
|
23198
|
-
if (isNodeOfType(node, "BinaryExpression") || isNodeOfType(node, "LogicalExpression")) return analyzeHelperExpression(node.left, environment, usedParameterIndices) && analyzeHelperExpression(node.right, environment, usedParameterIndices);
|
|
23199
|
-
if (isNodeOfType(node, "ConditionalExpression")) return analyzeHelperExpression(node.test, environment, usedParameterIndices) && analyzeHelperExpression(node.consequent, environment, usedParameterIndices) && analyzeHelperExpression(node.alternate, environment, usedParameterIndices);
|
|
23200
|
-
if (isNodeOfType(node, "MemberExpression")) {
|
|
23201
|
-
if (!analyzeHelperExpression(node.object, environment, usedParameterIndices)) return false;
|
|
23202
|
-
return !node.computed || analyzeHelperExpression(node.property, environment, usedParameterIndices);
|
|
23203
|
-
}
|
|
23204
|
-
if (isFunctionLike$1(node)) {
|
|
23205
|
-
if (isAsyncOrGeneratorFunction(node)) return false;
|
|
23206
|
-
const callbackParameterIndices = new Map(environment.parameterIndices);
|
|
23207
|
-
for (const parameter of getFunctionParameters(node)) {
|
|
23208
|
-
if (!isNodeOfType(parameter, "Identifier")) return false;
|
|
23209
|
-
callbackParameterIndices.set(parameter.name, null);
|
|
23210
|
-
}
|
|
23211
|
-
const callbackEnvironment = {
|
|
23212
|
-
parameterIndices: callbackParameterIndices,
|
|
23213
|
-
recursiveNames: environment.recursiveNames,
|
|
23214
|
-
shadowedGlobalNames: environment.shadowedGlobalNames
|
|
23215
|
-
};
|
|
23216
|
-
if (!isNodeOfType(node.body, "BlockStatement")) return analyzeHelperExpression(node.body, callbackEnvironment, usedParameterIndices);
|
|
23217
|
-
const callbackSummary = analyzeHelperStatements(node.body.body ?? [], callbackEnvironment, usedParameterIndices, analyzeHelperExpression);
|
|
23218
|
-
return callbackSummary.isValid && !callbackSummary.canContinue;
|
|
23219
|
-
}
|
|
23220
|
-
if (isNodeOfType(node, "CallExpression")) {
|
|
23221
|
-
const callee = stripParenExpression(node.callee);
|
|
23222
|
-
const calleeRoot = getMemberRoot(callee);
|
|
23223
|
-
const isPureGlobalCall = isNodeOfType(callee, "Identifier") && PURE_GLOBAL_CALLEE_NAMES.has(callee.name) && !environment.parameterIndices.has(callee.name) && !environment.recursiveNames.has(callee.name) && !environment.shadowedGlobalNames.has(callee.name);
|
|
23224
|
-
const namespaceName = isNodeOfType(calleeRoot, "Identifier") && !environment.parameterIndices.has(calleeRoot.name) && !environment.recursiveNames.has(calleeRoot.name) && !environment.shadowedGlobalNames.has(calleeRoot.name) ? calleeRoot.name : null;
|
|
23225
|
-
const namespaceMemberName = getStaticMemberName(callee);
|
|
23226
|
-
const isPureNamespaceCall = isNodeOfType(callee, "MemberExpression") && namespaceName !== null && namespaceMemberName !== null && PURE_HELPER_NAMESPACE_MEMBER_NAMES.get(namespaceName)?.has(namespaceMemberName) === true;
|
|
23227
|
-
const isPureMemberTransform = isNodeOfType(callee, "MemberExpression") && PURE_MEMBER_TRANSFORM_NAMES.has(getStaticMemberName(callee) ?? "");
|
|
23228
|
-
if (!isPureGlobalCall && !isPureNamespaceCall && !isPureMemberTransform) return false;
|
|
23229
|
-
if (isPureMemberTransform && isNodeOfType(callee, "MemberExpression") && !analyzeHelperExpression(callee.object, environment, usedParameterIndices)) return false;
|
|
23230
|
-
return (node.arguments ?? []).every((argument) => analyzeHelperExpression(argument, environment, usedParameterIndices));
|
|
23231
|
-
}
|
|
23232
|
-
if (isNodeOfType(node, "SpreadElement")) return analyzeHelperExpression(node.argument, environment, usedParameterIndices);
|
|
23233
|
-
return false;
|
|
23234
|
-
};
|
|
23235
|
-
const helperSummaryCache = /* @__PURE__ */ new WeakMap();
|
|
23236
|
-
const summarizeHelperReturn = (functionNode) => {
|
|
23237
|
-
if (!isFunctionLike$1(functionNode) || !isModuleFunction(functionNode)) return null;
|
|
23238
|
-
if (helperSummaryCache.has(functionNode)) return helperSummaryCache.get(functionNode) ?? null;
|
|
23239
|
-
if (isAsyncOrGeneratorFunction(functionNode)) {
|
|
23240
|
-
helperSummaryCache.set(functionNode, null);
|
|
23241
|
-
return null;
|
|
23242
|
-
}
|
|
23243
|
-
const parameterIndices = /* @__PURE__ */ new Map();
|
|
23244
|
-
const parameters = getFunctionParameters(functionNode);
|
|
23245
|
-
for (let parameterIndex = 0; parameterIndex < parameters.length; parameterIndex += 1) {
|
|
23246
|
-
const parameter = parameters[parameterIndex];
|
|
23247
|
-
if (!parameter || !isNodeOfType(parameter, "Identifier") || parameterIndices.has(parameter.name)) {
|
|
23248
|
-
helperSummaryCache.set(functionNode, null);
|
|
23249
|
-
return null;
|
|
23250
|
-
}
|
|
23251
|
-
parameterIndices.set(parameter.name, parameterIndex);
|
|
23252
|
-
}
|
|
23253
|
-
const environment = {
|
|
23254
|
-
parameterIndices,
|
|
23255
|
-
recursiveNames: getFunctionBindingNames(functionNode),
|
|
23256
|
-
shadowedGlobalNames: collectModuleBindingNames(functionNode)
|
|
23257
|
-
};
|
|
23258
|
-
const usedParameterIndices = /* @__PURE__ */ new Set();
|
|
23259
|
-
if (!isNodeOfType(functionNode.body, "BlockStatement")) {
|
|
23260
|
-
if (!analyzeHelperExpression(functionNode.body, environment, usedParameterIndices)) {
|
|
23261
|
-
helperSummaryCache.set(functionNode, null);
|
|
23262
|
-
return null;
|
|
23263
|
-
}
|
|
23264
|
-
} else {
|
|
23265
|
-
const controlFlowSummary = analyzeHelperStatements(functionNode.body.body ?? [], environment, usedParameterIndices, analyzeHelperExpression);
|
|
23266
|
-
if (!controlFlowSummary.isValid || controlFlowSummary.canContinue) {
|
|
23267
|
-
helperSummaryCache.set(functionNode, null);
|
|
23268
|
-
return null;
|
|
23269
|
-
}
|
|
23270
|
-
}
|
|
23271
|
-
const summary = { usedParameterIndices };
|
|
23272
|
-
helperSummaryCache.set(functionNode, summary);
|
|
23273
|
-
return summary;
|
|
23274
|
-
};
|
|
23275
|
-
const resolveValueHelperFunction = (analysis, callee, currentFilename) => {
|
|
23276
|
-
if (!isNodeOfType(callee, "Identifier")) return null;
|
|
23277
|
-
const reference = getRef(analysis, callee);
|
|
23278
|
-
if (!reference?.resolved) return null;
|
|
23279
|
-
const importDefinition = reference.resolved.defs.find((definition) => definition.type === "ImportBinding");
|
|
23280
|
-
if (importDefinition) {
|
|
23281
|
-
if (!currentFilename) return null;
|
|
23282
|
-
const specifier = importDefinition.node;
|
|
23283
|
-
if (!isNodeOfType(specifier, "ImportSpecifier") && !isNodeOfType(specifier, "ImportDefaultSpecifier")) return null;
|
|
23284
|
-
const importDeclaration = specifier.parent;
|
|
23285
|
-
if (!importDeclaration || !isNodeOfType(importDeclaration, "ImportDeclaration")) return null;
|
|
23286
|
-
if (importDeclaration.importKind === "type" || isNodeOfType(specifier, "ImportSpecifier") && specifier.importKind === "type") return null;
|
|
23287
|
-
const source = importDeclaration.source?.value;
|
|
23288
|
-
if (typeof source !== "string") return null;
|
|
23289
|
-
const exportedName = resolveImportedExportName(specifier);
|
|
23290
|
-
if (!exportedName) return null;
|
|
23291
|
-
return resolveCrossFileFunctionExport(currentFilename, source, exportedName);
|
|
23292
|
-
}
|
|
23293
|
-
const callable = resolveWrappedCallable(analysis, callee);
|
|
23294
|
-
return callable && isModuleFunction(callable) ? callable : null;
|
|
23295
|
-
};
|
|
23296
23076
|
const isLocallyConstructedObjectMember = (reference, memberExpression) => isNodeOfType(memberExpression, "MemberExpression") && reference.resolved?.defs.some((definition) => {
|
|
23297
23077
|
const definitionNode = definition.node;
|
|
23298
23078
|
return isNodeOfType(definitionNode, "VariableDeclarator") && Boolean(definitionNode.init) && (isNodeOfType(definitionNode.init, "ObjectExpression") || isNodeOfType(definitionNode.init, "ArrayExpression"));
|
|
@@ -23378,55 +23158,38 @@ const collectValueEvidence = (analysis, expression, frame, remainingCallFrames,
|
|
|
23378
23158
|
const calleeRoot = getMemberRoot(callee);
|
|
23379
23159
|
const isPureGlobalCall = isNodeOfType(callee, "Identifier") && PURE_GLOBAL_CALLEE_NAMES.has(callee.name) && getIdentifierBindingIdentity(analysis, callee) === null || isNodeOfType(calleeRoot, "Identifier") && PURE_GLOBAL_NAMESPACE_NAMES.has(calleeRoot.name) && getIdentifierBindingIdentity(analysis, calleeRoot) === null;
|
|
23380
23160
|
const isPureMemberTransform = isNodeOfType(callee, "MemberExpression") && PURE_MEMBER_TRANSFORM_NAMES.has(getStaticMemberName(callee) ?? "");
|
|
23381
|
-
if (
|
|
23382
|
-
|
|
23383
|
-
|
|
23384
|
-
|
|
23385
|
-
|
|
23386
|
-
|
|
23161
|
+
if (isPureMemberTransform) mergeEvidence(evidence, collectValueEvidence(analysis, callee.object, frame, remainingCallFrames, new Set(visitedBindings)));
|
|
23162
|
+
for (const argument of node.arguments ?? []) {
|
|
23163
|
+
if (isFunctionLike$1(argument)) continue;
|
|
23164
|
+
mergeEvidence(evidence, collectValueEvidence(analysis, argument, frame, remainingCallFrames, new Set(visitedBindings)));
|
|
23165
|
+
}
|
|
23166
|
+
if (isPureGlobalCall || isPureMemberTransform) return evidence;
|
|
23167
|
+
if (isNodeOfType(callee, "MemberExpression")) {
|
|
23168
|
+
evidence.hasUnknownSource = true;
|
|
23387
23169
|
return evidence;
|
|
23388
23170
|
}
|
|
23389
23171
|
if (remainingCallFrames <= 0) {
|
|
23390
23172
|
evidence.hasUnknownSource = true;
|
|
23391
23173
|
return evidence;
|
|
23392
23174
|
}
|
|
23393
|
-
const
|
|
23394
|
-
if (
|
|
23395
|
-
|
|
23396
|
-
evidence.hasUnknownSource = true;
|
|
23397
|
-
return evidence;
|
|
23398
|
-
}
|
|
23399
|
-
const localHelperFrame = {
|
|
23400
|
-
functionNode: localHelperFunction,
|
|
23401
|
-
invocation: node,
|
|
23402
|
-
isDeferred: false,
|
|
23403
|
-
introducedBindings: /* @__PURE__ */ new Set(),
|
|
23404
|
-
substitutions: buildSubstitutions(analysis, localHelperFunction, node.arguments ?? [], frame),
|
|
23405
|
-
currentFilename: frame.currentFilename
|
|
23406
|
-
};
|
|
23407
|
-
const returnedExpressions = getReturnedExpressions(localHelperFunction);
|
|
23408
|
-
if (returnedExpressions.length === 0) {
|
|
23409
|
-
evidence.hasUnknownSource = true;
|
|
23410
|
-
return evidence;
|
|
23411
|
-
}
|
|
23412
|
-
for (const returnedExpression of returnedExpressions) mergeEvidence(evidence, collectValueEvidence(analysis, returnedExpression, localHelperFrame, remainingCallFrames - 1, new Set(visitedBindings)));
|
|
23175
|
+
const callable = resolveWrappedCallable(analysis, callee);
|
|
23176
|
+
if (!callable || callable.async === true || functionInvokesItself(analysis, callable)) {
|
|
23177
|
+
evidence.hasUnknownSource = true;
|
|
23413
23178
|
return evidence;
|
|
23414
23179
|
}
|
|
23415
|
-
const
|
|
23416
|
-
|
|
23417
|
-
|
|
23180
|
+
const valueFrame = {
|
|
23181
|
+
functionNode: callable,
|
|
23182
|
+
invocation: node,
|
|
23183
|
+
isDeferred: false,
|
|
23184
|
+
introducedBindings: /* @__PURE__ */ new Set(),
|
|
23185
|
+
substitutions: buildSubstitutions(analysis, callable, node.arguments ?? [], frame)
|
|
23186
|
+
};
|
|
23187
|
+
const returnedExpressions = getReturnedExpressions(callable);
|
|
23188
|
+
if (returnedExpressions.length === 0) {
|
|
23418
23189
|
evidence.hasUnknownSource = true;
|
|
23419
23190
|
return evidence;
|
|
23420
23191
|
}
|
|
23421
|
-
const
|
|
23422
|
-
for (const parameterIndex of helperSummary.usedParameterIndices) {
|
|
23423
|
-
const argument = argumentsForHelper[parameterIndex];
|
|
23424
|
-
if (!argument) {
|
|
23425
|
-
evidence.hasUnknownSource = true;
|
|
23426
|
-
return evidence;
|
|
23427
|
-
}
|
|
23428
|
-
mergeEvidence(evidence, collectValueEvidence(analysis, argument, frame, remainingCallFrames - 1, new Set(visitedBindings)));
|
|
23429
|
-
}
|
|
23192
|
+
for (const returnedExpression of returnedExpressions) mergeEvidence(evidence, collectValueEvidence(analysis, returnedExpression, valueFrame, remainingCallFrames - 1, new Set(visitedBindings)));
|
|
23430
23193
|
return evidence;
|
|
23431
23194
|
}
|
|
23432
23195
|
if (isFunctionLike$1(node) || isNodeOfType(node, "AwaitExpression")) {
|
|
@@ -23444,20 +23207,6 @@ const collectValueEvidence = (analysis, expression, frame, remainingCallFrames,
|
|
|
23444
23207
|
}
|
|
23445
23208
|
return evidence;
|
|
23446
23209
|
};
|
|
23447
|
-
const collectRenderValueEvidence = (analysis, expression, componentFunction, currentFilename) => {
|
|
23448
|
-
const evidence = collectValueEvidence(analysis, expression, {
|
|
23449
|
-
functionNode: componentFunction,
|
|
23450
|
-
invocation: null,
|
|
23451
|
-
isDeferred: false,
|
|
23452
|
-
introducedBindings: /* @__PURE__ */ new Set(),
|
|
23453
|
-
substitutions: /* @__PURE__ */ new Map(),
|
|
23454
|
-
currentFilename
|
|
23455
|
-
}, 1);
|
|
23456
|
-
return {
|
|
23457
|
-
sourceReferences: evidence.sourceReferences,
|
|
23458
|
-
isExclusivelyRenderKnown: evidence.sourceReferences.size > 0 && !evidence.hasUnknownSource && !evidence.hasDeferredIntroducedValue && !evidence.readsExternalValue
|
|
23459
|
-
};
|
|
23460
|
-
};
|
|
23461
23210
|
const findStateSetterReference = (analysis, callExpression) => {
|
|
23462
23211
|
if (!isNodeOfType(callExpression, "CallExpression")) return null;
|
|
23463
23212
|
const callee = stripParenExpression(callExpression.callee);
|
|
@@ -23520,8 +23269,8 @@ const areInMutuallyExclusiveBranches = (leftNode, rightNode) => {
|
|
|
23520
23269
|
}
|
|
23521
23270
|
return false;
|
|
23522
23271
|
};
|
|
23523
|
-
const collectEffectStateWriteFacts = (analysis, effectNode
|
|
23524
|
-
const frames = collectBoundedEffectExecutionFrames(analysis, effectNode
|
|
23272
|
+
const collectEffectStateWriteFacts = (analysis, effectNode) => {
|
|
23273
|
+
const frames = collectBoundedEffectExecutionFrames(analysis, effectNode);
|
|
23525
23274
|
if (frames.length === 0) return [];
|
|
23526
23275
|
const effectHasCleanup = hasCleanup(analysis, effectNode);
|
|
23527
23276
|
const cleanupManagedStateDeclarators = /* @__PURE__ */ new Set();
|
|
@@ -23541,8 +23290,7 @@ const collectEffectStateWriteFacts = (analysis, effectNode, currentFilename) =>
|
|
|
23541
23290
|
invocation: callExpression,
|
|
23542
23291
|
isDeferred: frame.isDeferred,
|
|
23543
23292
|
introducedBindings: collectIntroducedBindings(analysis, writtenValue),
|
|
23544
|
-
substitutions: /* @__PURE__ */ new Map()
|
|
23545
|
-
currentFilename
|
|
23293
|
+
substitutions: /* @__PURE__ */ new Map()
|
|
23546
23294
|
};
|
|
23547
23295
|
valueEvidence = emptyEvidence();
|
|
23548
23296
|
const returnedExpressions = getReturnedExpressions(writtenValue);
|
|
@@ -23591,7 +23339,7 @@ const noAdjustStateOnPropChange = defineRule({
|
|
|
23591
23339
|
const dependencyReferences = getEffectDepsRefs(analysis, node);
|
|
23592
23340
|
if (!dependencyReferences) return;
|
|
23593
23341
|
if (!dependencyReferences.flatMap((reference) => isState(analysis, reference) ? [] : getUpstreamRefs(analysis, reference)).some((reference) => isProp(analysis, reference))) return;
|
|
23594
|
-
for (const fact of collectEffectStateWriteFacts(analysis, node
|
|
23342
|
+
for (const fact of collectEffectStateWriteFacts(analysis, node)) {
|
|
23595
23343
|
if (!fact.isRenderKnownCopy || fact.resetsSourceState) continue;
|
|
23596
23344
|
context.report({
|
|
23597
23345
|
node: fact.callExpression,
|
|
@@ -24852,139 +24600,6 @@ const createRelativeImportSource = (filename, targetFilePath) => {
|
|
|
24852
24600
|
return relativePath.startsWith(".") ? relativePath : `./${relativePath}`;
|
|
24853
24601
|
};
|
|
24854
24602
|
//#endregion
|
|
24855
|
-
//#region src/plugin/utils/is-barrel-index-module.ts
|
|
24856
|
-
const INDEX_MODULE_FILE_PATTERN = /^index\.(?:[cm]?[jt]sx?|mjs)$/;
|
|
24857
|
-
const BINDING_IMPORT_DECLARATION_PATTERN = /^\s*import\s+(type\s+)?(?!["'])([^;]*?)\s+from\s+["']([^"']+)["']\s*;?\s*(?:(?:\/\/[^\n]*)?\s*)/gm;
|
|
24858
|
-
const BARREL_REEXPORT_DECLARATION_PATTERN = /^\s*export\s+(type\s+)?(?:\*(?:\s+as\s+([\w$]+))?|\{([\s\S]*?)\})\s+from\s+["']([^"']+)["']\s*;?\s*(?:(?:\/\/[^\n]*)?\s*)/gm;
|
|
24859
|
-
const LOCAL_EXPORT_SPECIFIER_DECLARATION_PATTERN = /^\s*export\s+(type\s+)?\{([\s\S]*?)\}\s*;?\s*(?:(?:\/\/[^\n]*)?\s*)/gm;
|
|
24860
|
-
const barrelIndexModuleInfoCache = /* @__PURE__ */ new Map();
|
|
24861
|
-
const isIndexModuleFilePath = (filePath) => INDEX_MODULE_FILE_PATTERN.test(path.basename(filePath));
|
|
24862
|
-
const createNonBarrelInfo = () => ({
|
|
24863
|
-
isBarrel: false,
|
|
24864
|
-
exportsByName: /* @__PURE__ */ new Map(),
|
|
24865
|
-
starExportSources: []
|
|
24866
|
-
});
|
|
24867
|
-
const addImportedBinding = (importedBindings, binding) => {
|
|
24868
|
-
importedBindings.set(binding.localName, {
|
|
24869
|
-
...binding,
|
|
24870
|
-
didExport: false
|
|
24871
|
-
});
|
|
24872
|
-
};
|
|
24873
|
-
const collectNamedImportBindings = (namedSpecifiersText, source, declarationIsTypeOnly, importedBindings) => {
|
|
24874
|
-
for (const specifier of parseExportSpecifiers(namedSpecifiersText, declarationIsTypeOnly)) addImportedBinding(importedBindings, {
|
|
24875
|
-
localName: specifier.exportedName,
|
|
24876
|
-
importedName: specifier.localName,
|
|
24877
|
-
source,
|
|
24878
|
-
isTypeOnly: specifier.isTypeOnly
|
|
24879
|
-
});
|
|
24880
|
-
};
|
|
24881
|
-
const collectImportBindings = (importClause, source, declarationIsTypeOnly, importedBindings) => {
|
|
24882
|
-
const trimmedImportClause = importClause.trim();
|
|
24883
|
-
const namespaceMatch = trimmedImportClause.match(/(?:^|,\s*)\*\s+as\s+([\w$]+)/);
|
|
24884
|
-
if (namespaceMatch?.[1]) addImportedBinding(importedBindings, {
|
|
24885
|
-
localName: namespaceMatch[1],
|
|
24886
|
-
importedName: "*",
|
|
24887
|
-
source,
|
|
24888
|
-
isTypeOnly: declarationIsTypeOnly
|
|
24889
|
-
});
|
|
24890
|
-
const namedImportMatch = trimmedImportClause.match(/\{([\s\S]*?)\}/);
|
|
24891
|
-
if (namedImportMatch?.[1]) collectNamedImportBindings(namedImportMatch[1], source, declarationIsTypeOnly, importedBindings);
|
|
24892
|
-
const defaultImportName = trimmedImportClause.split(",")[0]?.trim();
|
|
24893
|
-
if (defaultImportName && !defaultImportName.startsWith("{") && !defaultImportName.startsWith("*")) addImportedBinding(importedBindings, {
|
|
24894
|
-
localName: defaultImportName,
|
|
24895
|
-
importedName: "default",
|
|
24896
|
-
source,
|
|
24897
|
-
isTypeOnly: declarationIsTypeOnly
|
|
24898
|
-
});
|
|
24899
|
-
};
|
|
24900
|
-
const replaceKnownDeclarations = (sourceText, importedBindings, exportsByName, starExportSources) => {
|
|
24901
|
-
let withoutKnownDeclarations = sourceText.replace(BINDING_IMPORT_DECLARATION_PATTERN, (_match, typeKeyword, importClause, source) => {
|
|
24902
|
-
collectImportBindings(importClause, source, Boolean(typeKeyword), importedBindings);
|
|
24903
|
-
return "";
|
|
24904
|
-
});
|
|
24905
|
-
withoutKnownDeclarations = withoutKnownDeclarations.replace(BARREL_REEXPORT_DECLARATION_PATTERN, (_match, typeKeyword, namespaceExportName, specifiersText, source) => {
|
|
24906
|
-
const isTypeOnly = Boolean(typeKeyword);
|
|
24907
|
-
if (namespaceExportName) {
|
|
24908
|
-
exportsByName.set(namespaceExportName, {
|
|
24909
|
-
exportedName: namespaceExportName,
|
|
24910
|
-
importedName: "*",
|
|
24911
|
-
source,
|
|
24912
|
-
isTypeOnly
|
|
24913
|
-
});
|
|
24914
|
-
return "";
|
|
24915
|
-
}
|
|
24916
|
-
if (specifiersText) {
|
|
24917
|
-
for (const specifier of parseExportSpecifiers(specifiersText, isTypeOnly)) exportsByName.set(specifier.exportedName, {
|
|
24918
|
-
exportedName: specifier.exportedName,
|
|
24919
|
-
importedName: specifier.localName,
|
|
24920
|
-
source,
|
|
24921
|
-
isTypeOnly: specifier.isTypeOnly
|
|
24922
|
-
});
|
|
24923
|
-
return "";
|
|
24924
|
-
}
|
|
24925
|
-
starExportSources.push(source);
|
|
24926
|
-
return "";
|
|
24927
|
-
});
|
|
24928
|
-
withoutKnownDeclarations = withoutKnownDeclarations.replace(LOCAL_EXPORT_SPECIFIER_DECLARATION_PATTERN, (_match, typeKeyword, specifiersText) => {
|
|
24929
|
-
for (const specifier of parseExportSpecifiers(specifiersText, Boolean(typeKeyword))) {
|
|
24930
|
-
const importedBinding = importedBindings.get(specifier.localName);
|
|
24931
|
-
if (!importedBinding) return _match;
|
|
24932
|
-
importedBinding.didExport = true;
|
|
24933
|
-
exportsByName.set(specifier.exportedName, {
|
|
24934
|
-
exportedName: specifier.exportedName,
|
|
24935
|
-
importedName: importedBinding.importedName,
|
|
24936
|
-
source: importedBinding.source,
|
|
24937
|
-
isTypeOnly: specifier.isTypeOnly || importedBinding.isTypeOnly
|
|
24938
|
-
});
|
|
24939
|
-
}
|
|
24940
|
-
return "";
|
|
24941
|
-
});
|
|
24942
|
-
return withoutKnownDeclarations;
|
|
24943
|
-
};
|
|
24944
|
-
const hasUnexportedRuntimeImport = (importedBindings) => {
|
|
24945
|
-
for (const binding of importedBindings.values()) if (!binding.isTypeOnly && !binding.didExport) return true;
|
|
24946
|
-
return false;
|
|
24947
|
-
};
|
|
24948
|
-
const classifyBarrelModule = (sourceText) => {
|
|
24949
|
-
const strippedSource = stripJsComments(sourceText).trim();
|
|
24950
|
-
if (!strippedSource) return createNonBarrelInfo();
|
|
24951
|
-
const importedBindings = /* @__PURE__ */ new Map();
|
|
24952
|
-
const exportsByName = /* @__PURE__ */ new Map();
|
|
24953
|
-
const starExportSources = [];
|
|
24954
|
-
if (replaceKnownDeclarations(strippedSource, importedBindings, exportsByName, starExportSources).trim() || hasUnexportedRuntimeImport(importedBindings)) return createNonBarrelInfo();
|
|
24955
|
-
return {
|
|
24956
|
-
isBarrel: exportsByName.size > 0 || starExportSources.length > 0,
|
|
24957
|
-
exportsByName,
|
|
24958
|
-
starExportSources
|
|
24959
|
-
};
|
|
24960
|
-
};
|
|
24961
|
-
const getBarrelIndexModuleInfo = (filePath) => {
|
|
24962
|
-
if (!isIndexModuleFilePath(filePath)) return createNonBarrelInfo();
|
|
24963
|
-
recordContentProbe(filePath);
|
|
24964
|
-
let fileStat;
|
|
24965
|
-
try {
|
|
24966
|
-
fileStat = fs.statSync(filePath);
|
|
24967
|
-
} catch {
|
|
24968
|
-
fileStat = null;
|
|
24969
|
-
}
|
|
24970
|
-
const cachedResult = barrelIndexModuleInfoCache.get(filePath);
|
|
24971
|
-
if (cachedResult !== void 0 && fileStat !== null && cachedResult.mtimeMs === fileStat.mtimeMs && cachedResult.size === fileStat.size) return cachedResult.moduleInfo;
|
|
24972
|
-
if (fileStat === null) return createNonBarrelInfo();
|
|
24973
|
-
let moduleInfo = createNonBarrelInfo();
|
|
24974
|
-
try {
|
|
24975
|
-
moduleInfo = classifyBarrelModule(fs.readFileSync(filePath, "utf8"));
|
|
24976
|
-
} catch {
|
|
24977
|
-
moduleInfo = createNonBarrelInfo();
|
|
24978
|
-
}
|
|
24979
|
-
barrelIndexModuleInfoCache.set(filePath, {
|
|
24980
|
-
mtimeMs: fileStat.mtimeMs,
|
|
24981
|
-
size: fileStat.size,
|
|
24982
|
-
moduleInfo
|
|
24983
|
-
});
|
|
24984
|
-
return moduleInfo;
|
|
24985
|
-
};
|
|
24986
|
-
const isBarrelIndexModule = (filePath) => getBarrelIndexModuleInfo(filePath).isBarrel;
|
|
24987
|
-
//#endregion
|
|
24988
24603
|
//#region src/react-native-dependency-names.ts
|
|
24989
24604
|
const EXPO_MANAGED_DEPENDENCY_NAMES = new Set([
|
|
24990
24605
|
"expo",
|
|
@@ -25187,35 +24802,6 @@ const classifyReactNativeFileTarget = (context) => {
|
|
|
25187
24802
|
};
|
|
25188
24803
|
const isReactNativeFileActive = (context) => classifyReactNativeFileTarget(context) !== "web";
|
|
25189
24804
|
//#endregion
|
|
25190
|
-
//#region src/plugin/utils/resolve-barrel-export-file-path.ts
|
|
25191
|
-
const getUniqueFilePath = (filePaths) => {
|
|
25192
|
-
const uniqueFilePaths = new Set(filePaths);
|
|
25193
|
-
if (uniqueFilePaths.size !== 1) return null;
|
|
25194
|
-
const [filePath] = uniqueFilePaths;
|
|
25195
|
-
return filePath ?? null;
|
|
25196
|
-
};
|
|
25197
|
-
const resolveStarExportFilePath = (barrelFilePath, exportedName, source, visitedFilePaths) => {
|
|
25198
|
-
const resolvedTargetPath = resolveRelativeImportPath(barrelFilePath, source);
|
|
25199
|
-
if (!resolvedTargetPath) return null;
|
|
25200
|
-
const nestedTargetPath = resolveBarrelExportFilePath(resolvedTargetPath, exportedName, new Set(visitedFilePaths));
|
|
25201
|
-
if (nestedTargetPath) return nestedTargetPath;
|
|
25202
|
-
return doesModuleExportName(resolvedTargetPath, exportedName) ? resolvedTargetPath : null;
|
|
25203
|
-
};
|
|
25204
|
-
const resolveBarrelExportFilePath = (barrelFilePath, exportedName, visitedFilePaths = /* @__PURE__ */ new Set()) => {
|
|
25205
|
-
if (visitedFilePaths.has(barrelFilePath)) return null;
|
|
25206
|
-
visitedFilePaths.add(barrelFilePath);
|
|
25207
|
-
const moduleInfo = getBarrelIndexModuleInfo(barrelFilePath);
|
|
25208
|
-
if (!moduleInfo.isBarrel) return null;
|
|
25209
|
-
const target = moduleInfo.exportsByName.get(exportedName);
|
|
25210
|
-
if (target) {
|
|
25211
|
-
const resolvedTargetPath = resolveRelativeImportPath(barrelFilePath, target.source);
|
|
25212
|
-
if (!resolvedTargetPath) return null;
|
|
25213
|
-
return resolveBarrelExportFilePath(resolvedTargetPath, target.importedName, visitedFilePaths) ?? resolvedTargetPath;
|
|
25214
|
-
}
|
|
25215
|
-
if (exportedName === "default") return null;
|
|
25216
|
-
return getUniqueFilePath(moduleInfo.starExportSources.map((source) => resolveStarExportFilePath(barrelFilePath, exportedName, source, visitedFilePaths)).filter((filePath) => Boolean(filePath)));
|
|
25217
|
-
};
|
|
25218
|
-
//#endregion
|
|
25219
24805
|
//#region src/plugin/rules/bundle-size/no-barrel-import.ts
|
|
25220
24806
|
const TYPE_DECLARATION_FILE_PATTERN = /\.d\.[cm]?ts$/;
|
|
25221
24807
|
const SERVER_ONLY_FILE_PATTERN = /\.server\.[cm]?[jt]sx?$/;
|
|
@@ -26510,6 +26096,57 @@ const noDefaultProps = defineRule({
|
|
|
26510
26096
|
} })
|
|
26511
26097
|
});
|
|
26512
26098
|
//#endregion
|
|
26099
|
+
//#region src/plugin/rules/state-and-effects/no-derived-state.ts
|
|
26100
|
+
const getStateName$1 = (stateDeclarator) => {
|
|
26101
|
+
if (!isNodeOfType(stateDeclarator, "VariableDeclarator")) return "<state>";
|
|
26102
|
+
if (!isNodeOfType(stateDeclarator.id, "ArrayPattern")) return "<state>";
|
|
26103
|
+
const stateBinding = stateDeclarator.id.elements?.[0];
|
|
26104
|
+
if (stateBinding && isNodeOfType(stateBinding, "Identifier")) return stateBinding.name;
|
|
26105
|
+
const setterBinding = stateDeclarator.id.elements?.[1];
|
|
26106
|
+
if (!setterBinding || !isNodeOfType(setterBinding, "Identifier")) return "<state>";
|
|
26107
|
+
if (!setterBinding.name.startsWith("set") || setterBinding.name.length <= 3) return setterBinding.name;
|
|
26108
|
+
return setterBinding.name[3].toLowerCase() + setterBinding.name.slice(4);
|
|
26109
|
+
};
|
|
26110
|
+
const noDerivedState = defineRule({
|
|
26111
|
+
id: "no-derived-state",
|
|
26112
|
+
title: "Derived value copied into state",
|
|
26113
|
+
severity: "warn",
|
|
26114
|
+
tags: ["test-noise"],
|
|
26115
|
+
recommendation: "Work out the value while rendering (or with useMemo if it's expensive) instead of copying it into useState through a useEffect. See https://react.dev/learn/you-might-not-need-an-effect#updating-state-based-on-props-or-state",
|
|
26116
|
+
create: (context) => ({ CallExpression(node) {
|
|
26117
|
+
if (!isUseEffect(node)) return;
|
|
26118
|
+
const analysis = getProgramAnalysis(node);
|
|
26119
|
+
if (!analysis) return;
|
|
26120
|
+
for (const fact of collectEffectStateWriteFacts(analysis, node)) {
|
|
26121
|
+
if (!fact.isRenderKnownCopy || fact.resetsSourceState) continue;
|
|
26122
|
+
const stateName = getStateName$1(fact.stateDeclarator);
|
|
26123
|
+
context.report({
|
|
26124
|
+
node: fact.callExpression,
|
|
26125
|
+
message: `Storing "${stateName}" in state when you can derive it from other values costs an extra render.`
|
|
26126
|
+
});
|
|
26127
|
+
}
|
|
26128
|
+
} })
|
|
26129
|
+
});
|
|
26130
|
+
//#endregion
|
|
26131
|
+
//#region src/plugin/rules/state-and-effects/no-derived-state-effect.ts
|
|
26132
|
+
const noDerivedStateEffect = defineRule({
|
|
26133
|
+
id: "no-derived-state-effect",
|
|
26134
|
+
title: "Derived state stored in an effect",
|
|
26135
|
+
severity: "warn",
|
|
26136
|
+
tags: ["test-noise"],
|
|
26137
|
+
recommendation: "Work out derived values while rendering: `const x = fn(dep)`. To reset a component's state when a prop changes, give it a key prop: `<Component key={prop} />`. See https://react.dev/learn/you-might-not-need-an-effect",
|
|
26138
|
+
create: (context) => ({ CallExpression(node) {
|
|
26139
|
+
if (!isHookCall$2(node, EFFECT_HOOK_NAMES$1)) return;
|
|
26140
|
+
const analysis = getProgramAnalysis(node);
|
|
26141
|
+
if (!analysis) return;
|
|
26142
|
+
if (!collectEffectStateWriteFacts(analysis, node).find((fact) => fact.isRenderKnownCopy && !fact.resetsSourceState)) return;
|
|
26143
|
+
context.report({
|
|
26144
|
+
node,
|
|
26145
|
+
message: "You pay an extra render for state you can derive from other values."
|
|
26146
|
+
});
|
|
26147
|
+
} })
|
|
26148
|
+
});
|
|
26149
|
+
//#endregion
|
|
26513
26150
|
//#region src/plugin/utils/is-component-function.ts
|
|
26514
26151
|
const isFunctionAssignedToComponent = (functionNode) => {
|
|
26515
26152
|
let cursor = functionNode.parent ?? null;
|
|
@@ -26643,236 +26280,6 @@ const createComponentPropStackTracker = (callbacks) => {
|
|
|
26643
26280
|
};
|
|
26644
26281
|
};
|
|
26645
26282
|
//#endregion
|
|
26646
|
-
//#region src/plugin/rules/state-and-effects/utils/collect-render-state-write-facts.ts
|
|
26647
|
-
const findReferenceDeclarator = (reference) => {
|
|
26648
|
-
for (const definition of reference.resolved?.defs ?? []) {
|
|
26649
|
-
const definitionNode = definition.node;
|
|
26650
|
-
if (isNodeOfType(definitionNode, "VariableDeclarator")) return definitionNode;
|
|
26651
|
-
}
|
|
26652
|
-
return null;
|
|
26653
|
-
};
|
|
26654
|
-
const resolveSimpleRenderSource = (analysis, expression, visitedBindings = /* @__PURE__ */ new Set()) => {
|
|
26655
|
-
const node = stripParenExpression(expression);
|
|
26656
|
-
if (!isNodeOfType(node, "Identifier")) return null;
|
|
26657
|
-
const reference = getRef(analysis, node);
|
|
26658
|
-
if (!reference?.resolved || visitedBindings.has(reference.resolved)) return null;
|
|
26659
|
-
if (isProp(analysis, reference)) {
|
|
26660
|
-
if (isWholePropsObjectReference(analysis, reference)) return null;
|
|
26661
|
-
return { bindingIdentity: reference.resolved };
|
|
26662
|
-
}
|
|
26663
|
-
if (isState(analysis, reference)) {
|
|
26664
|
-
const stateDeclarator = getUseStateDecl(analysis, reference);
|
|
26665
|
-
if (!stateDeclarator || !isGenuineReactHookDeclarator(analysis, stateDeclarator, "useState") || isExternallyDrivenState(analysis, reference)) return null;
|
|
26666
|
-
return { bindingIdentity: reference.resolved };
|
|
26667
|
-
}
|
|
26668
|
-
if (reference.resolved.references.some((candidateReference) => candidateReference.isWrite() && !candidateReference.init)) return null;
|
|
26669
|
-
const declarator = findReferenceDeclarator(reference);
|
|
26670
|
-
if (!declarator || !isNodeOfType(declarator, "VariableDeclarator") || !declarator.init) return null;
|
|
26671
|
-
const nextVisitedBindings = new Set(visitedBindings);
|
|
26672
|
-
nextVisitedBindings.add(reference.resolved);
|
|
26673
|
-
return resolveSimpleRenderSource(analysis, declarator.init, nextVisitedBindings);
|
|
26674
|
-
};
|
|
26675
|
-
const doesEvidenceMatchSource = (evidence, source) => evidence.isExclusivelyRenderKnown && evidence.sourceReferences.size > 0 && [...evidence.sourceReferences].every((sourceReference) => sourceReference.resolved === source.bindingIdentity);
|
|
26676
|
-
const getDirectBranchStatements = (branch) => {
|
|
26677
|
-
if (isNodeOfType(branch, "BlockStatement")) return branch.body ?? [];
|
|
26678
|
-
return [branch];
|
|
26679
|
-
};
|
|
26680
|
-
const getDirectCallExpression = (statement) => {
|
|
26681
|
-
if (!isNodeOfType(statement, "ExpressionStatement")) return null;
|
|
26682
|
-
const expression = stripParenExpression(statement.expression);
|
|
26683
|
-
return isNodeOfType(expression, "CallExpression") ? expression : null;
|
|
26684
|
-
};
|
|
26685
|
-
const getDirectAssignmentExpression = (statement) => {
|
|
26686
|
-
if (!isNodeOfType(statement, "ExpressionStatement")) return null;
|
|
26687
|
-
const expression = stripParenExpression(statement.expression);
|
|
26688
|
-
return isNodeOfType(expression, "AssignmentExpression") && expression.operator === "=" ? expression : null;
|
|
26689
|
-
};
|
|
26690
|
-
const findDirectStateSetterReference = (analysis, callExpression) => {
|
|
26691
|
-
if (!isNodeOfType(callExpression, "CallExpression")) return null;
|
|
26692
|
-
const callee = stripParenExpression(callExpression.callee);
|
|
26693
|
-
if (!isNodeOfType(callee, "Identifier")) return null;
|
|
26694
|
-
const reference = getRef(analysis, callee);
|
|
26695
|
-
return reference && isStateSetter(analysis, reference) ? reference : null;
|
|
26696
|
-
};
|
|
26697
|
-
const getStaticRefCurrentReference = (analysis, expression) => {
|
|
26698
|
-
const node = stripParenExpression(expression);
|
|
26699
|
-
if (!isNodeOfType(node, "MemberExpression") || node.computed || !isNodeOfType(node.object, "Identifier") || !isNodeOfType(node.property, "Identifier") || node.property.name !== "current") return null;
|
|
26700
|
-
const reference = getRef(analysis, node.object);
|
|
26701
|
-
if (!reference) return null;
|
|
26702
|
-
const declarator = findReferenceDeclarator(reference);
|
|
26703
|
-
return declarator && isGenuineReactHookDeclarator(analysis, declarator, "useRef") ? reference : null;
|
|
26704
|
-
};
|
|
26705
|
-
const getStateTracker = (analysis, expression) => {
|
|
26706
|
-
const node = stripParenExpression(expression);
|
|
26707
|
-
if (!isNodeOfType(node, "Identifier")) return null;
|
|
26708
|
-
const reference = getRef(analysis, node);
|
|
26709
|
-
if (!reference || !isState(analysis, reference)) return null;
|
|
26710
|
-
const declarator = getUseStateDecl(analysis, reference);
|
|
26711
|
-
if (!declarator || !isGenuineReactHookDeclarator(analysis, declarator, "useState")) return null;
|
|
26712
|
-
return {
|
|
26713
|
-
kind: "state",
|
|
26714
|
-
declarator
|
|
26715
|
-
};
|
|
26716
|
-
};
|
|
26717
|
-
const getRefTracker = (analysis, expression) => {
|
|
26718
|
-
const reference = getStaticRefCurrentReference(analysis, expression);
|
|
26719
|
-
return reference ? {
|
|
26720
|
-
kind: "ref",
|
|
26721
|
-
reference
|
|
26722
|
-
} : null;
|
|
26723
|
-
};
|
|
26724
|
-
const getTrackerInitializer = (tracker) => {
|
|
26725
|
-
const declarator = tracker.kind === "state" ? tracker.declarator : findReferenceDeclarator(tracker.reference);
|
|
26726
|
-
if (!declarator || !isNodeOfType(declarator, "VariableDeclarator") || !isNodeOfType(declarator.init, "CallExpression")) return null;
|
|
26727
|
-
const initializer = declarator.init.arguments?.[0];
|
|
26728
|
-
return initializer ? initializer : null;
|
|
26729
|
-
};
|
|
26730
|
-
const isTrackerSynchronized = (analysis, guard) => {
|
|
26731
|
-
for (const statement of guard.statements) {
|
|
26732
|
-
if (guard.tracker.kind === "state") {
|
|
26733
|
-
const callExpression = getDirectCallExpression(statement);
|
|
26734
|
-
if (!callExpression || !isNodeOfType(callExpression, "CallExpression")) continue;
|
|
26735
|
-
const setterReference = findDirectStateSetterReference(analysis, callExpression);
|
|
26736
|
-
if (!setterReference || getUseStateDecl(analysis, setterReference) !== guard.tracker.declarator || (callExpression.arguments ?? []).length !== 1) continue;
|
|
26737
|
-
const writtenValue = callExpression.arguments?.[0];
|
|
26738
|
-
if (writtenValue && resolveSimpleRenderSource(analysis, writtenValue)?.bindingIdentity === guard.source.bindingIdentity) return true;
|
|
26739
|
-
continue;
|
|
26740
|
-
}
|
|
26741
|
-
const assignmentExpression = getDirectAssignmentExpression(statement);
|
|
26742
|
-
if (!assignmentExpression || !isNodeOfType(assignmentExpression, "AssignmentExpression")) continue;
|
|
26743
|
-
if (getStaticRefCurrentReference(analysis, assignmentExpression.left)?.resolved !== guard.tracker.reference.resolved) continue;
|
|
26744
|
-
if (resolveSimpleRenderSource(analysis, assignmentExpression.right)?.bindingIdentity === guard.source.bindingIdentity) return true;
|
|
26745
|
-
}
|
|
26746
|
-
return false;
|
|
26747
|
-
};
|
|
26748
|
-
const parseRenderTrackerGuard = (analysis, statement) => {
|
|
26749
|
-
if (!isNodeOfType(statement, "IfStatement") || statement.alternate || !isNodeOfType(statement.test, "BinaryExpression") || statement.test.operator !== "!==" || !isNodeOfType(statement.consequent, "BlockStatement")) return null;
|
|
26750
|
-
const left = statement.test.left;
|
|
26751
|
-
const right = statement.test.right;
|
|
26752
|
-
const candidates = [{
|
|
26753
|
-
sourceExpression: left,
|
|
26754
|
-
trackerExpression: right
|
|
26755
|
-
}, {
|
|
26756
|
-
sourceExpression: right,
|
|
26757
|
-
trackerExpression: left
|
|
26758
|
-
}];
|
|
26759
|
-
for (const candidate of candidates) {
|
|
26760
|
-
const source = resolveSimpleRenderSource(analysis, candidate.sourceExpression);
|
|
26761
|
-
if (!source) continue;
|
|
26762
|
-
const tracker = getStateTracker(analysis, candidate.trackerExpression) ?? getRefTracker(analysis, candidate.trackerExpression);
|
|
26763
|
-
if (!tracker) continue;
|
|
26764
|
-
const trackerInitializer = getTrackerInitializer(tracker);
|
|
26765
|
-
if (!trackerInitializer || resolveSimpleRenderSource(analysis, trackerInitializer)?.bindingIdentity !== source.bindingIdentity) continue;
|
|
26766
|
-
const guard = {
|
|
26767
|
-
source,
|
|
26768
|
-
tracker,
|
|
26769
|
-
statements: getDirectBranchStatements(statement.consequent)
|
|
26770
|
-
};
|
|
26771
|
-
return isTrackerSynchronized(analysis, guard) ? guard : null;
|
|
26772
|
-
}
|
|
26773
|
-
return null;
|
|
26774
|
-
};
|
|
26775
|
-
const isExclusiveDestinationSetterReference = (setterReference, callExpression) => {
|
|
26776
|
-
if (!setterReference.resolved || !isNodeOfType(callExpression, "CallExpression")) return false;
|
|
26777
|
-
const references = setterReference.resolved.references.filter((reference) => !reference.init);
|
|
26778
|
-
if (references.length !== 1) return false;
|
|
26779
|
-
return references[0].identifier === callExpression.callee;
|
|
26780
|
-
};
|
|
26781
|
-
const getStateInitializer = (stateDeclarator) => {
|
|
26782
|
-
if (!isNodeOfType(stateDeclarator, "VariableDeclarator") || !isNodeOfType(stateDeclarator.init, "CallExpression")) return null;
|
|
26783
|
-
const initializer = stateDeclarator.init.arguments?.[0];
|
|
26784
|
-
return initializer ? initializer : null;
|
|
26785
|
-
};
|
|
26786
|
-
const collectRenderStateWriteFacts = (analysis, componentBody, currentFilename) => {
|
|
26787
|
-
if (!isNodeOfType(componentBody, "BlockStatement") || !componentBody.parent || !isFunctionLike$1(componentBody.parent)) return [];
|
|
26788
|
-
const componentFunction = componentBody.parent;
|
|
26789
|
-
const facts = [];
|
|
26790
|
-
for (const statement of componentBody.body ?? []) {
|
|
26791
|
-
const guard = parseRenderTrackerGuard(analysis, statement);
|
|
26792
|
-
if (!guard) continue;
|
|
26793
|
-
for (const branchStatement of guard.statements) {
|
|
26794
|
-
const callExpression = getDirectCallExpression(branchStatement);
|
|
26795
|
-
if (!callExpression || !isNodeOfType(callExpression, "CallExpression")) continue;
|
|
26796
|
-
const setterReference = findDirectStateSetterReference(analysis, callExpression);
|
|
26797
|
-
if (!setterReference || (callExpression.arguments ?? []).length !== 1 || !isExclusiveDestinationSetterReference(setterReference, callExpression)) continue;
|
|
26798
|
-
const stateDeclarator = getUseStateDecl(analysis, setterReference);
|
|
26799
|
-
if (!stateDeclarator || !isGenuineReactHookDeclarator(analysis, stateDeclarator, "useState") || guard.tracker.kind === "state" && stateDeclarator === guard.tracker.declarator) continue;
|
|
26800
|
-
const writtenValue = callExpression.arguments?.[0];
|
|
26801
|
-
const initializer = getStateInitializer(stateDeclarator);
|
|
26802
|
-
if (!writtenValue || !initializer || isFunctionLike$1(writtenValue) || !doesEvidenceMatchSource(collectRenderValueEvidence(analysis, writtenValue, componentFunction, currentFilename), guard.source) || !doesEvidenceMatchSource(collectRenderValueEvidence(analysis, initializer, componentFunction, currentFilename), guard.source)) continue;
|
|
26803
|
-
facts.push({
|
|
26804
|
-
callExpression,
|
|
26805
|
-
stateDeclarator
|
|
26806
|
-
});
|
|
26807
|
-
}
|
|
26808
|
-
}
|
|
26809
|
-
return facts;
|
|
26810
|
-
};
|
|
26811
|
-
//#endregion
|
|
26812
|
-
//#region src/plugin/rules/state-and-effects/no-derived-state.ts
|
|
26813
|
-
const getStateName$1 = (stateDeclarator) => {
|
|
26814
|
-
if (!isNodeOfType(stateDeclarator, "VariableDeclarator")) return "<state>";
|
|
26815
|
-
if (!isNodeOfType(stateDeclarator.id, "ArrayPattern")) return "<state>";
|
|
26816
|
-
const stateBinding = stateDeclarator.id.elements?.[0];
|
|
26817
|
-
if (stateBinding && isNodeOfType(stateBinding, "Identifier")) return stateBinding.name;
|
|
26818
|
-
const setterBinding = stateDeclarator.id.elements?.[1];
|
|
26819
|
-
if (!setterBinding || !isNodeOfType(setterBinding, "Identifier")) return "<state>";
|
|
26820
|
-
if (!setterBinding.name.startsWith("set") || setterBinding.name.length <= 3) return setterBinding.name;
|
|
26821
|
-
return setterBinding.name[3].toLowerCase() + setterBinding.name.slice(4);
|
|
26822
|
-
};
|
|
26823
|
-
const noDerivedState = defineRule({
|
|
26824
|
-
id: "no-derived-state",
|
|
26825
|
-
title: "Derived value copied into state",
|
|
26826
|
-
severity: "warn",
|
|
26827
|
-
tags: ["test-noise"],
|
|
26828
|
-
recommendation: "Work out the value while rendering (or with useMemo if it's expensive) instead of copying it into state and synchronizing it during render or through an effect. See https://react.dev/learn/you-might-not-need-an-effect#updating-state-based-on-props-or-state",
|
|
26829
|
-
create: (context) => {
|
|
26830
|
-
const reportStateWrite = (callExpression, stateDeclarator) => {
|
|
26831
|
-
const stateName = getStateName$1(stateDeclarator);
|
|
26832
|
-
context.report({
|
|
26833
|
-
node: callExpression,
|
|
26834
|
-
message: `Storing "${stateName}" in state when you can derive it from other values costs an extra render.`
|
|
26835
|
-
});
|
|
26836
|
-
};
|
|
26837
|
-
return {
|
|
26838
|
-
...createComponentPropStackTracker({ onComponentEnter: (componentBody) => {
|
|
26839
|
-
if (!componentBody) return;
|
|
26840
|
-
const analysis = getProgramAnalysis(componentBody);
|
|
26841
|
-
if (!analysis) return;
|
|
26842
|
-
for (const fact of collectRenderStateWriteFacts(analysis, componentBody, context.filename)) reportStateWrite(fact.callExpression, fact.stateDeclarator);
|
|
26843
|
-
} }).visitors,
|
|
26844
|
-
CallExpression(node) {
|
|
26845
|
-
if (!isUseEffect(node)) return;
|
|
26846
|
-
const analysis = getProgramAnalysis(node);
|
|
26847
|
-
if (!analysis) return;
|
|
26848
|
-
for (const fact of collectEffectStateWriteFacts(analysis, node, context.filename)) {
|
|
26849
|
-
if (!fact.isRenderKnownCopy || fact.resetsSourceState) continue;
|
|
26850
|
-
reportStateWrite(fact.callExpression, fact.stateDeclarator);
|
|
26851
|
-
}
|
|
26852
|
-
}
|
|
26853
|
-
};
|
|
26854
|
-
}
|
|
26855
|
-
});
|
|
26856
|
-
//#endregion
|
|
26857
|
-
//#region src/plugin/rules/state-and-effects/no-derived-state-effect.ts
|
|
26858
|
-
const noDerivedStateEffect = defineRule({
|
|
26859
|
-
id: "no-derived-state-effect",
|
|
26860
|
-
title: "Derived state stored in an effect",
|
|
26861
|
-
severity: "warn",
|
|
26862
|
-
tags: ["test-noise"],
|
|
26863
|
-
recommendation: "Work out derived values while rendering: `const x = fn(dep)`. To reset a component's state when a prop changes, give it a key prop: `<Component key={prop} />`. See https://react.dev/learn/you-might-not-need-an-effect",
|
|
26864
|
-
create: (context) => ({ CallExpression(node) {
|
|
26865
|
-
if (!isHookCall$2(node, EFFECT_HOOK_NAMES$1)) return;
|
|
26866
|
-
const analysis = getProgramAnalysis(node);
|
|
26867
|
-
if (!analysis) return;
|
|
26868
|
-
if (!collectEffectStateWriteFacts(analysis, node, context.filename).find((fact) => fact.isRenderKnownCopy && !fact.resetsSourceState)) return;
|
|
26869
|
-
context.report({
|
|
26870
|
-
node,
|
|
26871
|
-
message: "You pay an extra render for state you can derive from other values."
|
|
26872
|
-
});
|
|
26873
|
-
} })
|
|
26874
|
-
});
|
|
26875
|
-
//#endregion
|
|
26876
26283
|
//#region src/plugin/utils/is-initial-only-prop-name.ts
|
|
26877
26284
|
const isInitialOnlyPropName = (propName) => {
|
|
26878
26285
|
if (propName === "initialValue" || propName === "defaultValue" || propName === "seedValue") return true;
|
|
@@ -30236,26 +29643,28 @@ const noGiantComponent = defineRule({
|
|
|
30236
29643
|
const lineCount = bodyNode.loc.end.line - bodyNode.loc.start.line + 1;
|
|
30237
29644
|
return lineCount > 300 ? lineCount : null;
|
|
30238
29645
|
};
|
|
30239
|
-
const reportOversizedComponent = (nameNode, componentName) => {
|
|
29646
|
+
const reportOversizedComponent = (nameNode, componentName, lineCount) => {
|
|
30240
29647
|
context.report({
|
|
30241
29648
|
node: nameNode,
|
|
30242
|
-
message: `Component "${componentName}" is
|
|
29649
|
+
message: `Component "${componentName}" is ${lineCount} lines long, which is hard to read & change. Split it into a few smaller components.`
|
|
30243
29650
|
});
|
|
30244
29651
|
};
|
|
30245
29652
|
return {
|
|
30246
29653
|
FunctionDeclaration(node) {
|
|
30247
29654
|
if (!node.id?.name || !isUppercaseName(node.id.name)) return;
|
|
30248
|
-
|
|
29655
|
+
const lineCount = getOversizedComponentLineCount(node);
|
|
29656
|
+
if (lineCount === null) return;
|
|
30249
29657
|
if (!functionContainsReactRenderOutput(node, context.scopes)) return;
|
|
30250
|
-
reportOversizedComponent(node.id, node.id.name);
|
|
29658
|
+
reportOversizedComponent(node.id, node.id.name, lineCount);
|
|
30251
29659
|
},
|
|
30252
29660
|
VariableDeclarator(node) {
|
|
30253
29661
|
if (!isNodeOfType(node.id, "Identifier") || !isUppercaseName(node.id.name)) return;
|
|
30254
29662
|
const functionNode = unwrapReactHocFunction(node.init);
|
|
30255
29663
|
if (!functionNode) return;
|
|
30256
|
-
|
|
29664
|
+
const lineCount = getOversizedComponentLineCount(functionNode);
|
|
29665
|
+
if (lineCount === null) return;
|
|
30257
29666
|
if (!functionContainsReactRenderOutput(functionNode, context.scopes)) return;
|
|
30258
|
-
reportOversizedComponent(node.id, node.id.name);
|
|
29667
|
+
reportOversizedComponent(node.id, node.id.name, lineCount);
|
|
30259
29668
|
}
|
|
30260
29669
|
};
|
|
30261
29670
|
}
|
|
@@ -30892,131 +30301,6 @@ const noImgLazyWithHighFetchpriority = defineRule({
|
|
|
30892
30301
|
} })
|
|
30893
30302
|
});
|
|
30894
30303
|
//#endregion
|
|
30895
|
-
//#region src/plugin/rules/state-and-effects/no-impure-state-updater.ts
|
|
30896
|
-
const TIMER_FUNCTION_NAMES = new Set([
|
|
30897
|
-
"cancelAnimationFrame",
|
|
30898
|
-
"clearInterval",
|
|
30899
|
-
"clearTimeout",
|
|
30900
|
-
"queueMicrotask",
|
|
30901
|
-
"requestAnimationFrame",
|
|
30902
|
-
"setInterval",
|
|
30903
|
-
"setTimeout"
|
|
30904
|
-
]);
|
|
30905
|
-
const STORAGE_MUTATION_METHOD_NAMES = new Set([
|
|
30906
|
-
"clear",
|
|
30907
|
-
"removeItem",
|
|
30908
|
-
"setItem"
|
|
30909
|
-
]);
|
|
30910
|
-
const STORAGE_RECEIVER_NAMES = new Set(["localStorage", "sessionStorage"]);
|
|
30911
|
-
const EXTERNAL_READ_METHOD_NAMES = new Set(["getBoundingClientRect", "getClientRects"]);
|
|
30912
|
-
const NOTIFICATION_RECEIVER_NAMES = new Set([
|
|
30913
|
-
"message",
|
|
30914
|
-
"notification",
|
|
30915
|
-
"toast"
|
|
30916
|
-
]);
|
|
30917
|
-
const NOTIFICATION_METHOD_NAMES = new Set([
|
|
30918
|
-
"error",
|
|
30919
|
-
"info",
|
|
30920
|
-
"loading",
|
|
30921
|
-
"open",
|
|
30922
|
-
"show",
|
|
30923
|
-
"success",
|
|
30924
|
-
"warning"
|
|
30925
|
-
]);
|
|
30926
|
-
const getMemberCall = (node) => {
|
|
30927
|
-
if (!isNodeOfType(node, "CallExpression")) return null;
|
|
30928
|
-
if (!isNodeOfType(node.callee, "MemberExpression") || node.callee.computed) return null;
|
|
30929
|
-
if (!isNodeOfType(node.callee.property, "Identifier")) return null;
|
|
30930
|
-
return {
|
|
30931
|
-
methodName: node.callee.property.name,
|
|
30932
|
-
receiver: stripParenExpression(node.callee.object)
|
|
30933
|
-
};
|
|
30934
|
-
};
|
|
30935
|
-
const isNotificationReceiver = (receiver, scopes) => {
|
|
30936
|
-
if (!isNodeOfType(receiver, "Identifier")) return false;
|
|
30937
|
-
if (!NOTIFICATION_RECEIVER_NAMES.has(receiver.name)) return false;
|
|
30938
|
-
const symbol = scopes.symbolFor(receiver);
|
|
30939
|
-
if (symbol?.kind === "import") return true;
|
|
30940
|
-
if (!isNodeOfType(symbol?.initializer, "CallExpression")) return false;
|
|
30941
|
-
const callee = symbol.initializer.callee;
|
|
30942
|
-
return isNodeOfType(callee, "Identifier") && /^use(?:Message|Notification|Toast)$/.test(callee.name);
|
|
30943
|
-
};
|
|
30944
|
-
const getKnownImpureCall = (callExpression, scopes) => {
|
|
30945
|
-
if (isNodeOfType(callExpression.callee, "Identifier") && TIMER_FUNCTION_NAMES.has(callExpression.callee.name) && scopes.isGlobalReference(callExpression.callee)) return `${callExpression.callee.name}()`;
|
|
30946
|
-
const memberCall = getMemberCall(callExpression);
|
|
30947
|
-
if (!memberCall) return null;
|
|
30948
|
-
const { methodName, receiver } = memberCall;
|
|
30949
|
-
if (STORAGE_MUTATION_METHOD_NAMES.has(methodName) && isNodeOfType(receiver, "Identifier") && STORAGE_RECEIVER_NAMES.has(receiver.name) && scopes.isGlobalReference(receiver)) return `${receiver.name}.${methodName}()`;
|
|
30950
|
-
if (EXTERNAL_READ_METHOD_NAMES.has(methodName)) return `.${methodName}()`;
|
|
30951
|
-
if (NOTIFICATION_METHOD_NAMES.has(methodName) && isNodeOfType(receiver, "Identifier") && isNotificationReceiver(receiver, scopes)) return `${receiver.name}.${methodName}()`;
|
|
30952
|
-
return null;
|
|
30953
|
-
};
|
|
30954
|
-
const getExternalAssignmentDescription = (assignmentTarget, updater, scopes) => {
|
|
30955
|
-
let rootIdentifier = null;
|
|
30956
|
-
if (isNodeOfType(assignmentTarget, "Identifier")) rootIdentifier = assignmentTarget;
|
|
30957
|
-
else if (isNodeOfType(assignmentTarget, "MemberExpression") && isNodeOfType(assignmentTarget.object, "Identifier")) rootIdentifier = assignmentTarget.object;
|
|
30958
|
-
if (!rootIdentifier) return null;
|
|
30959
|
-
const updaterScope = scopes.ownScopeFor(updater);
|
|
30960
|
-
if (!updaterScope) return null;
|
|
30961
|
-
const symbol = scopes.symbolFor(rootIdentifier);
|
|
30962
|
-
if (!symbol) return `the external value "${rootIdentifier.name}"`;
|
|
30963
|
-
if (symbol.kind === "parameter" && symbol.scope === updaterScope) return `the updater argument "${rootIdentifier.name}"`;
|
|
30964
|
-
return isDescendantScope(symbol.scope, updaterScope) ? null : `the captured value "${rootIdentifier.name}"`;
|
|
30965
|
-
};
|
|
30966
|
-
const findImpureUpdaterOperation = (updater, scopes) => {
|
|
30967
|
-
const analysis = getProgramAnalysis(updater);
|
|
30968
|
-
let operation = null;
|
|
30969
|
-
walkAst(updater, (child) => {
|
|
30970
|
-
if (operation) return false;
|
|
30971
|
-
if (child !== updater && isFunctionLike$1(child) && !executesDuringRender(child, scopes)) return false;
|
|
30972
|
-
if (isNodeOfType(child, "CallExpression")) {
|
|
30973
|
-
if (isNodeOfType(child.callee, "Identifier") && analysis) {
|
|
30974
|
-
const calleeReference = getRef(analysis, child.callee);
|
|
30975
|
-
if (calleeReference && isStateSetterCall(analysis, calleeReference)) {
|
|
30976
|
-
operation = `the nested state update "${child.callee.name}()"`;
|
|
30977
|
-
return false;
|
|
30978
|
-
}
|
|
30979
|
-
}
|
|
30980
|
-
const impureCall = getKnownImpureCall(child, scopes);
|
|
30981
|
-
if (impureCall) {
|
|
30982
|
-
operation = impureCall;
|
|
30983
|
-
return false;
|
|
30984
|
-
}
|
|
30985
|
-
}
|
|
30986
|
-
if (isNodeOfType(child, "AssignmentExpression")) {
|
|
30987
|
-
operation = getExternalAssignmentDescription(child.left, updater, scopes);
|
|
30988
|
-
if (operation) return false;
|
|
30989
|
-
}
|
|
30990
|
-
if (isNodeOfType(child, "UpdateExpression")) {
|
|
30991
|
-
operation = getExternalAssignmentDescription(child.argument, updater, scopes);
|
|
30992
|
-
if (operation) return false;
|
|
30993
|
-
}
|
|
30994
|
-
});
|
|
30995
|
-
return operation;
|
|
30996
|
-
};
|
|
30997
|
-
const noImpureStateUpdater = defineRule({
|
|
30998
|
-
id: "no-impure-state-updater",
|
|
30999
|
-
title: "State updater has side effects",
|
|
31000
|
-
severity: "error",
|
|
31001
|
-
recommendation: "Keep state updater callbacks pure and return only the next state. Move notifications, storage, timers, ref writes, and other external work into the event or effect that queues the update.",
|
|
31002
|
-
create: (context) => ({ CallExpression(node) {
|
|
31003
|
-
const updater = node.arguments?.[0];
|
|
31004
|
-
if (!updater || !isFunctionLike$1(updater)) return;
|
|
31005
|
-
const analysis = getProgramAnalysis(node);
|
|
31006
|
-
if (!analysis || !isNodeOfType(node.callee, "Identifier")) return;
|
|
31007
|
-
const calleeReference = getRef(analysis, node.callee);
|
|
31008
|
-
if (!calleeReference || !isStateSetterCall(analysis, calleeReference)) return;
|
|
31009
|
-
const stateDeclarator = getUseStateDecl(analysis, calleeReference);
|
|
31010
|
-
if (!isNodeOfType(stateDeclarator, "VariableDeclarator") || !isNodeOfType(stateDeclarator.init, "CallExpression") || !isReactApiCall(stateDeclarator.init, "useState", context.scopes, { allowGlobalReactNamespace: true })) return;
|
|
31011
|
-
const operation = findImpureUpdaterOperation(updater, context.scopes);
|
|
31012
|
-
if (!operation) return;
|
|
31013
|
-
context.report({
|
|
31014
|
-
node: updater,
|
|
31015
|
-
message: `This state updater performs ${operation}. React may run updater functions more than once, so side effects here can repeat or observe inconsistent external state.`
|
|
31016
|
-
});
|
|
31017
|
-
} })
|
|
31018
|
-
});
|
|
31019
|
-
//#endregion
|
|
31020
30304
|
//#region src/plugin/rules/correctness/no-indeterminate-attribute.ts
|
|
31021
30305
|
const MESSAGE$21 = "The `indeterminate` HTML attribute does not set a checkbox's visual state. Assign the `HTMLInputElement.indeterminate` DOM property instead.";
|
|
31022
30306
|
const REACT_USE_REF_OPTIONS = {
|
|
@@ -31183,7 +30467,7 @@ const noInitializeState = defineRule({
|
|
|
31183
30467
|
if (!dependencies || !isNodeOfType(dependencies, "ArrayExpression") || (dependencies.elements ?? []).length !== 0) return;
|
|
31184
30468
|
const analysis = getProgramAnalysis(node);
|
|
31185
30469
|
if (!analysis) return;
|
|
31186
|
-
for (const fact of collectEffectStateWriteFacts(analysis, node
|
|
30470
|
+
for (const fact of collectEffectStateWriteFacts(analysis, node)) {
|
|
31187
30471
|
if (!fact.isRenderKnownCopy || fact.matchesStateInitializer || fact.resetsSourceState) continue;
|
|
31188
30472
|
const stateName = getStateName(fact.stateDeclarator);
|
|
31189
30473
|
context.report({
|
|
@@ -34185,166 +33469,6 @@ const isDirectParentCallbackRef = (analysis, ref) => {
|
|
|
34185
33469
|
return getDownstreamRefs(analysis, initializer).some((initializerRef) => getUpstreamRefs(analysis, initializerRef).some((upstreamRef) => isProp(analysis, upstreamRef)));
|
|
34186
33470
|
}));
|
|
34187
33471
|
};
|
|
34188
|
-
const getDeclarationKind = (declarator) => {
|
|
34189
|
-
const declaration = declarator.parent;
|
|
34190
|
-
return declaration && isNodeOfType(declaration, "VariableDeclaration") ? declaration.kind : null;
|
|
34191
|
-
};
|
|
34192
|
-
const hasMutableBindingWrite = (reference) => Boolean(reference.resolved?.references.some((candidateReference) => candidateReference.isWrite() && !candidateReference.init));
|
|
34193
|
-
const getDestructuredPropertyName = (bindingIdentifier) => {
|
|
34194
|
-
const property = bindingIdentifier.parent;
|
|
34195
|
-
if (!property || !isNodeOfType(property, "Property")) return null;
|
|
34196
|
-
if (property.computed) return isNodeOfType(property.key, "Literal") && typeof property.key.value === "string" ? String(property.key.value) : null;
|
|
34197
|
-
return isNodeOfType(property.key, "Identifier") ? property.key.name : null;
|
|
34198
|
-
};
|
|
34199
|
-
const getParentCallbackPropName = (analysis, expression, visitedVariables = /* @__PURE__ */ new Set()) => {
|
|
34200
|
-
const unwrappedExpression = stripParenExpression(expression);
|
|
34201
|
-
if (isNodeOfType(unwrappedExpression, "Identifier")) {
|
|
34202
|
-
const callbackReference = getRef(analysis, unwrappedExpression);
|
|
34203
|
-
const callbackVariable = callbackReference?.resolved;
|
|
34204
|
-
if (!callbackReference || !callbackVariable || visitedVariables.has(callbackVariable)) return null;
|
|
34205
|
-
if (isProp(analysis, callbackReference)) {
|
|
34206
|
-
if (isWholePropsObjectReference(analysis, callbackReference)) return null;
|
|
34207
|
-
const bindingIdentifier = callbackVariable.defs.find((definition) => definition.type === "Parameter")?.name;
|
|
34208
|
-
return (bindingIdentifier && getDestructuredPropertyName(bindingIdentifier)) ?? unwrappedExpression.name;
|
|
34209
|
-
}
|
|
34210
|
-
if (hasMutableBindingWrite(callbackReference)) return null;
|
|
34211
|
-
const definitions = callbackVariable.defs.map((definition) => definition.node).filter((definitionNode) => isNodeOfType(definitionNode, "VariableDeclarator"));
|
|
34212
|
-
if (definitions.length !== 1) return null;
|
|
34213
|
-
const declarator = definitions[0];
|
|
34214
|
-
if (!declarator || !isNodeOfType(declarator, "VariableDeclarator") || getDeclarationKind(declarator) !== "const" || !declarator.init) return null;
|
|
34215
|
-
visitedVariables.add(callbackVariable);
|
|
34216
|
-
if (isNodeOfType(declarator.id, "ObjectPattern")) {
|
|
34217
|
-
const bindingIdentifier = callbackVariable.defs[0]?.name;
|
|
34218
|
-
const propertyName = bindingIdentifier ? getDestructuredPropertyName(bindingIdentifier) : null;
|
|
34219
|
-
const propsReference = getDownstreamRefs(analysis, declarator.init).find((candidateReference) => isWholePropsObjectReference(analysis, candidateReference));
|
|
34220
|
-
return propertyName && propsReference ? propertyName : null;
|
|
34221
|
-
}
|
|
34222
|
-
if (!isNodeOfType(declarator.id, "Identifier")) return null;
|
|
34223
|
-
return getParentCallbackPropName(analysis, declarator.init, visitedVariables);
|
|
34224
|
-
}
|
|
34225
|
-
if (!isNodeOfType(unwrappedExpression, "MemberExpression")) return null;
|
|
34226
|
-
const callbackName = getStaticMemberPropertyName(unwrappedExpression);
|
|
34227
|
-
if (!callbackName) return null;
|
|
34228
|
-
const receiver = stripParenExpression(unwrappedExpression.object);
|
|
34229
|
-
if (!isNodeOfType(receiver, "Identifier")) return null;
|
|
34230
|
-
const receiverReference = getRef(analysis, receiver);
|
|
34231
|
-
if (!receiverReference || !isWholePropsObjectReference(analysis, receiverReference)) return null;
|
|
34232
|
-
return callbackName;
|
|
34233
|
-
};
|
|
34234
|
-
const isNullishRefInitializer = (initializer) => {
|
|
34235
|
-
if (!initializer) return true;
|
|
34236
|
-
const unwrappedInitializer = stripParenExpression(initializer);
|
|
34237
|
-
if (isNodeOfType(unwrappedInitializer, "Literal")) return unwrappedInitializer.value === null;
|
|
34238
|
-
if (!isNodeOfType(unwrappedInitializer, "Identifier")) return false;
|
|
34239
|
-
return unwrappedInitializer.name === "undefined";
|
|
34240
|
-
};
|
|
34241
|
-
const getDirectComponentBodyStatement = (node, componentBody) => {
|
|
34242
|
-
let current = node;
|
|
34243
|
-
while (current?.parent && current.parent !== componentBody) current = current.parent;
|
|
34244
|
-
return current?.parent === componentBody ? current : null;
|
|
34245
|
-
};
|
|
34246
|
-
const getVariableForDeclarator = (analysis, declarator) => {
|
|
34247
|
-
for (const scope of analysis.scopeManager.scopes) {
|
|
34248
|
-
const variable = scope.variables.find((candidateVariable) => candidateVariable.defs.some((definition) => definition.node === declarator));
|
|
34249
|
-
if (variable) return variable;
|
|
34250
|
-
}
|
|
34251
|
-
return null;
|
|
34252
|
-
};
|
|
34253
|
-
const getRefMember = (identifier) => {
|
|
34254
|
-
const receiver = findTransparentExpressionRoot(identifier);
|
|
34255
|
-
const member = receiver.parent;
|
|
34256
|
-
if (!member || !isNodeOfType(member, "MemberExpression") || member.object !== receiver) return null;
|
|
34257
|
-
return member;
|
|
34258
|
-
};
|
|
34259
|
-
const getRefMemberAssignment = (identifier) => {
|
|
34260
|
-
const member = getRefMember(identifier);
|
|
34261
|
-
if (!member) return null;
|
|
34262
|
-
const memberRoot = findTransparentExpressionRoot(member);
|
|
34263
|
-
const assignment = memberRoot.parent;
|
|
34264
|
-
if (!assignment || !isNodeOfType(assignment, "AssignmentExpression") || assignment.left !== memberRoot) return null;
|
|
34265
|
-
return assignment;
|
|
34266
|
-
};
|
|
34267
|
-
const getRefAliasDeclarator = (identifier) => {
|
|
34268
|
-
const initializer = findTransparentExpressionRoot(identifier);
|
|
34269
|
-
const declarator = initializer.parent;
|
|
34270
|
-
if (!declarator || !isNodeOfType(declarator, "VariableDeclarator") || declarator.init !== initializer || !isNodeOfType(declarator.id, "Identifier") || getDeclarationKind(declarator) !== "const") return null;
|
|
34271
|
-
return declarator;
|
|
34272
|
-
};
|
|
34273
|
-
const getRefBindingProvenance = (analysis, receiver, isReactUseRefCall) => {
|
|
34274
|
-
if (!isNodeOfType(receiver, "Identifier")) return null;
|
|
34275
|
-
const receiverReference = getRef(analysis, receiver);
|
|
34276
|
-
if (!receiverReference?.resolved || hasMutableBindingWrite(receiverReference)) return null;
|
|
34277
|
-
const variables = /* @__PURE__ */ new Set();
|
|
34278
|
-
let currentVariable = receiverReference.resolved;
|
|
34279
|
-
let refCall = null;
|
|
34280
|
-
while (currentVariable && !variables.has(currentVariable)) {
|
|
34281
|
-
variables.add(currentVariable);
|
|
34282
|
-
const definitions = currentVariable.defs.map((definition) => definition.node).filter((definitionNode) => isNodeOfType(definitionNode, "VariableDeclarator"));
|
|
34283
|
-
if (definitions.length !== 1) return null;
|
|
34284
|
-
const declarator = definitions[0];
|
|
34285
|
-
if (!declarator || !isNodeOfType(declarator, "VariableDeclarator") || !isNodeOfType(declarator.id, "Identifier") || !declarator.init) return null;
|
|
34286
|
-
if (isNodeOfType(declarator.init, "CallExpression") && isReactUseRefCall(declarator.init)) {
|
|
34287
|
-
refCall = declarator.init;
|
|
34288
|
-
break;
|
|
34289
|
-
}
|
|
34290
|
-
if (getDeclarationKind(declarator) !== "const" || !isNodeOfType(stripParenExpression(declarator.init), "Identifier")) return null;
|
|
34291
|
-
const upstreamReference = getRef(analysis, stripParenExpression(declarator.init));
|
|
34292
|
-
if (!upstreamReference?.resolved || hasMutableBindingWrite(upstreamReference)) return null;
|
|
34293
|
-
currentVariable = upstreamReference.resolved;
|
|
34294
|
-
}
|
|
34295
|
-
if (!refCall) return null;
|
|
34296
|
-
const pendingVariables = [...variables];
|
|
34297
|
-
while (pendingVariables.length > 0) {
|
|
34298
|
-
const variable = pendingVariables.pop();
|
|
34299
|
-
if (!variable) continue;
|
|
34300
|
-
for (const candidateReference of variable.references) {
|
|
34301
|
-
const aliasDeclarator = getRefAliasDeclarator(candidateReference.identifier);
|
|
34302
|
-
if (!aliasDeclarator) continue;
|
|
34303
|
-
const aliasVariable = getVariableForDeclarator(analysis, aliasDeclarator);
|
|
34304
|
-
if (!aliasVariable || variables.has(aliasVariable)) continue;
|
|
34305
|
-
if (aliasVariable.references.some((aliasReference) => aliasReference.isWrite() && !aliasReference.init)) return null;
|
|
34306
|
-
variables.add(aliasVariable);
|
|
34307
|
-
pendingVariables.push(aliasVariable);
|
|
34308
|
-
}
|
|
34309
|
-
}
|
|
34310
|
-
return {
|
|
34311
|
-
refCall,
|
|
34312
|
-
variables
|
|
34313
|
-
};
|
|
34314
|
-
};
|
|
34315
|
-
const getCallbackRefProvenance = (analysis, effectCall, callExpression, isReactUseRefCall) => {
|
|
34316
|
-
const callee = stripParenExpression(callExpression.callee);
|
|
34317
|
-
if (!isNodeOfType(callee, "MemberExpression") || getStaticMemberPropertyName(callee) !== "current") return null;
|
|
34318
|
-
const bindingProvenance = getRefBindingProvenance(analysis, stripParenExpression(callee.object), isReactUseRefCall);
|
|
34319
|
-
if (!bindingProvenance) return null;
|
|
34320
|
-
const { refCall, variables } = bindingProvenance;
|
|
34321
|
-
const componentFunction = findEnclosingFunction(effectCall);
|
|
34322
|
-
if (!componentFunction || !isFunctionLike$1(componentFunction) || !isComponentFunction$1(componentFunction) || !isNodeOfType(componentFunction.body, "BlockStatement")) return null;
|
|
34323
|
-
if (!getDirectComponentBodyStatement(effectCall, componentFunction.body)) return null;
|
|
34324
|
-
const callbackPropNames = /* @__PURE__ */ new Set();
|
|
34325
|
-
const initializer = refCall.arguments?.[0];
|
|
34326
|
-
const initializerCallbackName = initializer ? getParentCallbackPropName(analysis, initializer) : null;
|
|
34327
|
-
if (initializerCallbackName) callbackPropNames.add(initializerCallbackName);
|
|
34328
|
-
else if (!isNullishRefInitializer(initializer)) return null;
|
|
34329
|
-
for (const variable of variables) for (const candidateReference of variable.references) {
|
|
34330
|
-
if (candidateReference.init) continue;
|
|
34331
|
-
if (candidateReference.isWrite()) return null;
|
|
34332
|
-
const identifier = candidateReference.identifier;
|
|
34333
|
-
if (getRefAliasDeclarator(identifier)) continue;
|
|
34334
|
-
const member = getRefMember(identifier);
|
|
34335
|
-
if (!member || getStaticMemberPropertyName(member) !== "current") return null;
|
|
34336
|
-
const memberParent = findTransparentExpressionRoot(member).parent;
|
|
34337
|
-
if (memberParent && (isNodeOfType(memberParent, "UpdateExpression") || isNodeOfType(memberParent, "UnaryExpression") && memberParent.operator === "delete")) return null;
|
|
34338
|
-
const assignment = getRefMemberAssignment(identifier);
|
|
34339
|
-
if (!assignment) continue;
|
|
34340
|
-
const assignmentStatement = findTransparentExpressionRoot(assignment).parent;
|
|
34341
|
-
if (assignment.operator !== "=" || !assignmentStatement || !isNodeOfType(assignmentStatement, "ExpressionStatement") || assignmentStatement.parent !== componentFunction.body) return null;
|
|
34342
|
-
const assignedCallbackName = getParentCallbackPropName(analysis, assignment.right);
|
|
34343
|
-
if (!assignedCallbackName) return null;
|
|
34344
|
-
callbackPropNames.add(assignedCallbackName);
|
|
34345
|
-
}
|
|
34346
|
-
return callbackPropNames.size > 0 ? { callbackPropNames } : null;
|
|
34347
|
-
};
|
|
34348
33472
|
const isWrapperHookCallbackRef = (analysis, ref) => Boolean(ref.resolved?.defs.some((def) => {
|
|
34349
33473
|
const node = def.node;
|
|
34350
33474
|
if (!isNodeOfType(node, "VariableDeclarator") || !node.init) return false;
|
|
@@ -34400,79 +33524,71 @@ const noPassDataToParent = defineRule({
|
|
|
34400
33524
|
severity: "warn",
|
|
34401
33525
|
tags: ["test-noise"],
|
|
34402
33526
|
recommendation: "Fetch the data in the parent and pass it down as a prop (or return it from the hook), instead of handing it back up through a prop callback in a useEffect. See https://react.dev/learn/you-might-not-need-an-effect#passing-data-to-the-parent",
|
|
34403
|
-
create: (context) => {
|
|
34404
|
-
|
|
34405
|
-
|
|
34406
|
-
|
|
34407
|
-
|
|
34408
|
-
|
|
34409
|
-
|
|
34410
|
-
|
|
34411
|
-
|
|
34412
|
-
|
|
34413
|
-
const
|
|
34414
|
-
if (!
|
|
34415
|
-
|
|
34416
|
-
if (!effectFn)
|
|
34417
|
-
|
|
34418
|
-
|
|
34419
|
-
|
|
34420
|
-
|
|
34421
|
-
if (
|
|
34422
|
-
|
|
34423
|
-
|
|
34424
|
-
|
|
34425
|
-
|
|
34426
|
-
|
|
34427
|
-
|
|
34428
|
-
|
|
34429
|
-
|
|
34430
|
-
|
|
34431
|
-
|
|
34432
|
-
|
|
34433
|
-
|
|
34434
|
-
|
|
34435
|
-
|
|
34436
|
-
|
|
34437
|
-
|
|
34438
|
-
|
|
34439
|
-
|
|
34440
|
-
|
|
34441
|
-
|
|
34442
|
-
|
|
34443
|
-
|
|
34444
|
-
|
|
34445
|
-
|
|
34446
|
-
|
|
34447
|
-
|
|
34448
|
-
|
|
34449
|
-
|
|
34450
|
-
|
|
34451
|
-
|
|
34452
|
-
|
|
34453
|
-
|
|
34454
|
-
if (
|
|
34455
|
-
if (
|
|
34456
|
-
|
|
34457
|
-
|
|
34458
|
-
|
|
34459
|
-
|
|
34460
|
-
|
|
34461
|
-
|
|
34462
|
-
|
|
34463
|
-
|
|
34464
|
-
|
|
34465
|
-
|
|
34466
|
-
|
|
34467
|
-
|
|
34468
|
-
})) continue;
|
|
34469
|
-
context.report({
|
|
34470
|
-
node: callExpr,
|
|
34471
|
-
message: "Handing data back to a parent from a useEffect costs your users an extra render."
|
|
34472
|
-
});
|
|
34473
|
-
}
|
|
34474
|
-
} };
|
|
34475
|
-
}
|
|
33527
|
+
create: (context) => ({ CallExpression(node) {
|
|
33528
|
+
if (!isUseEffect(node)) return;
|
|
33529
|
+
const analysis = getProgramAnalysis(node);
|
|
33530
|
+
if (!analysis) return;
|
|
33531
|
+
if (hasCleanup(analysis, node)) return;
|
|
33532
|
+
const effectFnRefs = getEffectFnRefs(analysis, node);
|
|
33533
|
+
if (!effectFnRefs) return;
|
|
33534
|
+
const effectFn = getEffectFn(analysis, node);
|
|
33535
|
+
if (!effectFn) return;
|
|
33536
|
+
for (const ref of effectFnRefs) {
|
|
33537
|
+
const callExpr = getCallExpr(ref);
|
|
33538
|
+
if (!callExpr || !isNodeOfType(callExpr, "CallExpression")) continue;
|
|
33539
|
+
if (isRefCall(analysis, ref)) continue;
|
|
33540
|
+
if (!isSynchronous(ref.identifier, effectFn)) continue;
|
|
33541
|
+
const calleeNode = unwrapChainExpression$1(callExpr.callee);
|
|
33542
|
+
const identifier = ref.identifier;
|
|
33543
|
+
if (calleeNode === identifier) {
|
|
33544
|
+
if (!isDirectParentCallbackRef(analysis, ref)) continue;
|
|
33545
|
+
if (isNodeOfType(identifier, "Identifier") && COMMAND_PROP_NAME_PATTERN.test(identifier.name)) continue;
|
|
33546
|
+
} else if (isNodeOfType(calleeNode, "MemberExpression") && stripParenExpression(calleeNode.object) === identifier) {
|
|
33547
|
+
if (!isWholePropsObjectReference(analysis, ref)) continue;
|
|
33548
|
+
if (isCustomHookParameter(ref)) continue;
|
|
33549
|
+
} else continue;
|
|
33550
|
+
const methodName = getCallMethodName(calleeNode);
|
|
33551
|
+
const isPropCallbackNamedLikeStringRead = Boolean(methodName && STRING_READ_METHOD_NAMES.has(methodName) && isNodeOfType(calleeNode, "MemberExpression") && stripParenExpression(calleeNode.object) === ref.identifier && isWholePropsObjectReference(analysis, ref));
|
|
33552
|
+
if (methodName && DATA_SINK_METHOD_NAMES.has(methodName) && !isPropCallbackNamedLikeStringRead) continue;
|
|
33553
|
+
if (methodName && COMMAND_PROP_NAME_PATTERN.test(methodName)) continue;
|
|
33554
|
+
if (isNamespacedApiCallee(calleeNode)) continue;
|
|
33555
|
+
const calleeName = isNodeOfType(identifier, "Identifier") ? identifier.name : methodName;
|
|
33556
|
+
const isSetterNamedCallee = Boolean(calleeName && SETTER_NAMED_PROP_PATTERN.test(calleeName));
|
|
33557
|
+
const isLeafRef = (argRef) => getUpstreamRefs(analysis, argRef).length === 1;
|
|
33558
|
+
const argsUpstreamRefs = (callExpr.arguments ?? []).flatMap((argument) => {
|
|
33559
|
+
if (isFunctionLike$1(argument)) {
|
|
33560
|
+
if (!isSetterNamedCallee) return [];
|
|
33561
|
+
return getFunctionalUpdaterDataRefs(analysis, argument);
|
|
33562
|
+
}
|
|
33563
|
+
if (isHandlerBagArgument(analysis, argument)) return [];
|
|
33564
|
+
if (isParentWiredHookResultArgument(analysis, argument)) return [];
|
|
33565
|
+
if (isNodeOfType(argument, "Identifier")) {
|
|
33566
|
+
const argumentRef = getRef(analysis, argument);
|
|
33567
|
+
if (argumentRef && resolveToFunction(argumentRef)) return [];
|
|
33568
|
+
}
|
|
33569
|
+
return getDownstreamRefs(analysis, argument);
|
|
33570
|
+
}).flatMap((argumentRef) => getUpstreamRefs(analysis, argumentRef)).filter(isLeafRef);
|
|
33571
|
+
if (calleeNode === identifier && isWrapperHookCallbackRef(analysis, ref)) argsUpstreamRefs.push(...getArgsUpstreamRefs(analysis, ref).filter(isLeafRef));
|
|
33572
|
+
if (!argsUpstreamRefs.some((argRef) => {
|
|
33573
|
+
if (isUseStateIdentifier(argRef.identifier)) return false;
|
|
33574
|
+
if (isProp(analysis, argRef)) return false;
|
|
33575
|
+
if (isUseRefIdentifier(argRef.identifier)) return false;
|
|
33576
|
+
if (isRefCurrent(argRef)) return false;
|
|
33577
|
+
if (isConstant(argRef)) return false;
|
|
33578
|
+
if (isParentWiredHookResultRef(analysis, argRef)) return false;
|
|
33579
|
+
if (isParentWiredHookCalleeRef(analysis, argRef)) return false;
|
|
33580
|
+
if (resolvesToFunctionBinding(argRef)) return false;
|
|
33581
|
+
const argIdentifier = argRef.identifier;
|
|
33582
|
+
if (isImportBindingRef(argRef) && !isCalleePosition(argIdentifier)) return false;
|
|
33583
|
+
if (isNodeOfType(argIdentifier, "Identifier") && argIdentifier.name === "undefined") return false;
|
|
33584
|
+
return true;
|
|
33585
|
+
})) continue;
|
|
33586
|
+
context.report({
|
|
33587
|
+
node: callExpr,
|
|
33588
|
+
message: "Handing data back to a parent from a useEffect costs your users an extra render."
|
|
33589
|
+
});
|
|
33590
|
+
}
|
|
33591
|
+
} })
|
|
34476
33592
|
});
|
|
34477
33593
|
//#endregion
|
|
34478
33594
|
//#region src/plugin/utils/is-call-result-consumed-as-argument.ts
|
|
@@ -35026,66 +34142,6 @@ const noPropCallbackInEffect = defineRule({
|
|
|
35026
34142
|
}
|
|
35027
34143
|
});
|
|
35028
34144
|
//#endregion
|
|
35029
|
-
//#region src/plugin/rules/state-and-effects/no-prop-callback-in-render.ts
|
|
35030
|
-
const isPreservedThroughConciseArrow = (callExpression, scopes) => {
|
|
35031
|
-
let node = callExpression;
|
|
35032
|
-
let parent = node.parent;
|
|
35033
|
-
while (parent) {
|
|
35034
|
-
if (isNodeOfType(parent, "ChainExpression")) {
|
|
35035
|
-
node = parent;
|
|
35036
|
-
parent = node.parent;
|
|
35037
|
-
continue;
|
|
35038
|
-
}
|
|
35039
|
-
if (isNodeOfType(parent, "LogicalExpression") && parent.right === node) {
|
|
35040
|
-
node = parent;
|
|
35041
|
-
parent = node.parent;
|
|
35042
|
-
continue;
|
|
35043
|
-
}
|
|
35044
|
-
if (isNodeOfType(parent, "ConditionalExpression") && (parent.consequent === node || parent.alternate === node)) {
|
|
35045
|
-
node = parent;
|
|
35046
|
-
parent = node.parent;
|
|
35047
|
-
continue;
|
|
35048
|
-
}
|
|
35049
|
-
if (isNodeOfType(parent, "SequenceExpression")) {
|
|
35050
|
-
const expressions = parent.expressions ?? [];
|
|
35051
|
-
if (expressions[expressions.length - 1] !== node) return false;
|
|
35052
|
-
node = parent;
|
|
35053
|
-
parent = node.parent;
|
|
35054
|
-
continue;
|
|
35055
|
-
}
|
|
35056
|
-
if (!isNodeOfType(parent, "ArrowFunctionExpression") || parent.body !== node) return !isResultDiscardedCall(node);
|
|
35057
|
-
const invocation = parent.parent;
|
|
35058
|
-
if (!isNodeOfType(invocation, "CallExpression") || !executesDuringRender(parent, scopes)) return true;
|
|
35059
|
-
if (invocation.arguments?.[0] === parent || invocation.arguments?.[1] === parent) {
|
|
35060
|
-
const callee = stripParenExpression(invocation.callee);
|
|
35061
|
-
return !(isNodeOfType(callee, "MemberExpression") && !callee.computed && isNodeOfType(callee.property, "Identifier") && callee.property.name === "forEach" && invocation.arguments[0] === parent);
|
|
35062
|
-
}
|
|
35063
|
-
node = invocation;
|
|
35064
|
-
parent = node.parent;
|
|
35065
|
-
}
|
|
35066
|
-
return false;
|
|
35067
|
-
};
|
|
35068
|
-
const noPropCallbackInRender = defineRule({
|
|
35069
|
-
id: "no-prop-callback-in-render",
|
|
35070
|
-
title: "Prop callback invoked during render",
|
|
35071
|
-
severity: "error",
|
|
35072
|
-
recommendation: "Invoke the callback from the event or asynchronous operation that produced the value, or from an effect when synchronizing with an external system. Render must stay pure because React can replay or discard it.",
|
|
35073
|
-
create: (context) => ({ CallExpression(node) {
|
|
35074
|
-
if (!isResultDiscardedCall(node)) return;
|
|
35075
|
-
if (isPreservedThroughConciseArrow(node, context.scopes)) return;
|
|
35076
|
-
if (!findRenderPhaseComponentOrHook(node, context.scopes)) return;
|
|
35077
|
-
const analysis = getProgramAnalysis(node);
|
|
35078
|
-
if (!analysis) return;
|
|
35079
|
-
const callee = stripParenExpression(node.callee);
|
|
35080
|
-
if (isFunctionLike$1(callee)) return;
|
|
35081
|
-
if (!getDownstreamRefs(analysis, callee).some((reference) => isPropCallbackInvocationRef(analysis, reference))) return;
|
|
35082
|
-
context.report({
|
|
35083
|
-
node,
|
|
35084
|
-
message: "This prop callback runs during render. React can replay or discard render work, so the callback can fire more than once or for UI that never commits."
|
|
35085
|
-
});
|
|
35086
|
-
} })
|
|
35087
|
-
});
|
|
35088
|
-
//#endregion
|
|
35089
34145
|
//#region src/plugin/rules/architecture/no-prop-types.ts
|
|
35090
34146
|
const PROP_TYPES_PROPERTY = "propTypes";
|
|
35091
34147
|
const isPropTypesKey = (key, computed) => {
|
|
@@ -35780,63 +34836,6 @@ const noRedundantShouldComponentUpdate = defineRule({
|
|
|
35780
34836
|
}
|
|
35781
34837
|
});
|
|
35782
34838
|
//#endregion
|
|
35783
|
-
//#region src/plugin/rules/state-and-effects/no-ref-current-in-render.ts
|
|
35784
|
-
const resolveReactRefSymbol = (memberExpression, scopes) => {
|
|
35785
|
-
const receiver = isNodeOfType(memberExpression, "MemberExpression") ? stripParenExpression(memberExpression.object) : null;
|
|
35786
|
-
if (!isNodeOfType(memberExpression, "MemberExpression") || memberExpression.computed || !isNodeOfType(memberExpression.property, "Identifier") || memberExpression.property.name !== "current" || !isNodeOfType(receiver, "Identifier")) return null;
|
|
35787
|
-
const symbol = resolveConstIdentifierAlias(receiver, scopes);
|
|
35788
|
-
if (!symbol?.initializer) return null;
|
|
35789
|
-
const initializer = stripParenExpression(symbol.initializer);
|
|
35790
|
-
if (!isNodeOfType(initializer, "CallExpression")) return null;
|
|
35791
|
-
return isReactApiCall(initializer, "useRef", scopes, { allowGlobalReactNamespace: true }) ? symbol : null;
|
|
35792
|
-
};
|
|
35793
|
-
const isSameRefCurrentMember = (node, refSymbol, scopes) => {
|
|
35794
|
-
if (!isNodeOfType(node, "MemberExpression") || node.computed || !isNodeOfType(node.property, "Identifier") || node.property.name !== "current") return false;
|
|
35795
|
-
const receiver = stripParenExpression(node.object);
|
|
35796
|
-
return isNodeOfType(receiver, "Identifier") && resolveConstIdentifierAlias(receiver, scopes)?.id === refSymbol.id;
|
|
35797
|
-
};
|
|
35798
|
-
const isDocumentedLazyInitialization = (assignmentExpression, refSymbol, scopes) => {
|
|
35799
|
-
if (assignmentExpression.operator !== "=" || !isNodeOfType(assignmentExpression.right, "NewExpression")) return false;
|
|
35800
|
-
let descendant = assignmentExpression;
|
|
35801
|
-
let ancestor = descendant.parent;
|
|
35802
|
-
while (ancestor) {
|
|
35803
|
-
if (isNodeOfType(ancestor, "IfStatement") && ancestor.consequent === descendant && isNodeOfType(ancestor.test, "BinaryExpression") && (ancestor.test.operator === "===" || ancestor.test.operator === "==")) {
|
|
35804
|
-
const { left, right } = ancestor.test;
|
|
35805
|
-
if (isSameRefCurrentMember(left, refSymbol, scopes) && isNodeOfType(right, "Literal") && right.value === null || isSameRefCurrentMember(right, refSymbol, scopes) && isNodeOfType(left, "Literal") && left.value === null) return true;
|
|
35806
|
-
}
|
|
35807
|
-
descendant = ancestor;
|
|
35808
|
-
ancestor = descendant.parent;
|
|
35809
|
-
}
|
|
35810
|
-
return false;
|
|
35811
|
-
};
|
|
35812
|
-
const noRefCurrentInRender = defineRule({
|
|
35813
|
-
id: "no-ref-current-in-render",
|
|
35814
|
-
title: "Ref mutated during render",
|
|
35815
|
-
severity: "error",
|
|
35816
|
-
recommendation: "Move ref writes into an event handler or effect. Render must stay pure because React can replay or discard it. The predictable null-guarded lazy initialization pattern remains supported.",
|
|
35817
|
-
create: (context) => {
|
|
35818
|
-
const report = (memberExpression) => {
|
|
35819
|
-
if (!resolveReactRefSymbol(memberExpression, context.scopes)) return;
|
|
35820
|
-
if (!findRenderPhaseComponentOrHook(memberExpression, context.scopes)) return;
|
|
35821
|
-
context.report({
|
|
35822
|
-
node: memberExpression,
|
|
35823
|
-
message: "This ref is mutated during render. React can replay or discard render work, so the mutation can leak from UI that never commits."
|
|
35824
|
-
});
|
|
35825
|
-
};
|
|
35826
|
-
return {
|
|
35827
|
-
AssignmentExpression(node) {
|
|
35828
|
-
const refSymbol = resolveReactRefSymbol(node.left, context.scopes);
|
|
35829
|
-
if (!refSymbol) return;
|
|
35830
|
-
if (isDocumentedLazyInitialization(node, refSymbol, context.scopes)) return;
|
|
35831
|
-
report(node.left);
|
|
35832
|
-
},
|
|
35833
|
-
UpdateExpression(node) {
|
|
35834
|
-
report(node.argument);
|
|
35835
|
-
}
|
|
35836
|
-
};
|
|
35837
|
-
}
|
|
35838
|
-
});
|
|
35839
|
-
//#endregion
|
|
35840
34839
|
//#region src/plugin/rules/architecture/no-render-in-render.ts
|
|
35841
34840
|
const isInsideComponentContext = (node) => {
|
|
35842
34841
|
let cursor = node.parent;
|
|
@@ -56799,18 +55798,6 @@ const reactDoctorRules = [
|
|
|
56799
55798
|
requires: [...new Set(["react", ...noImgLazyWithHighFetchpriority.requires ?? []])]
|
|
56800
55799
|
}
|
|
56801
55800
|
},
|
|
56802
|
-
{
|
|
56803
|
-
key: "react-doctor/no-impure-state-updater",
|
|
56804
|
-
id: "no-impure-state-updater",
|
|
56805
|
-
source: "react-doctor",
|
|
56806
|
-
originallyExternal: false,
|
|
56807
|
-
rule: {
|
|
56808
|
-
...noImpureStateUpdater,
|
|
56809
|
-
framework: "global",
|
|
56810
|
-
category: "Bugs",
|
|
56811
|
-
requires: [...new Set(["react", ...noImpureStateUpdater.requires ?? []])]
|
|
56812
|
-
}
|
|
56813
|
-
},
|
|
56814
55801
|
{
|
|
56815
55802
|
key: "react-doctor/no-indeterminate-attribute",
|
|
56816
55803
|
id: "no-indeterminate-attribute",
|
|
@@ -57227,18 +56214,6 @@ const reactDoctorRules = [
|
|
|
57227
56214
|
requires: [...new Set(["react", ...noPropCallbackInEffect.requires ?? []])]
|
|
57228
56215
|
}
|
|
57229
56216
|
},
|
|
57230
|
-
{
|
|
57231
|
-
key: "react-doctor/no-prop-callback-in-render",
|
|
57232
|
-
id: "no-prop-callback-in-render",
|
|
57233
|
-
source: "react-doctor",
|
|
57234
|
-
originallyExternal: false,
|
|
57235
|
-
rule: {
|
|
57236
|
-
...noPropCallbackInRender,
|
|
57237
|
-
framework: "global",
|
|
57238
|
-
category: "Bugs",
|
|
57239
|
-
requires: [...new Set(["react", ...noPropCallbackInRender.requires ?? []])]
|
|
57240
|
-
}
|
|
57241
|
-
},
|
|
57242
56217
|
{
|
|
57243
56218
|
key: "react-doctor/no-prop-types",
|
|
57244
56219
|
id: "no-prop-types",
|
|
@@ -57330,18 +56305,6 @@ const reactDoctorRules = [
|
|
|
57330
56305
|
requires: [...new Set(["react", ...noRedundantShouldComponentUpdate.requires ?? []])]
|
|
57331
56306
|
}
|
|
57332
56307
|
},
|
|
57333
|
-
{
|
|
57334
|
-
key: "react-doctor/no-ref-current-in-render",
|
|
57335
|
-
id: "no-ref-current-in-render",
|
|
57336
|
-
source: "react-doctor",
|
|
57337
|
-
originallyExternal: false,
|
|
57338
|
-
rule: {
|
|
57339
|
-
...noRefCurrentInRender,
|
|
57340
|
-
framework: "global",
|
|
57341
|
-
category: "Bugs",
|
|
57342
|
-
requires: [...new Set(["react", ...noRefCurrentInRender.requires ?? []])]
|
|
57343
|
-
}
|
|
57344
|
-
},
|
|
57345
56308
|
{
|
|
57346
56309
|
key: "react-doctor/no-render-in-render",
|
|
57347
56310
|
id: "no-render-in-render",
|
|
@@ -59934,11 +58897,6 @@ const CROSS_FILE_RULE_IDS = new Set([
|
|
|
59934
58897
|
"no-indeterminate-attribute",
|
|
59935
58898
|
"no-locale-format-in-render",
|
|
59936
58899
|
"no-match-media-in-state-initializer",
|
|
59937
|
-
"no-adjust-state-on-prop-change",
|
|
59938
|
-
"no-derived-state",
|
|
59939
|
-
"no-derived-state-effect",
|
|
59940
|
-
"no-event-handler",
|
|
59941
|
-
"no-initialize-state",
|
|
59942
58900
|
"no-mutating-reducer-state",
|
|
59943
58901
|
"no-unguarded-browser-global-in-render-or-hook-init",
|
|
59944
58902
|
"prefer-dynamic-import",
|
|
@@ -59992,9 +58950,6 @@ const collectNextjsSearchParamsDependencies = ({ absoluteFilePath, staticImports
|
|
|
59992
58950
|
if (hasAncestorSuspenseLayout(absoluteFilePath)) return;
|
|
59993
58951
|
for (const entry of flattenImportEntries(staticImports)) resolveCrossFileFunctionExport(absoluteFilePath, entry.source, entry.exportedName);
|
|
59994
58952
|
};
|
|
59995
|
-
const collectEffectValueHelperDependencies = ({ absoluteFilePath, staticImports }) => {
|
|
59996
|
-
for (const entry of flattenImportEntries(staticImports)) resolveCrossFileFunctionExport(absoluteFilePath, entry.source, entry.exportedName);
|
|
59997
|
-
};
|
|
59998
58953
|
const collectMutatingReducerDependencies = ({ absoluteFilePath, sourceText, staticImports, program }) => {
|
|
59999
58954
|
const namedUseReducerLocals = /* @__PURE__ */ new Set();
|
|
60000
58955
|
const reactObjectLocals = /* @__PURE__ */ new Set();
|
|
@@ -60081,11 +59036,6 @@ const CROSS_FILE_DEPENDENCY_COLLECTORS = new Map([
|
|
|
60081
59036
|
["no-indeterminate-attribute", collectNearestManifestDependencies],
|
|
60082
59037
|
["no-locale-format-in-render", collectNearestManifestDependencies],
|
|
60083
59038
|
["no-match-media-in-state-initializer", collectNearestManifestDependencies],
|
|
60084
|
-
["no-adjust-state-on-prop-change", collectEffectValueHelperDependencies],
|
|
60085
|
-
["no-derived-state", collectEffectValueHelperDependencies],
|
|
60086
|
-
["no-derived-state-effect", collectEffectValueHelperDependencies],
|
|
60087
|
-
["no-event-handler", collectEffectValueHelperDependencies],
|
|
60088
|
-
["no-initialize-state", collectEffectValueHelperDependencies],
|
|
60089
59039
|
["no-mutating-reducer-state", collectMutatingReducerDependencies],
|
|
60090
59040
|
["no-unguarded-browser-global-in-render-or-hook-init", collectNearestManifestDependencies],
|
|
60091
59041
|
["prefer-dynamic-import", collectNearestManifestDependencies],
|