Skip to content

Commit c31262f

Browse files
authored
refactor: rename 'prop' to 'attribute' and improve related utilities (#919)
1 parent 366ca47 commit c31262f

35 files changed

+369
-330
lines changed

packages/core/docs/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@
4242
## Functions
4343

4444
- [getComponentNameFromIdentifier](functions/getComponentNameFromIdentifier.md)
45-
- [getElementNameAndRepresentName](functions/getElementNameAndRepresentName.md)
45+
- [getElementNameOnJsxAndHtml](functions/getElementNameOnJsxAndHtml.md)
4646
- [getFunctionComponentIdentifier](functions/getFunctionComponentIdentifier.md)
4747
- [getId](functions/getId.md)
4848
- [hasNoneOrValidComponentName](functions/hasNoneOrValidComponentName.md)

packages/core/docs/functions/getElementNameAndRepresentName.md renamed to packages/core/docs/functions/getElementNameOnJsxAndHtml.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@
22

33
***
44

5-
[@eslint-react/core](../README.md) / getElementNameAndRepresentName
5+
[@eslint-react/core](../README.md) / getElementNameOnJsxAndHtml
66

7-
# Function: getElementNameAndRepresentName()
7+
# Function: getElementNameOnJsxAndHtml()
88

9-
> **getElementNameAndRepresentName**(`node`, `context`, `polymorphicPropName`?, `additionalComponents`?): \[`string`, `string`\]
9+
> **getElementNameOnJsxAndHtml**(`node`, `context`, `polymorphicPropName`?, `additionalComponents`?): \[`string`, `string`\]
1010
1111
## Parameters
1212

packages/core/src/element/element-name.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import type { CustomComponentNormalized, RuleContext } from "@eslint-react/share
33
import * as VAR from "@eslint-react/var";
44
import type { TSESTree } from "@typescript-eslint/types";
55

6-
export function getElementNameAndRepresentName(
6+
export function getElementNameOnJsxAndHtml(
77
node: TSESTree.JSXOpeningElement,
88
context: RuleContext,
99
polymorphicPropName?: string,
@@ -19,15 +19,15 @@ export function getElementNameAndRepresentName(
1919
if (polymorphicPropName == null) return [name, name];
2020
// Get the component name using the `settings["react-x"].polymorphicPropName` setting
2121
const initialScope = context.sourceCode.getScope(node);
22-
const polymorphicProp = JSX.findPropInAttributes(
22+
const attributeNode = JSX.getAttributeNode(
2323
polymorphicPropName,
2424
initialScope,
2525
node.attributes,
2626
);
27-
if (polymorphicProp == null) return [name, name];
28-
const polymorphicName = VAR.toResolved(JSX.getPropValue(polymorphicProp, initialScope)).value;
29-
if (typeof polymorphicName === "string") {
30-
return [name, polymorphicName];
27+
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];
3131
}
3232
return [name, name];
3333
}

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,13 @@ export default createRule<[], MessageID>({
2929
},
3030
name: RULE_NAME,
3131
create(context) {
32+
if (!context.sourceCode.text.includes("dangerouslySetInnerHTML")) return {};
3233
return {
3334
JSXElement(node) {
3435
const attributes = node.openingElement.attributes;
3536
const initialScope = context.sourceCode.getScope(node);
36-
const hasChildren = hasChildrenWithin(node) || JSX.hasProp("children", initialScope, attributes);
37-
const hasDangerouslySetInnerHTML = JSX.hasProp("dangerouslySetInnerHTML", initialScope, attributes);
38-
if (hasChildren && hasDangerouslySetInnerHTML) {
37+
const hasChildren = hasChildrenWithin(node) || JSX.hasAttribute("children", initialScope, attributes);
38+
if (hasChildren && JSX.hasAttribute("dangerouslySetInnerHTML", initialScope, attributes)) {
3939
context.report({
4040
messageId: "noDangerouslySetInnerhtmlWithChildren",
4141
node,

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

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,19 @@ export default createRule<[], MessageID>({
2727
},
2828
name: RULE_NAME,
2929
create(context) {
30+
if (!context.sourceCode.text.includes("dangerouslySetInnerHTML")) return {};
3031
return {
3132
JSXElement(node) {
3233
const attributes = node.openingElement.attributes;
33-
const prop = JSX.getProp("dangerouslySetInnerHTML", context.sourceCode.getScope(node), attributes);
34-
if (prop == null) return;
34+
const attribute = JSX.getAttributeNode(
35+
"dangerouslySetInnerHTML",
36+
context.sourceCode.getScope(node),
37+
attributes,
38+
);
39+
if (attribute == null) return;
3540
context.report({
3641
messageId: "noDangerouslySetInnerhtml",
37-
node: prop,
42+
node: attribute,
3843
});
3944
},
4045
};

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ export default createRule<[], MessageID>({
2626
},
2727
name: RULE_NAME,
2828
create(context) {
29+
if (!context.sourceCode.text.includes("findDOMNode")) return {};
2930
return {
3031
CallExpression(node) {
3132
const { callee } = node;

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

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
import { getElementNameAndRepresentName } from "@eslint-react/core";
1+
import { getElementNameOnJsxAndHtml } from "@eslint-react/core";
22
import type { RuleFeature } from "@eslint-react/shared";
33
import { getSettingsFromContext } from "@eslint-react/shared";
44
import type { CamelCase } from "string-ts";
55

6-
import { createRule, getAdditionalAttributes, getAttributeStringValue } from "../utils";
6+
import { createRule, getAdditionalAttributes, getAttributeNodeAndStringValue } from "../utils";
77

88
export const RULE_NAME = "no-missing-button-type";
99

@@ -13,7 +13,6 @@ export const RULE_FEATURES = [
1313

1414
export type MessageID = CamelCase<typeof RULE_NAME>;
1515

16-
// TODO: Use the information in `settings["react-x"].additionalComponents` to add support for user-defined components
1716
export default createRule<[], MessageID>({
1817
meta: {
1918
type: "problem",
@@ -29,29 +28,28 @@ export default createRule<[], MessageID>({
2928
name: RULE_NAME,
3029
create(context) {
3130
const settings = getSettingsFromContext(context);
31+
const polymorphicPropName = settings.polymorphicPropName;
3232
const additionalComponents = settings.additionalComponents.filter((c) => c.as === "button");
3333
return {
3434
JSXElement(node) {
35-
const [name, representName] = getElementNameAndRepresentName(
35+
const [elementNameOnJsx, elementNameOnHtml] = getElementNameOnJsxAndHtml(
3636
node.openingElement,
3737
context,
38-
settings.polymorphicPropName,
39-
settings.additionalComponents,
38+
polymorphicPropName,
39+
additionalComponents,
4040
);
41-
if (representName !== "button") return;
42-
const getPropValue = (propName: string) => {
43-
return getAttributeStringValue(
44-
propName,
45-
node,
46-
context,
47-
getAdditionalAttributes(name, additionalComponents),
48-
);
49-
};
50-
const prop = getPropValue("type");
51-
if (typeof prop !== "string") {
41+
if (elementNameOnHtml !== "button") return;
42+
43+
const { attributeNode, attributeValue } = getAttributeNodeAndStringValue(
44+
"type",
45+
node,
46+
context,
47+
getAdditionalAttributes(elementNameOnJsx, additionalComponents),
48+
);
49+
if (typeof attributeValue !== "string") {
5250
context.report({
5351
messageId: "noMissingButtonType",
54-
node,
52+
node: attributeNode ?? node,
5553
});
5654
}
5755
},

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

Lines changed: 22 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
import { getElementNameAndRepresentName } from "@eslint-react/core";
1+
import { getElementNameOnJsxAndHtml } from "@eslint-react/core";
22
import type { RuleFeature } from "@eslint-react/shared";
33
import { getSettingsFromContext } from "@eslint-react/shared";
44
import type { CamelCase } from "string-ts";
55

6-
import { createRule, getAdditionalAttributes, getAttributeStringValue } from "../utils";
6+
import { createRule, getAdditionalAttributes, getAttributeNodeAndStringValue } from "../utils";
77

88
export const RULE_NAME = "no-missing-iframe-sandbox";
99

@@ -32,7 +32,6 @@ const validTypes = [
3232
"allow-top-navigation-to-custom-protocols",
3333
] as const;
3434

35-
// TODO: Use the information in `settings["react-x"].additionalComponents` to add support for user-defined components that add the 'sandbox' attribute internally.
3635
export default createRule<[], MessageID>({
3736
meta: {
3837
type: "problem",
@@ -48,42 +47,33 @@ export default createRule<[], MessageID>({
4847
name: RULE_NAME,
4948
create(context) {
5049
const settings = getSettingsFromContext(context);
50+
const polymorphicPropName = settings.polymorphicPropName;
5151
const additionalComponents = settings.additionalComponents.filter((c) => c.as === "iframe");
5252
return {
5353
JSXElement(node) {
54-
const [name, representName] = getElementNameAndRepresentName(
54+
const [elementNameOnJsx, elementNameOnHtml] = getElementNameOnJsxAndHtml(
5555
node.openingElement,
5656
context,
57-
settings.polymorphicPropName,
58-
settings.additionalComponents,
57+
polymorphicPropName,
58+
additionalComponents,
5959
);
60-
if (representName !== "iframe") return;
60+
if (elementNameOnHtml !== "iframe") return;
6161

62-
const getPropValue = (propName: string) => {
63-
return getAttributeStringValue(
64-
propName,
65-
node,
66-
context,
67-
getAdditionalAttributes(name, additionalComponents),
68-
);
69-
};
70-
71-
const sandboxValue = getPropValue("sandbox");
72-
if (sandboxValue == null) {
73-
context.report({
74-
messageId: "noMissingIframeSandbox",
75-
node: node.openingElement,
76-
});
77-
return;
78-
}
79-
80-
const values = sandboxValue.split(" ");
81-
if (!values.every((value) => validTypes.some((validType) => validType === value))) {
82-
context.report({
83-
messageId: "noMissingIframeSandbox",
84-
node: node.openingElement,
85-
});
86-
}
62+
const { attributeNode, attributeValue } = getAttributeNodeAndStringValue(
63+
"sandbox",
64+
node,
65+
context,
66+
getAdditionalAttributes(elementNameOnJsx, additionalComponents),
67+
);
68+
const hasValidSandboxValue = typeof attributeValue === "string"
69+
&& attributeValue
70+
.split(" ")
71+
.every((value) => validTypes.some((valid) => valid === value));
72+
if (hasValidSandboxValue) return;
73+
context.report({
74+
messageId: "noMissingIframeSandbox",
75+
node: attributeNode ?? node.openingElement,
76+
});
8777
},
8878
};
8979
},

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

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,11 @@ export default createRule<[], MessageID>({
3838
if (node.name.type !== T.JSXIdentifier || node.value == null) {
3939
return;
4040
}
41-
const propScope = context.sourceCode.getScope(node);
42-
const propValue = JSX.getPropValue(node, propScope);
43-
const propValueResolved = VAR.toResolved(propValue).value;
44-
if (typeof propValueResolved !== "string") return;
45-
if (RE_JAVASCRIPT_PROTOCOL.test(propValueResolved)) {
41+
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)) {
4646
context.report({
4747
messageId: "noScriptUrl",
4848
node: node.value,

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

Lines changed: 20 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
import { getElementNameAndRepresentName } from "@eslint-react/core";
1+
import { getElementNameOnJsxAndHtml } from "@eslint-react/core";
22
import type { RuleFeature } from "@eslint-react/shared";
33
import { getSettingsFromContext } from "@eslint-react/shared";
44
import type { CamelCase } from "string-ts";
55

6-
import { createRule, getAdditionalAttributes, getAttributeStringValue } from "../utils";
6+
import { createRule, getAdditionalAttributes, getAttributeNodeAndStringValue } from "../utils";
7+
78
export const RULE_NAME = "no-unsafe-iframe-sandbox";
89

910
export const RULE_FEATURES = [
@@ -32,34 +33,30 @@ export default createRule<[], MessageID>({
3233
name: RULE_NAME,
3334
create(context) {
3435
const settings = getSettingsFromContext(context);
36+
const polymorphicPropName = settings.polymorphicPropName;
3537
const additionalComponents = settings.additionalComponents.filter((c) => c.as === "iframe");
3638
return {
3739
JSXElement(node) {
38-
const [name, representName] = getElementNameAndRepresentName(
40+
const [elementNameOnJsx, elementNameOnHtml] = getElementNameOnJsxAndHtml(
3941
node.openingElement,
4042
context,
41-
settings.polymorphicPropName,
42-
settings.additionalComponents,
43+
polymorphicPropName,
44+
additionalComponents,
4345
);
44-
if (representName !== "iframe") return;
46+
if (elementNameOnHtml !== "iframe") return;
4547

46-
const getPropValue = (propName: string) => {
47-
return getAttributeStringValue(
48-
propName,
49-
node,
50-
context,
51-
getAdditionalAttributes(name, additionalComponents),
52-
);
53-
};
54-
const sandboxValue = getPropValue("sandbox");
55-
if (sandboxValue == null) return;
56-
const values = sandboxValue.split(" ");
57-
if (unsafeCombinations.some((unsafes) => unsafes.every((x) => values.includes(x)))) {
58-
context.report({
59-
messageId: "noUnsafeIframeSandbox",
60-
node: node.openingElement,
61-
});
62-
}
48+
const { attributeNode, attributeValue } = getAttributeNodeAndStringValue(
49+
"sandbox",
50+
node,
51+
context,
52+
getAdditionalAttributes(elementNameOnJsx, additionalComponents),
53+
);
54+
if (attributeValue == null) return;
55+
if (!unsafeCombinations.some((c) => c.every((v) => attributeValue.includes(v)))) return;
56+
context.report({
57+
messageId: "noUnsafeIframeSandbox",
58+
node: attributeNode ?? node.openingElement,
59+
});
6360
},
6461
};
6562
},

0 commit comments

Comments
 (0)