oxlint-plugin-react-doctor 0.7.5-dev.8fc5848 → 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 +116 -231
- 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];
|
|
@@ -23108,73 +23039,61 @@ const PURE_GLOBAL_CALLEE_NAMES = new Set([
|
|
|
23108
23039
|
"Array",
|
|
23109
23040
|
"BigInt",
|
|
23110
23041
|
"Boolean",
|
|
23111
|
-
"encodeURIComponent",
|
|
23112
23042
|
"Number",
|
|
23113
23043
|
"Object",
|
|
23114
23044
|
"String",
|
|
23115
23045
|
"parseFloat",
|
|
23116
|
-
"parseInt"
|
|
23117
|
-
"structuredClone"
|
|
23118
|
-
]);
|
|
23119
|
-
const PURE_GLOBAL_CONSTRUCTOR_NAMES = new Set(["Date", "Set"]);
|
|
23120
|
-
const PURE_HELPER_NAMESPACE_MEMBER_NAMES = new Map([
|
|
23121
|
-
["Array", new Set(["from"])],
|
|
23122
|
-
["JSON", new Set([
|
|
23123
|
-
"isRawJSON",
|
|
23124
|
-
"parse",
|
|
23125
|
-
"rawJSON",
|
|
23126
|
-
"stringify"
|
|
23127
|
-
])],
|
|
23128
|
-
["Math", new Set([
|
|
23129
|
-
"abs",
|
|
23130
|
-
"acos",
|
|
23131
|
-
"acosh",
|
|
23132
|
-
"asin",
|
|
23133
|
-
"asinh",
|
|
23134
|
-
"atan",
|
|
23135
|
-
"atan2",
|
|
23136
|
-
"atanh",
|
|
23137
|
-
"cbrt",
|
|
23138
|
-
"ceil",
|
|
23139
|
-
"clz32",
|
|
23140
|
-
"cos",
|
|
23141
|
-
"cosh",
|
|
23142
|
-
"exp",
|
|
23143
|
-
"floor",
|
|
23144
|
-
"fround",
|
|
23145
|
-
"hypot",
|
|
23146
|
-
"imul",
|
|
23147
|
-
"log",
|
|
23148
|
-
"log10",
|
|
23149
|
-
"log1p",
|
|
23150
|
-
"log2",
|
|
23151
|
-
"max",
|
|
23152
|
-
"min",
|
|
23153
|
-
"pow",
|
|
23154
|
-
"round",
|
|
23155
|
-
"sign",
|
|
23156
|
-
"sin",
|
|
23157
|
-
"sinh",
|
|
23158
|
-
"sqrt",
|
|
23159
|
-
"tan",
|
|
23160
|
-
"tanh",
|
|
23161
|
-
"trunc"
|
|
23162
|
-
])],
|
|
23163
|
-
["Object", new Set(["assign"])]
|
|
23046
|
+
"parseInt"
|
|
23164
23047
|
]);
|
|
23048
|
+
const PURE_GLOBAL_NAMESPACE_NAMES = new Set(["JSON", "Math"]);
|
|
23049
|
+
const PURE_HELPER_NAMESPACE_MEMBER_NAMES = new Map([["JSON", new Set([
|
|
23050
|
+
"isRawJSON",
|
|
23051
|
+
"parse",
|
|
23052
|
+
"rawJSON",
|
|
23053
|
+
"stringify"
|
|
23054
|
+
])], ["Math", new Set([
|
|
23055
|
+
"abs",
|
|
23056
|
+
"acos",
|
|
23057
|
+
"acosh",
|
|
23058
|
+
"asin",
|
|
23059
|
+
"asinh",
|
|
23060
|
+
"atan",
|
|
23061
|
+
"atan2",
|
|
23062
|
+
"atanh",
|
|
23063
|
+
"cbrt",
|
|
23064
|
+
"ceil",
|
|
23065
|
+
"clz32",
|
|
23066
|
+
"cos",
|
|
23067
|
+
"cosh",
|
|
23068
|
+
"exp",
|
|
23069
|
+
"floor",
|
|
23070
|
+
"fround",
|
|
23071
|
+
"hypot",
|
|
23072
|
+
"imul",
|
|
23073
|
+
"log",
|
|
23074
|
+
"log10",
|
|
23075
|
+
"log1p",
|
|
23076
|
+
"log2",
|
|
23077
|
+
"max",
|
|
23078
|
+
"min",
|
|
23079
|
+
"pow",
|
|
23080
|
+
"round",
|
|
23081
|
+
"sign",
|
|
23082
|
+
"sin",
|
|
23083
|
+
"sinh",
|
|
23084
|
+
"sqrt",
|
|
23085
|
+
"tan",
|
|
23086
|
+
"tanh",
|
|
23087
|
+
"trunc"
|
|
23088
|
+
])]]);
|
|
23165
23089
|
const PURE_MEMBER_TRANSFORM_NAMES = new Set([
|
|
23166
23090
|
"concat",
|
|
23167
23091
|
"filter",
|
|
23168
|
-
"flatMap",
|
|
23169
23092
|
"join",
|
|
23170
23093
|
"map",
|
|
23171
|
-
"reduce",
|
|
23172
|
-
"replace",
|
|
23173
|
-
"slice",
|
|
23174
23094
|
"split",
|
|
23175
23095
|
"toLowerCase",
|
|
23176
23096
|
"toString",
|
|
23177
|
-
"toSorted",
|
|
23178
23097
|
"toUpperCase",
|
|
23179
23098
|
"trim"
|
|
23180
23099
|
]);
|
|
@@ -23556,11 +23475,6 @@ const analyzeHelperExpression = (expression, environment, usedParameterIndices)
|
|
|
23556
23475
|
const callbackSummary = analyzeHelperStatements(node.body.body ?? [], callbackEnvironment, usedParameterIndices, analyzeHelperExpression);
|
|
23557
23476
|
return callbackSummary.isValid && !callbackSummary.canContinue;
|
|
23558
23477
|
}
|
|
23559
|
-
if (isNodeOfType(node, "NewExpression")) {
|
|
23560
|
-
const callee = stripParenExpression(node.callee);
|
|
23561
|
-
if (!isNodeOfType(callee, "Identifier") || !PURE_GLOBAL_CONSTRUCTOR_NAMES.has(callee.name) || environment.parameterIndices.has(callee.name) || environment.recursiveNames.has(callee.name) || environment.shadowedGlobalNames.has(callee.name)) return false;
|
|
23562
|
-
return (node.arguments ?? []).every((argument) => analyzeHelperExpression(argument, environment, usedParameterIndices));
|
|
23563
|
-
}
|
|
23564
23478
|
if (isNodeOfType(node, "CallExpression")) {
|
|
23565
23479
|
const callee = stripParenExpression(node.callee);
|
|
23566
23480
|
const calleeRoot = getMemberRoot(callee);
|
|
@@ -23763,7 +23677,7 @@ const collectValueEvidence = (analysis, expression, frame, remainingCallFrames,
|
|
|
23763
23677
|
}
|
|
23764
23678
|
const callee = stripParenExpression(node.callee);
|
|
23765
23679
|
const calleeRoot = getMemberRoot(callee);
|
|
23766
|
-
const isPureGlobalCall = isNodeOfType(callee, "Identifier") && PURE_GLOBAL_CALLEE_NAMES.has(callee.name) && getIdentifierBindingIdentity(analysis, callee) === null || isNodeOfType(
|
|
23680
|
+
const isPureGlobalCall = isNodeOfType(callee, "Identifier") && PURE_GLOBAL_CALLEE_NAMES.has(callee.name) && getIdentifierBindingIdentity(analysis, callee) === null || isNodeOfType(calleeRoot, "Identifier") && PURE_GLOBAL_NAMESPACE_NAMES.has(calleeRoot.name) && getIdentifierBindingIdentity(analysis, calleeRoot) === null;
|
|
23767
23681
|
const isPureMemberTransform = isNodeOfType(callee, "MemberExpression") && PURE_MEMBER_TRANSFORM_NAMES.has(getStaticMemberName$1(callee) ?? "");
|
|
23768
23682
|
if (isPureGlobalCall || isPureMemberTransform) {
|
|
23769
23683
|
if (isPureMemberTransform && isNodeOfType(callee, "MemberExpression")) mergeEvidence(evidence, collectValueEvidence(analysis, callee.object, frame, remainingCallFrames, new Set(visitedBindings)));
|
|
@@ -23816,15 +23730,6 @@ const collectValueEvidence = (analysis, expression, frame, remainingCallFrames,
|
|
|
23816
23730
|
}
|
|
23817
23731
|
return evidence;
|
|
23818
23732
|
}
|
|
23819
|
-
if (isNodeOfType(node, "NewExpression")) {
|
|
23820
|
-
const callee = stripParenExpression(node.callee);
|
|
23821
|
-
if (!isNodeOfType(callee, "Identifier") || !PURE_GLOBAL_CONSTRUCTOR_NAMES.has(callee.name) || getIdentifierBindingIdentity(analysis, callee) !== null) {
|
|
23822
|
-
evidence.hasUnknownSource = true;
|
|
23823
|
-
return evidence;
|
|
23824
|
-
}
|
|
23825
|
-
for (const argument of node.arguments ?? []) mergeEvidence(evidence, collectValueEvidence(analysis, argument, frame, remainingCallFrames, new Set(visitedBindings)));
|
|
23826
|
-
return evidence;
|
|
23827
|
-
}
|
|
23828
23733
|
if (isFunctionLike$1(node) || isNodeOfType(node, "AwaitExpression")) {
|
|
23829
23734
|
evidence.hasUnknownSource = true;
|
|
23830
23735
|
return evidence;
|
|
@@ -25165,11 +25070,7 @@ const noAsyncEffectCallback = defineRule({
|
|
|
25165
25070
|
severity: "warn",
|
|
25166
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.",
|
|
25167
25072
|
create: (context) => ({ CallExpression(node) {
|
|
25168
|
-
if (!
|
|
25169
|
-
allowGlobalReactNamespace: true,
|
|
25170
|
-
allowUnboundBareCalls: true,
|
|
25171
|
-
resolveNamedAliases: true
|
|
25172
|
-
})) return;
|
|
25073
|
+
if (!isHookCall$2(node, EFFECT_HOOK_NAMES$1)) return;
|
|
25173
25074
|
const callback = getEffectCallback(node);
|
|
25174
25075
|
if (!callback) return;
|
|
25175
25076
|
if (!isNodeOfType(callback, "ArrowFunctionExpression") && !isNodeOfType(callback, "FunctionExpression")) return;
|
|
@@ -26367,19 +26268,6 @@ const noCloneElement = defineRule({
|
|
|
26367
26268
|
} })
|
|
26368
26269
|
});
|
|
26369
26270
|
//#endregion
|
|
26370
|
-
//#region src/plugin/utils/get-call-method-name.ts
|
|
26371
|
-
/**
|
|
26372
|
-
* Returns the static method name of a call's callee when it's a
|
|
26373
|
-
* non-computed MemberExpression (`obj.method` → `"method"`), or
|
|
26374
|
-
* `null` otherwise. Used by `no-pass-data-to-parent` and
|
|
26375
|
-
* `no-pass-live-state-to-parent` to match the receiver-bound
|
|
26376
|
-
* method-call shape against an allow/block list.
|
|
26377
|
-
*/
|
|
26378
|
-
const getCallMethodName = (callee) => {
|
|
26379
|
-
if (isNodeOfType(callee, "MemberExpression") && !callee.computed && isNodeOfType(callee.property, "Identifier")) return callee.property.name;
|
|
26380
|
-
return null;
|
|
26381
|
-
};
|
|
26382
|
-
//#endregion
|
|
26383
26271
|
//#region src/plugin/rules/state-and-effects/no-create-context-in-render.ts
|
|
26384
26272
|
const MESSAGE$31 = "createContext() builds a new context every render, so every consumer gets cut off & resets.";
|
|
26385
26273
|
const CONTEXT_MODULES = [
|
|
@@ -26387,35 +26275,23 @@ const CONTEXT_MODULES = [
|
|
|
26387
26275
|
"use-context-selector",
|
|
26388
26276
|
"react-tracked"
|
|
26389
26277
|
];
|
|
26390
|
-
const
|
|
26391
|
-
if (symbol?.kind !== "import") return null;
|
|
26392
|
-
const importDeclaration = symbol.declarationNode.parent;
|
|
26393
|
-
if (!importDeclaration || !isNodeOfType(importDeclaration, "ImportDeclaration") || typeof importDeclaration.source.value !== "string" || !CONTEXT_MODULES.includes(importDeclaration.source.value)) return null;
|
|
26394
|
-
return importDeclaration.source.value;
|
|
26395
|
-
};
|
|
26396
|
-
const isSupportedNamespaceSymbol = (symbol) => getSupportedContextImportSource(symbol) !== null && Boolean(symbol && (isNodeOfType(symbol.declarationNode, "ImportDefaultSpecifier") || isNodeOfType(symbol.declarationNode, "ImportNamespaceSpecifier")));
|
|
26397
|
-
const isDestructuredCreateContextBinding = (identifier, scopes) => {
|
|
26398
|
-
if (!isNodeOfType(identifier, "Identifier")) return false;
|
|
26399
|
-
const symbol = scopes.symbolFor(identifier);
|
|
26400
|
-
if (symbol?.kind !== "const" || !symbol.initializer || !isNodeOfType(symbol.declarationNode, "VariableDeclarator") || !isNodeOfType(symbol.declarationNode.id, "ObjectPattern")) return false;
|
|
26401
|
-
const property = symbol.bindingIdentifier.parent;
|
|
26402
|
-
if (!property || !isNodeOfType(property, "Property") || getStaticPropertyKeyName(property, { allowComputedString: true }) !== "createContext") return false;
|
|
26403
|
-
const initializer = stripParenExpression(symbol.initializer);
|
|
26404
|
-
return isNodeOfType(initializer, "Identifier") && isSupportedNamespaceSymbol(resolveConstIdentifierAlias(initializer, scopes));
|
|
26405
|
-
};
|
|
26406
|
-
const isCreateContextCall = (node, scopes) => {
|
|
26407
|
-
const callee = stripParenExpression(node.callee);
|
|
26278
|
+
const isCreateContextCallee = (callee) => {
|
|
26408
26279
|
if (isNodeOfType(callee, "Identifier")) {
|
|
26409
|
-
|
|
26410
|
-
|
|
26411
|
-
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);
|
|
26412
26282
|
}
|
|
26413
|
-
if (
|
|
26414
|
-
|
|
26415
|
-
|
|
26416
|
-
|
|
26417
|
-
|
|
26418
|
-
|
|
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;
|
|
26419
26295
|
};
|
|
26420
26296
|
const noCreateContextInRender = defineRule({
|
|
26421
26297
|
id: "no-create-context-in-render",
|
|
@@ -26424,7 +26300,7 @@ const noCreateContextInRender = defineRule({
|
|
|
26424
26300
|
category: "Correctness",
|
|
26425
26301
|
recommendation: "Move `createContext(...)` outside the component, to the top level of the file, so it stays the same on every render.",
|
|
26426
26302
|
create: (context) => ({ CallExpression(node) {
|
|
26427
|
-
if (!
|
|
26303
|
+
if (!isCreateContextCallee(node.callee)) return;
|
|
26428
26304
|
const componentOrHookName = enclosingComponentOrHookName(node);
|
|
26429
26305
|
if (!componentOrHookName) return;
|
|
26430
26306
|
context.report({
|
|
@@ -30475,11 +30351,7 @@ const noFetchInEffect = defineRule({
|
|
|
30475
30351
|
severity: "warn",
|
|
30476
30352
|
recommendation: "Use a data-fetching layer or Server Component so fetches do not race, double-fire, or leak from `useEffect`.",
|
|
30477
30353
|
create: (context) => ({ CallExpression(node) {
|
|
30478
|
-
if (!
|
|
30479
|
-
allowGlobalReactNamespace: true,
|
|
30480
|
-
allowUnboundBareCalls: true,
|
|
30481
|
-
resolveNamedAliases: true
|
|
30482
|
-
})) return;
|
|
30354
|
+
if (!isHookCall$2(node, EFFECT_HOOK_NAMES$1)) return;
|
|
30483
30355
|
const callback = getEffectCallback(node);
|
|
30484
30356
|
if (!callback) return;
|
|
30485
30357
|
const analysisFunctions = collectEffectAnalysisFunctions(callback, context);
|
|
@@ -34689,6 +34561,19 @@ const DATA_SINK_METHOD_NAMES = new Set([
|
|
|
34689
34561
|
"deserialize"
|
|
34690
34562
|
]);
|
|
34691
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
|
|
34692
34577
|
//#region src/plugin/rules/state-and-effects/no-pass-data-to-parent.ts
|
|
34693
34578
|
const isUseStateIdentifier = (identifier) => {
|
|
34694
34579
|
if (!isNodeOfType(identifier, "Identifier")) return false;
|