oxlint-plugin-react-doctor 0.7.6-dev.dfd47a7 → 0.7.6-dev.e026a23
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 +65 -19
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -13653,6 +13653,28 @@ const jsAsyncReduceWithoutAwaitedAcc = defineRule({
|
|
|
13653
13653
|
} })
|
|
13654
13654
|
});
|
|
13655
13655
|
//#endregion
|
|
13656
|
+
//#region src/plugin/utils/unwrap-discarded-expression.ts
|
|
13657
|
+
const unwrapDiscardedExpression = (node) => {
|
|
13658
|
+
let expression = isNodeOfType(node, "ExpressionStatement") ? node.expression : node;
|
|
13659
|
+
for (;;) {
|
|
13660
|
+
expression = stripParenExpression(expression);
|
|
13661
|
+
if (isNodeOfType(expression, "UnaryExpression") && expression.operator === "void") {
|
|
13662
|
+
expression = expression.argument;
|
|
13663
|
+
continue;
|
|
13664
|
+
}
|
|
13665
|
+
if (isNodeOfType(expression, "SequenceExpression")) {
|
|
13666
|
+
const expressions = expression.expressions ?? [];
|
|
13667
|
+
const finalExpression = expressions.at(-1);
|
|
13668
|
+
const prefixExpressions = expressions.slice(0, -1);
|
|
13669
|
+
if (finalExpression && prefixExpressions.length > 0 && prefixExpressions.every((prefixExpression) => isNodeOfType(stripParenExpression(prefixExpression), "Literal"))) {
|
|
13670
|
+
expression = finalExpression;
|
|
13671
|
+
continue;
|
|
13672
|
+
}
|
|
13673
|
+
}
|
|
13674
|
+
return expression;
|
|
13675
|
+
}
|
|
13676
|
+
};
|
|
13677
|
+
//#endregion
|
|
13656
13678
|
//#region src/plugin/rules/js-performance/js-batch-dom-css.ts
|
|
13657
13679
|
const ITERATOR_METHOD_NAMES$2 = new Set([
|
|
13658
13680
|
"forEach",
|
|
@@ -13837,9 +13859,19 @@ const hasAttachmentBefore = (scopeOwner, elementName, beforeStart) => {
|
|
|
13837
13859
|
});
|
|
13838
13860
|
return foundAttachment;
|
|
13839
13861
|
};
|
|
13862
|
+
const getStyleAssignment = (node) => {
|
|
13863
|
+
if (!isNodeOfType(node, "ExpressionStatement")) return null;
|
|
13864
|
+
const expression = unwrapDiscardedExpression(node);
|
|
13865
|
+
if (!isNodeOfType(expression, "AssignmentExpression")) return null;
|
|
13866
|
+
if (!isNodeOfType(expression.left, "MemberExpression")) return null;
|
|
13867
|
+
if (!isNodeOfType(expression.left.object, "MemberExpression")) return null;
|
|
13868
|
+
if (!isNodeOfType(expression.left.object.property, "Identifier")) return null;
|
|
13869
|
+
return expression.left.object.property.name === "style" ? expression : null;
|
|
13870
|
+
};
|
|
13840
13871
|
const isProvablyDetachedAtWrite = (styleWriteStatement) => {
|
|
13841
|
-
|
|
13842
|
-
|
|
13872
|
+
const assignment = getStyleAssignment(styleWriteStatement);
|
|
13873
|
+
if (!assignment || !isNodeOfType(assignment.left, "MemberExpression") || !isNodeOfType(assignment.left.object, "MemberExpression")) return false;
|
|
13874
|
+
const elementExpression = assignment.left.object.object;
|
|
13843
13875
|
const creationRoot = resolveDetachedCreationRoot(elementExpression, 0);
|
|
13844
13876
|
if (!creationRoot) return false;
|
|
13845
13877
|
return !hasAttachmentBefore(creationRoot.scopeOwner, creationRoot.rootName, getNodeStart$1(styleWriteStatement));
|
|
@@ -13851,15 +13883,17 @@ const jsBatchDomCss = defineRule({
|
|
|
13851
13883
|
severity: "warn",
|
|
13852
13884
|
recommendation: "Do all your reads first, then all your writes. Mixing them inside a loop makes the browser recalculate the layout again and again, which is slow",
|
|
13853
13885
|
create: (context) => {
|
|
13854
|
-
const
|
|
13855
|
-
|
|
13886
|
+
const writesLayoutAffectingProperty = (node) => {
|
|
13887
|
+
const assignment = getStyleAssignment(node);
|
|
13888
|
+
return assignment !== null && isNodeOfType(assignment.left, "MemberExpression") && isNodeOfType(assignment.left.property, "Identifier") && !LAYOUT_NEUTRAL_STYLE_PROPERTY_NAMES.has(assignment.left.property.name);
|
|
13889
|
+
};
|
|
13856
13890
|
return { BlockStatement(node) {
|
|
13857
13891
|
const perIterationBody = findEnclosingPerIterationBody(node);
|
|
13858
13892
|
if (!perIterationBody) return;
|
|
13859
13893
|
const statements = node.body ?? [];
|
|
13860
13894
|
let layoutReads = null;
|
|
13861
13895
|
for (let statementIndex = 1; statementIndex < statements.length; statementIndex++) {
|
|
13862
|
-
if (
|
|
13896
|
+
if (getStyleAssignment(statements[statementIndex]) === null || getStyleAssignment(statements[statementIndex - 1]) === null) continue;
|
|
13863
13897
|
if (!writesLayoutAffectingProperty(statements[statementIndex]) && !writesLayoutAffectingProperty(statements[statementIndex - 1])) continue;
|
|
13864
13898
|
layoutReads ??= scanPerIterationLayoutReads(perIterationBody);
|
|
13865
13899
|
if (!layoutReads.hasUsedLayoutRead || layoutReads.hasDeliberateForcedReflow) return;
|
|
@@ -15257,9 +15291,21 @@ const isSmallFixedListMember = (receiver) => {
|
|
|
15257
15291
|
};
|
|
15258
15292
|
const getResolvedInitializer = (receiver) => {
|
|
15259
15293
|
if (!isNodeOfType(receiver, "Identifier")) return null;
|
|
15260
|
-
const
|
|
15261
|
-
|
|
15262
|
-
return
|
|
15294
|
+
const binding = findVariableInitializer(receiver, receiver.name);
|
|
15295
|
+
const initializer = binding?.initializer ?? null;
|
|
15296
|
+
if (!binding || !initializer) return null;
|
|
15297
|
+
const isDefault = isNodeOfType(binding.bindingIdentifier.parent, "AssignmentPattern");
|
|
15298
|
+
if (isNodeOfType(initializer, "Identifier")) {
|
|
15299
|
+
const aliased = findVariableInitializer(initializer, initializer.name);
|
|
15300
|
+
if (aliased?.initializer) return {
|
|
15301
|
+
initializer: aliased.initializer,
|
|
15302
|
+
isDefault: isDefault || isNodeOfType(aliased.bindingIdentifier.parent, "AssignmentPattern")
|
|
15303
|
+
};
|
|
15304
|
+
}
|
|
15305
|
+
return {
|
|
15306
|
+
initializer,
|
|
15307
|
+
isDefault
|
|
15308
|
+
};
|
|
15263
15309
|
};
|
|
15264
15310
|
const isSplitCall = (expression) => {
|
|
15265
15311
|
if (!expression) return false;
|
|
@@ -15425,8 +15471,8 @@ const isBoundedConstantCollection = (collection) => {
|
|
|
15425
15471
|
if (isScreamingSnakeCaseConstantReceiver(stripped)) return true;
|
|
15426
15472
|
if (isSmallInlineLiteralArray(stripped)) return true;
|
|
15427
15473
|
if (isNodeOfType(stripped, "Identifier")) {
|
|
15428
|
-
const
|
|
15429
|
-
if (
|
|
15474
|
+
const resolved = getResolvedInitializer(stripped);
|
|
15475
|
+
if (resolved && !resolved.isDefault && isSmallInlineLiteralArray(resolved.initializer)) return true;
|
|
15430
15476
|
}
|
|
15431
15477
|
return false;
|
|
15432
15478
|
};
|
|
@@ -15478,8 +15524,8 @@ const jsSetMapLookups = defineRule({
|
|
|
15478
15524
|
if (isIndexedArrayElementWithStringArgument(receiver, node.arguments?.[0])) return;
|
|
15479
15525
|
const resolvedInitializer = getResolvedInitializer(receiver);
|
|
15480
15526
|
if (resolvedInitializer) {
|
|
15481
|
-
if (isLikelyStringReceiver(resolvedInitializer)) return;
|
|
15482
|
-
if (isSmallInlineLiteralArray(resolvedInitializer)) return;
|
|
15527
|
+
if (isLikelyStringReceiver(resolvedInitializer.initializer)) return;
|
|
15528
|
+
if (!resolvedInitializer.isDefault && isSmallInlineLiteralArray(resolvedInitializer.initializer)) return;
|
|
15483
15529
|
}
|
|
15484
15530
|
if (isStringElementOfSplitIteration(receiver)) return;
|
|
15485
15531
|
if (isReceiverDeclaredInNearestLoop(receiver, node)) return;
|
|
@@ -28875,7 +28921,7 @@ const findTopLevelEffectCalls = (componentBody) => {
|
|
|
28875
28921
|
if (!isNodeOfType(componentBody, "BlockStatement")) return effectCalls;
|
|
28876
28922
|
for (const statement of componentBody.body ?? []) {
|
|
28877
28923
|
if (!isNodeOfType(statement, "ExpressionStatement")) continue;
|
|
28878
|
-
const expression = statement
|
|
28924
|
+
const expression = unwrapDiscardedExpression(statement);
|
|
28879
28925
|
if (!isNodeOfType(expression, "CallExpression")) continue;
|
|
28880
28926
|
if (!isHookCall$2(expression, EFFECT_HOOK_NAMES$1)) continue;
|
|
28881
28927
|
effectCalls.push(expression);
|
|
@@ -33107,7 +33153,7 @@ const noMirrorPropEffect = defineRule({
|
|
|
33107
33153
|
if (mirrorBindings.length === 0) return;
|
|
33108
33154
|
for (const statement of componentBody.body ?? []) {
|
|
33109
33155
|
if (!isNodeOfType(statement, "ExpressionStatement")) continue;
|
|
33110
|
-
const effectCall = statement
|
|
33156
|
+
const effectCall = unwrapDiscardedExpression(statement);
|
|
33111
33157
|
if (!isNodeOfType(effectCall, "CallExpression")) continue;
|
|
33112
33158
|
if (!isHookCall$2(effectCall, EFFECT_HOOK_NAMES$1)) continue;
|
|
33113
33159
|
if ((effectCall.arguments?.length ?? 0) < 2) continue;
|
|
@@ -33121,7 +33167,7 @@ const noMirrorPropEffect = defineRule({
|
|
|
33121
33167
|
const bodyStatements = getCallbackStatements(callback);
|
|
33122
33168
|
if (bodyStatements.length !== 1) continue;
|
|
33123
33169
|
const onlyStatement = bodyStatements[0];
|
|
33124
|
-
const expression =
|
|
33170
|
+
const expression = unwrapDiscardedExpression(onlyStatement);
|
|
33125
33171
|
if (!isNodeOfType(expression, "CallExpression")) continue;
|
|
33126
33172
|
if (!isNodeOfType(expression.callee, "Identifier")) continue;
|
|
33127
33173
|
if (!isSetterIdentifier(expression.callee.name)) continue;
|
|
@@ -37222,7 +37268,7 @@ const isNonSettlingSetterArgument = (setterCall, stateName) => {
|
|
|
37222
37268
|
return expressionReadsStateValue(argument, stateName);
|
|
37223
37269
|
};
|
|
37224
37270
|
const getUnconditionalSetterCall = (statement, setterNames) => {
|
|
37225
|
-
const expression =
|
|
37271
|
+
const expression = unwrapDiscardedExpression(statement);
|
|
37226
37272
|
if (!isNodeOfType(expression, "CallExpression")) return null;
|
|
37227
37273
|
if (!isNodeOfType(expression.callee, "Identifier")) return null;
|
|
37228
37274
|
if (!setterNames.has(expression.callee.name)) return null;
|
|
@@ -37571,7 +37617,7 @@ const noSelfUpdatingEffect = defineRule({
|
|
|
37571
37617
|
const setterNames = new Set(setterNameToStateName.keys());
|
|
37572
37618
|
for (const statement of functionBody.body ?? []) {
|
|
37573
37619
|
if (!isNodeOfType(statement, "ExpressionStatement")) continue;
|
|
37574
|
-
const effectCall = statement
|
|
37620
|
+
const effectCall = unwrapDiscardedExpression(statement);
|
|
37575
37621
|
if (!isNodeOfType(effectCall, "CallExpression")) continue;
|
|
37576
37622
|
if (!isHookCall$2(effectCall, EFFECT_HOOK_NAMES$1)) continue;
|
|
37577
37623
|
if ((effectCall.arguments?.length ?? 0) < 2) continue;
|
|
@@ -45996,8 +46042,8 @@ const rnAnimationReactionAsDerived = defineRule({
|
|
|
45996
46042
|
if (statements.length !== 1) return;
|
|
45997
46043
|
const onlyStatement = statements[0];
|
|
45998
46044
|
if (!isNodeOfType(onlyStatement, "ExpressionStatement")) return;
|
|
45999
|
-
singleAssignment = onlyStatement
|
|
46000
|
-
} else if (body) singleAssignment = body;
|
|
46045
|
+
singleAssignment = unwrapDiscardedExpression(onlyStatement);
|
|
46046
|
+
} else if (body) singleAssignment = unwrapDiscardedExpression(body);
|
|
46001
46047
|
if (!singleAssignment) return;
|
|
46002
46048
|
const isValueAssignment = isNodeOfType(singleAssignment, "AssignmentExpression") && isNodeOfType(singleAssignment.left, "MemberExpression") && isNodeOfType(singleAssignment.left.object, "Identifier") && isNodeOfType(singleAssignment.left.property, "Identifier") && singleAssignment.left.property.name === "value";
|
|
46003
46049
|
const isSetCall = isNodeOfType(singleAssignment, "CallExpression") && isNodeOfType(singleAssignment.callee, "MemberExpression") && isNodeOfType(singleAssignment.callee.property, "Identifier") && singleAssignment.callee.property.name === "set" && (singleAssignment.arguments?.length ?? 0) === 1;
|