oxlint-plugin-react-doctor 0.7.9-dev.c88a39a → 0.7.9-dev.d8a20e0
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 +9 -174
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -35902,167 +35902,15 @@ const visitSynchronousFunctionBodies = (analysisFunctions, visitor) => {
|
|
|
35902
35902
|
walkInsideStatementBlocks(analysisFunction.body, visitor);
|
|
35903
35903
|
}
|
|
35904
35904
|
};
|
|
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();
|
|
35905
|
+
const collectWrittenStateNamesInEffect = (analysisFunctions, setterToStateName) => {
|
|
35906
|
+
const writtenStateNames = /* @__PURE__ */ new Set();
|
|
35988
35907
|
visitSynchronousFunctionBodies(analysisFunctions, (child) => {
|
|
35989
35908
|
if (!isNodeOfType(child, "CallExpression")) return;
|
|
35990
35909
|
if (!isNodeOfType(child.callee, "Identifier")) return;
|
|
35991
35910
|
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);
|
|
35911
|
+
if (stateName) writtenStateNames.add(stateName);
|
|
36001
35912
|
});
|
|
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;
|
|
35913
|
+
return writtenStateNames;
|
|
36066
35914
|
};
|
|
36067
35915
|
const EMPTY_CLEANUP_NAME_SET = /* @__PURE__ */ new Set();
|
|
36068
35916
|
const isFunctionShapedReturn = (returnedValue, setterToStateName, isExplicitReturnStatement) => {
|
|
@@ -36151,29 +35999,18 @@ const noEffectChain = defineRule({
|
|
|
36151
35999
|
const useStateBindings = collectUseStateBindings(componentBody);
|
|
36152
36000
|
if (useStateBindings.length === 0) return;
|
|
36153
36001
|
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
|
-
}
|
|
36002
|
+
for (const binding of useStateBindings) setterToStateName.set(binding.setterName, binding.valueName);
|
|
36164
36003
|
const storageSetterNames = collectStorageHookSetterNames(componentBody);
|
|
36165
36004
|
const effectInfos = [];
|
|
36166
36005
|
for (const effectCall of findTopLevelEffectCalls(componentBody)) {
|
|
36167
36006
|
const callback = getEffectCallback(effectCall, context.scopes);
|
|
36168
36007
|
if (!callback || !isFunctionLike$1(callback) || callback.async) continue;
|
|
36169
36008
|
const analysisFunctions = collectSynchronouslyInvokedFunctions(callback, context.scopes);
|
|
36170
|
-
const
|
|
36171
|
-
const writtenStateNames = new Set(stateWrites.keys());
|
|
36009
|
+
const writtenStateNames = collectWrittenStateNamesInEffect(analysisFunctions, setterToStateName);
|
|
36172
36010
|
effectInfos.push({
|
|
36173
36011
|
node: effectCall,
|
|
36174
36012
|
depNames: collectDepIdentifierNames(effectCall),
|
|
36175
|
-
|
|
36176
|
-
analysisFunctions,
|
|
36013
|
+
writtenStateNames,
|
|
36177
36014
|
isExternalSync: isExternalSyncEffect(callback, analysisFunctions, setterToStateName) || callsStorageHookSetter(analysisFunctions, storageSetterNames) || writtenStateNames.size === 0 && callsOpaqueExternalSetter(analysisFunctions, setterToStateName)
|
|
36178
36015
|
});
|
|
36179
36016
|
}
|
|
@@ -36181,15 +36018,13 @@ const noEffectChain = defineRule({
|
|
|
36181
36018
|
const reportedNodes = /* @__PURE__ */ new Set();
|
|
36182
36019
|
for (const writerEffect of effectInfos) {
|
|
36183
36020
|
if (writerEffect.isExternalSync) continue;
|
|
36184
|
-
if (writerEffect.
|
|
36021
|
+
if (writerEffect.writtenStateNames.size === 0) continue;
|
|
36185
36022
|
for (const readerEffect of effectInfos) {
|
|
36186
36023
|
if (readerEffect === writerEffect) continue;
|
|
36187
36024
|
if (readerEffect.isExternalSync) continue;
|
|
36188
36025
|
if (readerEffect.depNames.size === 0) continue;
|
|
36189
36026
|
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;
|
|
36027
|
+
for (const writtenName of writerEffect.writtenStateNames) if (readerEffect.depNames.has(writtenName)) {
|
|
36193
36028
|
chainedStateName = writtenName;
|
|
36194
36029
|
break;
|
|
36195
36030
|
}
|