eslint-plugin-react-dom 2.0.4 → 2.0.5-beta.0
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 +81 -89
- package/package.json +9 -9
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { DEFAULT_ESLINT_REACT_SETTINGS, getConfigAdapters, getDocsUrl, getSettingsFromContext } from "@eslint-react/shared";
|
|
2
|
-
import {
|
|
2
|
+
import { getJsxAttribute, getJsxElementType, hasJsxAttribute, isJsxHostElement, isJsxText, resolveJsxAttributeValue } from "@eslint-react/core";
|
|
3
3
|
import { ESLintUtils } from "@typescript-eslint/utils";
|
|
4
4
|
import { AST_NODE_TYPES } from "@typescript-eslint/types";
|
|
5
5
|
import { compare } from "compare-versions";
|
|
@@ -47,7 +47,7 @@ const settings = { "react-x": DEFAULT_ESLINT_REACT_SETTINGS };
|
|
|
47
47
|
//#endregion
|
|
48
48
|
//#region package.json
|
|
49
49
|
var name = "eslint-plugin-react-dom";
|
|
50
|
-
var version = "2.0.
|
|
50
|
+
var version = "2.0.5-beta.0";
|
|
51
51
|
|
|
52
52
|
//#endregion
|
|
53
53
|
//#region src/utils/create-jsx-element-resolver.ts
|
|
@@ -65,15 +65,15 @@ var version = "2.0.4";
|
|
|
65
65
|
function createJsxElementResolver(context) {
|
|
66
66
|
const { polymorphicPropName } = getSettingsFromContext(context);
|
|
67
67
|
return { resolve(node) {
|
|
68
|
-
const elementName =
|
|
68
|
+
const elementName = getJsxElementType(context, node);
|
|
69
69
|
const result = {
|
|
70
70
|
domElementType: elementName,
|
|
71
71
|
jsxElementType: elementName
|
|
72
72
|
};
|
|
73
73
|
if (elementName === elementName.toLowerCase() || polymorphicPropName == null) return result;
|
|
74
|
-
const polymorphicProp =
|
|
74
|
+
const polymorphicProp = getJsxAttribute(context, node.openingElement.attributes, context.sourceCode.getScope(node))(polymorphicPropName);
|
|
75
75
|
if (polymorphicProp != null) {
|
|
76
|
-
const staticValue =
|
|
76
|
+
const staticValue = resolveJsxAttributeValue(context, polymorphicProp).toStatic(polymorphicPropName);
|
|
77
77
|
if (typeof staticValue === "string") return {
|
|
78
78
|
...result,
|
|
79
79
|
domElementType: staticValue
|
|
@@ -91,6 +91,7 @@ const createRule = ESLintUtils.RuleCreator(getDocsUrl("dom"));
|
|
|
91
91
|
//#region src/rules/no-dangerously-set-innerhtml.ts
|
|
92
92
|
const RULE_NAME$17 = "no-dangerously-set-innerhtml";
|
|
93
93
|
const RULE_FEATURES$16 = [];
|
|
94
|
+
const DSIH$1 = "dangerouslySetInnerHTML";
|
|
94
95
|
var no_dangerously_set_innerhtml_default = createRule({
|
|
95
96
|
meta: {
|
|
96
97
|
type: "problem",
|
|
@@ -105,15 +106,14 @@ var no_dangerously_set_innerhtml_default = createRule({
|
|
|
105
106
|
create: create$17,
|
|
106
107
|
defaultOptions: []
|
|
107
108
|
});
|
|
108
|
-
const dangerouslySetInnerHTML$1 = "dangerouslySetInnerHTML";
|
|
109
109
|
function create$17(context) {
|
|
110
|
-
if (!context.sourceCode.text.includes(
|
|
110
|
+
if (!context.sourceCode.text.includes(DSIH$1)) return {};
|
|
111
111
|
return { JSXElement(node) {
|
|
112
|
-
const
|
|
113
|
-
if (
|
|
112
|
+
const attr = getJsxAttribute(context, node.openingElement.attributes, context.sourceCode.getScope(node))(DSIH$1);
|
|
113
|
+
if (attr == null) return;
|
|
114
114
|
context.report({
|
|
115
115
|
messageId: "noDangerouslySetInnerhtml",
|
|
116
|
-
node:
|
|
116
|
+
node: attr
|
|
117
117
|
});
|
|
118
118
|
} };
|
|
119
119
|
}
|
|
@@ -136,36 +136,36 @@ var no_dangerously_set_innerhtml_with_children_default = createRule({
|
|
|
136
136
|
create: create$16,
|
|
137
137
|
defaultOptions: []
|
|
138
138
|
});
|
|
139
|
-
const
|
|
140
|
-
function create$16(context) {
|
|
141
|
-
if (!context.sourceCode.text.includes(dangerouslySetInnerHTML)) return {};
|
|
142
|
-
return { JSXElement(node) {
|
|
143
|
-
const attributes = node.openingElement.attributes;
|
|
144
|
-
const initialScope = context.sourceCode.getScope(node);
|
|
145
|
-
if ((node.children.some(isSignificantChildren) || hasAttribute(context, "children", attributes, initialScope)) && hasAttribute(context, dangerouslySetInnerHTML, attributes, initialScope)) context.report({
|
|
146
|
-
messageId: "noDangerouslySetInnerhtmlWithChildren",
|
|
147
|
-
node
|
|
148
|
-
});
|
|
149
|
-
} };
|
|
150
|
-
}
|
|
139
|
+
const DSIH = "dangerouslySetInnerHTML";
|
|
151
140
|
/**
|
|
152
|
-
*
|
|
153
|
-
* @param node The
|
|
154
|
-
* @returns
|
|
141
|
+
* Checks if a JSX child node is considered significant (i.e., not just whitespace for formatting).
|
|
142
|
+
* @param node The JSX child node to check.
|
|
143
|
+
* @returns `true` if the node is significant, `false` otherwise.
|
|
155
144
|
*/
|
|
156
|
-
function
|
|
157
|
-
|
|
145
|
+
function isSignificantChildren(node) {
|
|
146
|
+
if (!isJsxText(node)) return true;
|
|
147
|
+
return !(node.raw.trim() === "" && node.raw.includes("\n"));
|
|
158
148
|
}
|
|
159
149
|
/**
|
|
160
|
-
*
|
|
161
|
-
* @param
|
|
162
|
-
* @
|
|
150
|
+
* Checks if a JSX element has children, either through the `children` prop or as JSX children.
|
|
151
|
+
* @param context The rule context.
|
|
152
|
+
* @param node The JSX element to check.
|
|
153
|
+
* @returns `true` if the element has children, `false` otherwise.
|
|
163
154
|
*/
|
|
164
|
-
function
|
|
165
|
-
|
|
155
|
+
function hasChildren(context, node) {
|
|
156
|
+
if (getJsxAttribute(context, node.openingElement.attributes, context.sourceCode.getScope(node))("children") != null) return true;
|
|
157
|
+
return node.children.some(isSignificantChildren);
|
|
166
158
|
}
|
|
167
|
-
function
|
|
168
|
-
|
|
159
|
+
function create$16(context) {
|
|
160
|
+
if (!context.sourceCode.text.includes(DSIH)) return {};
|
|
161
|
+
return { JSXElement(node) {
|
|
162
|
+
const DSIHAttr = getJsxAttribute(context, node.openingElement.attributes, context.sourceCode.getScope(node))(DSIH);
|
|
163
|
+
if (DSIHAttr == null) return;
|
|
164
|
+
if (hasChildren(context, node)) context.report({
|
|
165
|
+
messageId: "noDangerouslySetInnerhtmlWithChildren",
|
|
166
|
+
node: DSIHAttr
|
|
167
|
+
});
|
|
168
|
+
} };
|
|
169
169
|
}
|
|
170
170
|
|
|
171
171
|
//#endregion
|
|
@@ -348,25 +348,19 @@ var no_missing_button_type_default = createRule({
|
|
|
348
348
|
function create$12(context) {
|
|
349
349
|
const resolver = createJsxElementResolver(context);
|
|
350
350
|
return { JSXElement(node) {
|
|
351
|
-
|
|
352
|
-
if (
|
|
353
|
-
if (getAttribute(context, node.openingElement.attributes, context.sourceCode.getScope(node))("type") != null) return;
|
|
351
|
+
if (resolver.resolve(node).domElementType !== "button") return;
|
|
352
|
+
if (getJsxAttribute(context, node.openingElement.attributes, context.sourceCode.getScope(node))("type") != null) return;
|
|
354
353
|
context.report({
|
|
355
354
|
messageId: "noMissingButtonType",
|
|
356
355
|
node: node.openingElement,
|
|
357
|
-
suggest:
|
|
358
|
-
|
|
359
|
-
|
|
356
|
+
suggest: BUTTON_TYPES.map((type) => ({
|
|
357
|
+
messageId: "addButtonType",
|
|
358
|
+
data: { type },
|
|
359
|
+
fix: (fixer) => fixer.insertTextAfter(node.openingElement.name, ` type="${type}"`)
|
|
360
|
+
}))
|
|
360
361
|
});
|
|
361
362
|
} };
|
|
362
363
|
}
|
|
363
|
-
function getSuggest(getFix$3) {
|
|
364
|
-
return BUTTON_TYPES.map((type) => ({
|
|
365
|
-
messageId: "addButtonType",
|
|
366
|
-
data: { type },
|
|
367
|
-
fix: getFix$3(type)
|
|
368
|
-
}));
|
|
369
|
-
}
|
|
370
364
|
|
|
371
365
|
//#endregion
|
|
372
366
|
//#region src/rules/no-missing-iframe-sandbox.ts
|
|
@@ -396,8 +390,8 @@ function create$11(context) {
|
|
|
396
390
|
return { JSXElement(node) {
|
|
397
391
|
const { domElementType } = resolver.resolve(node);
|
|
398
392
|
if (domElementType !== "iframe") return;
|
|
399
|
-
const
|
|
400
|
-
if (
|
|
393
|
+
const sandboxAttr = getJsxAttribute(context, node.openingElement.attributes, context.sourceCode.getScope(node))("sandbox");
|
|
394
|
+
if (sandboxAttr == null) {
|
|
401
395
|
context.report({
|
|
402
396
|
messageId: "noMissingIframeSandbox",
|
|
403
397
|
node: node.openingElement,
|
|
@@ -411,17 +405,17 @@ function create$11(context) {
|
|
|
411
405
|
});
|
|
412
406
|
return;
|
|
413
407
|
}
|
|
414
|
-
const
|
|
415
|
-
if (typeof
|
|
408
|
+
const sandboxValue = resolveJsxAttributeValue(context, sandboxAttr);
|
|
409
|
+
if (typeof sandboxValue.toStatic("sandbox") === "string") return;
|
|
416
410
|
context.report({
|
|
417
411
|
messageId: "noMissingIframeSandbox",
|
|
418
|
-
node:
|
|
412
|
+
node: sandboxValue.node ?? sandboxAttr,
|
|
419
413
|
suggest: [{
|
|
420
414
|
messageId: "addIframeSandbox",
|
|
421
415
|
data: { value: "" },
|
|
422
416
|
fix(fixer) {
|
|
423
|
-
if (
|
|
424
|
-
return fixer.replaceText(
|
|
417
|
+
if (sandboxValue.kind.startsWith("spread")) return null;
|
|
418
|
+
return fixer.replaceText(sandboxAttr, `sandbox=""`);
|
|
425
419
|
}
|
|
426
420
|
}]
|
|
427
421
|
});
|
|
@@ -448,7 +442,7 @@ var no_namespace_default = createRule({
|
|
|
448
442
|
});
|
|
449
443
|
function create$10(context) {
|
|
450
444
|
return { JSXElement(node) {
|
|
451
|
-
const name$2 =
|
|
445
|
+
const name$2 = getJsxElementType(context, node);
|
|
452
446
|
if (typeof name$2 !== "string" || !name$2.includes(":")) return;
|
|
453
447
|
context.report({
|
|
454
448
|
messageId: "noNamespace",
|
|
@@ -610,7 +604,7 @@ var no_script_url_default = createRule({
|
|
|
610
604
|
function create$7(context) {
|
|
611
605
|
return { JSXAttribute(node) {
|
|
612
606
|
if (node.name.type !== AST_NODE_TYPES.JSXIdentifier || node.value == null) return;
|
|
613
|
-
const value =
|
|
607
|
+
const value = resolveJsxAttributeValue(context, node).toStatic();
|
|
614
608
|
if (typeof value === "string" && RE_JAVASCRIPT_PROTOCOL.test(value)) context.report({
|
|
615
609
|
messageId: "noScriptUrl",
|
|
616
610
|
node: node.value
|
|
@@ -638,13 +632,13 @@ var no_string_style_prop_default = createRule({
|
|
|
638
632
|
});
|
|
639
633
|
function create$6(context) {
|
|
640
634
|
return { JSXElement(node) {
|
|
641
|
-
if (!
|
|
642
|
-
const
|
|
643
|
-
if (
|
|
644
|
-
const
|
|
645
|
-
if (typeof
|
|
635
|
+
if (!isJsxHostElement(context, node)) return;
|
|
636
|
+
const styleAttr = getJsxAttribute(context, node.openingElement.attributes, context.sourceCode.getScope(node))("style");
|
|
637
|
+
if (styleAttr == null) return;
|
|
638
|
+
const styleValue = resolveJsxAttributeValue(context, styleAttr);
|
|
639
|
+
if (typeof styleValue.toStatic() === "string") context.report({
|
|
646
640
|
messageId: "noStringStyleProp",
|
|
647
|
-
node:
|
|
641
|
+
node: styleValue.node ?? styleAttr
|
|
648
642
|
});
|
|
649
643
|
} };
|
|
650
644
|
}
|
|
@@ -1787,12 +1781,15 @@ function create$5(context) {
|
|
|
1787
1781
|
//#region src/rules/no-unsafe-iframe-sandbox.ts
|
|
1788
1782
|
const RULE_NAME$4 = "no-unsafe-iframe-sandbox";
|
|
1789
1783
|
const RULE_FEATURES$4 = [];
|
|
1790
|
-
const
|
|
1791
|
-
|
|
1784
|
+
const UNSAFE_SANDBOX_VALUES = ["allow-scripts", "allow-same-origin"];
|
|
1785
|
+
/**
|
|
1786
|
+
* Checks if the sandbox attribute value contains an unsafe combination.
|
|
1787
|
+
* @param value The value of the sandbox attribute.
|
|
1788
|
+
* @returns `true` if the value is a string and contains an unsafe combination, `false` otherwise.
|
|
1789
|
+
*/
|
|
1790
|
+
function isUnsafeSandboxCombination(value) {
|
|
1792
1791
|
if (typeof value !== "string") return false;
|
|
1793
|
-
return
|
|
1794
|
-
return values.every((v) => value.includes(v));
|
|
1795
|
-
});
|
|
1792
|
+
return UNSAFE_SANDBOX_VALUES.every((v) => value.includes(v));
|
|
1796
1793
|
}
|
|
1797
1794
|
var no_unsafe_iframe_sandbox_default = createRule({
|
|
1798
1795
|
meta: {
|
|
@@ -1811,15 +1808,14 @@ var no_unsafe_iframe_sandbox_default = createRule({
|
|
|
1811
1808
|
function create$4(context) {
|
|
1812
1809
|
const resolver = createJsxElementResolver(context);
|
|
1813
1810
|
return { JSXElement(node) {
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
const sandboxValue = resolveAttributeValue(context, sandboxAttribute);
|
|
1811
|
+
if (resolver.resolve(node).domElementType !== "iframe") return;
|
|
1812
|
+
const sandboxAttr = getJsxAttribute(context, node.openingElement.attributes, context.sourceCode.getScope(node))("sandbox");
|
|
1813
|
+
if (sandboxAttr == null) return;
|
|
1814
|
+
const sandboxValue = resolveJsxAttributeValue(context, sandboxAttr);
|
|
1819
1815
|
const sandboxValueStatic = sandboxValue.toStatic("sandbox");
|
|
1820
|
-
if (
|
|
1816
|
+
if (isUnsafeSandboxCombination(sandboxValueStatic)) context.report({
|
|
1821
1817
|
messageId: "noUnsafeIframeSandbox",
|
|
1822
|
-
node: sandboxValue.node ??
|
|
1818
|
+
node: sandboxValue.node ?? sandboxAttr
|
|
1823
1819
|
});
|
|
1824
1820
|
} };
|
|
1825
1821
|
}
|
|
@@ -1872,15 +1868,15 @@ function create$3(context) {
|
|
|
1872
1868
|
return { JSXElement(node) {
|
|
1873
1869
|
const { domElementType } = resolver.resolve(node);
|
|
1874
1870
|
if (domElementType !== "a") return;
|
|
1875
|
-
const
|
|
1876
|
-
const targetAttribute =
|
|
1871
|
+
const findAttribute = getJsxAttribute(context, node.openingElement.attributes, context.sourceCode.getScope(node));
|
|
1872
|
+
const targetAttribute = findAttribute("target");
|
|
1877
1873
|
if (targetAttribute == null) return;
|
|
1878
|
-
if (
|
|
1879
|
-
const hrefAttribute =
|
|
1874
|
+
if (resolveJsxAttributeValue(context, targetAttribute).toStatic("target") !== "_blank") return;
|
|
1875
|
+
const hrefAttribute = findAttribute("href");
|
|
1880
1876
|
if (hrefAttribute == null) return;
|
|
1881
|
-
const hrefAttributeValue =
|
|
1877
|
+
const hrefAttributeValue = resolveJsxAttributeValue(context, hrefAttribute).toStatic("href");
|
|
1882
1878
|
if (!isExternalLinkLike(hrefAttributeValue)) return;
|
|
1883
|
-
const relAttribute =
|
|
1879
|
+
const relAttribute = findAttribute("rel");
|
|
1884
1880
|
if (relAttribute == null) {
|
|
1885
1881
|
context.report({
|
|
1886
1882
|
messageId: "noUnsafeTargetBlank",
|
|
@@ -1894,7 +1890,7 @@ function create$3(context) {
|
|
|
1894
1890
|
});
|
|
1895
1891
|
return;
|
|
1896
1892
|
}
|
|
1897
|
-
const relAttributeValue =
|
|
1893
|
+
const relAttributeValue = resolveJsxAttributeValue(context, relAttribute).toStatic("rel");
|
|
1898
1894
|
if (isSafeRel(relAttributeValue)) return;
|
|
1899
1895
|
context.report({
|
|
1900
1896
|
messageId: "noUnsafeTargetBlank",
|
|
@@ -2016,16 +2012,12 @@ function create$1(context) {
|
|
|
2016
2012
|
const resolver = createJsxElementResolver(context);
|
|
2017
2013
|
return { JSXElement(node) {
|
|
2018
2014
|
const { domElementType: elementName } = resolver.resolve(node);
|
|
2019
|
-
if (
|
|
2020
|
-
if (node.children.length > 0) context.report({
|
|
2021
|
-
messageId: "noVoidElementsWithChildren",
|
|
2022
|
-
node,
|
|
2023
|
-
data: { element: elementName }
|
|
2024
|
-
});
|
|
2015
|
+
if (!voidElements.has(elementName)) return;
|
|
2025
2016
|
const { attributes } = node.openingElement;
|
|
2026
2017
|
const initialScope = context.sourceCode.getScope(node);
|
|
2027
|
-
const
|
|
2028
|
-
|
|
2018
|
+
const hasChildrenProp = hasJsxAttribute(context, "children", attributes, initialScope);
|
|
2019
|
+
const hasDangerouslySetInnerHTML = hasJsxAttribute(context, "dangerouslySetInnerHTML", attributes, initialScope);
|
|
2020
|
+
if (node.children.length > 0 || hasChildrenProp || hasDangerouslySetInnerHTML) context.report({
|
|
2029
2021
|
messageId: "noVoidElementsWithChildren",
|
|
2030
2022
|
node,
|
|
2031
2023
|
data: { element: elementName }
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eslint-plugin-react-dom",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.5-beta.0",
|
|
4
4
|
"description": "ESLint React's ESLint plugin for React DOM related rules.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react",
|
|
@@ -41,17 +41,17 @@
|
|
|
41
41
|
"compare-versions": "^6.1.1",
|
|
42
42
|
"string-ts": "^2.2.1",
|
|
43
43
|
"ts-pattern": "^5.8.0",
|
|
44
|
-
"@eslint-react/ast": "2.0.
|
|
45
|
-
"@eslint-react/
|
|
46
|
-
"@eslint-react/shared": "2.0.
|
|
47
|
-
"@eslint-react/
|
|
48
|
-
"@eslint-react/
|
|
49
|
-
"@eslint-react/var": "2.0.
|
|
44
|
+
"@eslint-react/ast": "2.0.5-beta.0",
|
|
45
|
+
"@eslint-react/core": "2.0.5-beta.0",
|
|
46
|
+
"@eslint-react/shared": "2.0.5-beta.0",
|
|
47
|
+
"@eslint-react/eff": "2.0.5-beta.0",
|
|
48
|
+
"@eslint-react/kit": "2.0.5-beta.0",
|
|
49
|
+
"@eslint-react/var": "2.0.5-beta.0"
|
|
50
50
|
},
|
|
51
51
|
"devDependencies": {
|
|
52
|
-
"@types/react": "^19.1.
|
|
52
|
+
"@types/react": "^19.1.16",
|
|
53
53
|
"@types/react-dom": "^19.1.9",
|
|
54
|
-
"tsdown": "^0.15.
|
|
54
|
+
"tsdown": "^0.15.6",
|
|
55
55
|
"@local/configs": "0.0.0"
|
|
56
56
|
},
|
|
57
57
|
"peerDependencies": {
|