Skip to content

Commit 0a11eed

Browse files
committed
refactor: introduce new utility functions for JSX attribute and element name handling
1 parent 5ddf3e0 commit 0a11eed

32 files changed

+152
-145
lines changed

packages/plugins/eslint-plugin-react-dom/src/rules/no-dangerously-set-innerhtml.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ export default createRule<[], MessageID>({
3131
return {
3232
JSXElement(node) {
3333
const attributes = node.openingElement.attributes;
34-
const attribute = JSX.getAttributeNode(
34+
const attribute = JSX.getAttribute(
3535
"dangerouslySetInnerHTML",
3636
context.sourceCode.getScope(node),
3737
attributes,

packages/plugins/eslint-plugin-react-dom/src/rules/no-missing-button-type.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ export default createRule<[], MessageID>({
3434
return {
3535
JSXElement(node) {
3636
const [elementNameOnJsx, elementNameOnDom] = getElementNameOnJsxAndDom(
37-
node.openingElement,
37+
node,
3838
context,
3939
polymorphicPropName,
4040
additionalComponents,
@@ -46,16 +46,15 @@ export default createRule<[], MessageID>({
4646
const customComponent = findCustomComponent(elementNameOnJsx, additionalComponents);
4747
const customComponentProp = findCustomComponentProp("type", customComponent?.attributes ?? []);
4848
const propNameOnJsx = customComponentProp?.name ?? "type";
49-
const attributeNode = JSX.getAttributeNode(
49+
const attributeNode = JSX.getAttribute(
5050
propNameOnJsx,
5151
elementScope,
5252
node.openingElement.attributes,
5353
);
5454
if (attributeNode != null) {
5555
const attributeScope = context.sourceCode.getScope(attributeNode);
56-
const attributeStaticValue = JSX.getAttributeStaticValue(attributeNode, attributeScope);
57-
const attributeStringValue = JSX.toResolvedAttributeValue(propNameOnJsx, attributeStaticValue);
58-
if (typeof attributeStringValue !== "string") {
56+
const attributeValue = JSX.getAttributeValue(propNameOnJsx, attributeNode, attributeScope);
57+
if (attributeValue.kind === "some" && typeof attributeValue.value !== "string") {
5958
context.report({
6059
messageId: "noMissingButtonType",
6160
node: attributeNode,

packages/plugins/eslint-plugin-react-dom/src/rules/no-missing-iframe-sandbox.ts

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import type { _ } from "@eslint-react/eff";
21
import * as JSX from "@eslint-react/jsx";
32
import type { RuleFeature } from "@eslint-react/shared";
43
import { getSettingsFromContext } from "@eslint-react/shared";
@@ -33,7 +32,7 @@ const validTypes = [
3332
"allow-top-navigation-to-custom-protocols",
3433
] as const;
3534

36-
function hasValidSandBox(value: string | _) {
35+
function hasValidSandBox(value: unknown) {
3736
return typeof value === "string"
3837
&& value
3938
.split(" ")
@@ -60,7 +59,7 @@ export default createRule<[], MessageID>({
6059
return {
6160
JSXElement(node) {
6261
const [elementNameOnJsx, elementNameOnDom] = getElementNameOnJsxAndDom(
63-
node.openingElement,
62+
node,
6463
context,
6564
polymorphicPropName,
6665
additionalComponents,
@@ -72,16 +71,15 @@ export default createRule<[], MessageID>({
7271
const customComponent = findCustomComponent(elementNameOnJsx, additionalComponents);
7372
const customComponentProp = findCustomComponentProp("sandbox", customComponent?.attributes ?? []);
7473
const propNameOnJsx = customComponentProp?.name ?? "sandbox";
75-
const attributeNode = JSX.getAttributeNode(
74+
const attributeNode = JSX.getAttribute(
7675
propNameOnJsx,
7776
elementScope,
7877
node.openingElement.attributes,
7978
);
8079
if (attributeNode != null) {
8180
const attributeScope = context.sourceCode.getScope(attributeNode);
82-
const attributeStaticValue = JSX.getAttributeStaticValue(attributeNode, attributeScope);
83-
const attributeStringValue = JSX.toResolvedAttributeValue(propNameOnJsx, attributeStaticValue);
84-
if (hasValidSandBox(attributeStringValue)) return;
81+
const attributeValue = JSX.getAttributeValue(propNameOnJsx, attributeNode, attributeScope);
82+
if (attributeValue.kind === "some" && hasValidSandBox(attributeValue.value)) return;
8583
context.report({
8684
messageId: "noMissingIframeSandbox",
8785
node: attributeNode,

packages/plugins/eslint-plugin-react-dom/src/rules/no-namespace.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,14 @@ export default createRule<[], MessageID>({
2727
name: RULE_NAME,
2828
create(context) {
2929
return {
30-
JSXOpeningElement(node) {
30+
JSXElement(node) {
3131
const name = JSX.getElementName(node);
3232
if (typeof name !== "string" || !name.includes(":")) {
3333
return;
3434
}
3535
context.report({
3636
messageId: "noNamespace",
37-
node,
37+
node: node.openingElement.name,
3838
data: {
3939
name,
4040
},

packages/plugins/eslint-plugin-react-dom/src/rules/no-script-url.ts

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import * as JSX from "@eslint-react/jsx";
22
import type { RuleFeature } from "@eslint-react/shared";
33
import { RE_JAVASCRIPT_PROTOCOL } from "@eslint-react/shared";
4-
import * as VAR from "@eslint-react/var";
54
import { AST_NODE_TYPES as T } from "@typescript-eslint/types";
65
import type { CamelCase } from "string-ts";
76

@@ -39,10 +38,9 @@ export default createRule<[], MessageID>({
3938
return;
4039
}
4140
const attributeScope = context.sourceCode.getScope(node);
42-
const attributeValue = JSX.getAttributeStaticValue(node, attributeScope);
43-
const attributeValueResolved = VAR.toResolved(attributeValue).value;
44-
if (typeof attributeValueResolved !== "string") return;
45-
if (RE_JAVASCRIPT_PROTOCOL.test(attributeValueResolved)) {
41+
const attributeValue = JSX.getAttributeValue(JSX.toString(node.name), node, attributeScope);
42+
if (attributeValue.kind === "none" || typeof attributeValue.value !== "string") return;
43+
if (RE_JAVASCRIPT_PROTOCOL.test(attributeValue.value)) {
4644
context.report({
4745
messageId: "noScriptUrl",
4846
node: node.value,

packages/plugins/eslint-plugin-react-dom/src/rules/no-unsafe-iframe-sandbox.ts

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import type { _ } from "@eslint-react/eff";
21
import * as JSX from "@eslint-react/jsx";
32
import type { RuleFeature } from "@eslint-react/shared";
43
import { getSettingsFromContext } from "@eslint-react/shared";
@@ -18,8 +17,8 @@ const unsafeSandboxValues = [
1817
["allow-scripts", "allow-same-origin"],
1918
] as const;
2019

21-
function hasNoneOrSafeSandbox(value: string | _) {
22-
if (value == null) return true;
20+
function hasSafeSandbox(value: unknown) {
21+
if (typeof value !== "string") return false;
2322
return !unsafeSandboxValues.some((values) => {
2423
return values.every((v) => value.includes(v));
2524
});
@@ -45,7 +44,7 @@ export default createRule<[], MessageID>({
4544
return {
4645
JSXElement(node) {
4746
const [elementNameOnJsx, elementNameOnDom] = getElementNameOnJsxAndDom(
48-
node.openingElement,
47+
node,
4948
context,
5049
polymorphicPropName,
5150
additionalComponents,
@@ -57,23 +56,24 @@ export default createRule<[], MessageID>({
5756
const customComponent = findCustomComponent(elementNameOnJsx, additionalComponents);
5857
const customComponentProp = findCustomComponentProp("sandbox", customComponent?.attributes ?? []);
5958
const propNameOnJsx = customComponentProp?.name ?? "sandbox";
60-
const attributeNode = JSX.getAttributeNode(
59+
const attributeNode = JSX.getAttribute(
6160
propNameOnJsx,
6261
elementScope,
6362
node.openingElement.attributes,
6463
);
6564
if (attributeNode != null) {
6665
const attributeScope = context.sourceCode.getScope(attributeNode);
67-
const attributeStaticValue = JSX.getAttributeStaticValue(attributeNode, attributeScope);
68-
const attributeStringValue = JSX.toResolvedAttributeValue(propNameOnJsx, attributeStaticValue);
69-
if (hasNoneOrSafeSandbox(attributeStringValue)) return;
70-
context.report({
71-
messageId: "noUnsafeIframeSandbox",
72-
node: attributeNode,
73-
});
74-
return;
66+
const attributeValue = JSX.getAttributeValue(propNameOnJsx, attributeNode, attributeScope);
67+
if (attributeValue.kind === "some" && !hasSafeSandbox(attributeValue.value)) {
68+
context.report({
69+
messageId: "noUnsafeIframeSandbox",
70+
node: attributeNode,
71+
});
72+
return;
73+
}
7574
}
76-
if (!hasNoneOrSafeSandbox(customComponentProp?.defaultValue)) {
75+
if (customComponentProp?.defaultValue == null) return;
76+
if (!hasSafeSandbox(customComponentProp.defaultValue)) {
7777
context.report({
7878
messageId: "noUnsafeIframeSandbox",
7979
node,

packages/plugins/eslint-plugin-react-dom/src/rules/no-unsafe-target-blank.ts

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { _ } from "@eslint-react/eff";
1+
import { _ } from "@eslint-react/eff";
22
import * as JSX from "@eslint-react/jsx";
33
import type { RuleFeature } from "@eslint-react/shared";
44
import { getSettingsFromContext } from "@eslint-react/shared";
@@ -49,7 +49,7 @@ export default createRule<[], MessageID>({
4949
return {
5050
JSXElement(node: TSESTree.JSXElement) {
5151
const [elementNameOnJsx, elementNameOnDom] = getElementNameOnJsxAndDom(
52-
node.openingElement,
52+
node,
5353
context,
5454
polymorphicPropName,
5555
additionalComponents,
@@ -58,27 +58,30 @@ export default createRule<[], MessageID>({
5858
const elementScope = context.sourceCode.getScope(node);
5959
const customComponent = findCustomComponent(elementNameOnJsx, additionalComponents);
6060

61-
const getAttributeValue = (name: string) => {
61+
const getAttributeStringValue = (name: string) => {
6262
const customComponentProp = findCustomComponentProp(name, customComponent?.attributes ?? []);
6363
const propNameOnJsx = customComponentProp?.name ?? name;
64-
const attributeNode = JSX.getAttributeNode(
64+
const attributeNode = JSX.getAttribute(
6565
propNameOnJsx,
6666
elementScope,
6767
node.openingElement.attributes,
6868
);
6969
if (attributeNode == null) return customComponentProp?.defaultValue;
7070
const attributeScope = context.sourceCode.getScope(attributeNode);
71-
const attributeStaticValue = JSX.getAttributeStaticValue(attributeNode, attributeScope);
72-
return JSX.toResolvedAttributeValue(propNameOnJsx, attributeStaticValue);
71+
const attributeValue = JSX.getAttributeValue(propNameOnJsx, attributeNode, attributeScope);
72+
if (attributeValue.kind === "some" && typeof attributeValue.value === "string") {
73+
return attributeValue.value;
74+
}
75+
return _;
7376
};
7477

75-
if (getAttributeValue("target") !== "_blank") {
78+
if (getAttributeStringValue("target") !== "_blank") {
7679
return;
7780
}
78-
if (!isExternalLinkLike(getAttributeValue("href"))) {
81+
if (!isExternalLinkLike(getAttributeStringValue("href"))) {
7982
return;
8083
}
81-
if (isSafeRel(getAttributeValue("rel"))) {
84+
if (isSafeRel(getAttributeStringValue("rel"))) {
8285
return;
8386
}
8487
context.report({

packages/plugins/eslint-plugin-react-dom/src/rules/no-void-elements-with-children.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ export default createRule<[], MessageID>({
4848
create(context) {
4949
return {
5050
JSXElement(node) {
51-
const elementName = JSX.getElementName(node.openingElement);
51+
const elementName = JSX.getElementName(node);
5252
if (elementName.length === 0 || !voidElements.has(elementName)) {
5353
return;
5454
}
Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
import * as JSX from "@eslint-react/jsx";
22
import type { CustomComponentNormalized, RuleContext } from "@eslint-react/shared";
3-
import * as VAR from "@eslint-react/var";
43
import type { TSESTree } from "@typescript-eslint/types";
54

65
export function getElementNameOnJsxAndDom(
7-
node: TSESTree.JSXOpeningElement,
6+
node: TSESTree.JSXElement,
87
context: RuleContext,
98
polymorphicPropName?: string,
109
additionalComponents: CustomComponentNormalized[] = [],
@@ -19,15 +18,15 @@ export function getElementNameOnJsxAndDom(
1918
if (polymorphicPropName == null) return [name, name];
2019
// Get the component name using the `settings["react-x"].polymorphicPropName` setting
2120
const initialScope = context.sourceCode.getScope(node);
22-
const attributeNode = JSX.getAttributeNode(
21+
const attributeNode = JSX.getAttribute(
2322
polymorphicPropName,
2423
initialScope,
25-
node.attributes,
24+
node.openingElement.attributes,
2625
);
2726
if (attributeNode == null) return [name, name];
28-
const polymorphicPropValue = VAR.toResolved(JSX.getAttributeStaticValue(attributeNode, initialScope)).value;
29-
if (typeof polymorphicPropValue === "string") {
30-
return [name, polymorphicPropValue];
27+
const polymorphicPropValue = JSX.getAttributeValue(polymorphicPropName, attributeNode, initialScope);
28+
if (polymorphicPropValue.kind === "some" && typeof polymorphicPropValue.value === "string") {
29+
return [name, polymorphicPropValue.value];
3130
}
3231
return [name, name];
3332
}

packages/plugins/eslint-plugin-react-hooks-extra/src/utils/is-set-function-call.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ export function isSetFunctionCall(context: RuleContext, settings: ESLintReactSet
2525
return false;
2626
}
2727
const indexScope = context.sourceCode.getScope(node);
28-
const indexValue = VAR.toResolved({
28+
const indexValue = VAR.toStaticValue({
2929
kind: "lazy",
3030
node: index,
3131
initialScope: indexScope,
@@ -45,7 +45,7 @@ export function isSetFunctionCall(context: RuleContext, settings: ESLintReactSet
4545
}
4646
const property = node.callee.property;
4747
const propertyScope = context.sourceCode.getScope(node);
48-
const propertyValue = VAR.toResolved({
48+
const propertyValue = VAR.toStaticValue({
4949
kind: "lazy",
5050
node: property,
5151
initialScope: propertyScope,

0 commit comments

Comments
 (0)