oxlint-plugin-react-doctor 0.7.9-dev.c7f61cd → 0.7.9-dev.d7d38d7
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.js +90 -566
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -579,22 +579,6 @@ const EXTERNAL_SYNC_OBSERVER_CONSTRUCTORS = new Set([
|
|
|
579
579
|
"ResizeObserver",
|
|
580
580
|
"PerformanceObserver"
|
|
581
581
|
]);
|
|
582
|
-
const EXTERNAL_SYNC_DOM_MEMBER_METHOD_NAMES = new Set([
|
|
583
|
-
"blur",
|
|
584
|
-
"focus",
|
|
585
|
-
"getBoundingClientRect",
|
|
586
|
-
"getClientRects",
|
|
587
|
-
"measure",
|
|
588
|
-
"measureInWindow",
|
|
589
|
-
"measureLayout",
|
|
590
|
-
"scroll",
|
|
591
|
-
"scrollBy",
|
|
592
|
-
"scrollIntoView",
|
|
593
|
-
"scrollTo",
|
|
594
|
-
"select",
|
|
595
|
-
"setRangeText",
|
|
596
|
-
"setSelectionRange"
|
|
597
|
-
]);
|
|
598
582
|
const STORAGE_OBJECTS = new Set(["localStorage", "sessionStorage"]);
|
|
599
583
|
//#endregion
|
|
600
584
|
//#region src/plugin/constants/react.ts
|
|
@@ -20012,28 +19996,6 @@ const jsLengthCheckFirst = defineRule({
|
|
|
20012
19996
|
} })
|
|
20013
19997
|
});
|
|
20014
19998
|
//#endregion
|
|
20015
|
-
//#region src/plugin/utils/is-proven-global-namespace-reference.ts
|
|
20016
|
-
const isProvenGlobalObjectReference = (expression, scopes, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
|
|
20017
|
-
const strippedExpression = stripParenExpression(expression);
|
|
20018
|
-
if (!isNodeOfType(strippedExpression, "Identifier")) return false;
|
|
20019
|
-
if ((strippedExpression.name === "globalThis" || strippedExpression.name === "window" || strippedExpression.name === "self" || strippedExpression.name === "global") && scopes.isGlobalReference(strippedExpression)) return true;
|
|
20020
|
-
const symbol = scopes.symbolFor(strippedExpression);
|
|
20021
|
-
if (!symbol?.initializer || symbol.kind !== "const" || visitedSymbolIds.has(symbol.id)) return false;
|
|
20022
|
-
visitedSymbolIds.add(symbol.id);
|
|
20023
|
-
return isProvenGlobalObjectReference(symbol.initializer, scopes, visitedSymbolIds);
|
|
20024
|
-
};
|
|
20025
|
-
const isProvenGlobalNamespaceReference = (expression, namespaceName, scopes, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
|
|
20026
|
-
const strippedExpression = stripParenExpression(expression);
|
|
20027
|
-
if (isNodeOfType(strippedExpression, "Identifier")) {
|
|
20028
|
-
if (strippedExpression.name === namespaceName && scopes.isGlobalReference(strippedExpression)) return true;
|
|
20029
|
-
const symbol = scopes.symbolFor(strippedExpression);
|
|
20030
|
-
if (!symbol?.initializer || symbol.kind !== "const" || visitedSymbolIds.has(symbol.id)) return false;
|
|
20031
|
-
visitedSymbolIds.add(symbol.id);
|
|
20032
|
-
return isProvenGlobalNamespaceReference(symbol.initializer, namespaceName, scopes, visitedSymbolIds);
|
|
20033
|
-
}
|
|
20034
|
-
return isNodeOfType(strippedExpression, "MemberExpression") && getStaticPropertyName(strippedExpression) === namespaceName && isProvenGlobalObjectReference(strippedExpression.object, scopes);
|
|
20035
|
-
};
|
|
20036
|
-
//#endregion
|
|
20037
19999
|
//#region src/plugin/rules/js-performance/js-min-max-loop.ts
|
|
20038
20000
|
const builtinMutationByProgram = /* @__PURE__ */ new WeakMap();
|
|
20039
20001
|
const RUNTIMELESS_SYMBOL_KINDS = new Set(["ts-interface", "ts-type-alias"]);
|
|
@@ -20090,6 +20052,26 @@ const isSafeFreshNumericArray = (arrayExpression) => {
|
|
|
20090
20052
|
}
|
|
20091
20053
|
return !(didFindPositiveZero && didFindNegativeZero);
|
|
20092
20054
|
};
|
|
20055
|
+
const isGlobalObjectReference = (expression, scopes, visitedSymbols = /* @__PURE__ */ new Set()) => {
|
|
20056
|
+
const strippedExpression = stripParenExpression(expression);
|
|
20057
|
+
if (!isNodeOfType(strippedExpression, "Identifier")) return false;
|
|
20058
|
+
if ((strippedExpression.name === "globalThis" || strippedExpression.name === "window" || strippedExpression.name === "self" || strippedExpression.name === "global") && scopes.isGlobalReference(strippedExpression)) return true;
|
|
20059
|
+
const symbol = scopes.symbolFor(strippedExpression);
|
|
20060
|
+
if (!symbol?.initializer || symbol.kind !== "const" || visitedSymbols.has(symbol.id)) return false;
|
|
20061
|
+
visitedSymbols.add(symbol.id);
|
|
20062
|
+
return isGlobalObjectReference(symbol.initializer, scopes, visitedSymbols);
|
|
20063
|
+
};
|
|
20064
|
+
const resolvesToGlobalNamespace = (expression, namespaceName, scopes, visitedSymbols = /* @__PURE__ */ new Set()) => {
|
|
20065
|
+
const strippedExpression = stripParenExpression(expression);
|
|
20066
|
+
if (isNodeOfType(strippedExpression, "Identifier")) {
|
|
20067
|
+
if (strippedExpression.name === namespaceName && scopes.isGlobalReference(strippedExpression)) return true;
|
|
20068
|
+
const symbol = scopes.symbolFor(strippedExpression);
|
|
20069
|
+
if (!symbol?.initializer || symbol.kind !== "const" || visitedSymbols.has(symbol.id)) return false;
|
|
20070
|
+
visitedSymbols.add(symbol.id);
|
|
20071
|
+
return resolvesToGlobalNamespace(symbol.initializer, namespaceName, scopes, visitedSymbols);
|
|
20072
|
+
}
|
|
20073
|
+
return isNodeOfType(strippedExpression, "MemberExpression") && getStaticPropertyName(strippedExpression) === namespaceName && isGlobalObjectReference(strippedExpression.object, scopes);
|
|
20074
|
+
};
|
|
20093
20075
|
const resolvesToGlobalMethod = (expression, namespaceName, methodNames, scopes, visitedSymbols = /* @__PURE__ */ new Set()) => {
|
|
20094
20076
|
const strippedExpression = stripParenExpression(expression);
|
|
20095
20077
|
if (isNodeOfType(strippedExpression, "Identifier")) {
|
|
@@ -20098,7 +20080,7 @@ const resolvesToGlobalMethod = (expression, namespaceName, methodNames, scopes,
|
|
|
20098
20080
|
visitedSymbols.add(symbol.id);
|
|
20099
20081
|
return resolvesToGlobalMethod(symbol.initializer, namespaceName, methodNames, scopes, visitedSymbols);
|
|
20100
20082
|
}
|
|
20101
|
-
return isNodeOfType(strippedExpression, "MemberExpression") && methodNames.has(getStaticPropertyName(strippedExpression) ?? "") &&
|
|
20083
|
+
return isNodeOfType(strippedExpression, "MemberExpression") && methodNames.has(getStaticPropertyName(strippedExpression) ?? "") && resolvesToGlobalNamespace(strippedExpression.object, namespaceName, scopes);
|
|
20102
20084
|
};
|
|
20103
20085
|
const resolvesToNativeArrayPrototype = (expression, scopes, visitedSymbols = /* @__PURE__ */ new Set()) => {
|
|
20104
20086
|
const strippedExpression = stripParenExpression(expression);
|
|
@@ -20110,7 +20092,7 @@ const resolvesToNativeArrayPrototype = (expression, scopes, visitedSymbols = /*
|
|
|
20110
20092
|
}
|
|
20111
20093
|
if (isNodeOfType(strippedExpression, "MemberExpression")) {
|
|
20112
20094
|
const propertyName = getStaticPropertyName(strippedExpression);
|
|
20113
|
-
if (propertyName === "prototype") return
|
|
20095
|
+
if (propertyName === "prototype") return resolvesToGlobalNamespace(strippedExpression.object, "Array", scopes);
|
|
20114
20096
|
return propertyName === "__proto__" && isNodeOfType(stripParenExpression(strippedExpression.object), "ArrayExpression");
|
|
20115
20097
|
}
|
|
20116
20098
|
if (!isNodeOfType(strippedExpression, "CallExpression")) return false;
|
|
@@ -20121,23 +20103,23 @@ const resolvesToNativeArrayPrototype = (expression, scopes, visitedSymbols = /*
|
|
|
20121
20103
|
const isGlobalNamespaceReplacementTarget = (target, namespaceName, scopes) => {
|
|
20122
20104
|
const strippedTarget = stripParenExpression(target);
|
|
20123
20105
|
if (isNodeOfType(strippedTarget, "Identifier")) return strippedTarget.name === namespaceName && scopes.isGlobalReference(strippedTarget);
|
|
20124
|
-
return isNodeOfType(strippedTarget, "MemberExpression") && getStaticPropertyName(strippedTarget) === namespaceName &&
|
|
20106
|
+
return isNodeOfType(strippedTarget, "MemberExpression") && getStaticPropertyName(strippedTarget) === namespaceName && isGlobalObjectReference(strippedTarget.object, scopes);
|
|
20125
20107
|
};
|
|
20126
20108
|
const isUnsafeBuiltinMemberTarget = (target, targetFunction, scopes) => {
|
|
20127
20109
|
const strippedTarget = stripParenExpression(target);
|
|
20128
20110
|
if (!isNodeOfType(strippedTarget, "MemberExpression")) return false;
|
|
20129
20111
|
const propertyName = getStaticPropertyName(strippedTarget);
|
|
20130
20112
|
if (resolvesToNativeArrayPrototype(strippedTarget.object, scopes)) return propertyName === null || propertyName === "sort";
|
|
20131
|
-
if (
|
|
20132
|
-
return
|
|
20113
|
+
if (resolvesToGlobalNamespace(strippedTarget.object, "Math", scopes)) return propertyName === null || propertyName === targetFunction;
|
|
20114
|
+
return isGlobalObjectReference(strippedTarget.object, scopes) && (propertyName === null || propertyName === "Math");
|
|
20133
20115
|
};
|
|
20134
20116
|
const isUnsafeBuiltinMutationApiCall = (callExpression, targetFunction, scopes) => {
|
|
20135
20117
|
const target = callExpression.arguments[0];
|
|
20136
20118
|
if (!target) return false;
|
|
20137
20119
|
let propertyName = null;
|
|
20138
20120
|
if (resolvesToNativeArrayPrototype(target, scopes)) propertyName = "sort";
|
|
20139
|
-
else if (
|
|
20140
|
-
else if (
|
|
20121
|
+
else if (resolvesToGlobalNamespace(target, "Math", scopes)) propertyName = targetFunction;
|
|
20122
|
+
else if (isGlobalObjectReference(target, scopes)) propertyName = "Math";
|
|
20141
20123
|
if (!propertyName) return false;
|
|
20142
20124
|
const canObjectExpressionSetProperty = (properties) => {
|
|
20143
20125
|
if (!isNodeOfType(properties, "ObjectExpression")) return true;
|
|
@@ -20188,7 +20170,7 @@ const hasUnsafeMathBinding = (node, scopes) => {
|
|
|
20188
20170
|
let scope = scopes.scopeFor(node);
|
|
20189
20171
|
while (scope) {
|
|
20190
20172
|
const symbol = scope.symbolsByName.get("Math");
|
|
20191
|
-
if (symbol && !RUNTIMELESS_SYMBOL_KINDS.has(symbol.kind)) return !(symbol.kind === "const" && symbol.initializer &&
|
|
20173
|
+
if (symbol && !RUNTIMELESS_SYMBOL_KINDS.has(symbol.kind)) return !(symbol.kind === "const" && symbol.initializer && resolvesToGlobalNamespace(symbol.initializer, "Math", scopes));
|
|
20192
20174
|
scope = scope.parent;
|
|
20193
20175
|
}
|
|
20194
20176
|
return false;
|
|
@@ -28485,11 +28467,7 @@ const isProvenNativeReadMethod = (ref, methodName) => Boolean(ref.resolved?.defs
|
|
|
28485
28467
|
}));
|
|
28486
28468
|
//#endregion
|
|
28487
28469
|
//#region src/plugin/rules/state-and-effects/utils/effect/react.ts
|
|
28488
|
-
const
|
|
28489
|
-
"memo",
|
|
28490
|
-
"forwardRef",
|
|
28491
|
-
"observer"
|
|
28492
|
-
]);
|
|
28470
|
+
const KNOWN_PURE_HOC_NAMES = new Set(["memo", "forwardRef"]);
|
|
28493
28471
|
const startsWithUppercase = (name) => Boolean(name && name.length > 0 && name[0] >= "A" && name[0] <= "Z");
|
|
28494
28472
|
const isReactFunctionalComponent = (node) => {
|
|
28495
28473
|
if (!node) return false;
|
|
@@ -28511,7 +28489,7 @@ const isReactFunctionalHOC = (analysis, node) => {
|
|
|
28511
28489
|
const isWrappedInline = () => {
|
|
28512
28490
|
if (!isNodeOfType(init, "CallExpression")) return false;
|
|
28513
28491
|
if (!isNodeOfType(init.callee, "Identifier")) return false;
|
|
28514
|
-
if (
|
|
28492
|
+
if (KNOWN_PURE_HOC_NAMES.has(init.callee.name)) return false;
|
|
28515
28493
|
const firstArg = init.arguments?.[0];
|
|
28516
28494
|
if (!firstArg) return false;
|
|
28517
28495
|
return isNodeOfType(firstArg, "ArrowFunctionExpression") || isNodeOfType(firstArg, "FunctionExpression");
|
|
@@ -28531,7 +28509,7 @@ const isReactFunctionalHOC = (analysis, node) => {
|
|
|
28531
28509
|
if (!args.includes(refId)) continue;
|
|
28532
28510
|
const callee = parent.callee;
|
|
28533
28511
|
const calleeName = isNodeOfType(callee, "Identifier") ? callee.name : isNodeOfType(callee, "CallExpression") && isNodeOfType(callee.callee, "Identifier") ? callee.callee.name : null;
|
|
28534
|
-
if (calleeName != null && !
|
|
28512
|
+
if (calleeName != null && !KNOWN_PURE_HOC_NAMES.has(calleeName)) return true;
|
|
28535
28513
|
}
|
|
28536
28514
|
return false;
|
|
28537
28515
|
};
|
|
@@ -32959,18 +32937,6 @@ const isProvenIntrinsicJsxElement = (openingElement, scopes) => {
|
|
|
32959
32937
|
return isIntrinsicValue(openingElement.name);
|
|
32960
32938
|
};
|
|
32961
32939
|
//#endregion
|
|
32962
|
-
//#region src/plugin/utils/is-inline-intrinsic-ref-callback.ts
|
|
32963
|
-
const isInlineIntrinsicRefCallback = (functionNode, scopes) => {
|
|
32964
|
-
const functionExpression = findTransparentExpressionRoot(functionNode);
|
|
32965
|
-
if (!isFunctionLike$1(functionExpression) || functionExpression.async || functionExpression.generator) return false;
|
|
32966
|
-
const container = functionExpression.parent;
|
|
32967
|
-
if (!container || !isNodeOfType(container, "JSXExpressionContainer")) return false;
|
|
32968
|
-
const attribute = container.parent;
|
|
32969
|
-
if (!attribute || !isNodeOfType(attribute, "JSXAttribute") || getJsxAttributeName(attribute.name) !== "ref") return false;
|
|
32970
|
-
const openingElement = attribute.parent;
|
|
32971
|
-
return Boolean(openingElement && isNodeOfType(openingElement, "JSXOpeningElement") && isProvenIntrinsicJsxElement(openingElement, scopes));
|
|
32972
|
-
};
|
|
32973
|
-
//#endregion
|
|
32974
32940
|
//#region src/plugin/rules/react-builtins/is-safe-create-ref-callback-current-write.ts
|
|
32975
32941
|
const pathStartsWith$1 = (propertyPath, prefix) => prefix.every((propertyName, index) => propertyPath[index] === propertyName);
|
|
32976
32942
|
const collectMemberExpression = (identifier) => {
|
|
@@ -32981,6 +32947,16 @@ const collectMemberExpression = (identifier) => {
|
|
|
32981
32947
|
}
|
|
32982
32948
|
return expression;
|
|
32983
32949
|
};
|
|
32950
|
+
const isInlineIntrinsicRefCallback = (functionNode, scopes) => {
|
|
32951
|
+
const functionExpression = findTransparentExpressionRoot(functionNode);
|
|
32952
|
+
if (!isFunctionLike$1(functionExpression) || functionExpression.async || functionExpression.generator) return false;
|
|
32953
|
+
const container = functionExpression.parent;
|
|
32954
|
+
if (!container || !isNodeOfType(container, "JSXExpressionContainer")) return false;
|
|
32955
|
+
const attribute = container.parent;
|
|
32956
|
+
if (!attribute || !isNodeOfType(attribute, "JSXAttribute") || getJsxAttributeName(attribute.name) !== "ref") return false;
|
|
32957
|
+
const openingElement = attribute.parent;
|
|
32958
|
+
return Boolean(openingElement && isNodeOfType(openingElement, "JSXOpeningElement") && isProvenIntrinsicJsxElement(openingElement, scopes));
|
|
32959
|
+
};
|
|
32984
32960
|
const isSafeCreateRefCallbackCurrentWrite = (referenceNode, accessedPropertyPath, targetPropertyPath, scopes) => {
|
|
32985
32961
|
if (accessedPropertyPath.length !== targetPropertyPath.length + 1 || !pathStartsWith$1(accessedPropertyPath, targetPropertyPath) || accessedPropertyPath[targetPropertyPath.length] !== "current") return false;
|
|
32986
32962
|
const memberExpression = collectMemberExpression(referenceNode);
|
|
@@ -35924,178 +35900,17 @@ const visitSynchronousFunctionBodies = (analysisFunctions, visitor) => {
|
|
|
35924
35900
|
walkInsideStatementBlocks(analysisFunction.body, visitor);
|
|
35925
35901
|
}
|
|
35926
35902
|
};
|
|
35927
|
-
const
|
|
35928
|
-
const
|
|
35929
|
-
if (isNodeOfType(unwrappedExpression, "Literal")) {
|
|
35930
|
-
const literalValue = unwrappedExpression.value;
|
|
35931
|
-
if (literalValue === null || typeof literalValue === "boolean" || typeof literalValue === "number" || typeof literalValue === "string") return { value: literalValue };
|
|
35932
|
-
return null;
|
|
35933
|
-
}
|
|
35934
|
-
if (isNodeOfType(unwrappedExpression, "Identifier")) {
|
|
35935
|
-
if (scopes.symbolFor(unwrappedExpression)?.id === stateSymbolId) return stateValue;
|
|
35936
|
-
if (unwrappedExpression.name === "undefined" && scopes.isGlobalReference(unwrappedExpression)) return { value: void 0 };
|
|
35937
|
-
const immutableSymbol = scopes.symbolFor(unwrappedExpression);
|
|
35938
|
-
if (immutableSymbol?.kind !== "const" || !immutableSymbol.initializer || !isNodeOfType(immutableSymbol.declarationNode, "VariableDeclarator") || immutableSymbol.declarationNode.id !== immutableSymbol.bindingIdentifier || immutableSymbol.declarationNode.init !== immutableSymbol.initializer || immutableSymbol.references.some((reference) => reference.flag !== "read") || visitedSymbolIds.has(immutableSymbol.id)) return null;
|
|
35939
|
-
return readStaticEffectValue(immutableSymbol.initializer, scopes, stateSymbolId, stateValue, new Set(visitedSymbolIds).add(immutableSymbol.id));
|
|
35940
|
-
}
|
|
35941
|
-
if (isNodeOfType(unwrappedExpression, "UnaryExpression")) {
|
|
35942
|
-
if (unwrappedExpression.operator === "void") return { value: void 0 };
|
|
35943
|
-
if (unwrappedExpression.operator !== "!") return null;
|
|
35944
|
-
const argumentValue = readStaticEffectValue(unwrappedExpression.argument, scopes, stateSymbolId, stateValue, visitedSymbolIds);
|
|
35945
|
-
return argumentValue ? { value: !argumentValue.value } : null;
|
|
35946
|
-
}
|
|
35947
|
-
if (isNodeOfType(unwrappedExpression, "CallExpression")) {
|
|
35948
|
-
if (isNodeOfType(unwrappedExpression.callee, "Identifier") && unwrappedExpression.callee.name === "Boolean" && scopes.isGlobalReference(unwrappedExpression.callee) && unwrappedExpression.arguments.length === 1 && unwrappedExpression.arguments[0] && !isNodeOfType(unwrappedExpression.arguments[0], "SpreadElement")) {
|
|
35949
|
-
const argumentValue = readStaticEffectValue(unwrappedExpression.arguments[0], scopes, stateSymbolId, stateValue, visitedSymbolIds);
|
|
35950
|
-
return argumentValue ? { value: Boolean(argumentValue.value) } : null;
|
|
35951
|
-
}
|
|
35952
|
-
return null;
|
|
35953
|
-
}
|
|
35954
|
-
if (isNodeOfType(unwrappedExpression, "LogicalExpression")) {
|
|
35955
|
-
const leftValue = readStaticEffectValue(unwrappedExpression.left, scopes, stateSymbolId, stateValue, visitedSymbolIds);
|
|
35956
|
-
if (!leftValue) return null;
|
|
35957
|
-
if (unwrappedExpression.operator === "&&" && !leftValue.value) return leftValue;
|
|
35958
|
-
if (unwrappedExpression.operator === "||" && leftValue.value) return leftValue;
|
|
35959
|
-
if (unwrappedExpression.operator === "??" && leftValue.value !== null && leftValue.value !== void 0) return leftValue;
|
|
35960
|
-
return readStaticEffectValue(unwrappedExpression.right, scopes, stateSymbolId, stateValue, visitedSymbolIds);
|
|
35961
|
-
}
|
|
35962
|
-
if (isNodeOfType(unwrappedExpression, "ConditionalExpression")) {
|
|
35963
|
-
const testValue = readStaticEffectValue(unwrappedExpression.test, scopes, stateSymbolId, stateValue, visitedSymbolIds);
|
|
35964
|
-
if (!testValue) return null;
|
|
35965
|
-
return readStaticEffectValue(testValue.value ? unwrappedExpression.consequent : unwrappedExpression.alternate, scopes, stateSymbolId, stateValue, visitedSymbolIds);
|
|
35966
|
-
}
|
|
35967
|
-
if (isNodeOfType(unwrappedExpression, "MemberExpression") && unwrappedExpression.optional) {
|
|
35968
|
-
const objectValue = readStaticEffectValue(unwrappedExpression.object, scopes, stateSymbolId, stateValue, visitedSymbolIds);
|
|
35969
|
-
if (objectValue?.value === null || objectValue?.value === void 0) return { value: void 0 };
|
|
35970
|
-
return null;
|
|
35971
|
-
}
|
|
35972
|
-
if (isNodeOfType(unwrappedExpression, "BinaryExpression")) {
|
|
35973
|
-
const leftValue = readStaticEffectValue(unwrappedExpression.left, scopes, stateSymbolId, stateValue, visitedSymbolIds);
|
|
35974
|
-
const rightValue = readStaticEffectValue(unwrappedExpression.right, scopes, stateSymbolId, stateValue, visitedSymbolIds);
|
|
35975
|
-
if (!leftValue || !rightValue) return null;
|
|
35976
|
-
if (unwrappedExpression.operator === "===" || unwrappedExpression.operator === "!==") {
|
|
35977
|
-
const areEqual = leftValue.value === rightValue.value;
|
|
35978
|
-
return { value: unwrappedExpression.operator === "===" ? areEqual : !areEqual };
|
|
35979
|
-
}
|
|
35980
|
-
if (unwrappedExpression.operator === "==" || unwrappedExpression.operator === "!=") {
|
|
35981
|
-
const isLeftNullish = leftValue.value === null || leftValue.value === void 0;
|
|
35982
|
-
const isRightNullish = rightValue.value === null || rightValue.value === void 0;
|
|
35983
|
-
if (!isLeftNullish && !isRightNullish && typeof leftValue.value !== typeof rightValue.value) return null;
|
|
35984
|
-
const areEqual = isLeftNullish || isRightNullish ? isLeftNullish && isRightNullish : leftValue.value === rightValue.value;
|
|
35985
|
-
return { value: unwrappedExpression.operator === "==" ? areEqual : !areEqual };
|
|
35986
|
-
}
|
|
35987
|
-
}
|
|
35988
|
-
return null;
|
|
35989
|
-
};
|
|
35990
|
-
const readStaticUpdaterReturnValue = (updater, scopes) => {
|
|
35991
|
-
if (!isFunctionLike$1(updater) || updater.async || updater.generator) return null;
|
|
35992
|
-
if (!isNodeOfType(updater.body, "BlockStatement")) return readStaticEffectValue(updater.body, scopes, null, null);
|
|
35993
|
-
if (updater.body.body.length === 0) return { value: void 0 };
|
|
35994
|
-
if (updater.body.body.length !== 1) return null;
|
|
35995
|
-
const returnStatement = updater.body.body[0];
|
|
35996
|
-
if (!isNodeOfType(returnStatement, "ReturnStatement")) return null;
|
|
35997
|
-
if (!returnStatement.argument) return { value: void 0 };
|
|
35998
|
-
return readStaticEffectValue(returnStatement.argument, scopes, null, null);
|
|
35999
|
-
};
|
|
36000
|
-
const readStaticSetterValue = (setterCall, scopes) => {
|
|
36001
|
-
const argument = setterCall.arguments[0];
|
|
36002
|
-
if (!argument) return { value: void 0 };
|
|
36003
|
-
if (isNodeOfType(argument, "SpreadElement")) return null;
|
|
36004
|
-
const updater = resolveExactLocalFunction(argument, scopes);
|
|
36005
|
-
if (updater) return readStaticUpdaterReturnValue(updater, scopes);
|
|
36006
|
-
return readStaticEffectValue(argument, scopes, null, null);
|
|
36007
|
-
};
|
|
36008
|
-
const collectStateWritesInEffect = (analysisFunctions, setterToStateName, scopes) => {
|
|
36009
|
-
const stateWrites = /* @__PURE__ */ new Map();
|
|
35903
|
+
const collectWrittenStateNamesInEffect = (analysisFunctions, setterToStateName) => {
|
|
35904
|
+
const writtenStateNames = /* @__PURE__ */ new Set();
|
|
36010
35905
|
visitSynchronousFunctionBodies(analysisFunctions, (child) => {
|
|
36011
35906
|
if (!isNodeOfType(child, "CallExpression")) return;
|
|
36012
35907
|
if (!isNodeOfType(child.callee, "Identifier")) return;
|
|
36013
35908
|
const stateName = setterToStateName.get(child.callee.name);
|
|
36014
|
-
if (
|
|
36015
|
-
const writeInfo = stateWrites.get(stateName) ?? {
|
|
36016
|
-
values: /* @__PURE__ */ new Set(),
|
|
36017
|
-
hasUnknownValue: false
|
|
36018
|
-
};
|
|
36019
|
-
const staticValue = readStaticSetterValue(child, scopes);
|
|
36020
|
-
if (staticValue) writeInfo.values.add(staticValue.value);
|
|
36021
|
-
else writeInfo.hasUnknownValue = true;
|
|
36022
|
-
stateWrites.set(stateName, writeInfo);
|
|
35909
|
+
if (stateName) writtenStateNames.add(stateName);
|
|
36023
35910
|
});
|
|
36024
|
-
return
|
|
36025
|
-
};
|
|
36026
|
-
const isGlobalBooleanCall = (node, scopes) => {
|
|
36027
|
-
return isNodeOfType(node, "CallExpression") && isNodeOfType(node.callee, "Identifier") && node.callee.name === "Boolean" && scopes.isGlobalReference(node.callee);
|
|
36028
|
-
};
|
|
36029
|
-
const isWorkNodeReachableForStateValue = (workNode, stateSymbolId, stateValue, scopes) => {
|
|
36030
|
-
let currentNode = workNode;
|
|
36031
|
-
while (currentNode.parent) {
|
|
36032
|
-
const parentNode = currentNode.parent;
|
|
36033
|
-
if (isFunctionLike$1(parentNode)) break;
|
|
36034
|
-
if (isNodeOfType(parentNode, "IfStatement")) {
|
|
36035
|
-
const testValue = readStaticEffectValue(parentNode.test, scopes, stateSymbolId, stateValue);
|
|
36036
|
-
if (testValue) {
|
|
36037
|
-
if (currentNode === parentNode.consequent && !testValue.value) return false;
|
|
36038
|
-
if (currentNode === parentNode.alternate && testValue.value) return false;
|
|
36039
|
-
}
|
|
36040
|
-
}
|
|
36041
|
-
if (isNodeOfType(parentNode, "ConditionalExpression")) {
|
|
36042
|
-
const testValue = readStaticEffectValue(parentNode.test, scopes, stateSymbolId, stateValue);
|
|
36043
|
-
if (testValue) {
|
|
36044
|
-
if (currentNode === parentNode.consequent && !testValue.value) return false;
|
|
36045
|
-
if (currentNode === parentNode.alternate && testValue.value) return false;
|
|
36046
|
-
}
|
|
36047
|
-
}
|
|
36048
|
-
if (isNodeOfType(parentNode, "LogicalExpression") && currentNode === parentNode.right) {
|
|
36049
|
-
const leftValue = readStaticEffectValue(parentNode.left, scopes, stateSymbolId, stateValue);
|
|
36050
|
-
if (leftValue) {
|
|
36051
|
-
if (parentNode.operator === "&&" && !leftValue.value) return false;
|
|
36052
|
-
if (parentNode.operator === "||" && leftValue.value) return false;
|
|
36053
|
-
if (parentNode.operator === "??" && leftValue.value !== null && leftValue.value !== void 0) return false;
|
|
36054
|
-
}
|
|
36055
|
-
}
|
|
36056
|
-
if (isNodeOfType(parentNode, "BlockStatement")) {
|
|
36057
|
-
const statementIndex = parentNode.body.findIndex((statement) => statement === currentNode);
|
|
36058
|
-
if (statementIndex >= 0) for (let index = 0; index < statementIndex; index += 1) {
|
|
36059
|
-
const earlierStatement = parentNode.body[index];
|
|
36060
|
-
if (!isNodeOfType(earlierStatement, "IfStatement") || earlierStatement.alternate || !statementAlwaysExits(earlierStatement.consequent)) continue;
|
|
36061
|
-
if (readStaticEffectValue(earlierStatement.test, scopes, stateSymbolId, stateValue)?.value) return false;
|
|
36062
|
-
}
|
|
36063
|
-
}
|
|
36064
|
-
currentNode = parentNode;
|
|
36065
|
-
}
|
|
36066
|
-
return true;
|
|
36067
|
-
};
|
|
36068
|
-
const isReaderWorkNode = (node, analysisFunctions, scopes) => {
|
|
36069
|
-
if (isNodeOfType(node, "CallExpression")) {
|
|
36070
|
-
if (isGlobalBooleanCall(node, scopes)) return false;
|
|
36071
|
-
const invokedFunction = resolveExactLocalFunction(node.callee, scopes);
|
|
36072
|
-
return !invokedFunction || !analysisFunctions.has(invokedFunction);
|
|
36073
|
-
}
|
|
36074
|
-
return isNodeOfType(node, "AssignmentExpression") || isNodeOfType(node, "UpdateExpression") || isNodeOfType(node, "NewExpression") || isNodeOfType(node, "TaggedTemplateExpression") || isNodeOfType(node, "ThrowStatement") || isNodeOfType(node, "UnaryExpression") && node.operator === "delete";
|
|
36075
|
-
};
|
|
36076
|
-
const canStateWriteReachReaderWork = (writeInfo, readerEffect, stateSymbolId, scopes) => {
|
|
36077
|
-
if (writeInfo.hasUnknownValue || stateSymbolId === null) return true;
|
|
36078
|
-
for (const writtenValue of writeInfo.values) {
|
|
36079
|
-
const stateValue = { value: writtenValue };
|
|
36080
|
-
let didFindReachableWork = false;
|
|
36081
|
-
visitSynchronousFunctionBodies(readerEffect.analysisFunctions, (child) => {
|
|
36082
|
-
if (didFindReachableWork || !isReaderWorkNode(child, readerEffect.analysisFunctions, scopes)) return;
|
|
36083
|
-
if (isWorkNodeReachableForStateValue(child, stateSymbolId, stateValue, scopes)) didFindReachableWork = true;
|
|
36084
|
-
});
|
|
36085
|
-
if (didFindReachableWork) return true;
|
|
36086
|
-
}
|
|
36087
|
-
return false;
|
|
35911
|
+
return writtenStateNames;
|
|
36088
35912
|
};
|
|
36089
35913
|
const EMPTY_CLEANUP_NAME_SET = /* @__PURE__ */ new Set();
|
|
36090
|
-
const NON_CONTAMINATING_MAP_METHOD_NAMES = new Set([
|
|
36091
|
-
"clear",
|
|
36092
|
-
"delete",
|
|
36093
|
-
"entries",
|
|
36094
|
-
"get",
|
|
36095
|
-
"has",
|
|
36096
|
-
"keys",
|
|
36097
|
-
"values"
|
|
36098
|
-
]);
|
|
36099
35914
|
const isFunctionShapedReturn = (returnedValue, setterToStateName, isExplicitReturnStatement) => {
|
|
36100
35915
|
if (isNodeOfType(returnedValue, "ArrowFunctionExpression") || isNodeOfType(returnedValue, "FunctionExpression")) return true;
|
|
36101
35916
|
if (isNodeOfType(returnedValue, "CallExpression")) {
|
|
@@ -36146,135 +35961,27 @@ const callsOpaqueExternalSetter = (analysisFunctions, setterToStateName) => {
|
|
|
36146
35961
|
});
|
|
36147
35962
|
return didFindOpaqueSetterCall;
|
|
36148
35963
|
};
|
|
36149
|
-
const isReactRefCall = (expression, scopes) => isNodeOfType(expression, "CallExpression") && (isReactApiCall(expression, "useRef", scopes, {
|
|
36150
|
-
allowGlobalReactNamespace: true,
|
|
36151
|
-
allowUnboundBareCalls: true,
|
|
36152
|
-
resolveNamedAliases: true
|
|
36153
|
-
}) || isReactApiCall(expression, "createRef", scopes, {
|
|
36154
|
-
allowGlobalReactNamespace: true,
|
|
36155
|
-
allowUnboundBareCalls: true,
|
|
36156
|
-
resolveNamedAliases: true
|
|
36157
|
-
}));
|
|
36158
|
-
const getDirectReactRefSymbol = (rawExpression, scopes) => {
|
|
36159
|
-
const expression = stripParenExpression(rawExpression);
|
|
36160
|
-
if (!isNodeOfType(expression, "Identifier")) return null;
|
|
36161
|
-
const symbol = scopes.symbolFor(expression);
|
|
36162
|
-
if (!symbol) return null;
|
|
36163
|
-
const initializer = getDirectUnreassignedInitializer(symbol);
|
|
36164
|
-
return initializer && isReactRefCall(stripParenExpression(initializer), scopes) ? symbol : null;
|
|
36165
|
-
};
|
|
36166
|
-
const isReactNativeJsxElement = (openingElement, scopes) => {
|
|
36167
|
-
if (!isNodeOfType(openingElement.name, "JSXIdentifier")) return false;
|
|
36168
|
-
const symbol = scopes.symbolFor(openingElement.name);
|
|
36169
|
-
const importDeclaration = symbol?.declarationNode.parent;
|
|
36170
|
-
return Boolean(symbol?.kind === "import" && importDeclaration && isNodeOfType(importDeclaration, "ImportDeclaration") && importDeclaration.source.value === "react-native");
|
|
36171
|
-
};
|
|
36172
|
-
const isDirectHostJsxRef = (symbol, scopes) => {
|
|
36173
|
-
let hostRefCount = 0;
|
|
36174
|
-
for (const reference of symbol.references) {
|
|
36175
|
-
const expression = findTransparentExpressionRoot(reference.identifier);
|
|
36176
|
-
const container = expression.parent;
|
|
36177
|
-
if (isNodeOfType(container, "MemberExpression") && container.object === expression && getStaticPropertyName(container) === "current") continue;
|
|
36178
|
-
if (!container || !isNodeOfType(container, "JSXExpressionContainer") || container.expression !== expression) return false;
|
|
36179
|
-
const attribute = container.parent;
|
|
36180
|
-
if (!attribute || !isNodeOfType(attribute, "JSXAttribute") || getJsxAttributeName(attribute.name) !== "ref") return false;
|
|
36181
|
-
const openingElement = attribute.parent;
|
|
36182
|
-
if (!openingElement || !isNodeOfType(openingElement, "JSXOpeningElement") || !isProvenIntrinsicJsxElement(openingElement, scopes) && !isReactNativeJsxElement(openingElement, scopes)) return false;
|
|
36183
|
-
hostRefCount += 1;
|
|
36184
|
-
}
|
|
36185
|
-
return hostRefCount > 0;
|
|
36186
|
-
};
|
|
36187
|
-
const isIntrinsicRefCallbackParameter = (expression, scopes) => {
|
|
36188
|
-
const identifier = stripParenExpression(expression);
|
|
36189
|
-
if (!isNodeOfType(identifier, "Identifier")) return false;
|
|
36190
|
-
const callback = findEnclosingFunction$1(identifier);
|
|
36191
|
-
if (!callback || !isFunctionLike$1(callback) || !isInlineIntrinsicRefCallback(callback, scopes)) return false;
|
|
36192
|
-
const rawFirstParameter = callback.params?.[0];
|
|
36193
|
-
const firstParameter = isNodeOfType(rawFirstParameter, "AssignmentPattern") ? rawFirstParameter.left : rawFirstParameter;
|
|
36194
|
-
const symbol = scopes.symbolFor(identifier);
|
|
36195
|
-
return Boolean(firstParameter && symbol?.bindingIdentifier === firstParameter);
|
|
36196
|
-
};
|
|
36197
|
-
const getDirectReactRefCall = (symbol, scopes) => {
|
|
36198
|
-
const initializer = getDirectUnreassignedInitializer(symbol);
|
|
36199
|
-
if (!initializer) return null;
|
|
36200
|
-
const expression = stripParenExpression(initializer);
|
|
36201
|
-
return isNodeOfType(expression, "CallExpression") && isReactRefCall(expression, scopes) ? expression : null;
|
|
36202
|
-
};
|
|
36203
|
-
const storesOnlyIntrinsicRefCallbackValues = (symbol, scopes) => {
|
|
36204
|
-
const initialValue = getDirectReactRefCall(symbol, scopes)?.arguments?.[0];
|
|
36205
|
-
if (!initialValue || !isNodeOfType(initialValue, "NewExpression") || !isNodeOfType(initialValue.callee, "Identifier") || initialValue.callee.name !== "Map" || !scopes.isGlobalReference(initialValue.callee) || initialValue.arguments.length !== 0) return false;
|
|
36206
|
-
let intrinsicValueWriteCount = 0;
|
|
36207
|
-
for (const reference of symbol.references) {
|
|
36208
|
-
const identifier = findTransparentExpressionRoot(reference.identifier);
|
|
36209
|
-
const currentMember = identifier.parent;
|
|
36210
|
-
if (!isNodeOfType(currentMember, "MemberExpression") || currentMember.object !== identifier || getStaticPropertyName(currentMember) !== "current") return false;
|
|
36211
|
-
const currentExpression = findTransparentExpressionRoot(currentMember);
|
|
36212
|
-
const methodMember = currentExpression.parent;
|
|
36213
|
-
if (!isNodeOfType(methodMember, "MemberExpression") || methodMember.object !== currentExpression) return false;
|
|
36214
|
-
const methodName = getStaticPropertyName(methodMember);
|
|
36215
|
-
if (methodName === "size") continue;
|
|
36216
|
-
const call = methodMember.parent;
|
|
36217
|
-
if (!isNodeOfType(call, "CallExpression") || call.callee !== methodMember) return false;
|
|
36218
|
-
if (methodName && NON_CONTAMINATING_MAP_METHOD_NAMES.has(methodName)) continue;
|
|
36219
|
-
if (methodName !== "set") return false;
|
|
36220
|
-
const storedValue = call.arguments[1];
|
|
36221
|
-
if (!storedValue || isNodeOfType(storedValue, "SpreadElement") || !isIntrinsicRefCallbackParameter(storedValue, scopes)) return false;
|
|
36222
|
-
intrinsicValueWriteCount += 1;
|
|
36223
|
-
}
|
|
36224
|
-
return intrinsicValueWriteCount > 0;
|
|
36225
|
-
};
|
|
36226
|
-
const isDerivedFromProvenDomRefCurrent = (rawExpression, scopes, didReadCollectionValue = false, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
|
|
36227
|
-
const expression = stripParenExpression(rawExpression);
|
|
36228
|
-
if (isNodeOfType(expression, "Identifier")) {
|
|
36229
|
-
const symbol = scopes.symbolFor(expression);
|
|
36230
|
-
if (!symbol || visitedSymbolIds.has(symbol.id)) return false;
|
|
36231
|
-
const initializer = getDirectUnreassignedInitializer(symbol);
|
|
36232
|
-
if (!initializer) return false;
|
|
36233
|
-
visitedSymbolIds.add(symbol.id);
|
|
36234
|
-
return isDerivedFromProvenDomRefCurrent(initializer, scopes, didReadCollectionValue, visitedSymbolIds);
|
|
36235
|
-
}
|
|
36236
|
-
if (isNodeOfType(expression, "MemberExpression")) {
|
|
36237
|
-
if (getStaticPropertyName(expression) === "current") {
|
|
36238
|
-
const symbol = getDirectReactRefSymbol(expression.object, scopes);
|
|
36239
|
-
return Boolean(symbol && (isDirectHostJsxRef(symbol, scopes) || didReadCollectionValue && storesOnlyIntrinsicRefCallbackValues(symbol, scopes)));
|
|
36240
|
-
}
|
|
36241
|
-
return isDerivedFromProvenDomRefCurrent(expression.object, scopes, didReadCollectionValue, visitedSymbolIds);
|
|
36242
|
-
}
|
|
36243
|
-
if (!isNodeOfType(expression, "CallExpression")) return false;
|
|
36244
|
-
const callee = stripParenExpression(expression.callee);
|
|
36245
|
-
if (!isNodeOfType(callee, "MemberExpression")) return false;
|
|
36246
|
-
return isDerivedFromProvenDomRefCurrent(callee.object, scopes, didReadCollectionValue || getStaticPropertyName(callee) === "get", visitedSymbolIds);
|
|
36247
|
-
};
|
|
36248
|
-
const isCommittedDomSyncNode = (node, scopes) => {
|
|
36249
|
-
if (!isNodeOfType(node, "CallExpression")) return false;
|
|
36250
|
-
const callee = stripParenExpression(node.callee);
|
|
36251
|
-
if (!isNodeOfType(callee, "MemberExpression")) return false;
|
|
36252
|
-
const propertyName = getStaticPropertyName(callee);
|
|
36253
|
-
if (propertyName === null || !EXTERNAL_SYNC_DOM_MEMBER_METHOD_NAMES.has(propertyName)) return false;
|
|
36254
|
-
return isDerivedFromProvenDomRefCurrent(callee.object, scopes) || isProvenBrowserApiReceiver(callee.object, "dom-event-target", scopes);
|
|
36255
|
-
};
|
|
36256
35964
|
const isExternalSyncNode = (node) => {
|
|
36257
35965
|
if (isNodeOfType(node, "NewExpression")) return isNodeOfType(node.callee, "Identifier") && EXTERNAL_SYNC_OBSERVER_CONSTRUCTORS.has(node.callee.name);
|
|
36258
35966
|
if (isNodeOfType(node, "AssignmentExpression")) return isNodeOfType(node.left, "MemberExpression") && isNodeOfType(node.left.property, "Identifier") && node.left.property.name === "current";
|
|
36259
35967
|
if (!isNodeOfType(node, "CallExpression")) return false;
|
|
36260
35968
|
if (isNodeOfType(node.callee, "Identifier")) return EXTERNAL_SYNC_DIRECT_CALLEE_NAMES.has(node.callee.name);
|
|
36261
|
-
if (!isNodeOfType(node.callee, "MemberExpression")) return false;
|
|
36262
|
-
const propertyName =
|
|
36263
|
-
if (propertyName === null) return false;
|
|
35969
|
+
if (!isNodeOfType(node.callee, "MemberExpression") || !isNodeOfType(node.callee.property, "Identifier")) return false;
|
|
35970
|
+
const propertyName = node.callee.property.name;
|
|
36264
35971
|
if (EXTERNAL_SYNC_MEMBER_METHOD_NAMES.has(propertyName)) return true;
|
|
36265
35972
|
if (isBrowserStorageReceiver(node.callee.object)) return true;
|
|
36266
35973
|
if (!EXTERNAL_SYNC_AMBIGUOUS_HTTP_METHOD_NAMES.has(propertyName)) return false;
|
|
36267
35974
|
const receiverRootName = getRootIdentifierName(node.callee.object);
|
|
36268
35975
|
return receiverRootName !== null && EXTERNAL_SYNC_HTTP_CLIENT_RECEIVERS.has(receiverRootName);
|
|
36269
35976
|
};
|
|
36270
|
-
const isExternalSyncEffect = (effectCallback, analysisFunctions, setterToStateName
|
|
35977
|
+
const isExternalSyncEffect = (effectCallback, analysisFunctions, setterToStateName) => {
|
|
36271
35978
|
if (!isFunctionLike$1(effectCallback)) return false;
|
|
36272
35979
|
if (!isNodeOfType(effectCallback.body, "BlockStatement")) {
|
|
36273
35980
|
if (isFunctionShapedReturn(effectCallback.body, setterToStateName, false)) return true;
|
|
36274
35981
|
} else for (const statement of effectCallback.body.body ?? []) if (isNodeOfType(statement, "ReturnStatement") && statement.argument && isFunctionShapedReturn(statement.argument, setterToStateName, true)) return true;
|
|
36275
35982
|
let didFindExternalCall = false;
|
|
36276
35983
|
visitSynchronousFunctionBodies(analysisFunctions, (child) => {
|
|
36277
|
-
if (isExternalSyncNode(child)
|
|
35984
|
+
if (isExternalSyncNode(child)) didFindExternalCall = true;
|
|
36278
35985
|
});
|
|
36279
35986
|
return didFindExternalCall;
|
|
36280
35987
|
};
|
|
@@ -36290,45 +35997,32 @@ const noEffectChain = defineRule({
|
|
|
36290
35997
|
const useStateBindings = collectUseStateBindings(componentBody);
|
|
36291
35998
|
if (useStateBindings.length === 0) return;
|
|
36292
35999
|
const setterToStateName = /* @__PURE__ */ new Map();
|
|
36293
|
-
const
|
|
36294
|
-
for (const binding of useStateBindings) {
|
|
36295
|
-
setterToStateName.set(binding.setterName, binding.valueName);
|
|
36296
|
-
if (!isNodeOfType(binding.declarator.id, "ArrayPattern")) continue;
|
|
36297
|
-
const stateIdentifier = binding.declarator.id.elements[0];
|
|
36298
|
-
if (isNodeOfType(stateIdentifier, "Identifier")) {
|
|
36299
|
-
const stateSymbol = context.scopes.symbolFor(stateIdentifier);
|
|
36300
|
-
if (stateSymbol) stateSymbolIds.set(binding.valueName, stateSymbol.id);
|
|
36301
|
-
}
|
|
36302
|
-
}
|
|
36000
|
+
for (const binding of useStateBindings) setterToStateName.set(binding.setterName, binding.valueName);
|
|
36303
36001
|
const storageSetterNames = collectStorageHookSetterNames(componentBody);
|
|
36304
36002
|
const effectInfos = [];
|
|
36305
36003
|
for (const effectCall of findTopLevelEffectCalls(componentBody)) {
|
|
36306
36004
|
const callback = getEffectCallback(effectCall, context.scopes);
|
|
36307
36005
|
if (!callback || !isFunctionLike$1(callback) || callback.async) continue;
|
|
36308
36006
|
const analysisFunctions = collectSynchronouslyInvokedFunctions(callback, context.scopes);
|
|
36309
|
-
const
|
|
36310
|
-
const writtenStateNames = new Set(stateWrites.keys());
|
|
36007
|
+
const writtenStateNames = collectWrittenStateNamesInEffect(analysisFunctions, setterToStateName);
|
|
36311
36008
|
effectInfos.push({
|
|
36312
36009
|
node: effectCall,
|
|
36313
36010
|
depNames: collectDepIdentifierNames(effectCall),
|
|
36314
|
-
|
|
36315
|
-
analysisFunctions,
|
|
36316
|
-
isExternalSync: isExternalSyncEffect(callback, analysisFunctions, setterToStateName, context.scopes, writtenStateNames.size === 0) || callsStorageHookSetter(analysisFunctions, storageSetterNames) || writtenStateNames.size === 0 && callsOpaqueExternalSetter(analysisFunctions, setterToStateName)
|
|
36011
|
+
writtenStateNames,
|
|
36012
|
+
isExternalSync: isExternalSyncEffect(callback, analysisFunctions, setterToStateName) || callsStorageHookSetter(analysisFunctions, storageSetterNames) || writtenStateNames.size === 0 && callsOpaqueExternalSetter(analysisFunctions, setterToStateName)
|
|
36317
36013
|
});
|
|
36318
36014
|
}
|
|
36319
36015
|
if (effectInfos.length < 2) return;
|
|
36320
36016
|
const reportedNodes = /* @__PURE__ */ new Set();
|
|
36321
36017
|
for (const writerEffect of effectInfos) {
|
|
36322
36018
|
if (writerEffect.isExternalSync) continue;
|
|
36323
|
-
if (writerEffect.
|
|
36019
|
+
if (writerEffect.writtenStateNames.size === 0) continue;
|
|
36324
36020
|
for (const readerEffect of effectInfos) {
|
|
36325
36021
|
if (readerEffect === writerEffect) continue;
|
|
36326
36022
|
if (readerEffect.isExternalSync) continue;
|
|
36327
36023
|
if (readerEffect.depNames.size === 0) continue;
|
|
36328
36024
|
let chainedStateName = null;
|
|
36329
|
-
for (const
|
|
36330
|
-
if (!readerEffect.depNames.has(writtenName)) continue;
|
|
36331
|
-
if (!canStateWriteReachReaderWork(writeInfo, readerEffect, stateSymbolIds.get(writtenName) ?? null, context.scopes)) continue;
|
|
36025
|
+
for (const writtenName of writerEffect.writtenStateNames) if (readerEffect.depNames.has(writtenName)) {
|
|
36332
36026
|
chainedStateName = writtenName;
|
|
36333
36027
|
break;
|
|
36334
36028
|
}
|
|
@@ -39995,18 +39689,6 @@ const functionContainsProvenReactHookCall = (functionNode, scopes) => {
|
|
|
39995
39689
|
return containsReactHookCall;
|
|
39996
39690
|
};
|
|
39997
39691
|
//#endregion
|
|
39998
|
-
//#region src/plugin/utils/function-returns-only-null.ts
|
|
39999
|
-
const isNullExpression = (expression) => {
|
|
40000
|
-
const candidate = stripParenExpression(expression);
|
|
40001
|
-
return isNodeOfType(candidate, "Literal") && candidate.value === null;
|
|
40002
|
-
};
|
|
40003
|
-
const functionReturnsOnlyNull = (functionNode) => {
|
|
40004
|
-
if (!isFunctionLike$1(functionNode)) return false;
|
|
40005
|
-
if (!isNodeOfType(functionNode.body, "BlockStatement")) return isNullExpression(functionNode.body);
|
|
40006
|
-
const returnStatements = collectFunctionReturnStatements(functionNode);
|
|
40007
|
-
return returnStatements.length > 0 && returnStatements.every((returnStatement) => Boolean(returnStatement.argument && isNullExpression(returnStatement.argument)));
|
|
40008
|
-
};
|
|
40009
|
-
//#endregion
|
|
40010
39692
|
//#region src/plugin/utils/function-returns-props-children.ts
|
|
40011
39693
|
const functionReturnsPropsChildren = (functionNode, scopes, controlFlow) => {
|
|
40012
39694
|
if (!isFunctionLike$1(functionNode) || functionNode.params.length === 0) return false;
|
|
@@ -40039,8 +39721,17 @@ const functionReturnsPropsChildren = (functionNode, scopes, controlFlow) => {
|
|
|
40039
39721
|
}, controlFlow);
|
|
40040
39722
|
};
|
|
40041
39723
|
//#endregion
|
|
40042
|
-
//#region src/plugin/utils/function-
|
|
40043
|
-
const
|
|
39724
|
+
//#region src/plugin/utils/function-returns-only-null.ts
|
|
39725
|
+
const isNullExpression = (expression) => {
|
|
39726
|
+
const candidate = stripParenExpression(expression);
|
|
39727
|
+
return isNodeOfType(candidate, "Literal") && candidate.value === null;
|
|
39728
|
+
};
|
|
39729
|
+
const functionReturnsOnlyNull = (functionNode) => {
|
|
39730
|
+
if (!isFunctionLike$1(functionNode)) return false;
|
|
39731
|
+
if (!isNodeOfType(functionNode.body, "BlockStatement")) return isNullExpression(functionNode.body);
|
|
39732
|
+
const returnStatements = collectFunctionReturnStatements(functionNode);
|
|
39733
|
+
return returnStatements.length > 0 && returnStatements.every((returnStatement) => Boolean(returnStatement.argument && isNullExpression(returnStatement.argument)));
|
|
39734
|
+
};
|
|
40044
39735
|
//#endregion
|
|
40045
39736
|
//#region src/plugin/utils/is-proven-styled-component-expression.ts
|
|
40046
39737
|
const findFactoryRoot = (node) => {
|
|
@@ -40073,16 +39764,17 @@ const isProvenStyledComponentExpression = (expression, scopes) => {
|
|
|
40073
39764
|
//#region src/plugin/utils/is-proven-react-component-symbol.ts
|
|
40074
39765
|
const REACT_COMPONENT_HOC_NAMES = new Set(["memo", "forwardRef"]);
|
|
40075
39766
|
const LEGACY_REACT_COMPONENT_FACTORY_NAMES = new Set(["createClass", "createReactClass"]);
|
|
39767
|
+
const functionHasComponentEvidence = (functionNode, scopes, controlFlow) => functionContainsReactRenderOutput(functionNode, scopes, controlFlow) || functionReturnsPropsChildren(functionNode, scopes, controlFlow) || functionContainsProvenReactHookCall(functionNode, scopes) && functionReturnsOnlyNull(functionNode);
|
|
40076
39768
|
const isProvenReactComponentExpression = (expression, scopes, controlFlow, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
|
|
40077
39769
|
const candidate = stripParenExpression(expression);
|
|
40078
|
-
if (isInlineFunctionExpression(candidate)) return
|
|
39770
|
+
if (isInlineFunctionExpression(candidate)) return functionHasComponentEvidence(candidate, scopes, controlFlow);
|
|
40079
39771
|
if (isNodeOfType(candidate, "ClassExpression")) return isProvenReactClassComponent(candidate, scopes);
|
|
40080
39772
|
if (isProvenStyledComponentExpression(candidate, scopes)) return true;
|
|
40081
39773
|
if (isNodeOfType(candidate, "Identifier")) {
|
|
40082
39774
|
const symbol = scopes.symbolFor(candidate);
|
|
40083
39775
|
if (!symbol || visitedSymbolIds.has(symbol.id) || hasSymbolWriteBefore(symbol, candidate, scopes)) return false;
|
|
40084
39776
|
visitedSymbolIds.add(symbol.id);
|
|
40085
|
-
if (isNodeOfType(symbol.declarationNode, "FunctionDeclaration")) return
|
|
39777
|
+
if (isNodeOfType(symbol.declarationNode, "FunctionDeclaration")) return functionHasComponentEvidence(symbol.declarationNode, scopes, controlFlow);
|
|
40086
39778
|
if (isNodeOfType(symbol.declarationNode, "ClassDeclaration") || isNodeOfType(symbol.declarationNode, "ClassExpression")) return isProvenReactClassComponent(symbol.declarationNode, scopes);
|
|
40087
39779
|
return Boolean(symbol.initializer && isProvenReactComponentExpression(symbol.initializer, scopes, controlFlow, visitedSymbolIds));
|
|
40088
39780
|
}
|
|
@@ -40109,7 +39801,7 @@ const isProvenReactComponentSymbol = (symbol, scopes, controlFlow, componentRefe
|
|
|
40109
39801
|
for (const candidateSymbol of candidateSymbols) {
|
|
40110
39802
|
if (hasSymbolWriteBefore(candidateSymbol, componentReference, scopes)) continue;
|
|
40111
39803
|
if (isComponentDeclaration(candidateSymbol.declarationNode)) {
|
|
40112
|
-
if (
|
|
39804
|
+
if (functionHasComponentEvidence(candidateSymbol.declarationNode, scopes, controlFlow)) return true;
|
|
40113
39805
|
continue;
|
|
40114
39806
|
}
|
|
40115
39807
|
const initializer = candidateSymbol.initializer ? stripParenExpression(candidateSymbol.initializer) : null;
|
|
@@ -43895,53 +43587,6 @@ const noPropCallbackInEffect = defineRule({
|
|
|
43895
43587
|
});
|
|
43896
43588
|
//#endregion
|
|
43897
43589
|
//#region src/plugin/rules/state-and-effects/no-prop-callback-in-render.ts
|
|
43898
|
-
const functionBindingSymbols = (functionNode, scopes) => {
|
|
43899
|
-
let bindingIdentifier = null;
|
|
43900
|
-
if (isNodeOfType(functionNode, "FunctionDeclaration") && functionNode.id) bindingIdentifier = functionNode.id;
|
|
43901
|
-
else {
|
|
43902
|
-
let bindingExpression = findTransparentExpressionRoot(functionNode);
|
|
43903
|
-
let parent = bindingExpression.parent;
|
|
43904
|
-
while (isNodeOfType(parent, "CallExpression") && parent.arguments[0] === bindingExpression) {
|
|
43905
|
-
const callee = parent.callee;
|
|
43906
|
-
const wrapperName = isNodeOfType(callee, "Identifier") ? callee.name : isNodeOfType(callee, "MemberExpression") && isNodeOfType(callee.property, "Identifier") ? callee.property.name : null;
|
|
43907
|
-
if (!isReactApiCall(parent, REACT_HOC_NAMES, scopes, {
|
|
43908
|
-
allowGlobalReactNamespace: true,
|
|
43909
|
-
resolveNamedAliases: true
|
|
43910
|
-
}) && (!wrapperName || REACT_HOC_NAMES.has(wrapperName) || !COMPONENT_HOC_WRAPPER_NAMES.has(wrapperName))) break;
|
|
43911
|
-
bindingExpression = findTransparentExpressionRoot(parent);
|
|
43912
|
-
parent = bindingExpression.parent;
|
|
43913
|
-
}
|
|
43914
|
-
if (isNodeOfType(parent, "VariableDeclarator") && parent.init === bindingExpression && isNodeOfType(parent.id, "Identifier")) bindingIdentifier = parent.id;
|
|
43915
|
-
}
|
|
43916
|
-
if (!bindingIdentifier) return [];
|
|
43917
|
-
let scope = scopes.scopeFor(functionNode);
|
|
43918
|
-
while (scope) {
|
|
43919
|
-
const symbols = scope.symbols.filter((symbol) => symbol.bindingIdentifier === bindingIdentifier);
|
|
43920
|
-
if (symbols.length > 0) return symbols;
|
|
43921
|
-
scope = scope.parent;
|
|
43922
|
-
}
|
|
43923
|
-
return [];
|
|
43924
|
-
};
|
|
43925
|
-
const symbolHasReactComponentUse = (symbol, scopes, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
|
|
43926
|
-
if (visitedSymbolIds.has(symbol.id)) return false;
|
|
43927
|
-
visitedSymbolIds.add(symbol.id);
|
|
43928
|
-
for (const reference of symbol.references) {
|
|
43929
|
-
const identifier = reference.identifier;
|
|
43930
|
-
if (hasSymbolWriteBefore(symbol, identifier, scopes)) continue;
|
|
43931
|
-
const parent = identifier.parent;
|
|
43932
|
-
if (isNodeOfType(parent, "JSXOpeningElement") && isNodeOfType(parent.name, "JSXIdentifier") && parent.name === identifier) return true;
|
|
43933
|
-
const expression = findTransparentExpressionRoot(identifier);
|
|
43934
|
-
const expressionParent = expression.parent;
|
|
43935
|
-
if (isNodeOfType(expressionParent, "CallExpression") && expressionParent.arguments[0] === expression && isReactApiCall(expressionParent, "createElement", scopes, { resolveNamedAliases: true })) return true;
|
|
43936
|
-
if (!isNodeOfType(expressionParent, "VariableDeclarator") || expressionParent.init !== expression || !isNodeOfType(expressionParent.id, "Identifier") || !isNodeOfType(expressionParent.parent, "VariableDeclaration") || expressionParent.parent.kind !== "const") continue;
|
|
43937
|
-
const aliasSymbol = scopes.symbolFor(expressionParent.id);
|
|
43938
|
-
if (aliasSymbol && symbolHasReactComponentUse(aliasSymbol, scopes, visitedSymbolIds)) return true;
|
|
43939
|
-
}
|
|
43940
|
-
return false;
|
|
43941
|
-
};
|
|
43942
|
-
const functionHasReactComponentUse = (functionNode, scopes) => {
|
|
43943
|
-
return functionBindingSymbols(functionNode, scopes).some((symbol) => symbolHasReactComponentUse(symbol, scopes));
|
|
43944
|
-
};
|
|
43945
43590
|
const isPreservedThroughConciseArrow = (callExpression, scopes) => {
|
|
43946
43591
|
let node = callExpression;
|
|
43947
43592
|
let parent = node.parent;
|
|
@@ -43988,10 +43633,7 @@ const noPropCallbackInRender = defineRule({
|
|
|
43988
43633
|
create: (context) => ({ CallExpression(node) {
|
|
43989
43634
|
if (!isResultDiscardedCall(node)) return;
|
|
43990
43635
|
if (isPreservedThroughConciseArrow(node, context.scopes)) return;
|
|
43991
|
-
|
|
43992
|
-
if (!renderPhaseOwner) return;
|
|
43993
|
-
const renderPhaseOwnerName = componentOrHookDisplayNameForFunction(renderPhaseOwner);
|
|
43994
|
-
if (!renderPhaseOwnerName || !isReactHookName(renderPhaseOwnerName) && !functionHasReactComponentEvidence(renderPhaseOwner, context.scopes, context.cfg) && !functionHasReactComponentUse(renderPhaseOwner, context.scopes)) return;
|
|
43636
|
+
if (!findRenderPhaseComponentOrHook(node, context.scopes)) return;
|
|
43995
43637
|
const analysis = getProgramAnalysis(node);
|
|
43996
43638
|
if (!analysis) return;
|
|
43997
43639
|
const callee = stripParenExpression(node.callee);
|
|
@@ -44727,110 +44369,11 @@ const isSameRefCurrentMember = (node, refSymbol, scopes) => {
|
|
|
44727
44369
|
return isNodeOfType(receiver, "Identifier") && resolveConstIdentifierAlias(receiver, scopes)?.id === refSymbol.id;
|
|
44728
44370
|
};
|
|
44729
44371
|
const isSameRefCurrentAlias = (node, refSymbol, scopes) => {
|
|
44730
|
-
|
|
44731
|
-
if (
|
|
44732
|
-
|
|
44733
|
-
const aliasSymbol = scopes.symbolFor(expression);
|
|
44372
|
+
if (isSameRefCurrentMember(node, refSymbol, scopes)) return true;
|
|
44373
|
+
if (!isNodeOfType(node, "Identifier")) return false;
|
|
44374
|
+
const aliasSymbol = scopes.symbolFor(node);
|
|
44734
44375
|
return aliasSymbol?.kind === "const" && aliasSymbol.initializer !== null && isSameRefCurrentMember(stripParenExpression(aliasSymbol.initializer), refSymbol, scopes);
|
|
44735
44376
|
};
|
|
44736
|
-
const resolveImmutableInitializationValue = (node, scopes, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
|
|
44737
|
-
const expression = stripParenExpression(node);
|
|
44738
|
-
if (!isNodeOfType(expression, "Identifier")) return expression;
|
|
44739
|
-
const symbol = scopes.symbolFor(expression);
|
|
44740
|
-
if (!symbol || symbol.kind !== "const" || !symbol.initializer || symbol.references.some((reference) => reference.flag !== "read") || visitedSymbolIds.has(symbol.id)) return null;
|
|
44741
|
-
visitedSymbolIds.add(symbol.id);
|
|
44742
|
-
return resolveImmutableInitializationValue(symbol.initializer, scopes, visitedSymbolIds);
|
|
44743
|
-
};
|
|
44744
|
-
const isProvablyTruthyInitializationValue = (node, scopes) => {
|
|
44745
|
-
const expression = resolveImmutableInitializationValue(node, scopes);
|
|
44746
|
-
return Boolean(expression && (isNodeOfType(expression, "NewExpression") || isNodeOfType(expression, "ObjectExpression") || isNodeOfType(expression, "ArrayExpression") || isNodeOfType(expression, "ArrowFunctionExpression") || isNodeOfType(expression, "FunctionExpression") || isNodeOfType(expression, "ClassExpression")));
|
|
44747
|
-
};
|
|
44748
|
-
const getInitializationConstructorName = (node, scopes) => {
|
|
44749
|
-
const expression = resolveImmutableInitializationValue(node, scopes);
|
|
44750
|
-
if (!expression) return null;
|
|
44751
|
-
if (isNodeOfType(expression, "NewExpression")) {
|
|
44752
|
-
const callee = stripParenExpression(expression.callee);
|
|
44753
|
-
return isNodeOfType(callee, "Identifier") ? callee.name : null;
|
|
44754
|
-
}
|
|
44755
|
-
return null;
|
|
44756
|
-
};
|
|
44757
|
-
const isClosedTruthyTypeDomain = (typeNode, initializationValue, scopes) => {
|
|
44758
|
-
const initializationExpression = stripParenExpression(initializationValue);
|
|
44759
|
-
if (isNodeOfType(typeNode, "TSTypeLiteral")) return isNodeOfType(initializationExpression, "ObjectExpression");
|
|
44760
|
-
if (isNodeOfType(typeNode, "TSArrayType") || isNodeOfType(typeNode, "TSTupleType")) return isNodeOfType(initializationExpression, "ArrayExpression");
|
|
44761
|
-
if (isNodeOfType(typeNode, "TSFunctionType") || isNodeOfType(typeNode, "TSConstructorType")) return isNodeOfType(initializationExpression, "ArrowFunctionExpression") || isNodeOfType(initializationExpression, "FunctionExpression") || isNodeOfType(initializationExpression, "ClassExpression");
|
|
44762
|
-
if (isNodeOfType(typeNode, "TSObjectKeyword")) return true;
|
|
44763
|
-
if (!isNodeOfType(typeNode, "TSTypeReference")) return false;
|
|
44764
|
-
const typeName = typeNode.typeName;
|
|
44765
|
-
return isNodeOfType(typeName, "Identifier") && typeName.name === getInitializationConstructorName(initializationExpression, scopes);
|
|
44766
|
-
};
|
|
44767
|
-
const refHasClosedFalsySentinelDomain = (refSymbol, initializationValue, scopes) => {
|
|
44768
|
-
const initializer = refSymbol.initializer ? stripParenExpression(refSymbol.initializer) : null;
|
|
44769
|
-
if (!initializer || !isNodeOfType(initializer, "CallExpression")) return false;
|
|
44770
|
-
const [initialValue] = initializer.arguments ?? [];
|
|
44771
|
-
if (!initialValue || isNodeOfType(initialValue, "SpreadElement") || !isEmptySentinel(initialValue, scopes)) return false;
|
|
44772
|
-
const [declaredType] = initializer.typeArguments?.params ?? [];
|
|
44773
|
-
if (!declaredType || !isNodeOfType(declaredType, "TSUnionType")) return false;
|
|
44774
|
-
let hasEmptySentinel = false;
|
|
44775
|
-
let hasTruthyDomain = false;
|
|
44776
|
-
for (const memberType of declaredType.types ?? []) {
|
|
44777
|
-
if (isNodeOfType(memberType, "TSNullKeyword") || isNodeOfType(memberType, "TSUndefinedKeyword")) {
|
|
44778
|
-
hasEmptySentinel = true;
|
|
44779
|
-
continue;
|
|
44780
|
-
}
|
|
44781
|
-
if (!isClosedTruthyTypeDomain(memberType, initializationValue, scopes)) return false;
|
|
44782
|
-
hasTruthyDomain = true;
|
|
44783
|
-
}
|
|
44784
|
-
return hasEmptySentinel && hasTruthyDomain;
|
|
44785
|
-
};
|
|
44786
|
-
const isSafeRefIdentifierUse = (identifier) => {
|
|
44787
|
-
const expressionRoot = findTransparentExpressionRoot(identifier);
|
|
44788
|
-
const parent = expressionRoot.parent;
|
|
44789
|
-
if (parent && isNodeOfType(parent, "VariableDeclarator") && parent.id === expressionRoot && parent.parent !== null && isNodeOfType(parent.parent, "VariableDeclaration") && parent.parent.kind === "const") return true;
|
|
44790
|
-
if (parent && isNodeOfType(parent, "MemberExpression") && parent.object === expressionRoot && getStaticPropertyName(parent) === "current") return true;
|
|
44791
|
-
if (!parent || !isNodeOfType(parent, "VariableDeclarator") || parent.init !== expressionRoot) return false;
|
|
44792
|
-
return isNodeOfType(parent.id, "Identifier") && parent.parent !== null && isNodeOfType(parent.parent, "VariableDeclaration") && parent.parent.kind === "const";
|
|
44793
|
-
};
|
|
44794
|
-
const refDoesNotEscape = (branchRoot, refSymbol, scopes) => {
|
|
44795
|
-
let didEscape = false;
|
|
44796
|
-
walkAst(branchRoot, (child) => {
|
|
44797
|
-
if (didEscape) return false;
|
|
44798
|
-
if (!isNodeOfType(child, "Identifier")) return;
|
|
44799
|
-
if (resolveConstIdentifierAlias(child, scopes)?.id !== refSymbol.id) return;
|
|
44800
|
-
if (child === refSymbol.bindingIdentifier || isSafeRefIdentifierUse(child)) return;
|
|
44801
|
-
didEscape = true;
|
|
44802
|
-
return false;
|
|
44803
|
-
});
|
|
44804
|
-
return !didEscape;
|
|
44805
|
-
};
|
|
44806
|
-
const expressionContainsRefCurrent = (expression, refSymbol, scopes) => {
|
|
44807
|
-
let didFindRefCurrent = false;
|
|
44808
|
-
walkAst(expression, (child) => {
|
|
44809
|
-
if (didFindRefCurrent) return false;
|
|
44810
|
-
if (resolveReactRefSymbol(child, scopes)?.id !== refSymbol.id) return;
|
|
44811
|
-
didFindRefCurrent = true;
|
|
44812
|
-
return false;
|
|
44813
|
-
});
|
|
44814
|
-
return didFindRefCurrent;
|
|
44815
|
-
};
|
|
44816
|
-
const hasNoCompetingRefCurrentWrite = (branchRoot, assignmentExpression, refSymbol, scopes) => {
|
|
44817
|
-
let writeCount = 0;
|
|
44818
|
-
walkAst(branchRoot, (child) => {
|
|
44819
|
-
if (writeCount > 1) return false;
|
|
44820
|
-
if (isNodeOfType(child, "AssignmentExpression")) {
|
|
44821
|
-
if (expressionContainsRefCurrent(child.left, refSymbol, scopes)) writeCount++;
|
|
44822
|
-
return;
|
|
44823
|
-
}
|
|
44824
|
-
if (isNodeOfType(child, "UpdateExpression") || isNodeOfType(child, "UnaryExpression") && child.operator === "delete") {
|
|
44825
|
-
if (expressionContainsRefCurrent(child.argument, refSymbol, scopes)) writeCount++;
|
|
44826
|
-
return;
|
|
44827
|
-
}
|
|
44828
|
-
if (isNodeOfType(child, "ForInStatement") || isNodeOfType(child, "ForOfStatement")) {
|
|
44829
|
-
if (expressionContainsRefCurrent(child.left, refSymbol, scopes)) writeCount++;
|
|
44830
|
-
}
|
|
44831
|
-
});
|
|
44832
|
-
return writeCount === 1 && expressionContainsRefCurrent(assignmentExpression.left, refSymbol, scopes);
|
|
44833
|
-
};
|
|
44834
44377
|
const isEmptySentinel = (node, scopes) => isNodeOfType(node, "Literal") && node.value === null || isNodeOfType(node, "Identifier") && node.name === "undefined" && scopes.isGlobalReference(node);
|
|
44835
44378
|
const hasRepeatedExecutionAncestor = (node, stop) => {
|
|
44836
44379
|
let ancestor = node.parent;
|
|
@@ -44879,22 +44422,18 @@ const hasNoPriorCoExecutableWrite = (assignmentExpression, branchRoot, refSymbol
|
|
|
44879
44422
|
const isDocumentedLazyInitialization = (assignmentExpression, refSymbol, scopes) => {
|
|
44880
44423
|
if (assignmentExpression.operator === "??=" || assignmentExpression.operator === "||=") return true;
|
|
44881
44424
|
if (assignmentExpression.operator !== "=") return false;
|
|
44882
|
-
const renderOwner = findRenderPhaseComponentOrHook(assignmentExpression, scopes);
|
|
44883
|
-
if (!renderOwner) return false;
|
|
44884
44425
|
let descendant = assignmentExpression;
|
|
44885
44426
|
let ancestor = descendant.parent;
|
|
44886
44427
|
while (ancestor) {
|
|
44887
|
-
|
|
44888
|
-
if (isNodeOfType(ancestor, "IfStatement") && test && isNodeOfType(test, "UnaryExpression") && test.operator === "!" && isSameRefCurrentAlias(test.argument, refSymbol, scopes) && ancestor.consequent === descendant && isProvablyTruthyInitializationValue(assignmentExpression.right, scopes) && refHasClosedFalsySentinelDomain(refSymbol, assignmentExpression.right, scopes) && !hasRepeatedExecutionAncestor(assignmentExpression, ancestor.consequent) && !hasRepeatedExecutionAncestor(ancestor, renderOwner) && hasNoPriorCoExecutableWrite(assignmentExpression, ancestor.consequent, refSymbol, scopes) && hasNoCompetingRefCurrentWrite(renderOwner, assignmentExpression, refSymbol, scopes) && refDoesNotEscape(renderOwner, refSymbol, scopes)) return true;
|
|
44889
|
-
if (isNodeOfType(ancestor, "IfStatement") && isNodeOfType(test, "BinaryExpression") && [
|
|
44428
|
+
if (isNodeOfType(ancestor, "IfStatement") && isNodeOfType(ancestor.test, "BinaryExpression") && [
|
|
44890
44429
|
"===",
|
|
44891
44430
|
"==",
|
|
44892
44431
|
"!==",
|
|
44893
44432
|
"!="
|
|
44894
|
-
].includes(test.operator)) {
|
|
44895
|
-
const { left, right } = test;
|
|
44433
|
+
].includes(ancestor.test.operator)) {
|
|
44434
|
+
const { left, right } = ancestor.test;
|
|
44896
44435
|
const comparesEmptySentinel = isSameRefCurrentAlias(left, refSymbol, scopes) && isEmptySentinel(right, scopes) || isSameRefCurrentAlias(right, refSymbol, scopes) && isEmptySentinel(left, scopes);
|
|
44897
|
-
const guardedBranch = test.operator === "===" || test.operator === "==" ? ancestor.consequent : ancestor.alternate;
|
|
44436
|
+
const guardedBranch = ancestor.test.operator === "===" || ancestor.test.operator === "==" ? ancestor.consequent : ancestor.alternate;
|
|
44898
44437
|
if (comparesEmptySentinel && guardedBranch === descendant && guardedBranch && !hasRepeatedExecutionAncestor(assignmentExpression, guardedBranch) && hasNoPriorCoExecutableWrite(assignmentExpression, guardedBranch, refSymbol, scopes)) return true;
|
|
44899
44438
|
}
|
|
44900
44439
|
descendant = ancestor;
|
|
@@ -52019,31 +51558,6 @@ const preferModuleScopePureFunction = defineRule({
|
|
|
52019
51558
|
}
|
|
52020
51559
|
});
|
|
52021
51560
|
//#endregion
|
|
52022
|
-
//#region src/plugin/utils/get-require-call-source.ts
|
|
52023
|
-
const getRequireCallSource = (expression) => {
|
|
52024
|
-
const unwrappedExpression = stripParenExpression(expression);
|
|
52025
|
-
if (isNodeOfType(unwrappedExpression, "MemberExpression")) return getRequireCallSource(unwrappedExpression.object);
|
|
52026
|
-
if (!isNodeOfType(unwrappedExpression, "CallExpression")) return null;
|
|
52027
|
-
if (!isNodeOfType(unwrappedExpression.callee, "Identifier") || unwrappedExpression.callee.name !== "require") return null;
|
|
52028
|
-
const [firstArgument] = unwrappedExpression.arguments ?? [];
|
|
52029
|
-
if (!firstArgument || !isNodeOfType(firstArgument, "Literal")) return null;
|
|
52030
|
-
return typeof firstArgument.value === "string" ? firstArgument.value : null;
|
|
52031
|
-
};
|
|
52032
|
-
//#endregion
|
|
52033
|
-
//#region src/plugin/utils/is-proven-node-crypto-namespace-reference.ts
|
|
52034
|
-
const NODE_CRYPTO_MODULE_SOURCES = new Set(["crypto", "node:crypto"]);
|
|
52035
|
-
const isProvenNodeCryptoNamespaceReference = (expression, scopes) => {
|
|
52036
|
-
const identifier = stripParenExpression(expression);
|
|
52037
|
-
if (!isNodeOfType(identifier, "Identifier")) return false;
|
|
52038
|
-
const symbol = resolveConstIdentifierAlias(identifier, scopes);
|
|
52039
|
-
if (!symbol) return false;
|
|
52040
|
-
if (symbol.kind === "import") {
|
|
52041
|
-
const importBinding = getImportBindingForName(identifier, symbol.name);
|
|
52042
|
-
return Boolean(importBinding && NODE_CRYPTO_MODULE_SOURCES.has(importBinding.source));
|
|
52043
|
-
}
|
|
52044
|
-
return Boolean(symbol.kind === "const" && symbol.initializer && NODE_CRYPTO_MODULE_SOURCES.has(getRequireCallSource(symbol.initializer) ?? ""));
|
|
52045
|
-
};
|
|
52046
|
-
//#endregion
|
|
52047
51561
|
//#region src/plugin/rules/architecture/prefer-module-scope-static-value.ts
|
|
52048
51562
|
const MUTATING_RECEIVER_METHOD_NAMES = new Set([...MUTATING_ARRAY_METHODS, ...MUTATING_COLLECTION_METHODS]);
|
|
52049
51563
|
const isMutationContext = (referenceIdentifier) => {
|
|
@@ -52157,9 +51671,9 @@ const isImpureCall = (node, scopes) => {
|
|
|
52157
51671
|
const callee = node.callee;
|
|
52158
51672
|
if (isNodeOfType(callee, "Identifier")) return isImpureBareCallee(callee, scopes);
|
|
52159
51673
|
if (!isNodeOfType(callee, "MemberExpression") || callee.computed) return false;
|
|
51674
|
+
if (!isNodeOfType(callee.object, "Identifier")) return false;
|
|
52160
51675
|
if (!isNodeOfType(callee.property, "Identifier")) return false;
|
|
52161
|
-
|
|
52162
|
-
return false;
|
|
51676
|
+
return Boolean(IMPURE_MEMBER_RECEIVERS.get(callee.object.name)?.has(callee.property.name));
|
|
52163
51677
|
};
|
|
52164
51678
|
const containsImpureExpression = (expression, scopes) => {
|
|
52165
51679
|
let foundImpure = false;
|
|
@@ -56928,6 +56442,16 @@ const rnListCallbackPerRow = defineRule({
|
|
|
56928
56442
|
}
|
|
56929
56443
|
});
|
|
56930
56444
|
//#endregion
|
|
56445
|
+
//#region src/plugin/utils/get-require-call-source.ts
|
|
56446
|
+
const getRequireCallSource = (expression) => {
|
|
56447
|
+
if (isNodeOfType(expression, "MemberExpression")) return getRequireCallSource(expression.object);
|
|
56448
|
+
if (!isNodeOfType(expression, "CallExpression")) return null;
|
|
56449
|
+
if (!isNodeOfType(expression.callee, "Identifier") || expression.callee.name !== "require") return null;
|
|
56450
|
+
const [firstArgument] = expression.arguments ?? [];
|
|
56451
|
+
if (!firstArgument || !isNodeOfType(firstArgument, "Literal")) return null;
|
|
56452
|
+
return typeof firstArgument.value === "string" ? firstArgument.value : null;
|
|
56453
|
+
};
|
|
56454
|
+
//#endregion
|
|
56931
56455
|
//#region src/plugin/utils/get-initializer-module-source.ts
|
|
56932
56456
|
const getInitializerModuleSource = (contextNode, initializer) => {
|
|
56933
56457
|
const requireSource = getRequireCallSource(initializer);
|