eslint-plugin-react-dom 2.7.4-beta.4 → 2.7.4-beta.6
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/README.md +29 -0
- package/dist/index.js +33 -33
- package/package.json +7 -7
package/README.md
CHANGED
|
@@ -36,6 +36,35 @@ export default defineConfig(
|
|
|
36
36
|
);
|
|
37
37
|
```
|
|
38
38
|
|
|
39
|
+
> [!NOTE]
|
|
40
|
+
> DOM rules target `React DOM`-specific concerns including security vulnerabilities, deprecated APIs, and DOM property usage.
|
|
41
|
+
|
|
42
|
+
**Security Rules:**
|
|
43
|
+
|
|
44
|
+
- [`no-dangerously-set-innerhtml`](./dom-no-dangerously-set-innerhtml) - Disallows `dangerouslySetInnerHTML`
|
|
45
|
+
- [`no-dangerously-set-innerhtml-with-children`](./dom-no-dangerously-set-innerhtml-with-children) - Prevents using `dangerouslySetInnerHTML` with `children`
|
|
46
|
+
- [`no-script-url`](./dom-no-script-url) - Disallows `javascript:` URLs
|
|
47
|
+
- [`no-unsafe-target-blank`](./dom-no-unsafe-target-blank) - Requires `rel="noreferrer noopener"` with `target="_blank"` (🔧 Fixable)
|
|
48
|
+
- [`no-missing-iframe-sandbox`](./dom-no-missing-iframe-sandbox) - Enforces `sandbox` attribute on `iframes` (🔧 Fixable)
|
|
49
|
+
- [`no-unsafe-iframe-sandbox`](./dom-no-unsafe-iframe-sandbox) - Prevents unsafe `sandbox` combinations
|
|
50
|
+
|
|
51
|
+
**Deprecated API Migrations:**
|
|
52
|
+
|
|
53
|
+
- [`no-find-dom-node`](./dom-no-find-dom-node) - Disallows `findDOMNode`
|
|
54
|
+
- [`no-flush-sync`](./dom-no-flush-sync) - Disallows `flushSync`
|
|
55
|
+
- [`no-hydrate`](./dom-no-hydrate) - Replaces `ReactDOM.hydrate()` with `hydrateRoot()` (🔄 Codemod, `React DOM` >=18.0.0)
|
|
56
|
+
- [`no-render`](./dom-no-render) - Replaces `ReactDOM.render()` with `createRoot().render()` (🔄 Codemod, `React DOM` >=18.0.0)
|
|
57
|
+
- [`no-render-return-value`](./dom-no-render-return-value) - Disallows return value from `ReactDOM.render`
|
|
58
|
+
- [`no-use-form-state`](./dom-no-use-form-state) - Replaces `useFormState` with `useActionState` (🔄 Codemod, `React DOM` >=19.0.0)
|
|
59
|
+
|
|
60
|
+
**DOM Properties:**
|
|
61
|
+
|
|
62
|
+
- [`no-missing-button-type`](./dom-no-missing-button-type) - Enforces explicit `type` on `buttons` (🔧 Fixable)
|
|
63
|
+
- [`no-namespace`](./dom-no-namespace) - Prevents namespace usage in `React` elements
|
|
64
|
+
- [`no-string-style-prop`](./dom-no-string-style-prop) - Disallows string values for `style` prop
|
|
65
|
+
- [`no-unknown-property`](./dom-no-unknown-property) - Disallows unknown `DOM` properties (🔧 Fixable, ⚙️ Configurable)
|
|
66
|
+
- [`no-void-elements-with-children`](./dom-no-void-elements-with-children) - Prevents `children` in void elements
|
|
67
|
+
|
|
39
68
|
## Rules
|
|
40
69
|
|
|
41
70
|
<https://eslint-react.xyz/docs/rules/overview#dom-rules>
|
package/dist/index.js
CHANGED
|
@@ -8,9 +8,9 @@ import { compare } from "compare-versions";
|
|
|
8
8
|
var __defProp = Object.defineProperty;
|
|
9
9
|
var __exportAll = (all, symbols) => {
|
|
10
10
|
let target = {};
|
|
11
|
-
for (var name
|
|
12
|
-
__defProp(target, name
|
|
13
|
-
get: all[name
|
|
11
|
+
for (var name in all) {
|
|
12
|
+
__defProp(target, name, {
|
|
13
|
+
get: all[name],
|
|
14
14
|
enumerable: true
|
|
15
15
|
});
|
|
16
16
|
}
|
|
@@ -23,7 +23,7 @@ var __exportAll = (all, symbols) => {
|
|
|
23
23
|
//#endregion
|
|
24
24
|
//#region package.json
|
|
25
25
|
var name$2 = "eslint-plugin-react-dom";
|
|
26
|
-
var version = "2.7.4-beta.
|
|
26
|
+
var version = "2.7.4-beta.6";
|
|
27
27
|
|
|
28
28
|
//#endregion
|
|
29
29
|
//#region src/utils/create-jsx-element-resolver.ts
|
|
@@ -260,11 +260,11 @@ function create$13(context) {
|
|
|
260
260
|
};
|
|
261
261
|
}
|
|
262
262
|
function getFix$2(context, node) {
|
|
263
|
-
const getText
|
|
263
|
+
const getText = (n) => context.sourceCode.getText(n);
|
|
264
264
|
return (fixer) => {
|
|
265
265
|
const [arg0, arg1] = node.arguments;
|
|
266
266
|
if (arg0 == null || arg1 == null) return null;
|
|
267
|
-
return [fixer.insertTextBefore(context.sourceCode.ast, "import { hydrateRoot } from \"react-dom/client\";\n"), fixer.replaceText(node, `hydrateRoot(${getText
|
|
267
|
+
return [fixer.insertTextBefore(context.sourceCode.ast, "import { hydrateRoot } from \"react-dom/client\";\n"), fixer.replaceText(node, `hydrateRoot(${getText(arg1)}, ${getText(arg0)})`)];
|
|
268
268
|
};
|
|
269
269
|
}
|
|
270
270
|
|
|
@@ -380,12 +380,12 @@ var no_namespace_default = createRule({
|
|
|
380
380
|
});
|
|
381
381
|
function create$10(context) {
|
|
382
382
|
return { JSXElement(node) {
|
|
383
|
-
const name
|
|
384
|
-
if (typeof name
|
|
383
|
+
const name = getJsxElementType(context, node);
|
|
384
|
+
if (typeof name !== "string" || !name.includes(":")) return;
|
|
385
385
|
context.report({
|
|
386
386
|
messageId: "noNamespace",
|
|
387
387
|
node: node.openingElement.name,
|
|
388
|
-
data: { name
|
|
388
|
+
data: { name }
|
|
389
389
|
});
|
|
390
390
|
} };
|
|
391
391
|
}
|
|
@@ -452,11 +452,11 @@ function create$9(context) {
|
|
|
452
452
|
* @returns A fixer function or null if the fix cannot be applied
|
|
453
453
|
*/
|
|
454
454
|
function getFix$1(context, node) {
|
|
455
|
-
const getText
|
|
455
|
+
const getText = (n) => context.sourceCode.getText(n);
|
|
456
456
|
return (fixer) => {
|
|
457
457
|
const [arg0, arg1] = node.arguments;
|
|
458
458
|
if (arg0 == null || arg1 == null) return null;
|
|
459
|
-
return [fixer.insertTextBefore(context.sourceCode.ast, "import { createRoot } from \"react-dom/client\";\n"), fixer.replaceText(node, `createRoot(${getText
|
|
459
|
+
return [fixer.insertTextBefore(context.sourceCode.ast, "import { createRoot } from \"react-dom/client\";\n"), fixer.replaceText(node, `createRoot(${getText(arg1)}).render(${getText(arg0)})`)];
|
|
460
460
|
};
|
|
461
461
|
}
|
|
462
462
|
|
|
@@ -1510,32 +1510,32 @@ function isValidHTMLTagInJSX(childNode) {
|
|
|
1510
1510
|
* @param name Attribute name to normalize
|
|
1511
1511
|
* @returns Normalized attribute name
|
|
1512
1512
|
*/
|
|
1513
|
-
function normalizeAttributeCase(name
|
|
1514
|
-
return DOM_PROPERTIES_IGNORE_CASE.find((element) => element.toLowerCase() === name
|
|
1513
|
+
function normalizeAttributeCase(name) {
|
|
1514
|
+
return DOM_PROPERTIES_IGNORE_CASE.find((element) => element.toLowerCase() === name.toLowerCase()) || name;
|
|
1515
1515
|
}
|
|
1516
1516
|
/**
|
|
1517
1517
|
* Checks if an attribute name is a valid data-* attribute
|
|
1518
1518
|
* @param name Attribute name to test
|
|
1519
1519
|
* @returns Whether the attribute is a valid data attribute
|
|
1520
1520
|
*/
|
|
1521
|
-
function isValidDataAttribute(name
|
|
1522
|
-
return !/^data-xml/i.test(name
|
|
1521
|
+
function isValidDataAttribute(name) {
|
|
1522
|
+
return !/^data-xml/i.test(name) && /^data-[^:]*$/.test(name);
|
|
1523
1523
|
}
|
|
1524
1524
|
/**
|
|
1525
1525
|
* Checks if an attribute name has uppercase characters
|
|
1526
1526
|
* @param name Attribute name to test
|
|
1527
1527
|
* @returns Whether the name has uppercase characters
|
|
1528
1528
|
*/
|
|
1529
|
-
function hasUpperCaseCharacter(name
|
|
1530
|
-
return name
|
|
1529
|
+
function hasUpperCaseCharacter(name) {
|
|
1530
|
+
return name.toLowerCase() !== name;
|
|
1531
1531
|
}
|
|
1532
1532
|
/**
|
|
1533
1533
|
* Checks if an attribute is a valid ARIA attribute
|
|
1534
1534
|
* @param name Attribute name to test
|
|
1535
1535
|
* @returns Whether the attribute is a valid ARIA attribute
|
|
1536
1536
|
*/
|
|
1537
|
-
function isValidAriaAttribute(name
|
|
1538
|
-
return ARIA_PROPERTIES.some((element) => element === name
|
|
1537
|
+
function isValidAriaAttribute(name) {
|
|
1538
|
+
return ARIA_PROPERTIES.some((element) => element === name);
|
|
1539
1539
|
}
|
|
1540
1540
|
/**
|
|
1541
1541
|
* Gets the tag name for a JSXAttribute
|
|
@@ -1560,10 +1560,10 @@ function tagNameHasDot(node) {
|
|
|
1560
1560
|
* @param context ESLint context
|
|
1561
1561
|
* @returns Standard name or undefined
|
|
1562
1562
|
*/
|
|
1563
|
-
function getStandardName(name
|
|
1564
|
-
if (has(DOM_ATTRIBUTE_NAMES, name
|
|
1565
|
-
if (has(SVGDOM_ATTRIBUTE_NAMES, name
|
|
1566
|
-
return getDOMPropertyNames(context).find((element) => element.toLowerCase() === name
|
|
1563
|
+
function getStandardName(name, context) {
|
|
1564
|
+
if (has(DOM_ATTRIBUTE_NAMES, name)) return DOM_ATTRIBUTE_NAMES[name];
|
|
1565
|
+
if (has(SVGDOM_ATTRIBUTE_NAMES, name)) return SVGDOM_ATTRIBUTE_NAMES[name];
|
|
1566
|
+
return getDOMPropertyNames(context).find((element) => element.toLowerCase() === name.toLowerCase());
|
|
1567
1567
|
}
|
|
1568
1568
|
/**
|
|
1569
1569
|
* Checks if an object has a property
|
|
@@ -1590,9 +1590,9 @@ function getText(context, node) {
|
|
|
1590
1590
|
* @param version Version to compare against
|
|
1591
1591
|
* @returns Comparison result
|
|
1592
1592
|
*/
|
|
1593
|
-
function testReactVersion(context, comparator, version
|
|
1593
|
+
function testReactVersion(context, comparator, version) {
|
|
1594
1594
|
const { version: localVersion } = getSettingsFromContext(context);
|
|
1595
|
-
return compare(localVersion, version
|
|
1595
|
+
return compare(localVersion, version, comparator);
|
|
1596
1596
|
}
|
|
1597
1597
|
const messages = {
|
|
1598
1598
|
dataLowercaseRequired: "React does not recognize data-* props with uppercase characters on a DOM element. Found '{{name}}', use '{{lowerCaseName}}' instead",
|
|
@@ -1649,10 +1649,10 @@ function create$5(context) {
|
|
|
1649
1649
|
const ignoreNames = getIgnoreConfig();
|
|
1650
1650
|
const actualName = getText(context, node.name);
|
|
1651
1651
|
if (ignoreNames.indexOf(actualName) >= 0) return;
|
|
1652
|
-
const name
|
|
1652
|
+
const name = normalizeAttributeCase(actualName);
|
|
1653
1653
|
if (tagNameHasDot(node)) return;
|
|
1654
|
-
if (isValidDataAttribute(name
|
|
1655
|
-
if (getRequireDataLowercase() && hasUpperCaseCharacter(name
|
|
1654
|
+
if (isValidDataAttribute(name)) {
|
|
1655
|
+
if (getRequireDataLowercase() && hasUpperCaseCharacter(name)) context.report({
|
|
1656
1656
|
node,
|
|
1657
1657
|
messageId: "dataLowercaseRequired",
|
|
1658
1658
|
data: {
|
|
@@ -1662,11 +1662,11 @@ function create$5(context) {
|
|
|
1662
1662
|
});
|
|
1663
1663
|
return;
|
|
1664
1664
|
}
|
|
1665
|
-
if (isValidAriaAttribute(name
|
|
1665
|
+
if (isValidAriaAttribute(name)) return;
|
|
1666
1666
|
const tagName = getTagName(node);
|
|
1667
1667
|
if (tagName === "fbt" || tagName === "fbs") return;
|
|
1668
1668
|
if (!isValidHTMLTagInJSX(node)) return;
|
|
1669
|
-
const allowedTags = has(ATTRIBUTE_TAGS_MAP, name
|
|
1669
|
+
const allowedTags = has(ATTRIBUTE_TAGS_MAP, name) ? ATTRIBUTE_TAGS_MAP[name] : null;
|
|
1670
1670
|
if (tagName && allowedTags) {
|
|
1671
1671
|
if (allowedTags.indexOf(tagName) === -1) context.report({
|
|
1672
1672
|
node,
|
|
@@ -1679,9 +1679,9 @@ function create$5(context) {
|
|
|
1679
1679
|
});
|
|
1680
1680
|
return;
|
|
1681
1681
|
}
|
|
1682
|
-
const standardName = getStandardName(name
|
|
1683
|
-
const hasStandardNameButIsNotUsed = !!standardName && standardName !== name
|
|
1684
|
-
if (!!standardName && standardName === name
|
|
1682
|
+
const standardName = getStandardName(name, context);
|
|
1683
|
+
const hasStandardNameButIsNotUsed = !!standardName && standardName !== name;
|
|
1684
|
+
if (!!standardName && standardName === name) return;
|
|
1685
1685
|
if (hasStandardNameButIsNotUsed) {
|
|
1686
1686
|
context.report({
|
|
1687
1687
|
node,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eslint-plugin-react-dom",
|
|
3
|
-
"version": "2.7.4-beta.
|
|
3
|
+
"version": "2.7.4-beta.6",
|
|
4
4
|
"description": "ESLint React's ESLint plugin for React DOM related rules.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react",
|
|
@@ -44,16 +44,16 @@
|
|
|
44
44
|
"compare-versions": "^6.1.1",
|
|
45
45
|
"string-ts": "^2.3.1",
|
|
46
46
|
"ts-pattern": "^5.9.0",
|
|
47
|
-
"@eslint-react/
|
|
48
|
-
"@eslint-react/
|
|
49
|
-
"@eslint-react/shared": "2.7.4-beta.
|
|
50
|
-
"@eslint-react/
|
|
51
|
-
"@eslint-react/
|
|
47
|
+
"@eslint-react/core": "2.7.4-beta.6",
|
|
48
|
+
"@eslint-react/eff": "2.7.4-beta.6",
|
|
49
|
+
"@eslint-react/shared": "2.7.4-beta.6",
|
|
50
|
+
"@eslint-react/var": "2.7.4-beta.6",
|
|
51
|
+
"@eslint-react/ast": "2.7.4-beta.6"
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|
|
54
54
|
"@types/react": "^19.2.9",
|
|
55
55
|
"@types/react-dom": "^19.2.3",
|
|
56
|
-
"tsdown": "^0.20.
|
|
56
|
+
"tsdown": "^0.20.1",
|
|
57
57
|
"@local/configs": "0.0.0"
|
|
58
58
|
},
|
|
59
59
|
"peerDependencies": {
|