oxlint-plugin-react-doctor 0.7.5-dev.18e8717 → 0.7.5-dev.20cd922
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 +61 -8
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -23998,6 +23998,26 @@ const STATEFUL_HTML_DESCENDANT_TAGS = new Set([
|
|
|
23998
23998
|
"dialog",
|
|
23999
23999
|
"canvas"
|
|
24000
24000
|
]);
|
|
24001
|
+
const STATEFUL_HTML_ATTRIBUTE_NAMES = new Set([
|
|
24002
|
+
"autofocus",
|
|
24003
|
+
"contenteditable",
|
|
24004
|
+
"draggable",
|
|
24005
|
+
"tabindex"
|
|
24006
|
+
]);
|
|
24007
|
+
const isStaticallyFalseAttributeValue = (attribute) => {
|
|
24008
|
+
if (!isNodeOfType(attribute, "JSXAttribute") || !attribute.value) return false;
|
|
24009
|
+
const value = isNodeOfType(attribute.value, "JSXExpressionContainer") ? attribute.value.expression : attribute.value;
|
|
24010
|
+
return isNodeOfType(value, "Literal") && (value.value === false || value.value === "false");
|
|
24011
|
+
};
|
|
24012
|
+
const hasStatefulHtmlAttribute = (openingElement) => {
|
|
24013
|
+
if (!isNodeOfType(openingElement, "JSXOpeningElement")) return false;
|
|
24014
|
+
return openingElement.attributes.some((attribute) => {
|
|
24015
|
+
if (!isNodeOfType(attribute, "JSXAttribute") || !isNodeOfType(attribute.name, "JSXIdentifier")) return false;
|
|
24016
|
+
const attributeName = attribute.name.name.toLowerCase();
|
|
24017
|
+
if (!STATEFUL_HTML_ATTRIBUTE_NAMES.has(attributeName)) return false;
|
|
24018
|
+
return attributeName === "tabindex" || !isStaticallyFalseAttributeValue(attribute);
|
|
24019
|
+
});
|
|
24020
|
+
};
|
|
24001
24021
|
const STATEFUL_DESCENDANT_SCAN_BUDGET = 200;
|
|
24002
24022
|
const rootIdentifierNameOf = (expression) => {
|
|
24003
24023
|
let object = expression;
|
|
@@ -24015,7 +24035,8 @@ const containsStatefulDescendant = (jsxElement, options = {}) => {
|
|
|
24015
24035
|
budget -= 1;
|
|
24016
24036
|
const node = stack.pop();
|
|
24017
24037
|
if (isNodeOfType(node, "JSXElement")) {
|
|
24018
|
-
const
|
|
24038
|
+
const opening = node.openingElement;
|
|
24039
|
+
const name = opening.name;
|
|
24019
24040
|
if (name && isNodeOfType(name, "JSXIdentifier")) {
|
|
24020
24041
|
const tagName = name.name;
|
|
24021
24042
|
const firstChar = tagName.charCodeAt(0);
|
|
@@ -24023,6 +24044,7 @@ const containsStatefulDescendant = (jsxElement, options = {}) => {
|
|
|
24023
24044
|
if (STATEFUL_HTML_DESCENDANT_TAGS.has(tagName)) return true;
|
|
24024
24045
|
}
|
|
24025
24046
|
if (name && isNodeOfType(name, "JSXMemberExpression")) return true;
|
|
24047
|
+
if (hasStatefulHtmlAttribute(opening)) return true;
|
|
24026
24048
|
const children = node.children ?? [];
|
|
24027
24049
|
for (const child of children) stack.push(child);
|
|
24028
24050
|
continue;
|
|
@@ -24582,6 +24604,14 @@ const templateHasOuterMemberIdentity = (template, bindingFunction) => {
|
|
|
24582
24604
|
}
|
|
24583
24605
|
return false;
|
|
24584
24606
|
};
|
|
24607
|
+
const findBareItemNamesReferencedByTemplate = (template, itemNames) => {
|
|
24608
|
+
const referencedItemNames = /* @__PURE__ */ new Set();
|
|
24609
|
+
for (const expression of template.expressions ?? []) {
|
|
24610
|
+
const unwrappedExpression = stripParenExpression(expression);
|
|
24611
|
+
if (isNodeOfType(unwrappedExpression, "Identifier") && itemNames.has(unwrappedExpression.name)) referencedItemNames.add(unwrappedExpression.name);
|
|
24612
|
+
}
|
|
24613
|
+
return referencedItemNames;
|
|
24614
|
+
};
|
|
24585
24615
|
const forLoopTestReadsDataLength = (test) => {
|
|
24586
24616
|
let didFindLengthRead = false;
|
|
24587
24617
|
walkAst(test, (child) => {
|
|
@@ -24718,9 +24748,11 @@ const noArrayIndexAsKey = defineRule({
|
|
|
24718
24748
|
} else if (STATELESS_HTML_LEAF_TAGS.has(elementName.name)) {
|
|
24719
24749
|
const jsxElement = openingElement.parent;
|
|
24720
24750
|
if (jsxElement && isNodeOfType(jsxElement, "JSXElement")) {
|
|
24751
|
+
const isInlineTextRun = INLINE_TEXT_LEAF_TAGS.has(elementName.name);
|
|
24752
|
+
const primitiveItemNames = keyTemplate ? findBareItemNamesReferencedByTemplate(keyTemplate, itemNames) : EMPTY_NAME_SET;
|
|
24721
24753
|
if (!containsStatefulDescendant(jsxElement, {
|
|
24722
|
-
memberRootNames:
|
|
24723
|
-
bareIdentifierNames: derivedNames
|
|
24754
|
+
memberRootNames: isInlineTextRun ? itemNames : EMPTY_NAME_SET,
|
|
24755
|
+
bareIdentifierNames: primitiveItemNames.size > 0 ? new Set([...derivedNames, ...primitiveItemNames]) : derivedNames
|
|
24724
24756
|
})) return;
|
|
24725
24757
|
}
|
|
24726
24758
|
}
|
|
@@ -35247,6 +35279,27 @@ const hasPreviousValueDep = (effectNode, depElements) => {
|
|
|
35247
35279
|
}
|
|
35248
35280
|
return false;
|
|
35249
35281
|
};
|
|
35282
|
+
const isStateLikeDependency = (analysis, element, isPropName) => {
|
|
35283
|
+
if (!isNodeOfType(element, "Identifier") || isPropName(element.name)) return false;
|
|
35284
|
+
if (!analysis) return true;
|
|
35285
|
+
const reference = getRef(analysis, element);
|
|
35286
|
+
if (!reference) return true;
|
|
35287
|
+
const upstreamReferences = getUpstreamRefs(analysis, reference);
|
|
35288
|
+
if (upstreamReferences.some((upstreamReference) => isState(analysis, upstreamReference))) return true;
|
|
35289
|
+
return !upstreamReferences.some((upstreamReference) => isProp(analysis, upstreamReference));
|
|
35290
|
+
};
|
|
35291
|
+
const getRefHeldPropCallbackName = (callExpression, isPropName) => {
|
|
35292
|
+
const callee = stripParenExpression(callExpression.callee);
|
|
35293
|
+
if (!isNodeOfType(callee, "MemberExpression") || !isNodeOfType(callee.property, "Identifier") || callee.property.name !== "current") return null;
|
|
35294
|
+
const receiver = stripParenExpression(callee.object);
|
|
35295
|
+
if (!isNodeOfType(receiver, "Identifier")) return null;
|
|
35296
|
+
const binding = findVariableInitializer(callExpression, receiver.name);
|
|
35297
|
+
if (!binding?.initializer || !isNodeOfType(binding.initializer, "CallExpression")) return null;
|
|
35298
|
+
if (getCalleeName$2(binding.initializer) !== "useRef") return null;
|
|
35299
|
+
const callbackArgument = binding.initializer.arguments?.[0];
|
|
35300
|
+
if (!callbackArgument || !isNodeOfType(callbackArgument, "Identifier")) return null;
|
|
35301
|
+
return isPropName(callbackArgument.name) ? callbackArgument.name : null;
|
|
35302
|
+
};
|
|
35250
35303
|
const noPropCallbackInEffect = defineRule({
|
|
35251
35304
|
id: "no-prop-callback-in-effect",
|
|
35252
35305
|
title: "Parent kept in sync with a callback effect",
|
|
@@ -35263,11 +35316,11 @@ const noPropCallbackInEffect = defineRule({
|
|
|
35263
35316
|
if (!callback || !isNodeOfType(callback, "ArrowFunctionExpression") && !isNodeOfType(callback, "FunctionExpression")) return;
|
|
35264
35317
|
const depsNode = node.arguments[1];
|
|
35265
35318
|
if (!isNodeOfType(depsNode, "ArrayExpression") || !depsNode.elements?.length) return;
|
|
35266
|
-
const
|
|
35319
|
+
const analysis = getProgramAnalysis(node);
|
|
35320
|
+
const stateLikeDeps = (depsNode.elements ?? []).filter((element) => element && isStateLikeDependency(analysis, element, propStackTracker.isPropName));
|
|
35267
35321
|
if (stateLikeDeps.length === 0) return;
|
|
35268
35322
|
if (isRefLatchGuardedEffect(callback.body)) return;
|
|
35269
35323
|
if (hasPreviousValueDep(node, depsNode.elements ?? [])) return;
|
|
35270
|
-
const analysis = getProgramAnalysis(node);
|
|
35271
35324
|
if (analysis) {
|
|
35272
35325
|
const stateLikeDepRefs = [];
|
|
35273
35326
|
for (const element of stateLikeDeps) {
|
|
@@ -35279,9 +35332,9 @@ const noPropCallbackInEffect = defineRule({
|
|
|
35279
35332
|
const reportedNodes = /* @__PURE__ */ new Set();
|
|
35280
35333
|
walkInsideStatementBlocks(callback.body, (child) => {
|
|
35281
35334
|
if (!isNodeOfType(child, "CallExpression")) return;
|
|
35282
|
-
|
|
35283
|
-
const calleeName =
|
|
35284
|
-
if (!
|
|
35335
|
+
const directCallee = stripParenExpression(child.callee);
|
|
35336
|
+
const calleeName = isNodeOfType(directCallee, "Identifier") && propStackTracker.isPropName(directCallee.name) && directCallee.name || getRefHeldPropCallbackName(child, propStackTracker.isPropName);
|
|
35337
|
+
if (!calleeName) return;
|
|
35285
35338
|
if (!isResultDiscardedCall(child)) return;
|
|
35286
35339
|
if (reportedNodes.has(child)) return;
|
|
35287
35340
|
reportedNodes.add(child);
|