oxlint-plugin-react-doctor 0.7.9-dev.c88a39a → 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 +56 -341
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -19996,28 +19996,6 @@ const jsLengthCheckFirst = defineRule({
|
|
|
19996
19996
|
} })
|
|
19997
19997
|
});
|
|
19998
19998
|
//#endregion
|
|
19999
|
-
//#region src/plugin/utils/is-proven-global-namespace-reference.ts
|
|
20000
|
-
const isProvenGlobalObjectReference = (expression, scopes, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
|
|
20001
|
-
const strippedExpression = stripParenExpression(expression);
|
|
20002
|
-
if (!isNodeOfType(strippedExpression, "Identifier")) return false;
|
|
20003
|
-
if ((strippedExpression.name === "globalThis" || strippedExpression.name === "window" || strippedExpression.name === "self" || strippedExpression.name === "global") && scopes.isGlobalReference(strippedExpression)) return true;
|
|
20004
|
-
const symbol = scopes.symbolFor(strippedExpression);
|
|
20005
|
-
if (!symbol?.initializer || symbol.kind !== "const" || visitedSymbolIds.has(symbol.id)) return false;
|
|
20006
|
-
visitedSymbolIds.add(symbol.id);
|
|
20007
|
-
return isProvenGlobalObjectReference(symbol.initializer, scopes, visitedSymbolIds);
|
|
20008
|
-
};
|
|
20009
|
-
const isProvenGlobalNamespaceReference = (expression, namespaceName, scopes, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
|
|
20010
|
-
const strippedExpression = stripParenExpression(expression);
|
|
20011
|
-
if (isNodeOfType(strippedExpression, "Identifier")) {
|
|
20012
|
-
if (strippedExpression.name === namespaceName && scopes.isGlobalReference(strippedExpression)) return true;
|
|
20013
|
-
const symbol = scopes.symbolFor(strippedExpression);
|
|
20014
|
-
if (!symbol?.initializer || symbol.kind !== "const" || visitedSymbolIds.has(symbol.id)) return false;
|
|
20015
|
-
visitedSymbolIds.add(symbol.id);
|
|
20016
|
-
return isProvenGlobalNamespaceReference(symbol.initializer, namespaceName, scopes, visitedSymbolIds);
|
|
20017
|
-
}
|
|
20018
|
-
return isNodeOfType(strippedExpression, "MemberExpression") && getStaticPropertyName(strippedExpression) === namespaceName && isProvenGlobalObjectReference(strippedExpression.object, scopes);
|
|
20019
|
-
};
|
|
20020
|
-
//#endregion
|
|
20021
19999
|
//#region src/plugin/rules/js-performance/js-min-max-loop.ts
|
|
20022
20000
|
const builtinMutationByProgram = /* @__PURE__ */ new WeakMap();
|
|
20023
20001
|
const RUNTIMELESS_SYMBOL_KINDS = new Set(["ts-interface", "ts-type-alias"]);
|
|
@@ -20074,6 +20052,26 @@ const isSafeFreshNumericArray = (arrayExpression) => {
|
|
|
20074
20052
|
}
|
|
20075
20053
|
return !(didFindPositiveZero && didFindNegativeZero);
|
|
20076
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
|
+
};
|
|
20077
20075
|
const resolvesToGlobalMethod = (expression, namespaceName, methodNames, scopes, visitedSymbols = /* @__PURE__ */ new Set()) => {
|
|
20078
20076
|
const strippedExpression = stripParenExpression(expression);
|
|
20079
20077
|
if (isNodeOfType(strippedExpression, "Identifier")) {
|
|
@@ -20082,7 +20080,7 @@ const resolvesToGlobalMethod = (expression, namespaceName, methodNames, scopes,
|
|
|
20082
20080
|
visitedSymbols.add(symbol.id);
|
|
20083
20081
|
return resolvesToGlobalMethod(symbol.initializer, namespaceName, methodNames, scopes, visitedSymbols);
|
|
20084
20082
|
}
|
|
20085
|
-
return isNodeOfType(strippedExpression, "MemberExpression") && methodNames.has(getStaticPropertyName(strippedExpression) ?? "") &&
|
|
20083
|
+
return isNodeOfType(strippedExpression, "MemberExpression") && methodNames.has(getStaticPropertyName(strippedExpression) ?? "") && resolvesToGlobalNamespace(strippedExpression.object, namespaceName, scopes);
|
|
20086
20084
|
};
|
|
20087
20085
|
const resolvesToNativeArrayPrototype = (expression, scopes, visitedSymbols = /* @__PURE__ */ new Set()) => {
|
|
20088
20086
|
const strippedExpression = stripParenExpression(expression);
|
|
@@ -20094,7 +20092,7 @@ const resolvesToNativeArrayPrototype = (expression, scopes, visitedSymbols = /*
|
|
|
20094
20092
|
}
|
|
20095
20093
|
if (isNodeOfType(strippedExpression, "MemberExpression")) {
|
|
20096
20094
|
const propertyName = getStaticPropertyName(strippedExpression);
|
|
20097
|
-
if (propertyName === "prototype") return
|
|
20095
|
+
if (propertyName === "prototype") return resolvesToGlobalNamespace(strippedExpression.object, "Array", scopes);
|
|
20098
20096
|
return propertyName === "__proto__" && isNodeOfType(stripParenExpression(strippedExpression.object), "ArrayExpression");
|
|
20099
20097
|
}
|
|
20100
20098
|
if (!isNodeOfType(strippedExpression, "CallExpression")) return false;
|
|
@@ -20105,23 +20103,23 @@ const resolvesToNativeArrayPrototype = (expression, scopes, visitedSymbols = /*
|
|
|
20105
20103
|
const isGlobalNamespaceReplacementTarget = (target, namespaceName, scopes) => {
|
|
20106
20104
|
const strippedTarget = stripParenExpression(target);
|
|
20107
20105
|
if (isNodeOfType(strippedTarget, "Identifier")) return strippedTarget.name === namespaceName && scopes.isGlobalReference(strippedTarget);
|
|
20108
|
-
return isNodeOfType(strippedTarget, "MemberExpression") && getStaticPropertyName(strippedTarget) === namespaceName &&
|
|
20106
|
+
return isNodeOfType(strippedTarget, "MemberExpression") && getStaticPropertyName(strippedTarget) === namespaceName && isGlobalObjectReference(strippedTarget.object, scopes);
|
|
20109
20107
|
};
|
|
20110
20108
|
const isUnsafeBuiltinMemberTarget = (target, targetFunction, scopes) => {
|
|
20111
20109
|
const strippedTarget = stripParenExpression(target);
|
|
20112
20110
|
if (!isNodeOfType(strippedTarget, "MemberExpression")) return false;
|
|
20113
20111
|
const propertyName = getStaticPropertyName(strippedTarget);
|
|
20114
20112
|
if (resolvesToNativeArrayPrototype(strippedTarget.object, scopes)) return propertyName === null || propertyName === "sort";
|
|
20115
|
-
if (
|
|
20116
|
-
return
|
|
20113
|
+
if (resolvesToGlobalNamespace(strippedTarget.object, "Math", scopes)) return propertyName === null || propertyName === targetFunction;
|
|
20114
|
+
return isGlobalObjectReference(strippedTarget.object, scopes) && (propertyName === null || propertyName === "Math");
|
|
20117
20115
|
};
|
|
20118
20116
|
const isUnsafeBuiltinMutationApiCall = (callExpression, targetFunction, scopes) => {
|
|
20119
20117
|
const target = callExpression.arguments[0];
|
|
20120
20118
|
if (!target) return false;
|
|
20121
20119
|
let propertyName = null;
|
|
20122
20120
|
if (resolvesToNativeArrayPrototype(target, scopes)) propertyName = "sort";
|
|
20123
|
-
else if (
|
|
20124
|
-
else if (
|
|
20121
|
+
else if (resolvesToGlobalNamespace(target, "Math", scopes)) propertyName = targetFunction;
|
|
20122
|
+
else if (isGlobalObjectReference(target, scopes)) propertyName = "Math";
|
|
20125
20123
|
if (!propertyName) return false;
|
|
20126
20124
|
const canObjectExpressionSetProperty = (properties) => {
|
|
20127
20125
|
if (!isNodeOfType(properties, "ObjectExpression")) return true;
|
|
@@ -20172,7 +20170,7 @@ const hasUnsafeMathBinding = (node, scopes) => {
|
|
|
20172
20170
|
let scope = scopes.scopeFor(node);
|
|
20173
20171
|
while (scope) {
|
|
20174
20172
|
const symbol = scope.symbolsByName.get("Math");
|
|
20175
|
-
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));
|
|
20176
20174
|
scope = scope.parent;
|
|
20177
20175
|
}
|
|
20178
20176
|
return false;
|
|
@@ -35902,167 +35900,15 @@ const visitSynchronousFunctionBodies = (analysisFunctions, visitor) => {
|
|
|
35902
35900
|
walkInsideStatementBlocks(analysisFunction.body, visitor);
|
|
35903
35901
|
}
|
|
35904
35902
|
};
|
|
35905
|
-
const
|
|
35906
|
-
const
|
|
35907
|
-
if (isNodeOfType(unwrappedExpression, "Literal")) {
|
|
35908
|
-
const literalValue = unwrappedExpression.value;
|
|
35909
|
-
if (literalValue === null || typeof literalValue === "boolean" || typeof literalValue === "number" || typeof literalValue === "string") return { value: literalValue };
|
|
35910
|
-
return null;
|
|
35911
|
-
}
|
|
35912
|
-
if (isNodeOfType(unwrappedExpression, "Identifier")) {
|
|
35913
|
-
if (scopes.symbolFor(unwrappedExpression)?.id === stateSymbolId) return stateValue;
|
|
35914
|
-
if (unwrappedExpression.name === "undefined" && scopes.isGlobalReference(unwrappedExpression)) return { value: void 0 };
|
|
35915
|
-
const immutableSymbol = scopes.symbolFor(unwrappedExpression);
|
|
35916
|
-
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;
|
|
35917
|
-
return readStaticEffectValue(immutableSymbol.initializer, scopes, stateSymbolId, stateValue, new Set(visitedSymbolIds).add(immutableSymbol.id));
|
|
35918
|
-
}
|
|
35919
|
-
if (isNodeOfType(unwrappedExpression, "UnaryExpression")) {
|
|
35920
|
-
if (unwrappedExpression.operator === "void") return { value: void 0 };
|
|
35921
|
-
if (unwrappedExpression.operator !== "!") return null;
|
|
35922
|
-
const argumentValue = readStaticEffectValue(unwrappedExpression.argument, scopes, stateSymbolId, stateValue, visitedSymbolIds);
|
|
35923
|
-
return argumentValue ? { value: !argumentValue.value } : null;
|
|
35924
|
-
}
|
|
35925
|
-
if (isNodeOfType(unwrappedExpression, "CallExpression")) {
|
|
35926
|
-
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")) {
|
|
35927
|
-
const argumentValue = readStaticEffectValue(unwrappedExpression.arguments[0], scopes, stateSymbolId, stateValue, visitedSymbolIds);
|
|
35928
|
-
return argumentValue ? { value: Boolean(argumentValue.value) } : null;
|
|
35929
|
-
}
|
|
35930
|
-
return null;
|
|
35931
|
-
}
|
|
35932
|
-
if (isNodeOfType(unwrappedExpression, "LogicalExpression")) {
|
|
35933
|
-
const leftValue = readStaticEffectValue(unwrappedExpression.left, scopes, stateSymbolId, stateValue, visitedSymbolIds);
|
|
35934
|
-
if (!leftValue) return null;
|
|
35935
|
-
if (unwrappedExpression.operator === "&&" && !leftValue.value) return leftValue;
|
|
35936
|
-
if (unwrappedExpression.operator === "||" && leftValue.value) return leftValue;
|
|
35937
|
-
if (unwrappedExpression.operator === "??" && leftValue.value !== null && leftValue.value !== void 0) return leftValue;
|
|
35938
|
-
return readStaticEffectValue(unwrappedExpression.right, scopes, stateSymbolId, stateValue, visitedSymbolIds);
|
|
35939
|
-
}
|
|
35940
|
-
if (isNodeOfType(unwrappedExpression, "ConditionalExpression")) {
|
|
35941
|
-
const testValue = readStaticEffectValue(unwrappedExpression.test, scopes, stateSymbolId, stateValue, visitedSymbolIds);
|
|
35942
|
-
if (!testValue) return null;
|
|
35943
|
-
return readStaticEffectValue(testValue.value ? unwrappedExpression.consequent : unwrappedExpression.alternate, scopes, stateSymbolId, stateValue, visitedSymbolIds);
|
|
35944
|
-
}
|
|
35945
|
-
if (isNodeOfType(unwrappedExpression, "MemberExpression") && unwrappedExpression.optional) {
|
|
35946
|
-
const objectValue = readStaticEffectValue(unwrappedExpression.object, scopes, stateSymbolId, stateValue, visitedSymbolIds);
|
|
35947
|
-
if (objectValue?.value === null || objectValue?.value === void 0) return { value: void 0 };
|
|
35948
|
-
return null;
|
|
35949
|
-
}
|
|
35950
|
-
if (isNodeOfType(unwrappedExpression, "BinaryExpression")) {
|
|
35951
|
-
const leftValue = readStaticEffectValue(unwrappedExpression.left, scopes, stateSymbolId, stateValue, visitedSymbolIds);
|
|
35952
|
-
const rightValue = readStaticEffectValue(unwrappedExpression.right, scopes, stateSymbolId, stateValue, visitedSymbolIds);
|
|
35953
|
-
if (!leftValue || !rightValue) return null;
|
|
35954
|
-
if (unwrappedExpression.operator === "===" || unwrappedExpression.operator === "!==") {
|
|
35955
|
-
const areEqual = leftValue.value === rightValue.value;
|
|
35956
|
-
return { value: unwrappedExpression.operator === "===" ? areEqual : !areEqual };
|
|
35957
|
-
}
|
|
35958
|
-
if (unwrappedExpression.operator === "==" || unwrappedExpression.operator === "!=") {
|
|
35959
|
-
const isLeftNullish = leftValue.value === null || leftValue.value === void 0;
|
|
35960
|
-
const isRightNullish = rightValue.value === null || rightValue.value === void 0;
|
|
35961
|
-
if (!isLeftNullish && !isRightNullish && typeof leftValue.value !== typeof rightValue.value) return null;
|
|
35962
|
-
const areEqual = isLeftNullish || isRightNullish ? isLeftNullish && isRightNullish : leftValue.value === rightValue.value;
|
|
35963
|
-
return { value: unwrappedExpression.operator === "==" ? areEqual : !areEqual };
|
|
35964
|
-
}
|
|
35965
|
-
}
|
|
35966
|
-
return null;
|
|
35967
|
-
};
|
|
35968
|
-
const readStaticUpdaterReturnValue = (updater, scopes) => {
|
|
35969
|
-
if (!isFunctionLike$1(updater) || updater.async || updater.generator) return null;
|
|
35970
|
-
if (!isNodeOfType(updater.body, "BlockStatement")) return readStaticEffectValue(updater.body, scopes, null, null);
|
|
35971
|
-
if (updater.body.body.length === 0) return { value: void 0 };
|
|
35972
|
-
if (updater.body.body.length !== 1) return null;
|
|
35973
|
-
const returnStatement = updater.body.body[0];
|
|
35974
|
-
if (!isNodeOfType(returnStatement, "ReturnStatement")) return null;
|
|
35975
|
-
if (!returnStatement.argument) return { value: void 0 };
|
|
35976
|
-
return readStaticEffectValue(returnStatement.argument, scopes, null, null);
|
|
35977
|
-
};
|
|
35978
|
-
const readStaticSetterValue = (setterCall, scopes) => {
|
|
35979
|
-
const argument = setterCall.arguments[0];
|
|
35980
|
-
if (!argument) return { value: void 0 };
|
|
35981
|
-
if (isNodeOfType(argument, "SpreadElement")) return null;
|
|
35982
|
-
const updater = resolveExactLocalFunction(argument, scopes);
|
|
35983
|
-
if (updater) return readStaticUpdaterReturnValue(updater, scopes);
|
|
35984
|
-
return readStaticEffectValue(argument, scopes, null, null);
|
|
35985
|
-
};
|
|
35986
|
-
const collectStateWritesInEffect = (analysisFunctions, setterToStateName, scopes) => {
|
|
35987
|
-
const stateWrites = /* @__PURE__ */ new Map();
|
|
35903
|
+
const collectWrittenStateNamesInEffect = (analysisFunctions, setterToStateName) => {
|
|
35904
|
+
const writtenStateNames = /* @__PURE__ */ new Set();
|
|
35988
35905
|
visitSynchronousFunctionBodies(analysisFunctions, (child) => {
|
|
35989
35906
|
if (!isNodeOfType(child, "CallExpression")) return;
|
|
35990
35907
|
if (!isNodeOfType(child.callee, "Identifier")) return;
|
|
35991
35908
|
const stateName = setterToStateName.get(child.callee.name);
|
|
35992
|
-
if (
|
|
35993
|
-
const writeInfo = stateWrites.get(stateName) ?? {
|
|
35994
|
-
values: /* @__PURE__ */ new Set(),
|
|
35995
|
-
hasUnknownValue: false
|
|
35996
|
-
};
|
|
35997
|
-
const staticValue = readStaticSetterValue(child, scopes);
|
|
35998
|
-
if (staticValue) writeInfo.values.add(staticValue.value);
|
|
35999
|
-
else writeInfo.hasUnknownValue = true;
|
|
36000
|
-
stateWrites.set(stateName, writeInfo);
|
|
35909
|
+
if (stateName) writtenStateNames.add(stateName);
|
|
36001
35910
|
});
|
|
36002
|
-
return
|
|
36003
|
-
};
|
|
36004
|
-
const isGlobalBooleanCall = (node, scopes) => {
|
|
36005
|
-
return isNodeOfType(node, "CallExpression") && isNodeOfType(node.callee, "Identifier") && node.callee.name === "Boolean" && scopes.isGlobalReference(node.callee);
|
|
36006
|
-
};
|
|
36007
|
-
const isWorkNodeReachableForStateValue = (workNode, stateSymbolId, stateValue, scopes) => {
|
|
36008
|
-
let currentNode = workNode;
|
|
36009
|
-
while (currentNode.parent) {
|
|
36010
|
-
const parentNode = currentNode.parent;
|
|
36011
|
-
if (isFunctionLike$1(parentNode)) break;
|
|
36012
|
-
if (isNodeOfType(parentNode, "IfStatement")) {
|
|
36013
|
-
const testValue = readStaticEffectValue(parentNode.test, scopes, stateSymbolId, stateValue);
|
|
36014
|
-
if (testValue) {
|
|
36015
|
-
if (currentNode === parentNode.consequent && !testValue.value) return false;
|
|
36016
|
-
if (currentNode === parentNode.alternate && testValue.value) return false;
|
|
36017
|
-
}
|
|
36018
|
-
}
|
|
36019
|
-
if (isNodeOfType(parentNode, "ConditionalExpression")) {
|
|
36020
|
-
const testValue = readStaticEffectValue(parentNode.test, scopes, stateSymbolId, stateValue);
|
|
36021
|
-
if (testValue) {
|
|
36022
|
-
if (currentNode === parentNode.consequent && !testValue.value) return false;
|
|
36023
|
-
if (currentNode === parentNode.alternate && testValue.value) return false;
|
|
36024
|
-
}
|
|
36025
|
-
}
|
|
36026
|
-
if (isNodeOfType(parentNode, "LogicalExpression") && currentNode === parentNode.right) {
|
|
36027
|
-
const leftValue = readStaticEffectValue(parentNode.left, scopes, stateSymbolId, stateValue);
|
|
36028
|
-
if (leftValue) {
|
|
36029
|
-
if (parentNode.operator === "&&" && !leftValue.value) return false;
|
|
36030
|
-
if (parentNode.operator === "||" && leftValue.value) return false;
|
|
36031
|
-
if (parentNode.operator === "??" && leftValue.value !== null && leftValue.value !== void 0) return false;
|
|
36032
|
-
}
|
|
36033
|
-
}
|
|
36034
|
-
if (isNodeOfType(parentNode, "BlockStatement")) {
|
|
36035
|
-
const statementIndex = parentNode.body.findIndex((statement) => statement === currentNode);
|
|
36036
|
-
if (statementIndex >= 0) for (let index = 0; index < statementIndex; index += 1) {
|
|
36037
|
-
const earlierStatement = parentNode.body[index];
|
|
36038
|
-
if (!isNodeOfType(earlierStatement, "IfStatement") || earlierStatement.alternate || !statementAlwaysExits(earlierStatement.consequent)) continue;
|
|
36039
|
-
if (readStaticEffectValue(earlierStatement.test, scopes, stateSymbolId, stateValue)?.value) return false;
|
|
36040
|
-
}
|
|
36041
|
-
}
|
|
36042
|
-
currentNode = parentNode;
|
|
36043
|
-
}
|
|
36044
|
-
return true;
|
|
36045
|
-
};
|
|
36046
|
-
const isReaderWorkNode = (node, analysisFunctions, scopes) => {
|
|
36047
|
-
if (isNodeOfType(node, "CallExpression")) {
|
|
36048
|
-
if (isGlobalBooleanCall(node, scopes)) return false;
|
|
36049
|
-
const invokedFunction = resolveExactLocalFunction(node.callee, scopes);
|
|
36050
|
-
return !invokedFunction || !analysisFunctions.has(invokedFunction);
|
|
36051
|
-
}
|
|
36052
|
-
return isNodeOfType(node, "AssignmentExpression") || isNodeOfType(node, "UpdateExpression") || isNodeOfType(node, "NewExpression") || isNodeOfType(node, "TaggedTemplateExpression") || isNodeOfType(node, "ThrowStatement") || isNodeOfType(node, "UnaryExpression") && node.operator === "delete";
|
|
36053
|
-
};
|
|
36054
|
-
const canStateWriteReachReaderWork = (writeInfo, readerEffect, stateSymbolId, scopes) => {
|
|
36055
|
-
if (writeInfo.hasUnknownValue || stateSymbolId === null) return true;
|
|
36056
|
-
for (const writtenValue of writeInfo.values) {
|
|
36057
|
-
const stateValue = { value: writtenValue };
|
|
36058
|
-
let didFindReachableWork = false;
|
|
36059
|
-
visitSynchronousFunctionBodies(readerEffect.analysisFunctions, (child) => {
|
|
36060
|
-
if (didFindReachableWork || !isReaderWorkNode(child, readerEffect.analysisFunctions, scopes)) return;
|
|
36061
|
-
if (isWorkNodeReachableForStateValue(child, stateSymbolId, stateValue, scopes)) didFindReachableWork = true;
|
|
36062
|
-
});
|
|
36063
|
-
if (didFindReachableWork) return true;
|
|
36064
|
-
}
|
|
36065
|
-
return false;
|
|
35911
|
+
return writtenStateNames;
|
|
36066
35912
|
};
|
|
36067
35913
|
const EMPTY_CLEANUP_NAME_SET = /* @__PURE__ */ new Set();
|
|
36068
35914
|
const isFunctionShapedReturn = (returnedValue, setterToStateName, isExplicitReturnStatement) => {
|
|
@@ -36151,29 +35997,18 @@ const noEffectChain = defineRule({
|
|
|
36151
35997
|
const useStateBindings = collectUseStateBindings(componentBody);
|
|
36152
35998
|
if (useStateBindings.length === 0) return;
|
|
36153
35999
|
const setterToStateName = /* @__PURE__ */ new Map();
|
|
36154
|
-
const
|
|
36155
|
-
for (const binding of useStateBindings) {
|
|
36156
|
-
setterToStateName.set(binding.setterName, binding.valueName);
|
|
36157
|
-
if (!isNodeOfType(binding.declarator.id, "ArrayPattern")) continue;
|
|
36158
|
-
const stateIdentifier = binding.declarator.id.elements[0];
|
|
36159
|
-
if (isNodeOfType(stateIdentifier, "Identifier")) {
|
|
36160
|
-
const stateSymbol = context.scopes.symbolFor(stateIdentifier);
|
|
36161
|
-
if (stateSymbol) stateSymbolIds.set(binding.valueName, stateSymbol.id);
|
|
36162
|
-
}
|
|
36163
|
-
}
|
|
36000
|
+
for (const binding of useStateBindings) setterToStateName.set(binding.setterName, binding.valueName);
|
|
36164
36001
|
const storageSetterNames = collectStorageHookSetterNames(componentBody);
|
|
36165
36002
|
const effectInfos = [];
|
|
36166
36003
|
for (const effectCall of findTopLevelEffectCalls(componentBody)) {
|
|
36167
36004
|
const callback = getEffectCallback(effectCall, context.scopes);
|
|
36168
36005
|
if (!callback || !isFunctionLike$1(callback) || callback.async) continue;
|
|
36169
36006
|
const analysisFunctions = collectSynchronouslyInvokedFunctions(callback, context.scopes);
|
|
36170
|
-
const
|
|
36171
|
-
const writtenStateNames = new Set(stateWrites.keys());
|
|
36007
|
+
const writtenStateNames = collectWrittenStateNamesInEffect(analysisFunctions, setterToStateName);
|
|
36172
36008
|
effectInfos.push({
|
|
36173
36009
|
node: effectCall,
|
|
36174
36010
|
depNames: collectDepIdentifierNames(effectCall),
|
|
36175
|
-
|
|
36176
|
-
analysisFunctions,
|
|
36011
|
+
writtenStateNames,
|
|
36177
36012
|
isExternalSync: isExternalSyncEffect(callback, analysisFunctions, setterToStateName) || callsStorageHookSetter(analysisFunctions, storageSetterNames) || writtenStateNames.size === 0 && callsOpaqueExternalSetter(analysisFunctions, setterToStateName)
|
|
36178
36013
|
});
|
|
36179
36014
|
}
|
|
@@ -36181,15 +36016,13 @@ const noEffectChain = defineRule({
|
|
|
36181
36016
|
const reportedNodes = /* @__PURE__ */ new Set();
|
|
36182
36017
|
for (const writerEffect of effectInfos) {
|
|
36183
36018
|
if (writerEffect.isExternalSync) continue;
|
|
36184
|
-
if (writerEffect.
|
|
36019
|
+
if (writerEffect.writtenStateNames.size === 0) continue;
|
|
36185
36020
|
for (const readerEffect of effectInfos) {
|
|
36186
36021
|
if (readerEffect === writerEffect) continue;
|
|
36187
36022
|
if (readerEffect.isExternalSync) continue;
|
|
36188
36023
|
if (readerEffect.depNames.size === 0) continue;
|
|
36189
36024
|
let chainedStateName = null;
|
|
36190
|
-
for (const
|
|
36191
|
-
if (!readerEffect.depNames.has(writtenName)) continue;
|
|
36192
|
-
if (!canStateWriteReachReaderWork(writeInfo, readerEffect, stateSymbolIds.get(writtenName) ?? null, context.scopes)) continue;
|
|
36025
|
+
for (const writtenName of writerEffect.writtenStateNames) if (readerEffect.depNames.has(writtenName)) {
|
|
36193
36026
|
chainedStateName = writtenName;
|
|
36194
36027
|
break;
|
|
36195
36028
|
}
|
|
@@ -44536,110 +44369,11 @@ const isSameRefCurrentMember = (node, refSymbol, scopes) => {
|
|
|
44536
44369
|
return isNodeOfType(receiver, "Identifier") && resolveConstIdentifierAlias(receiver, scopes)?.id === refSymbol.id;
|
|
44537
44370
|
};
|
|
44538
44371
|
const isSameRefCurrentAlias = (node, refSymbol, scopes) => {
|
|
44539
|
-
|
|
44540
|
-
if (
|
|
44541
|
-
|
|
44542
|
-
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);
|
|
44543
44375
|
return aliasSymbol?.kind === "const" && aliasSymbol.initializer !== null && isSameRefCurrentMember(stripParenExpression(aliasSymbol.initializer), refSymbol, scopes);
|
|
44544
44376
|
};
|
|
44545
|
-
const resolveImmutableInitializationValue = (node, scopes, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
|
|
44546
|
-
const expression = stripParenExpression(node);
|
|
44547
|
-
if (!isNodeOfType(expression, "Identifier")) return expression;
|
|
44548
|
-
const symbol = scopes.symbolFor(expression);
|
|
44549
|
-
if (!symbol || symbol.kind !== "const" || !symbol.initializer || symbol.references.some((reference) => reference.flag !== "read") || visitedSymbolIds.has(symbol.id)) return null;
|
|
44550
|
-
visitedSymbolIds.add(symbol.id);
|
|
44551
|
-
return resolveImmutableInitializationValue(symbol.initializer, scopes, visitedSymbolIds);
|
|
44552
|
-
};
|
|
44553
|
-
const isProvablyTruthyInitializationValue = (node, scopes) => {
|
|
44554
|
-
const expression = resolveImmutableInitializationValue(node, scopes);
|
|
44555
|
-
return Boolean(expression && (isNodeOfType(expression, "NewExpression") || isNodeOfType(expression, "ObjectExpression") || isNodeOfType(expression, "ArrayExpression") || isNodeOfType(expression, "ArrowFunctionExpression") || isNodeOfType(expression, "FunctionExpression") || isNodeOfType(expression, "ClassExpression")));
|
|
44556
|
-
};
|
|
44557
|
-
const getInitializationConstructorName = (node, scopes) => {
|
|
44558
|
-
const expression = resolveImmutableInitializationValue(node, scopes);
|
|
44559
|
-
if (!expression) return null;
|
|
44560
|
-
if (isNodeOfType(expression, "NewExpression")) {
|
|
44561
|
-
const callee = stripParenExpression(expression.callee);
|
|
44562
|
-
return isNodeOfType(callee, "Identifier") ? callee.name : null;
|
|
44563
|
-
}
|
|
44564
|
-
return null;
|
|
44565
|
-
};
|
|
44566
|
-
const isClosedTruthyTypeDomain = (typeNode, initializationValue, scopes) => {
|
|
44567
|
-
const initializationExpression = stripParenExpression(initializationValue);
|
|
44568
|
-
if (isNodeOfType(typeNode, "TSTypeLiteral")) return isNodeOfType(initializationExpression, "ObjectExpression");
|
|
44569
|
-
if (isNodeOfType(typeNode, "TSArrayType") || isNodeOfType(typeNode, "TSTupleType")) return isNodeOfType(initializationExpression, "ArrayExpression");
|
|
44570
|
-
if (isNodeOfType(typeNode, "TSFunctionType") || isNodeOfType(typeNode, "TSConstructorType")) return isNodeOfType(initializationExpression, "ArrowFunctionExpression") || isNodeOfType(initializationExpression, "FunctionExpression") || isNodeOfType(initializationExpression, "ClassExpression");
|
|
44571
|
-
if (isNodeOfType(typeNode, "TSObjectKeyword")) return true;
|
|
44572
|
-
if (!isNodeOfType(typeNode, "TSTypeReference")) return false;
|
|
44573
|
-
const typeName = typeNode.typeName;
|
|
44574
|
-
return isNodeOfType(typeName, "Identifier") && typeName.name === getInitializationConstructorName(initializationExpression, scopes);
|
|
44575
|
-
};
|
|
44576
|
-
const refHasClosedFalsySentinelDomain = (refSymbol, initializationValue, scopes) => {
|
|
44577
|
-
const initializer = refSymbol.initializer ? stripParenExpression(refSymbol.initializer) : null;
|
|
44578
|
-
if (!initializer || !isNodeOfType(initializer, "CallExpression")) return false;
|
|
44579
|
-
const [initialValue] = initializer.arguments ?? [];
|
|
44580
|
-
if (!initialValue || isNodeOfType(initialValue, "SpreadElement") || !isEmptySentinel(initialValue, scopes)) return false;
|
|
44581
|
-
const [declaredType] = initializer.typeArguments?.params ?? [];
|
|
44582
|
-
if (!declaredType || !isNodeOfType(declaredType, "TSUnionType")) return false;
|
|
44583
|
-
let hasEmptySentinel = false;
|
|
44584
|
-
let hasTruthyDomain = false;
|
|
44585
|
-
for (const memberType of declaredType.types ?? []) {
|
|
44586
|
-
if (isNodeOfType(memberType, "TSNullKeyword") || isNodeOfType(memberType, "TSUndefinedKeyword")) {
|
|
44587
|
-
hasEmptySentinel = true;
|
|
44588
|
-
continue;
|
|
44589
|
-
}
|
|
44590
|
-
if (!isClosedTruthyTypeDomain(memberType, initializationValue, scopes)) return false;
|
|
44591
|
-
hasTruthyDomain = true;
|
|
44592
|
-
}
|
|
44593
|
-
return hasEmptySentinel && hasTruthyDomain;
|
|
44594
|
-
};
|
|
44595
|
-
const isSafeRefIdentifierUse = (identifier) => {
|
|
44596
|
-
const expressionRoot = findTransparentExpressionRoot(identifier);
|
|
44597
|
-
const parent = expressionRoot.parent;
|
|
44598
|
-
if (parent && isNodeOfType(parent, "VariableDeclarator") && parent.id === expressionRoot && parent.parent !== null && isNodeOfType(parent.parent, "VariableDeclaration") && parent.parent.kind === "const") return true;
|
|
44599
|
-
if (parent && isNodeOfType(parent, "MemberExpression") && parent.object === expressionRoot && getStaticPropertyName(parent) === "current") return true;
|
|
44600
|
-
if (!parent || !isNodeOfType(parent, "VariableDeclarator") || parent.init !== expressionRoot) return false;
|
|
44601
|
-
return isNodeOfType(parent.id, "Identifier") && parent.parent !== null && isNodeOfType(parent.parent, "VariableDeclaration") && parent.parent.kind === "const";
|
|
44602
|
-
};
|
|
44603
|
-
const refDoesNotEscape = (branchRoot, refSymbol, scopes) => {
|
|
44604
|
-
let didEscape = false;
|
|
44605
|
-
walkAst(branchRoot, (child) => {
|
|
44606
|
-
if (didEscape) return false;
|
|
44607
|
-
if (!isNodeOfType(child, "Identifier")) return;
|
|
44608
|
-
if (resolveConstIdentifierAlias(child, scopes)?.id !== refSymbol.id) return;
|
|
44609
|
-
if (child === refSymbol.bindingIdentifier || isSafeRefIdentifierUse(child)) return;
|
|
44610
|
-
didEscape = true;
|
|
44611
|
-
return false;
|
|
44612
|
-
});
|
|
44613
|
-
return !didEscape;
|
|
44614
|
-
};
|
|
44615
|
-
const expressionContainsRefCurrent = (expression, refSymbol, scopes) => {
|
|
44616
|
-
let didFindRefCurrent = false;
|
|
44617
|
-
walkAst(expression, (child) => {
|
|
44618
|
-
if (didFindRefCurrent) return false;
|
|
44619
|
-
if (resolveReactRefSymbol(child, scopes)?.id !== refSymbol.id) return;
|
|
44620
|
-
didFindRefCurrent = true;
|
|
44621
|
-
return false;
|
|
44622
|
-
});
|
|
44623
|
-
return didFindRefCurrent;
|
|
44624
|
-
};
|
|
44625
|
-
const hasNoCompetingRefCurrentWrite = (branchRoot, assignmentExpression, refSymbol, scopes) => {
|
|
44626
|
-
let writeCount = 0;
|
|
44627
|
-
walkAst(branchRoot, (child) => {
|
|
44628
|
-
if (writeCount > 1) return false;
|
|
44629
|
-
if (isNodeOfType(child, "AssignmentExpression")) {
|
|
44630
|
-
if (expressionContainsRefCurrent(child.left, refSymbol, scopes)) writeCount++;
|
|
44631
|
-
return;
|
|
44632
|
-
}
|
|
44633
|
-
if (isNodeOfType(child, "UpdateExpression") || isNodeOfType(child, "UnaryExpression") && child.operator === "delete") {
|
|
44634
|
-
if (expressionContainsRefCurrent(child.argument, refSymbol, scopes)) writeCount++;
|
|
44635
|
-
return;
|
|
44636
|
-
}
|
|
44637
|
-
if (isNodeOfType(child, "ForInStatement") || isNodeOfType(child, "ForOfStatement")) {
|
|
44638
|
-
if (expressionContainsRefCurrent(child.left, refSymbol, scopes)) writeCount++;
|
|
44639
|
-
}
|
|
44640
|
-
});
|
|
44641
|
-
return writeCount === 1 && expressionContainsRefCurrent(assignmentExpression.left, refSymbol, scopes);
|
|
44642
|
-
};
|
|
44643
44377
|
const isEmptySentinel = (node, scopes) => isNodeOfType(node, "Literal") && node.value === null || isNodeOfType(node, "Identifier") && node.name === "undefined" && scopes.isGlobalReference(node);
|
|
44644
44378
|
const hasRepeatedExecutionAncestor = (node, stop) => {
|
|
44645
44379
|
let ancestor = node.parent;
|
|
@@ -44688,22 +44422,18 @@ const hasNoPriorCoExecutableWrite = (assignmentExpression, branchRoot, refSymbol
|
|
|
44688
44422
|
const isDocumentedLazyInitialization = (assignmentExpression, refSymbol, scopes) => {
|
|
44689
44423
|
if (assignmentExpression.operator === "??=" || assignmentExpression.operator === "||=") return true;
|
|
44690
44424
|
if (assignmentExpression.operator !== "=") return false;
|
|
44691
|
-
const renderOwner = findRenderPhaseComponentOrHook(assignmentExpression, scopes);
|
|
44692
|
-
if (!renderOwner) return false;
|
|
44693
44425
|
let descendant = assignmentExpression;
|
|
44694
44426
|
let ancestor = descendant.parent;
|
|
44695
44427
|
while (ancestor) {
|
|
44696
|
-
|
|
44697
|
-
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;
|
|
44698
|
-
if (isNodeOfType(ancestor, "IfStatement") && isNodeOfType(test, "BinaryExpression") && [
|
|
44428
|
+
if (isNodeOfType(ancestor, "IfStatement") && isNodeOfType(ancestor.test, "BinaryExpression") && [
|
|
44699
44429
|
"===",
|
|
44700
44430
|
"==",
|
|
44701
44431
|
"!==",
|
|
44702
44432
|
"!="
|
|
44703
|
-
].includes(test.operator)) {
|
|
44704
|
-
const { left, right } = test;
|
|
44433
|
+
].includes(ancestor.test.operator)) {
|
|
44434
|
+
const { left, right } = ancestor.test;
|
|
44705
44435
|
const comparesEmptySentinel = isSameRefCurrentAlias(left, refSymbol, scopes) && isEmptySentinel(right, scopes) || isSameRefCurrentAlias(right, refSymbol, scopes) && isEmptySentinel(left, scopes);
|
|
44706
|
-
const guardedBranch = test.operator === "===" || test.operator === "==" ? ancestor.consequent : ancestor.alternate;
|
|
44436
|
+
const guardedBranch = ancestor.test.operator === "===" || ancestor.test.operator === "==" ? ancestor.consequent : ancestor.alternate;
|
|
44707
44437
|
if (comparesEmptySentinel && guardedBranch === descendant && guardedBranch && !hasRepeatedExecutionAncestor(assignmentExpression, guardedBranch) && hasNoPriorCoExecutableWrite(assignmentExpression, guardedBranch, refSymbol, scopes)) return true;
|
|
44708
44438
|
}
|
|
44709
44439
|
descendant = ancestor;
|
|
@@ -51828,31 +51558,6 @@ const preferModuleScopePureFunction = defineRule({
|
|
|
51828
51558
|
}
|
|
51829
51559
|
});
|
|
51830
51560
|
//#endregion
|
|
51831
|
-
//#region src/plugin/utils/get-require-call-source.ts
|
|
51832
|
-
const getRequireCallSource = (expression) => {
|
|
51833
|
-
const unwrappedExpression = stripParenExpression(expression);
|
|
51834
|
-
if (isNodeOfType(unwrappedExpression, "MemberExpression")) return getRequireCallSource(unwrappedExpression.object);
|
|
51835
|
-
if (!isNodeOfType(unwrappedExpression, "CallExpression")) return null;
|
|
51836
|
-
if (!isNodeOfType(unwrappedExpression.callee, "Identifier") || unwrappedExpression.callee.name !== "require") return null;
|
|
51837
|
-
const [firstArgument] = unwrappedExpression.arguments ?? [];
|
|
51838
|
-
if (!firstArgument || !isNodeOfType(firstArgument, "Literal")) return null;
|
|
51839
|
-
return typeof firstArgument.value === "string" ? firstArgument.value : null;
|
|
51840
|
-
};
|
|
51841
|
-
//#endregion
|
|
51842
|
-
//#region src/plugin/utils/is-proven-node-crypto-namespace-reference.ts
|
|
51843
|
-
const NODE_CRYPTO_MODULE_SOURCES = new Set(["crypto", "node:crypto"]);
|
|
51844
|
-
const isProvenNodeCryptoNamespaceReference = (expression, scopes) => {
|
|
51845
|
-
const identifier = stripParenExpression(expression);
|
|
51846
|
-
if (!isNodeOfType(identifier, "Identifier")) return false;
|
|
51847
|
-
const symbol = resolveConstIdentifierAlias(identifier, scopes);
|
|
51848
|
-
if (!symbol) return false;
|
|
51849
|
-
if (symbol.kind === "import") {
|
|
51850
|
-
const importBinding = getImportBindingForName(identifier, symbol.name);
|
|
51851
|
-
return Boolean(importBinding && NODE_CRYPTO_MODULE_SOURCES.has(importBinding.source));
|
|
51852
|
-
}
|
|
51853
|
-
return Boolean(symbol.kind === "const" && symbol.initializer && NODE_CRYPTO_MODULE_SOURCES.has(getRequireCallSource(symbol.initializer) ?? ""));
|
|
51854
|
-
};
|
|
51855
|
-
//#endregion
|
|
51856
51561
|
//#region src/plugin/rules/architecture/prefer-module-scope-static-value.ts
|
|
51857
51562
|
const MUTATING_RECEIVER_METHOD_NAMES = new Set([...MUTATING_ARRAY_METHODS, ...MUTATING_COLLECTION_METHODS]);
|
|
51858
51563
|
const isMutationContext = (referenceIdentifier) => {
|
|
@@ -51966,9 +51671,9 @@ const isImpureCall = (node, scopes) => {
|
|
|
51966
51671
|
const callee = node.callee;
|
|
51967
51672
|
if (isNodeOfType(callee, "Identifier")) return isImpureBareCallee(callee, scopes);
|
|
51968
51673
|
if (!isNodeOfType(callee, "MemberExpression") || callee.computed) return false;
|
|
51674
|
+
if (!isNodeOfType(callee.object, "Identifier")) return false;
|
|
51969
51675
|
if (!isNodeOfType(callee.property, "Identifier")) return false;
|
|
51970
|
-
|
|
51971
|
-
return false;
|
|
51676
|
+
return Boolean(IMPURE_MEMBER_RECEIVERS.get(callee.object.name)?.has(callee.property.name));
|
|
51972
51677
|
};
|
|
51973
51678
|
const containsImpureExpression = (expression, scopes) => {
|
|
51974
51679
|
let foundImpure = false;
|
|
@@ -56737,6 +56442,16 @@ const rnListCallbackPerRow = defineRule({
|
|
|
56737
56442
|
}
|
|
56738
56443
|
});
|
|
56739
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
|
|
56740
56455
|
//#region src/plugin/utils/get-initializer-module-source.ts
|
|
56741
56456
|
const getInitializerModuleSource = (contextNode, initializer) => {
|
|
56742
56457
|
const requireSource = getRequireCallSource(initializer);
|