oxlint-plugin-react-doctor 0.7.5-dev.bc49aaa → 0.7.5-dev.d9676e2
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 +73 -162
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -5934,21 +5934,6 @@ const checkedRequiresOnchangeOrReadonly = defineRule({
|
|
|
5934
5934
|
}
|
|
5935
5935
|
});
|
|
5936
5936
|
//#endregion
|
|
5937
|
-
//#region src/plugin/utils/get-static-property-key-name.ts
|
|
5938
|
-
const getStaticPropertyKeyName = (node, options = {}) => {
|
|
5939
|
-
if (!isNodeOfType(node, "Property")) return null;
|
|
5940
|
-
if (node.computed) {
|
|
5941
|
-
if (options.allowComputedString && isNodeOfType(node.key, "Literal") && typeof node.key.value === "string") return node.key.value;
|
|
5942
|
-
return null;
|
|
5943
|
-
}
|
|
5944
|
-
if (isNodeOfType(node.key, "Identifier")) return node.key.name;
|
|
5945
|
-
if (isNodeOfType(node.key, "Literal")) {
|
|
5946
|
-
if (typeof node.key.value === "string") return node.key.value;
|
|
5947
|
-
if (options.stringifyNonStringLiterals) return String(node.key.value);
|
|
5948
|
-
}
|
|
5949
|
-
return null;
|
|
5950
|
-
};
|
|
5951
|
-
//#endregion
|
|
5952
5937
|
//#region src/plugin/utils/is-presentation-role.ts
|
|
5953
5938
|
const isPresentationRole = (openingElement) => {
|
|
5954
5939
|
const roleAttribute = hasJsxPropIgnoreCase(openingElement.attributes, "role");
|
|
@@ -5993,21 +5978,6 @@ const isPureEventBlockerHandler = (attribute) => {
|
|
|
5993
5978
|
return false;
|
|
5994
5979
|
};
|
|
5995
5980
|
//#endregion
|
|
5996
|
-
//#region src/plugin/utils/resolve-const-identifier-alias.ts
|
|
5997
|
-
const resolveConstIdentifierAlias = (identifier, scopes) => {
|
|
5998
|
-
if (!isNodeOfType(identifier, "Identifier")) return null;
|
|
5999
|
-
const visitedSymbolIds = /* @__PURE__ */ new Set();
|
|
6000
|
-
let symbol = scopes.symbolFor(identifier);
|
|
6001
|
-
while (symbol?.kind === "const") {
|
|
6002
|
-
if (visitedSymbolIds.has(symbol.id) || !symbol.initializer || !isNodeOfType(symbol.declarationNode, "VariableDeclarator") || symbol.declarationNode.id !== symbol.bindingIdentifier) return null;
|
|
6003
|
-
visitedSymbolIds.add(symbol.id);
|
|
6004
|
-
const initializer = stripParenExpression(symbol.initializer);
|
|
6005
|
-
if (!isNodeOfType(initializer, "Identifier")) return symbol;
|
|
6006
|
-
symbol = scopes.symbolFor(initializer);
|
|
6007
|
-
}
|
|
6008
|
-
return symbol;
|
|
6009
|
-
};
|
|
6010
|
-
//#endregion
|
|
6011
5981
|
//#region src/plugin/rules/a11y/click-events-have-key-events.ts
|
|
6012
5982
|
const MESSAGE$57 = "Keyboard users can't trigger this click handler because there's no keyboard one, so add `onKeyUp`, `onKeyDown`, or `onKeyPress`.";
|
|
6013
5983
|
const KEY_HANDLERS = [
|
|
@@ -6018,63 +5988,6 @@ const KEY_HANDLERS = [
|
|
|
6018
5988
|
"onKeyDownCapture",
|
|
6019
5989
|
"onKeyPressCapture"
|
|
6020
5990
|
];
|
|
6021
|
-
const CLICK_HANDLERS = ["onClick", "onClickCapture"];
|
|
6022
|
-
const TRANSPARENT_SPREAD_EVENT_NAMES = new Set([...CLICK_HANDLERS, ...KEY_HANDLERS].map((eventName) => eventName.toLowerCase()));
|
|
6023
|
-
const CONSERVATIVE_SPREAD_PROP_NAMES = new Set([
|
|
6024
|
-
"aria-hidden",
|
|
6025
|
-
"onmouseenter",
|
|
6026
|
-
"onmouseover",
|
|
6027
|
-
"role"
|
|
6028
|
-
]);
|
|
6029
|
-
const resolveSpreadObjectExpression = (expression, scopes) => {
|
|
6030
|
-
const innerExpression = stripParenExpression(expression);
|
|
6031
|
-
if (isNodeOfType(innerExpression, "ObjectExpression")) return innerExpression;
|
|
6032
|
-
if (!isNodeOfType(innerExpression, "Identifier")) return null;
|
|
6033
|
-
const symbol = resolveConstIdentifierAlias(innerExpression, scopes);
|
|
6034
|
-
if (symbol?.kind !== "const" || !symbol.initializer) return null;
|
|
6035
|
-
const initializer = stripParenExpression(symbol.initializer);
|
|
6036
|
-
return isNodeOfType(initializer, "ObjectExpression") ? initializer : null;
|
|
6037
|
-
};
|
|
6038
|
-
const collectTransparentSpreadEventNames = (expression, scopes, eventValues, visitedObjectExpressions) => {
|
|
6039
|
-
const objectExpression = resolveSpreadObjectExpression(expression, scopes);
|
|
6040
|
-
if (!objectExpression || visitedObjectExpressions.has(objectExpression)) return false;
|
|
6041
|
-
visitedObjectExpressions.add(objectExpression);
|
|
6042
|
-
let isTransparent = true;
|
|
6043
|
-
for (const property of objectExpression.properties) {
|
|
6044
|
-
if (isNodeOfType(property, "SpreadElement")) {
|
|
6045
|
-
if (!collectTransparentSpreadEventNames(property.argument, scopes, eventValues, visitedObjectExpressions)) {
|
|
6046
|
-
isTransparent = false;
|
|
6047
|
-
break;
|
|
6048
|
-
}
|
|
6049
|
-
continue;
|
|
6050
|
-
}
|
|
6051
|
-
if (!isNodeOfType(property, "Property")) {
|
|
6052
|
-
isTransparent = false;
|
|
6053
|
-
break;
|
|
6054
|
-
}
|
|
6055
|
-
const propertyName = getStaticPropertyKeyName(property, { allowComputedString: true });
|
|
6056
|
-
if (!propertyName) {
|
|
6057
|
-
isTransparent = false;
|
|
6058
|
-
break;
|
|
6059
|
-
}
|
|
6060
|
-
const normalizedPropertyName = propertyName.toLowerCase();
|
|
6061
|
-
if (CONSERVATIVE_SPREAD_PROP_NAMES.has(normalizedPropertyName)) {
|
|
6062
|
-
isTransparent = false;
|
|
6063
|
-
break;
|
|
6064
|
-
}
|
|
6065
|
-
if (TRANSPARENT_SPREAD_EVENT_NAMES.has(normalizedPropertyName)) eventValues.set(normalizedPropertyName, property.value);
|
|
6066
|
-
}
|
|
6067
|
-
visitedObjectExpressions.delete(objectExpression);
|
|
6068
|
-
return isTransparent;
|
|
6069
|
-
};
|
|
6070
|
-
const getTransparentSpreadEventValues = (attributes, scopes) => {
|
|
6071
|
-
const eventValues = /* @__PURE__ */ new Map();
|
|
6072
|
-
for (const attribute of attributes) {
|
|
6073
|
-
if (!isNodeOfType(attribute, "JSXSpreadAttribute")) continue;
|
|
6074
|
-
if (!collectTransparentSpreadEventNames(attribute.argument, scopes, eventValues, /* @__PURE__ */ new Set())) return null;
|
|
6075
|
-
}
|
|
6076
|
-
return eventValues;
|
|
6077
|
-
};
|
|
6078
5991
|
const FOCUSLESS_CONTAINER_TAGS = new Set([
|
|
6079
5992
|
"tr",
|
|
6080
5993
|
"td",
|
|
@@ -6122,10 +6035,7 @@ const isFocusForwardingFunctionBody = (body) => {
|
|
|
6122
6035
|
};
|
|
6123
6036
|
const resolveHandlerFunction = (attribute) => {
|
|
6124
6037
|
if (!attribute.value || !isNodeOfType(attribute.value, "JSXExpressionContainer")) return null;
|
|
6125
|
-
|
|
6126
|
-
};
|
|
6127
|
-
const resolveHandlerFunctionExpression = (handlerExpression) => {
|
|
6128
|
-
let expression = handlerExpression;
|
|
6038
|
+
let expression = attribute.value.expression;
|
|
6129
6039
|
if (isNodeOfType(expression, "Identifier")) {
|
|
6130
6040
|
const binding = findVariableInitializer(expression, expression.name);
|
|
6131
6041
|
if (!binding?.initializer) return null;
|
|
@@ -6224,22 +6134,18 @@ const clickEventsHaveKeyEvents = defineRule({
|
|
|
6224
6134
|
if (!HTML_TAGS.has(tag)) return;
|
|
6225
6135
|
if (tag === "label") return;
|
|
6226
6136
|
if (!FOCUSLESS_CONTAINER_TAGS.has(tag) && isInteractiveElement(tag, node)) return;
|
|
6227
|
-
const spreadEventValues = getTransparentSpreadEventValues(node.attributes, context.scopes);
|
|
6228
|
-
if (!spreadEventValues) return;
|
|
6229
6137
|
const onClick = hasJsxPropIgnoreCase(node.attributes, "onClick") ?? hasJsxPropIgnoreCase(node.attributes, "onClickCapture");
|
|
6230
|
-
|
|
6231
|
-
if (
|
|
6232
|
-
if (
|
|
6233
|
-
if (
|
|
6234
|
-
const spreadHandlerFunction = spreadOnClickExpression ? resolveHandlerFunctionExpression(spreadOnClickExpression) : null;
|
|
6235
|
-
if (spreadHandlerFunction && (isFocusForwardingFunctionBody(spreadHandlerFunction.body ?? null) || containsBackdropDismissComparison(spreadHandlerFunction.body ?? null))) return;
|
|
6138
|
+
if (!onClick) return;
|
|
6139
|
+
if (isPureEventBlockerHandler(onClick)) return;
|
|
6140
|
+
if (isFocusForwardingHandler(onClick)) return;
|
|
6141
|
+
if (hasJsxSpreadAttribute$1(node.attributes)) return;
|
|
6236
6142
|
if (hasCompositeItemRole(node)) return;
|
|
6237
6143
|
if (isHoverSelectionListItem(tag, node)) return;
|
|
6238
|
-
if (
|
|
6144
|
+
if (isBackdropDismissHandler(onClick)) return;
|
|
6239
6145
|
if (containsKeyboardActivatableDescendant(node.parent)) return;
|
|
6240
6146
|
if (isHiddenFromScreenReader(node, context.settings)) return;
|
|
6241
6147
|
if (isPresentationRole(node)) return;
|
|
6242
|
-
if (KEY_HANDLERS.some((handler) => hasJsxPropIgnoreCase(node.attributes, handler)
|
|
6148
|
+
if (KEY_HANDLERS.some((handler) => hasJsxPropIgnoreCase(node.attributes, handler))) return;
|
|
6243
6149
|
context.report({
|
|
6244
6150
|
node: node.name,
|
|
6245
6151
|
message: MESSAGE$57
|
|
@@ -7712,7 +7618,7 @@ const isCreateClassLikeCall = (node) => {
|
|
|
7712
7618
|
if (isNodeOfType(callee, "MemberExpression")) return getStaticMemberName$2(callee) === "createClass";
|
|
7713
7619
|
return false;
|
|
7714
7620
|
};
|
|
7715
|
-
const isCreateContextCall
|
|
7621
|
+
const isCreateContextCall = (node) => {
|
|
7716
7622
|
if (!isNodeOfType(node, "CallExpression")) return false;
|
|
7717
7623
|
const callee = node.callee;
|
|
7718
7624
|
if (isNodeOfType(callee, "Identifier")) return callee.name === "createContext";
|
|
@@ -7889,7 +7795,7 @@ const displayName = defineRule({
|
|
|
7889
7795
|
}
|
|
7890
7796
|
},
|
|
7891
7797
|
CallExpression(node) {
|
|
7892
|
-
if (settings.checkContextObjects && isReactVersionAtLeast$1(settings.reactVersion, 16, 3) && isCreateContextCall
|
|
7798
|
+
if (settings.checkContextObjects && isReactVersionAtLeast$1(settings.reactVersion, 16, 3) && isCreateContextCall(node)) {
|
|
7893
7799
|
const assignedName = getAssignedName(node);
|
|
7894
7800
|
if (assignedName) {
|
|
7895
7801
|
const programRoot = findProgramRoot(node);
|
|
@@ -7936,6 +7842,21 @@ const displayName = defineRule({
|
|
|
7936
7842
|
}
|
|
7937
7843
|
});
|
|
7938
7844
|
//#endregion
|
|
7845
|
+
//#region src/plugin/utils/get-static-property-key-name.ts
|
|
7846
|
+
const getStaticPropertyKeyName = (node, options = {}) => {
|
|
7847
|
+
if (!isNodeOfType(node, "Property")) return null;
|
|
7848
|
+
if (node.computed) {
|
|
7849
|
+
if (options.allowComputedString && isNodeOfType(node.key, "Literal") && typeof node.key.value === "string") return node.key.value;
|
|
7850
|
+
return null;
|
|
7851
|
+
}
|
|
7852
|
+
if (isNodeOfType(node.key, "Identifier")) return node.key.name;
|
|
7853
|
+
if (isNodeOfType(node.key, "Literal")) {
|
|
7854
|
+
if (typeof node.key.value === "string") return node.key.value;
|
|
7855
|
+
if (options.stringifyNonStringLiterals) return String(node.key.value);
|
|
7856
|
+
}
|
|
7857
|
+
return null;
|
|
7858
|
+
};
|
|
7859
|
+
//#endregion
|
|
7939
7860
|
//#region src/plugin/utils/read-static-boolean.ts
|
|
7940
7861
|
const readStaticBoolean = (node) => {
|
|
7941
7862
|
if (!node) return null;
|
|
@@ -7944,6 +7865,21 @@ const readStaticBoolean = (node) => {
|
|
|
7944
7865
|
return unwrappedNode.value;
|
|
7945
7866
|
};
|
|
7946
7867
|
//#endregion
|
|
7868
|
+
//#region src/plugin/utils/resolve-const-identifier-alias.ts
|
|
7869
|
+
const resolveConstIdentifierAlias = (identifier, scopes) => {
|
|
7870
|
+
if (!isNodeOfType(identifier, "Identifier")) return null;
|
|
7871
|
+
const visitedSymbolIds = /* @__PURE__ */ new Set();
|
|
7872
|
+
let symbol = scopes.symbolFor(identifier);
|
|
7873
|
+
while (symbol?.kind === "const") {
|
|
7874
|
+
if (visitedSymbolIds.has(symbol.id) || !symbol.initializer || !isNodeOfType(symbol.declarationNode, "VariableDeclarator") || symbol.declarationNode.id !== symbol.bindingIdentifier) return null;
|
|
7875
|
+
visitedSymbolIds.add(symbol.id);
|
|
7876
|
+
const initializer = stripParenExpression(symbol.initializer);
|
|
7877
|
+
if (!isNodeOfType(initializer, "Identifier")) return symbol;
|
|
7878
|
+
symbol = scopes.symbolFor(initializer);
|
|
7879
|
+
}
|
|
7880
|
+
return symbol;
|
|
7881
|
+
};
|
|
7882
|
+
//#endregion
|
|
7947
7883
|
//#region src/plugin/utils/is-react-api-call.ts
|
|
7948
7884
|
const includesApiName = (apiNames, apiName) => typeof apiNames === "string" ? apiNames === apiName : apiNames.has(apiName);
|
|
7949
7885
|
const isImportedFromReact = (symbol) => {
|
|
@@ -7951,9 +7887,9 @@ const isImportedFromReact = (symbol) => {
|
|
|
7951
7887
|
const importDeclaration = symbol.declarationNode.parent;
|
|
7952
7888
|
return Boolean(importDeclaration && isNodeOfType(importDeclaration, "ImportDeclaration") && typeof importDeclaration.source.value === "string" && REACT_RUNTIME_MODULE_SOURCES.has(importDeclaration.source.value));
|
|
7953
7889
|
};
|
|
7954
|
-
const isNamedReactApiImport = (identifier, apiNames, scopes
|
|
7890
|
+
const isNamedReactApiImport = (identifier, apiNames, scopes) => {
|
|
7955
7891
|
if (!isNodeOfType(identifier, "Identifier")) return false;
|
|
7956
|
-
const symbol =
|
|
7892
|
+
const symbol = scopes.symbolFor(identifier);
|
|
7957
7893
|
if (!symbol || !isImportedFromReact(symbol)) return false;
|
|
7958
7894
|
const importedName = getImportedName(symbol.declarationNode);
|
|
7959
7895
|
return Boolean(importedName && includesApiName(apiNames, importedName));
|
|
@@ -7967,7 +7903,7 @@ const isReactApiCall = (node, apiNames, scopes, options = {}) => {
|
|
|
7967
7903
|
if (!isNodeOfType(node, "CallExpression")) return false;
|
|
7968
7904
|
const callee = stripParenExpression(node.callee);
|
|
7969
7905
|
if (isNodeOfType(callee, "Identifier")) {
|
|
7970
|
-
if (isNamedReactApiImport(callee, apiNames, scopes
|
|
7906
|
+
if (isNamedReactApiImport(callee, apiNames, scopes)) return true;
|
|
7971
7907
|
return Boolean(options.allowUnboundBareCalls && includesApiName(apiNames, callee.name) && scopes.isGlobalReference(callee));
|
|
7972
7908
|
}
|
|
7973
7909
|
if (!isNodeOfType(callee, "MemberExpression") || callee.computed || !isNodeOfType(callee.property, "Identifier") || !includesApiName(apiNames, callee.property.name)) return false;
|
|
@@ -11176,11 +11112,6 @@ If the missing value is recreated every render, move it inside the hook or stabi
|
|
|
11176
11112
|
return { CallExpression(node) {
|
|
11177
11113
|
const hookName = getHookName(node.callee, context.scopes);
|
|
11178
11114
|
if (!hookName || !isHookOfInterest(hookName, node.callee)) return;
|
|
11179
|
-
if (HOOKS_REQUIRING_DEPS_MATCH.has(hookName) && !isReactApiCall(node, HOOKS_REQUIRING_DEPS_MATCH, context.scopes, {
|
|
11180
|
-
allowGlobalReactNamespace: true,
|
|
11181
|
-
allowUnboundBareCalls: true,
|
|
11182
|
-
resolveNamedAliases: true
|
|
11183
|
-
})) return;
|
|
11184
11115
|
const callbackArgumentIndex = getCallbackArgumentIndex(hookName);
|
|
11185
11116
|
const depsArgumentIndex = getDepsArgumentIndex(hookName);
|
|
11186
11117
|
const callbackArgument = node.arguments[callbackArgumentIndex];
|
|
@@ -25139,11 +25070,7 @@ const noAsyncEffectCallback = defineRule({
|
|
|
25139
25070
|
severity: "warn",
|
|
25140
25071
|
recommendation: "Don't make the effect callback `async`. Define an async function inside the effect and call it, then return a real cleanup function if you need one.",
|
|
25141
25072
|
create: (context) => ({ CallExpression(node) {
|
|
25142
|
-
if (!
|
|
25143
|
-
allowGlobalReactNamespace: true,
|
|
25144
|
-
allowUnboundBareCalls: true,
|
|
25145
|
-
resolveNamedAliases: true
|
|
25146
|
-
})) return;
|
|
25073
|
+
if (!isHookCall$2(node, EFFECT_HOOK_NAMES$1)) return;
|
|
25147
25074
|
const callback = getEffectCallback(node);
|
|
25148
25075
|
if (!callback) return;
|
|
25149
25076
|
if (!isNodeOfType(callback, "ArrowFunctionExpression") && !isNodeOfType(callback, "FunctionExpression")) return;
|
|
@@ -26341,19 +26268,6 @@ const noCloneElement = defineRule({
|
|
|
26341
26268
|
} })
|
|
26342
26269
|
});
|
|
26343
26270
|
//#endregion
|
|
26344
|
-
//#region src/plugin/utils/get-call-method-name.ts
|
|
26345
|
-
/**
|
|
26346
|
-
* Returns the static method name of a call's callee when it's a
|
|
26347
|
-
* non-computed MemberExpression (`obj.method` → `"method"`), or
|
|
26348
|
-
* `null` otherwise. Used by `no-pass-data-to-parent` and
|
|
26349
|
-
* `no-pass-live-state-to-parent` to match the receiver-bound
|
|
26350
|
-
* method-call shape against an allow/block list.
|
|
26351
|
-
*/
|
|
26352
|
-
const getCallMethodName = (callee) => {
|
|
26353
|
-
if (isNodeOfType(callee, "MemberExpression") && !callee.computed && isNodeOfType(callee.property, "Identifier")) return callee.property.name;
|
|
26354
|
-
return null;
|
|
26355
|
-
};
|
|
26356
|
-
//#endregion
|
|
26357
26271
|
//#region src/plugin/rules/state-and-effects/no-create-context-in-render.ts
|
|
26358
26272
|
const MESSAGE$31 = "createContext() builds a new context every render, so every consumer gets cut off & resets.";
|
|
26359
26273
|
const CONTEXT_MODULES = [
|
|
@@ -26361,35 +26275,23 @@ const CONTEXT_MODULES = [
|
|
|
26361
26275
|
"use-context-selector",
|
|
26362
26276
|
"react-tracked"
|
|
26363
26277
|
];
|
|
26364
|
-
const
|
|
26365
|
-
if (symbol?.kind !== "import") return null;
|
|
26366
|
-
const importDeclaration = symbol.declarationNode.parent;
|
|
26367
|
-
if (!importDeclaration || !isNodeOfType(importDeclaration, "ImportDeclaration") || typeof importDeclaration.source.value !== "string" || !CONTEXT_MODULES.includes(importDeclaration.source.value)) return null;
|
|
26368
|
-
return importDeclaration.source.value;
|
|
26369
|
-
};
|
|
26370
|
-
const isSupportedNamespaceSymbol = (symbol) => getSupportedContextImportSource(symbol) !== null && Boolean(symbol && (isNodeOfType(symbol.declarationNode, "ImportDefaultSpecifier") || isNodeOfType(symbol.declarationNode, "ImportNamespaceSpecifier")));
|
|
26371
|
-
const isDestructuredCreateContextBinding = (identifier, scopes) => {
|
|
26372
|
-
if (!isNodeOfType(identifier, "Identifier")) return false;
|
|
26373
|
-
const symbol = scopes.symbolFor(identifier);
|
|
26374
|
-
if (symbol?.kind !== "const" || !symbol.initializer || !isNodeOfType(symbol.declarationNode, "VariableDeclarator") || !isNodeOfType(symbol.declarationNode.id, "ObjectPattern")) return false;
|
|
26375
|
-
const property = symbol.bindingIdentifier.parent;
|
|
26376
|
-
if (!property || !isNodeOfType(property, "Property") || getStaticPropertyKeyName(property, { allowComputedString: true }) !== "createContext") return false;
|
|
26377
|
-
const initializer = stripParenExpression(symbol.initializer);
|
|
26378
|
-
return isNodeOfType(initializer, "Identifier") && isSupportedNamespaceSymbol(resolveConstIdentifierAlias(initializer, scopes));
|
|
26379
|
-
};
|
|
26380
|
-
const isCreateContextCall = (node, scopes) => {
|
|
26381
|
-
const callee = stripParenExpression(node.callee);
|
|
26278
|
+
const isCreateContextCallee = (callee) => {
|
|
26382
26279
|
if (isNodeOfType(callee, "Identifier")) {
|
|
26383
|
-
|
|
26384
|
-
|
|
26385
|
-
return getSupportedContextImportSource(symbol) !== null && Boolean(symbol && getImportedName(symbol.declarationNode) === "createContext");
|
|
26280
|
+
const binding = getImportBindingForName(callee, callee.name);
|
|
26281
|
+
return binding !== null && binding.exportedName === "createContext" && CONTEXT_MODULES.includes(binding.source);
|
|
26386
26282
|
}
|
|
26387
|
-
if (
|
|
26388
|
-
|
|
26389
|
-
|
|
26390
|
-
|
|
26391
|
-
|
|
26392
|
-
|
|
26283
|
+
if (isNodeOfType(callee, "MemberExpression") && !callee.computed) {
|
|
26284
|
+
const namespaceIdentifier = callee.object;
|
|
26285
|
+
const propertyIdentifier = callee.property;
|
|
26286
|
+
if (!isNodeOfType(namespaceIdentifier, "Identifier")) return false;
|
|
26287
|
+
if (!isNodeOfType(propertyIdentifier, "Identifier")) return false;
|
|
26288
|
+
if (propertyIdentifier.name !== "createContext") return false;
|
|
26289
|
+
const namespaceName = namespaceIdentifier.name;
|
|
26290
|
+
if (isCanonicalReactNamespaceName(namespaceName)) return true;
|
|
26291
|
+
const importSource = getImportSourceForName(namespaceIdentifier, namespaceName);
|
|
26292
|
+
return importSource !== null && CONTEXT_MODULES.includes(importSource);
|
|
26293
|
+
}
|
|
26294
|
+
return false;
|
|
26393
26295
|
};
|
|
26394
26296
|
const noCreateContextInRender = defineRule({
|
|
26395
26297
|
id: "no-create-context-in-render",
|
|
@@ -26398,7 +26300,7 @@ const noCreateContextInRender = defineRule({
|
|
|
26398
26300
|
category: "Correctness",
|
|
26399
26301
|
recommendation: "Move `createContext(...)` outside the component, to the top level of the file, so it stays the same on every render.",
|
|
26400
26302
|
create: (context) => ({ CallExpression(node) {
|
|
26401
|
-
if (!
|
|
26303
|
+
if (!isCreateContextCallee(node.callee)) return;
|
|
26402
26304
|
const componentOrHookName = enclosingComponentOrHookName(node);
|
|
26403
26305
|
if (!componentOrHookName) return;
|
|
26404
26306
|
context.report({
|
|
@@ -30449,11 +30351,7 @@ const noFetchInEffect = defineRule({
|
|
|
30449
30351
|
severity: "warn",
|
|
30450
30352
|
recommendation: "Use a data-fetching layer or Server Component so fetches do not race, double-fire, or leak from `useEffect`.",
|
|
30451
30353
|
create: (context) => ({ CallExpression(node) {
|
|
30452
|
-
if (!
|
|
30453
|
-
allowGlobalReactNamespace: true,
|
|
30454
|
-
allowUnboundBareCalls: true,
|
|
30455
|
-
resolveNamedAliases: true
|
|
30456
|
-
})) return;
|
|
30354
|
+
if (!isHookCall$2(node, EFFECT_HOOK_NAMES$1)) return;
|
|
30457
30355
|
const callback = getEffectCallback(node);
|
|
30458
30356
|
if (!callback) return;
|
|
30459
30357
|
const analysisFunctions = collectEffectAnalysisFunctions(callback, context);
|
|
@@ -34663,6 +34561,19 @@ const DATA_SINK_METHOD_NAMES = new Set([
|
|
|
34663
34561
|
"deserialize"
|
|
34664
34562
|
]);
|
|
34665
34563
|
//#endregion
|
|
34564
|
+
//#region src/plugin/utils/get-call-method-name.ts
|
|
34565
|
+
/**
|
|
34566
|
+
* Returns the static method name of a call's callee when it's a
|
|
34567
|
+
* non-computed MemberExpression (`obj.method` → `"method"`), or
|
|
34568
|
+
* `null` otherwise. Used by `no-pass-data-to-parent` and
|
|
34569
|
+
* `no-pass-live-state-to-parent` to match the receiver-bound
|
|
34570
|
+
* method-call shape against an allow/block list.
|
|
34571
|
+
*/
|
|
34572
|
+
const getCallMethodName = (callee) => {
|
|
34573
|
+
if (isNodeOfType(callee, "MemberExpression") && !callee.computed && isNodeOfType(callee.property, "Identifier")) return callee.property.name;
|
|
34574
|
+
return null;
|
|
34575
|
+
};
|
|
34576
|
+
//#endregion
|
|
34666
34577
|
//#region src/plugin/rules/state-and-effects/no-pass-data-to-parent.ts
|
|
34667
34578
|
const isUseStateIdentifier = (identifier) => {
|
|
34668
34579
|
if (!isNodeOfType(identifier, "Identifier")) return false;
|