Skip to content

Commit 736d4d6

Browse files
authored
feat: add fix suggestions for 'no-missing-iframe-sandbox' rule (#1138)
1 parent e546828 commit 736d4d6

File tree

4 files changed

+67
-35
lines changed

4 files changed

+67
-35
lines changed

apps/website/content/docs/rules/overview.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ The `jsx-*` rules check for issues exclusive to JSX syntax, which are absent fro
102102
| [`no-flush-sync`](./dom-no-flush-sync) | 2️⃣ | | Disallow `flushSync` | |
103103
| [`no-hydrate`](./dom-no-hydrate) | 2️⃣ | `🔄` | Replaces usages of `ReactDom.hydrate()` with `hydrateRoot()` | >=18.0.0 |
104104
| [`no-missing-button-type`](./dom-no-missing-button-type) | 1️⃣ | `🔧` | Enforces explicit `type` attribute for `button` elements | |
105-
| [`no-missing-iframe-sandbox`](./dom-no-missing-iframe-sandbox) | 1️⃣ | | Enforces explicit `sandbox` attribute for `iframe` elements | |
105+
| [`no-missing-iframe-sandbox`](./dom-no-missing-iframe-sandbox) | 1️⃣ | `🔧` | Enforces explicit `sandbox` attribute for `iframe` elements | |
106106
| [`no-namespace`](./dom-no-namespace) | 2️⃣ | | Enforces the absence of a `namespace` in React elements | |
107107
| [`no-render`](./dom-no-render) | 2️⃣ | `🔄` | Replaces usages of `ReactDom.render()` with `createRoot(node).render()` | >=18.0.0 |
108108
| [`no-render-return-value`](./dom-no-render-return-value) | 2️⃣ | | Disallow the return value of `ReactDOM.render` | |

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ react-dom/no-missing-iframe-sandbox
1414
@eslint-react/dom/no-missing-iframe-sandbox
1515
```
1616

17+
**Features**
18+
19+
`🔧`
20+
1721
**Presets**
1822

1923
- `dom`

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

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,13 @@ ruleTester.run(RULE_NAME, rule, {
1010
errors: [
1111
{
1212
messageId: "noMissingIframeSandbox",
13+
suggestions: [
14+
{
15+
messageId: "addIframeSandbox",
16+
data: { value: "" },
17+
output: tsx`<iframe sandbox="" />;`,
18+
},
19+
],
1320
},
1421
],
1522
},
@@ -18,14 +25,28 @@ ruleTester.run(RULE_NAME, rule, {
1825
errors: [
1926
{
2027
messageId: "noMissingIframeSandbox",
28+
suggestions: [
29+
{
30+
messageId: "addIframeSandbox",
31+
data: { value: "" },
32+
output: tsx`<iframe sandbox="" />;`,
33+
},
34+
],
2135
},
2236
],
2337
},
2438
{
25-
code: tsx`<PolyComponent as="iframe"/>;`,
39+
code: tsx`<PolyComponent as="iframe" />;`,
2640
errors: [
2741
{
2842
messageId: "noMissingIframeSandbox",
43+
suggestions: [
44+
{
45+
messageId: "addIframeSandbox",
46+
data: { value: "" },
47+
output: tsx`<PolyComponent sandbox="" as="iframe" />;`,
48+
},
49+
],
2950
},
3051
],
3152
settings: {
@@ -39,6 +60,13 @@ ruleTester.run(RULE_NAME, rule, {
3960
errors: [
4061
{
4162
messageId: "noMissingIframeSandbox",
63+
suggestions: [
64+
{
65+
messageId: "addIframeSandbox",
66+
data: { value: "" },
67+
output: tsx`<PolyComponent as="iframe" sandbox="" />;`,
68+
},
69+
],
4270
},
4371
],
4472
settings: {

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

Lines changed: 33 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -7,35 +7,13 @@ import { createJsxElementResolver, createRule, findCustomComponentProp } from ".
77

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

10-
export const RULE_FEATURES = [] as const satisfies RuleFeature[];
10+
export const RULE_FEATURES = [
11+
"FIX",
12+
] as const satisfies RuleFeature[];
1113

12-
export type MessageID = CamelCase<typeof RULE_NAME>;
14+
export type MessageID = CamelCase<typeof RULE_NAME> | RuleSuggestMessageID;
1315

14-
const validTypes = [
15-
"",
16-
"allow-downloads",
17-
"allow-downloads-without-user-activation",
18-
"allow-forms",
19-
"allow-modals",
20-
"allow-orientation-lock",
21-
"allow-pointer-lock",
22-
"allow-popups",
23-
"allow-popups-to-escape-sandbox",
24-
"allow-presentation",
25-
"allow-same-origin",
26-
"allow-scripts",
27-
"allow-storage-access-by-user-activation",
28-
"allow-top-navigation",
29-
"allow-top-navigation-by-user-activation",
30-
"allow-top-navigation-to-custom-protocols",
31-
] as const;
32-
33-
function hasValidSandBox(value: unknown) {
34-
return typeof value === "string"
35-
&& value
36-
.split(" ")
37-
.every((value) => validTypes.some((valid) => valid === value));
38-
}
16+
export type RuleSuggestMessageID = "addIframeSandbox";
3917

4018
export default createRule<[], MessageID>({
4119
meta: {
@@ -44,7 +22,10 @@ export default createRule<[], MessageID>({
4422
description: "Enforces explicit `sandbox` attribute for `iframe` elements.",
4523
[Symbol.for("rule_features")]: RULE_FEATURES,
4624
},
25+
fixable: "code",
26+
hasSuggestions: true,
4727
messages: {
28+
addIframeSandbox: "Add 'sandbox' attribute with value '{{value}}'.",
4829
noMissingIframeSandbox: "Add missing 'sandbox' attribute on 'iframe' component.",
4930
},
5031
schema: [],
@@ -74,17 +55,36 @@ export function create(context: RuleContext<MessageID, []>): RuleListener {
7455
attributeNode,
7556
propNameOnJsx,
7657
);
77-
if (attributeValue.kind === "some" && hasValidSandBox(attributeValue.value)) return;
78-
context.report({
79-
messageId: "noMissingIframeSandbox",
80-
node: attributeNode,
81-
});
58+
if (attributeValue.kind !== "some" || typeof attributeValue.value !== "string") {
59+
context.report({
60+
messageId: "noMissingIframeSandbox",
61+
node: attributeNode,
62+
suggest: [
63+
{
64+
messageId: "addIframeSandbox",
65+
data: { value: "" },
66+
fix(fixer) {
67+
return fixer.replaceText(attributeNode, `${propNameOnJsx}=""`);
68+
},
69+
},
70+
],
71+
});
72+
}
8273
return;
8374
}
84-
if (!hasValidSandBox(customComponentProp?.defaultValue)) {
75+
if (typeof customComponentProp?.defaultValue !== "string") {
8576
context.report({
8677
messageId: "noMissingIframeSandbox",
8778
node,
79+
suggest: [
80+
{
81+
messageId: "addIframeSandbox",
82+
data: { value: "" },
83+
fix(fixer) {
84+
return fixer.insertTextAfter(node.openingElement.name, ` ${propNameOnJsx}=""`);
85+
},
86+
},
87+
],
8888
});
8989
}
9090
},

0 commit comments

Comments
 (0)