eslint-plugin-react-dom 1.23.3-next.8 → 1.24.0-beta.1

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.
Files changed (3) hide show
  1. package/dist/index.js +153 -166
  2. package/dist/index.mjs +154 -168
  3. package/package.json +13 -13
package/dist/index.js CHANGED
@@ -1,13 +1,14 @@
1
1
  'use strict';
2
2
 
3
+ var JSX3 = require('@eslint-react/jsx');
3
4
  var eff = require('@eslint-react/eff');
4
- var JSX8 = require('@eslint-react/jsx');
5
+ var VAR = require('@eslint-react/var');
6
+ var tsPattern = require('ts-pattern');
5
7
  var shared = require('@eslint-react/shared');
6
8
  var types = require('@typescript-eslint/types');
7
9
  var core = require('@eslint-react/core');
8
10
  var AST = require('@eslint-react/ast');
9
11
  var compareVersions = require('compare-versions');
10
- var tsPattern = require('ts-pattern');
11
12
 
12
13
  function _interopNamespace(e) {
13
14
  if (e && e.__esModule) return e;
@@ -27,20 +28,31 @@ function _interopNamespace(e) {
27
28
  return Object.freeze(n);
28
29
  }
29
30
 
30
- var JSX8__namespace = /*#__PURE__*/_interopNamespace(JSX8);
31
+ var JSX3__namespace = /*#__PURE__*/_interopNamespace(JSX3);
32
+ var VAR__namespace = /*#__PURE__*/_interopNamespace(VAR);
31
33
  var AST__namespace = /*#__PURE__*/_interopNamespace(AST);
32
34
 
33
35
  // package.json
34
36
  var name = "eslint-plugin-react-dom";
35
- var version = "1.23.3-next.8";
36
- var createRule = shared.createRuleForPlugin("dom");
37
- function findAttrInCustomAttributes(name2, attributes) {
38
- return eff.F.pipe(
39
- eff.O.fromNullable(attributes.findLast((a) => a.as === name2)),
40
- eff.O.map((a) => [a.name, a.defaultValue]),
41
- eff.O.getOrElse(() => [name2])
37
+ var version = "1.24.0-beta.1";
38
+ function getAdditionalAttributes(name2, additionalComponents) {
39
+ return additionalComponents.findLast((c) => c.name === name2 || c.re.test(name2))?.attributes ?? [];
40
+ }
41
+ function getAttributeStringValue(attributeName, element, context, additionalAttributes) {
42
+ const attributeConfig = additionalAttributes.findLast((attr) => attr.as === attributeName);
43
+ const actualAttributeName = attributeConfig?.name ?? attributeName;
44
+ const actualAttributeNode = JSX3__namespace.findPropInAttributes(
45
+ actualAttributeName,
46
+ context.sourceCode.getScope(element),
47
+ element.openingElement.attributes
42
48
  );
49
+ if (actualAttributeNode == null) return attributeConfig?.defaultValue;
50
+ const actualAttributeScope = context.sourceCode.getScope(actualAttributeNode);
51
+ const actualAttributeValue = JSX3__namespace.getPropValue(actualAttributeNode, actualAttributeScope);
52
+ const actualAttributeValueResolved = VAR__namespace.toResolved(actualAttributeValue).value;
53
+ return tsPattern.match(actualAttributeValueResolved).with(tsPattern.P.string, eff.identity).with({ [actualAttributeName]: tsPattern.P.select(tsPattern.P.string) }, eff.identity).otherwise(() => eff._);
43
54
  }
55
+ var createRule = shared.createRuleForPlugin("dom");
44
56
 
45
57
  // src/rules/no-dangerously-set-innerhtml.ts
46
58
  var RULE_NAME = "no-dangerously-set-innerhtml";
@@ -63,12 +75,13 @@ var no_dangerously_set_innerhtml_default = createRule({
63
75
  create(context) {
64
76
  return {
65
77
  JSXElement(node) {
66
- eff.F.pipe(
67
- eff.O.some("dangerouslySetInnerHTML"),
68
- eff.O.flatMap(JSX8__namespace.findPropInAttributes(node.openingElement.attributes, context.sourceCode.getScope(node))),
69
- eff.O.map((prop) => ({ messageId: "noDangerouslySetInnerhtml", node: prop })),
70
- eff.O.map(context.report)
71
- );
78
+ const attributes = node.openingElement.attributes;
79
+ const prop = JSX3__namespace.getProp("dangerouslySetInnerHTML", context.sourceCode.getScope(node), attributes);
80
+ if (prop == null) return;
81
+ context.report({
82
+ messageId: "noDangerouslySetInnerhtml",
83
+ node: prop
84
+ });
72
85
  }
73
86
  };
74
87
  },
@@ -78,10 +91,6 @@ var RULE_NAME2 = "no-dangerously-set-innerhtml-with-children";
78
91
  var RULE_FEATURES2 = [
79
92
  "CHK"
80
93
  ];
81
- function firstChildIsText(node) {
82
- const [firstChild] = node.children;
83
- return node.children.length > 0 && !eff.isNullable(firstChild) && !JSX8__namespace.isLineBreak(firstChild);
84
- }
85
94
  var no_dangerously_set_innerhtml_with_children_default = createRule({
86
95
  meta: {
87
96
  type: "problem",
@@ -98,22 +107,24 @@ var no_dangerously_set_innerhtml_with_children_default = createRule({
98
107
  create(context) {
99
108
  return {
100
109
  JSXElement(node) {
110
+ const attributes = node.openingElement.attributes;
101
111
  const initialScope = context.sourceCode.getScope(node);
102
- const hasChildrenWithIn = () => node.children.length > 0 && firstChildIsText(node);
103
- const hasChildrenProp = () => JSX8__namespace.hasProp(node.openingElement.attributes, "children", initialScope);
104
- const hasDanger = () => JSX8__namespace.hasProp(node.openingElement.attributes, "dangerouslySetInnerHTML", initialScope);
105
- if (!(hasChildrenWithIn() || hasChildrenProp()) || !hasDanger()) {
106
- return;
112
+ const hasChildren = hasChildrenWithin(node) || JSX3__namespace.hasProp("children", initialScope, attributes);
113
+ const hasDangerouslySetInnerHTML = JSX3__namespace.hasProp("dangerouslySetInnerHTML", initialScope, attributes);
114
+ if (hasChildren && hasDangerouslySetInnerHTML) {
115
+ context.report({
116
+ messageId: "noDangerouslySetInnerhtmlWithChildren",
117
+ node
118
+ });
107
119
  }
108
- context.report({
109
- messageId: "noDangerouslySetInnerhtmlWithChildren",
110
- node
111
- });
112
120
  }
113
121
  };
114
122
  },
115
123
  defaultOptions: []
116
124
  });
125
+ function hasChildrenWithin(node) {
126
+ return node.children.length > 0 && node.children[0] != null && !JSX3__namespace.isLineBreak(node.children[0]);
127
+ }
117
128
  var RULE_NAME3 = "no-find-dom-node";
118
129
  var RULE_FEATURES3 = [
119
130
  "CHK"
@@ -170,22 +181,32 @@ var no_missing_button_type_default = createRule({
170
181
  },
171
182
  name: RULE_NAME4,
172
183
  create(context) {
184
+ const settings = shared.getSettingsFromContext(context);
185
+ const additionalComponents = settings.additionalComponents.filter((c) => c.as === "button");
173
186
  return {
174
187
  JSXElement(node) {
175
- const elementName = core.getElementRepresentName(node.openingElement, context);
176
- if (elementName !== "button") {
177
- return;
178
- }
179
- const { attributes } = node.openingElement;
180
- const initialScope = context.sourceCode.getScope(node);
181
- const mbProp = JSX8__namespace.findPropInAttributes(attributes, initialScope)("type");
182
- if (eff.O.isSome(mbProp)) {
183
- return;
188
+ const [name2, representName] = core.getElementNameAndRepresentName(
189
+ node.openingElement,
190
+ context,
191
+ settings.polymorphicPropName,
192
+ settings.additionalComponents
193
+ );
194
+ if (representName !== "button") return;
195
+ const getPropValue3 = (propName) => {
196
+ return getAttributeStringValue(
197
+ propName,
198
+ node,
199
+ context,
200
+ getAdditionalAttributes(name2, additionalComponents)
201
+ );
202
+ };
203
+ const prop = getPropValue3("type");
204
+ if (typeof prop !== "string") {
205
+ context.report({
206
+ messageId: "noMissingButtonType",
207
+ node
208
+ });
184
209
  }
185
- context.report({
186
- messageId: "noMissingButtonType",
187
- node
188
- });
189
210
  }
190
211
  };
191
212
  },
@@ -227,36 +248,40 @@ var no_missing_iframe_sandbox_default = createRule({
227
248
  },
228
249
  name: RULE_NAME5,
229
250
  create(context) {
251
+ const settings = shared.getSettingsFromContext(context);
252
+ const additionalComponents = settings.additionalComponents.filter((c) => c.as === "iframe");
230
253
  return {
231
254
  JSXElement(node) {
232
- const elementName = core.getElementRepresentName(node.openingElement, context);
233
- if (elementName !== "iframe") {
234
- return;
235
- }
236
- const { attributes } = node.openingElement;
237
- const initialScope = context.sourceCode.getScope(node);
238
- const mbProp = JSX8__namespace.findPropInAttributes(attributes, initialScope)("sandbox");
239
- if (eff.O.isNone(mbProp)) {
255
+ const [name2, representName] = core.getElementNameAndRepresentName(
256
+ node.openingElement,
257
+ context,
258
+ settings.polymorphicPropName,
259
+ settings.additionalComponents
260
+ );
261
+ if (representName !== "iframe") return;
262
+ const getPropValue3 = (propName) => {
263
+ return getAttributeStringValue(
264
+ propName,
265
+ node,
266
+ context,
267
+ getAdditionalAttributes(name2, additionalComponents)
268
+ );
269
+ };
270
+ const sandboxValue = getPropValue3("sandbox");
271
+ if (sandboxValue == null) {
240
272
  context.report({
241
273
  messageId: "noMissingIframeSandbox",
242
274
  node: node.openingElement
243
275
  });
244
276
  return;
245
277
  }
246
- const prop = mbProp.value;
247
- const hasValidSandbox = eff.F.pipe(
248
- JSX8__namespace.getPropValue(prop, context.sourceCode.getScope(prop)),
249
- eff.O.filter(eff.isString),
250
- eff.O.map((value) => value.split(" ")),
251
- eff.O.exists((values) => values.every((value) => validTypes.some((validType) => validType === value)))
252
- );
253
- if (hasValidSandbox) {
254
- return;
278
+ const values = sandboxValue.split(" ");
279
+ if (!values.every((value) => validTypes.some((validType) => validType === value))) {
280
+ context.report({
281
+ messageId: "noMissingIframeSandbox",
282
+ node: node.openingElement
283
+ });
255
284
  }
256
- context.report({
257
- messageId: "noMissingIframeSandbox",
258
- node: prop
259
- });
260
285
  }
261
286
  };
262
287
  },
@@ -282,8 +307,8 @@ var no_namespace_default = createRule({
282
307
  create(context) {
283
308
  return {
284
309
  JSXOpeningElement(node) {
285
- const name2 = JSX8__namespace.getElementName(node);
286
- if (!eff.isString(name2) || !name2.includes(":")) {
310
+ const name2 = JSX3__namespace.getElementName(node);
311
+ if (typeof name2 !== "string" || !name2.includes(":")) {
287
312
  return;
288
313
  }
289
314
  context.report({
@@ -374,12 +399,11 @@ var no_script_url_default = createRule({
374
399
  if (node.name.type !== types.AST_NODE_TYPES.JSXIdentifier || node.value == null) {
375
400
  return;
376
401
  }
377
- const isJavaScript = eff.F.pipe(
378
- JSX8__namespace.getPropValue(node, context.sourceCode.getScope(node)),
379
- eff.O.filter(eff.isString),
380
- eff.O.exists((v) => shared.RE_JAVASCRIPT_PROTOCOL.test(v))
381
- );
382
- if (isJavaScript) {
402
+ const propScope = context.sourceCode.getScope(node);
403
+ const propValue = JSX3__namespace.getPropValue(node, propScope);
404
+ const propValueResolved = VAR__namespace.toResolved(propValue).value;
405
+ if (typeof propValueResolved !== "string") return;
406
+ if (shared.RE_JAVASCRIPT_PROTOCOL.test(propValueResolved)) {
383
407
  context.report({
384
408
  messageId: "noScriptUrl",
385
409
  node: node.value
@@ -1453,7 +1477,6 @@ var RULE_FEATURES9 = [
1453
1477
  ];
1454
1478
  var unsafeCombinations = [
1455
1479
  ["allow-scripts", "allow-same-origin"]
1456
- // ...
1457
1480
  ];
1458
1481
  var no_unsafe_iframe_sandbox_default = createRule({
1459
1482
  meta: {
@@ -1469,37 +1492,34 @@ var no_unsafe_iframe_sandbox_default = createRule({
1469
1492
  },
1470
1493
  name: RULE_NAME10,
1471
1494
  create(context) {
1495
+ const settings = shared.getSettingsFromContext(context);
1496
+ const additionalComponents = settings.additionalComponents.filter((c) => c.as === "iframe");
1472
1497
  return {
1473
1498
  JSXElement(node) {
1474
- const elementName = core.getElementRepresentName(node.openingElement, context);
1475
- if (elementName !== "iframe") {
1476
- return;
1477
- }
1478
- const { attributes } = node.openingElement;
1479
- const initialScope = context.sourceCode.getScope(node);
1480
- const mbProp = JSX8__namespace.findPropInAttributes(attributes, initialScope)("sandbox");
1481
- if (eff.O.isNone(mbProp)) {
1482
- return;
1483
- }
1484
- const prop = mbProp.value;
1485
- const isSafeSandboxValue = !eff.F.pipe(
1486
- JSX8__namespace.getPropValue(prop, context.sourceCode.getScope(prop)),
1487
- eff.O.flatMapNullable(
1488
- (v) => tsPattern.match(v).with(tsPattern.P.string, eff.F.identity).with({ sandbox: tsPattern.P.string }, ({ sandbox }) => sandbox).otherwise(eff.F.constNull)
1489
- ),
1490
- eff.O.filter(eff.isString),
1491
- eff.O.map((value) => value.split(" ")),
1492
- eff.O.exists(
1493
- (values) => unsafeCombinations.some((combinations) => combinations.every((unsafeValue) => values.includes(unsafeValue)))
1494
- )
1499
+ const [name2, representName] = core.getElementNameAndRepresentName(
1500
+ node.openingElement,
1501
+ context,
1502
+ settings.polymorphicPropName,
1503
+ settings.additionalComponents
1495
1504
  );
1496
- if (isSafeSandboxValue) {
1497
- return;
1505
+ if (representName !== "iframe") return;
1506
+ const getPropValue3 = (propName) => {
1507
+ return getAttributeStringValue(
1508
+ propName,
1509
+ node,
1510
+ context,
1511
+ getAdditionalAttributes(name2, additionalComponents)
1512
+ );
1513
+ };
1514
+ const sandboxValue = getPropValue3("sandbox");
1515
+ if (sandboxValue == null) return;
1516
+ const values = sandboxValue.split(" ");
1517
+ if (unsafeCombinations.some((unsafes) => unsafes.every((x) => values.includes(x)))) {
1518
+ context.report({
1519
+ messageId: "noUnsafeIframeSandbox",
1520
+ node: node.openingElement
1521
+ });
1498
1522
  }
1499
- context.report({
1500
- messageId: "noUnsafeIframeSandbox",
1501
- node: prop
1502
- });
1503
1523
  }
1504
1524
  };
1505
1525
  },
@@ -1510,9 +1530,11 @@ var RULE_FEATURES10 = [
1510
1530
  "CHK"
1511
1531
  ];
1512
1532
  function isExternalLinkLike(value) {
1533
+ if (value == null) return false;
1513
1534
  return value.startsWith("https://") || /^(?:\w+:|\/\/)/u.test(value);
1514
1535
  }
1515
1536
  function isSafeRel(value) {
1537
+ if (value == null) return false;
1516
1538
  return value === "noreferrer" || /\bnoreferrer\b/u.test(value);
1517
1539
  }
1518
1540
  var no_unsafe_target_blank_default = createRule({
@@ -1531,70 +1553,36 @@ var no_unsafe_target_blank_default = createRule({
1531
1553
  create(context) {
1532
1554
  const settings = shared.getSettingsFromContext(context);
1533
1555
  const additionalComponents = settings.additionalComponents.filter((c) => c.as === "a");
1534
- function getReportDescriptor(node) {
1535
- const name2 = JSX8__namespace.getElementName(node.openingElement);
1536
- const elementName = core.getElementRepresentName(node.openingElement, context);
1537
- if (elementName !== "a" && !additionalComponents.some((c) => c.re.test(name2))) {
1538
- return eff.O.none();
1539
- }
1540
- const { attributes } = node.openingElement;
1541
- const initialScope = context.sourceCode.getScope(node);
1542
- const additionalAttributes = additionalComponents.findLast((c) => c.re.test(name2))?.attributes ?? [];
1543
- const [
1544
- targetPropName,
1545
- targetPropDefaultValue
1546
- ] = findAttrInCustomAttributes("target", additionalAttributes);
1547
- const targetProp = JSX8__namespace.findPropInAttributes(attributes, initialScope)(targetPropName);
1548
- const targetPropValue = eff.O.isNone(targetProp) ? eff.O.fromNullable(targetPropDefaultValue) : eff.F.pipe(
1549
- targetProp,
1550
- eff.O.flatMap((attr) => JSX8__namespace.getPropValue(attr, context.sourceCode.getScope(attr))),
1551
- eff.O.flatMapNullable(
1552
- (v) => tsPattern.match(v).with(tsPattern.P.string, eff.F.identity).with({ [targetPropName]: tsPattern.P.string }, (v2) => v2[targetPropName]).otherwise(eff.F.constNull)
1553
- ),
1554
- eff.O.filter(eff.isString)
1555
- );
1556
- if (!eff.O.exists(targetPropValue, (t) => t === "_blank")) {
1557
- return eff.O.none();
1558
- }
1559
- const [
1560
- hrefPropName,
1561
- hrefPropDefaultValue
1562
- ] = findAttrInCustomAttributes("href", additionalAttributes);
1563
- const hrefProp = JSX8__namespace.findPropInAttributes(attributes, initialScope)(hrefPropName);
1564
- const hrefPropValue = eff.O.isNone(hrefProp) ? eff.O.fromNullable(hrefPropDefaultValue) : eff.F.pipe(
1565
- hrefProp,
1566
- eff.O.flatMap((attr) => JSX8__namespace.getPropValue(attr, context.sourceCode.getScope(attr))),
1567
- eff.O.flatMapNullable(
1568
- (v) => tsPattern.match(v).with(tsPattern.P.string, eff.F.identity).with({ [hrefPropName]: tsPattern.P.string }, (v2) => v2[hrefPropName]).otherwise(eff.F.constNull)
1569
- ),
1570
- eff.O.filter(eff.isString)
1571
- );
1572
- if (!eff.O.exists(hrefPropValue, isExternalLinkLike)) {
1573
- return eff.O.none();
1574
- }
1575
- const [
1576
- relPropName,
1577
- relPropDefaultValue
1578
- ] = findAttrInCustomAttributes("rel", additionalAttributes);
1579
- const relProp = JSX8__namespace.findPropInAttributes(attributes, initialScope)(relPropName);
1580
- const relPropValue = eff.O.isNone(relProp) ? eff.O.fromNullable(relPropDefaultValue) : eff.F.pipe(
1581
- relProp,
1582
- eff.O.flatMap((attr) => JSX8__namespace.getPropValue(attr, context.sourceCode.getScope(attr))),
1583
- eff.O.flatMapNullable(
1584
- (v) => tsPattern.match(v).with(tsPattern.P.string, eff.F.identity).with({ [relPropName]: tsPattern.P.string }, (v2) => v2[relPropName]).otherwise(eff.F.constNull)
1585
- ),
1586
- eff.O.filter(eff.isString)
1587
- );
1588
- if (eff.O.exists(relPropValue, isSafeRel)) {
1589
- return eff.O.none();
1590
- }
1591
- return eff.O.some({
1592
- messageId: "noUnsafeTargetBlank",
1593
- node
1594
- });
1595
- }
1596
1556
  return {
1597
- JSXElement: eff.F.flow(getReportDescriptor, eff.O.map(context.report))
1557
+ JSXElement(node) {
1558
+ const [name2, representName] = core.getElementNameAndRepresentName(
1559
+ node.openingElement,
1560
+ context,
1561
+ settings.polymorphicPropName,
1562
+ settings.additionalComponents
1563
+ );
1564
+ if (representName !== "a") return;
1565
+ const getPropValue3 = (propName) => {
1566
+ return getAttributeStringValue(
1567
+ propName,
1568
+ node,
1569
+ context,
1570
+ getAdditionalAttributes(name2, additionalComponents)
1571
+ );
1572
+ };
1573
+ if (getPropValue3("target") !== "_blank") {
1574
+ return;
1575
+ }
1576
+ if (!isExternalLinkLike(getPropValue3("href"))) {
1577
+ return;
1578
+ }
1579
+ if (!isSafeRel(getPropValue3("rel"))) {
1580
+ context.report({
1581
+ messageId: "noUnsafeTargetBlank",
1582
+ node
1583
+ });
1584
+ }
1585
+ }
1598
1586
  };
1599
1587
  },
1600
1588
  defaultOptions: []
@@ -1637,7 +1625,7 @@ var no_void_elements_with_children_default = createRule({
1637
1625
  create(context) {
1638
1626
  return {
1639
1627
  JSXElement(node) {
1640
- const elementName = JSX8__namespace.getElementName(node.openingElement);
1628
+ const elementName = JSX3__namespace.getElementName(node.openingElement);
1641
1629
  if (elementName.length === 0 || !voidElements.has(elementName)) {
1642
1630
  return;
1643
1631
  }
@@ -1652,9 +1640,8 @@ var no_void_elements_with_children_default = createRule({
1652
1640
  }
1653
1641
  const { attributes } = node.openingElement;
1654
1642
  const initialScope = context.sourceCode.getScope(node);
1655
- const hasAttr = (name2) => eff.O.isSome(JSX8__namespace.findPropInAttributes(attributes, initialScope)(name2));
1656
- const hasChildrenOrDangerAttr = hasAttr("children") || hasAttr("dangerouslySetInnerHTML");
1657
- if (hasChildrenOrDangerAttr) {
1643
+ const hasAttribute = (name2) => JSX3__namespace.findPropInAttributes(name2, initialScope, attributes) != null;
1644
+ if (hasAttribute("children") || hasAttribute("dangerouslySetInnerHTML")) {
1658
1645
  context.report({
1659
1646
  messageId: "noVoidElementsWithChildren",
1660
1647
  node,
package/dist/index.mjs CHANGED
@@ -1,23 +1,34 @@
1
- import { F, O, isString, isNullable } from '@eslint-react/eff';
2
- import * as JSX8 from '@eslint-react/jsx';
3
- import { createRuleForPlugin, RE_JAVASCRIPT_PROTOCOL, getSettingsFromContext } from '@eslint-react/shared';
1
+ import * as JSX3 from '@eslint-react/jsx';
2
+ import { identity, _ } from '@eslint-react/eff';
3
+ import * as VAR from '@eslint-react/var';
4
+ import { match, P } from 'ts-pattern';
5
+ import { createRuleForPlugin, getSettingsFromContext, RE_JAVASCRIPT_PROTOCOL } from '@eslint-react/shared';
4
6
  import { AST_NODE_TYPES } from '@typescript-eslint/types';
5
- import { getElementRepresentName } from '@eslint-react/core';
7
+ import { getElementNameAndRepresentName } from '@eslint-react/core';
6
8
  import * as AST from '@eslint-react/ast';
7
9
  import { compare } from 'compare-versions';
8
- import { match, P } from 'ts-pattern';
9
10
 
10
11
  // package.json
11
12
  var name = "eslint-plugin-react-dom";
12
- var version = "1.23.3-next.8";
13
- var createRule = createRuleForPlugin("dom");
14
- function findAttrInCustomAttributes(name2, attributes) {
15
- return F.pipe(
16
- O.fromNullable(attributes.findLast((a) => a.as === name2)),
17
- O.map((a) => [a.name, a.defaultValue]),
18
- O.getOrElse(() => [name2])
13
+ var version = "1.24.0-beta.1";
14
+ function getAdditionalAttributes(name2, additionalComponents) {
15
+ return additionalComponents.findLast((c) => c.name === name2 || c.re.test(name2))?.attributes ?? [];
16
+ }
17
+ function getAttributeStringValue(attributeName, element, context, additionalAttributes) {
18
+ const attributeConfig = additionalAttributes.findLast((attr) => attr.as === attributeName);
19
+ const actualAttributeName = attributeConfig?.name ?? attributeName;
20
+ const actualAttributeNode = JSX3.findPropInAttributes(
21
+ actualAttributeName,
22
+ context.sourceCode.getScope(element),
23
+ element.openingElement.attributes
19
24
  );
25
+ if (actualAttributeNode == null) return attributeConfig?.defaultValue;
26
+ const actualAttributeScope = context.sourceCode.getScope(actualAttributeNode);
27
+ const actualAttributeValue = JSX3.getPropValue(actualAttributeNode, actualAttributeScope);
28
+ const actualAttributeValueResolved = VAR.toResolved(actualAttributeValue).value;
29
+ return match(actualAttributeValueResolved).with(P.string, identity).with({ [actualAttributeName]: P.select(P.string) }, identity).otherwise(() => _);
20
30
  }
31
+ var createRule = createRuleForPlugin("dom");
21
32
 
22
33
  // src/rules/no-dangerously-set-innerhtml.ts
23
34
  var RULE_NAME = "no-dangerously-set-innerhtml";
@@ -40,12 +51,13 @@ var no_dangerously_set_innerhtml_default = createRule({
40
51
  create(context) {
41
52
  return {
42
53
  JSXElement(node) {
43
- F.pipe(
44
- O.some("dangerouslySetInnerHTML"),
45
- O.flatMap(JSX8.findPropInAttributes(node.openingElement.attributes, context.sourceCode.getScope(node))),
46
- O.map((prop) => ({ messageId: "noDangerouslySetInnerhtml", node: prop })),
47
- O.map(context.report)
48
- );
54
+ const attributes = node.openingElement.attributes;
55
+ const prop = JSX3.getProp("dangerouslySetInnerHTML", context.sourceCode.getScope(node), attributes);
56
+ if (prop == null) return;
57
+ context.report({
58
+ messageId: "noDangerouslySetInnerhtml",
59
+ node: prop
60
+ });
49
61
  }
50
62
  };
51
63
  },
@@ -55,10 +67,6 @@ var RULE_NAME2 = "no-dangerously-set-innerhtml-with-children";
55
67
  var RULE_FEATURES2 = [
56
68
  "CHK"
57
69
  ];
58
- function firstChildIsText(node) {
59
- const [firstChild] = node.children;
60
- return node.children.length > 0 && !isNullable(firstChild) && !JSX8.isLineBreak(firstChild);
61
- }
62
70
  var no_dangerously_set_innerhtml_with_children_default = createRule({
63
71
  meta: {
64
72
  type: "problem",
@@ -75,22 +83,24 @@ var no_dangerously_set_innerhtml_with_children_default = createRule({
75
83
  create(context) {
76
84
  return {
77
85
  JSXElement(node) {
86
+ const attributes = node.openingElement.attributes;
78
87
  const initialScope = context.sourceCode.getScope(node);
79
- const hasChildrenWithIn = () => node.children.length > 0 && firstChildIsText(node);
80
- const hasChildrenProp = () => JSX8.hasProp(node.openingElement.attributes, "children", initialScope);
81
- const hasDanger = () => JSX8.hasProp(node.openingElement.attributes, "dangerouslySetInnerHTML", initialScope);
82
- if (!(hasChildrenWithIn() || hasChildrenProp()) || !hasDanger()) {
83
- return;
88
+ const hasChildren = hasChildrenWithin(node) || JSX3.hasProp("children", initialScope, attributes);
89
+ const hasDangerouslySetInnerHTML = JSX3.hasProp("dangerouslySetInnerHTML", initialScope, attributes);
90
+ if (hasChildren && hasDangerouslySetInnerHTML) {
91
+ context.report({
92
+ messageId: "noDangerouslySetInnerhtmlWithChildren",
93
+ node
94
+ });
84
95
  }
85
- context.report({
86
- messageId: "noDangerouslySetInnerhtmlWithChildren",
87
- node
88
- });
89
96
  }
90
97
  };
91
98
  },
92
99
  defaultOptions: []
93
100
  });
101
+ function hasChildrenWithin(node) {
102
+ return node.children.length > 0 && node.children[0] != null && !JSX3.isLineBreak(node.children[0]);
103
+ }
94
104
  var RULE_NAME3 = "no-find-dom-node";
95
105
  var RULE_FEATURES3 = [
96
106
  "CHK"
@@ -147,22 +157,32 @@ var no_missing_button_type_default = createRule({
147
157
  },
148
158
  name: RULE_NAME4,
149
159
  create(context) {
160
+ const settings = getSettingsFromContext(context);
161
+ const additionalComponents = settings.additionalComponents.filter((c) => c.as === "button");
150
162
  return {
151
163
  JSXElement(node) {
152
- const elementName = getElementRepresentName(node.openingElement, context);
153
- if (elementName !== "button") {
154
- return;
155
- }
156
- const { attributes } = node.openingElement;
157
- const initialScope = context.sourceCode.getScope(node);
158
- const mbProp = JSX8.findPropInAttributes(attributes, initialScope)("type");
159
- if (O.isSome(mbProp)) {
160
- return;
164
+ const [name2, representName] = getElementNameAndRepresentName(
165
+ node.openingElement,
166
+ context,
167
+ settings.polymorphicPropName,
168
+ settings.additionalComponents
169
+ );
170
+ if (representName !== "button") return;
171
+ const getPropValue3 = (propName) => {
172
+ return getAttributeStringValue(
173
+ propName,
174
+ node,
175
+ context,
176
+ getAdditionalAttributes(name2, additionalComponents)
177
+ );
178
+ };
179
+ const prop = getPropValue3("type");
180
+ if (typeof prop !== "string") {
181
+ context.report({
182
+ messageId: "noMissingButtonType",
183
+ node
184
+ });
161
185
  }
162
- context.report({
163
- messageId: "noMissingButtonType",
164
- node
165
- });
166
186
  }
167
187
  };
168
188
  },
@@ -204,36 +224,40 @@ var no_missing_iframe_sandbox_default = createRule({
204
224
  },
205
225
  name: RULE_NAME5,
206
226
  create(context) {
227
+ const settings = getSettingsFromContext(context);
228
+ const additionalComponents = settings.additionalComponents.filter((c) => c.as === "iframe");
207
229
  return {
208
230
  JSXElement(node) {
209
- const elementName = getElementRepresentName(node.openingElement, context);
210
- if (elementName !== "iframe") {
211
- return;
212
- }
213
- const { attributes } = node.openingElement;
214
- const initialScope = context.sourceCode.getScope(node);
215
- const mbProp = JSX8.findPropInAttributes(attributes, initialScope)("sandbox");
216
- if (O.isNone(mbProp)) {
231
+ const [name2, representName] = getElementNameAndRepresentName(
232
+ node.openingElement,
233
+ context,
234
+ settings.polymorphicPropName,
235
+ settings.additionalComponents
236
+ );
237
+ if (representName !== "iframe") return;
238
+ const getPropValue3 = (propName) => {
239
+ return getAttributeStringValue(
240
+ propName,
241
+ node,
242
+ context,
243
+ getAdditionalAttributes(name2, additionalComponents)
244
+ );
245
+ };
246
+ const sandboxValue = getPropValue3("sandbox");
247
+ if (sandboxValue == null) {
217
248
  context.report({
218
249
  messageId: "noMissingIframeSandbox",
219
250
  node: node.openingElement
220
251
  });
221
252
  return;
222
253
  }
223
- const prop = mbProp.value;
224
- const hasValidSandbox = F.pipe(
225
- JSX8.getPropValue(prop, context.sourceCode.getScope(prop)),
226
- O.filter(isString),
227
- O.map((value) => value.split(" ")),
228
- O.exists((values) => values.every((value) => validTypes.some((validType) => validType === value)))
229
- );
230
- if (hasValidSandbox) {
231
- return;
254
+ const values = sandboxValue.split(" ");
255
+ if (!values.every((value) => validTypes.some((validType) => validType === value))) {
256
+ context.report({
257
+ messageId: "noMissingIframeSandbox",
258
+ node: node.openingElement
259
+ });
232
260
  }
233
- context.report({
234
- messageId: "noMissingIframeSandbox",
235
- node: prop
236
- });
237
261
  }
238
262
  };
239
263
  },
@@ -259,8 +283,8 @@ var no_namespace_default = createRule({
259
283
  create(context) {
260
284
  return {
261
285
  JSXOpeningElement(node) {
262
- const name2 = JSX8.getElementName(node);
263
- if (!isString(name2) || !name2.includes(":")) {
286
+ const name2 = JSX3.getElementName(node);
287
+ if (typeof name2 !== "string" || !name2.includes(":")) {
264
288
  return;
265
289
  }
266
290
  context.report({
@@ -351,12 +375,11 @@ var no_script_url_default = createRule({
351
375
  if (node.name.type !== AST_NODE_TYPES.JSXIdentifier || node.value == null) {
352
376
  return;
353
377
  }
354
- const isJavaScript = F.pipe(
355
- JSX8.getPropValue(node, context.sourceCode.getScope(node)),
356
- O.filter(isString),
357
- O.exists((v) => RE_JAVASCRIPT_PROTOCOL.test(v))
358
- );
359
- if (isJavaScript) {
378
+ const propScope = context.sourceCode.getScope(node);
379
+ const propValue = JSX3.getPropValue(node, propScope);
380
+ const propValueResolved = VAR.toResolved(propValue).value;
381
+ if (typeof propValueResolved !== "string") return;
382
+ if (RE_JAVASCRIPT_PROTOCOL.test(propValueResolved)) {
360
383
  context.report({
361
384
  messageId: "noScriptUrl",
362
385
  node: node.value
@@ -1430,7 +1453,6 @@ var RULE_FEATURES9 = [
1430
1453
  ];
1431
1454
  var unsafeCombinations = [
1432
1455
  ["allow-scripts", "allow-same-origin"]
1433
- // ...
1434
1456
  ];
1435
1457
  var no_unsafe_iframe_sandbox_default = createRule({
1436
1458
  meta: {
@@ -1446,37 +1468,34 @@ var no_unsafe_iframe_sandbox_default = createRule({
1446
1468
  },
1447
1469
  name: RULE_NAME10,
1448
1470
  create(context) {
1471
+ const settings = getSettingsFromContext(context);
1472
+ const additionalComponents = settings.additionalComponents.filter((c) => c.as === "iframe");
1449
1473
  return {
1450
1474
  JSXElement(node) {
1451
- const elementName = getElementRepresentName(node.openingElement, context);
1452
- if (elementName !== "iframe") {
1453
- return;
1454
- }
1455
- const { attributes } = node.openingElement;
1456
- const initialScope = context.sourceCode.getScope(node);
1457
- const mbProp = JSX8.findPropInAttributes(attributes, initialScope)("sandbox");
1458
- if (O.isNone(mbProp)) {
1459
- return;
1460
- }
1461
- const prop = mbProp.value;
1462
- const isSafeSandboxValue = !F.pipe(
1463
- JSX8.getPropValue(prop, context.sourceCode.getScope(prop)),
1464
- O.flatMapNullable(
1465
- (v) => match(v).with(P.string, F.identity).with({ sandbox: P.string }, ({ sandbox }) => sandbox).otherwise(F.constNull)
1466
- ),
1467
- O.filter(isString),
1468
- O.map((value) => value.split(" ")),
1469
- O.exists(
1470
- (values) => unsafeCombinations.some((combinations) => combinations.every((unsafeValue) => values.includes(unsafeValue)))
1471
- )
1475
+ const [name2, representName] = getElementNameAndRepresentName(
1476
+ node.openingElement,
1477
+ context,
1478
+ settings.polymorphicPropName,
1479
+ settings.additionalComponents
1472
1480
  );
1473
- if (isSafeSandboxValue) {
1474
- return;
1481
+ if (representName !== "iframe") return;
1482
+ const getPropValue3 = (propName) => {
1483
+ return getAttributeStringValue(
1484
+ propName,
1485
+ node,
1486
+ context,
1487
+ getAdditionalAttributes(name2, additionalComponents)
1488
+ );
1489
+ };
1490
+ const sandboxValue = getPropValue3("sandbox");
1491
+ if (sandboxValue == null) return;
1492
+ const values = sandboxValue.split(" ");
1493
+ if (unsafeCombinations.some((unsafes) => unsafes.every((x) => values.includes(x)))) {
1494
+ context.report({
1495
+ messageId: "noUnsafeIframeSandbox",
1496
+ node: node.openingElement
1497
+ });
1475
1498
  }
1476
- context.report({
1477
- messageId: "noUnsafeIframeSandbox",
1478
- node: prop
1479
- });
1480
1499
  }
1481
1500
  };
1482
1501
  },
@@ -1487,9 +1506,11 @@ var RULE_FEATURES10 = [
1487
1506
  "CHK"
1488
1507
  ];
1489
1508
  function isExternalLinkLike(value) {
1509
+ if (value == null) return false;
1490
1510
  return value.startsWith("https://") || /^(?:\w+:|\/\/)/u.test(value);
1491
1511
  }
1492
1512
  function isSafeRel(value) {
1513
+ if (value == null) return false;
1493
1514
  return value === "noreferrer" || /\bnoreferrer\b/u.test(value);
1494
1515
  }
1495
1516
  var no_unsafe_target_blank_default = createRule({
@@ -1508,70 +1529,36 @@ var no_unsafe_target_blank_default = createRule({
1508
1529
  create(context) {
1509
1530
  const settings = getSettingsFromContext(context);
1510
1531
  const additionalComponents = settings.additionalComponents.filter((c) => c.as === "a");
1511
- function getReportDescriptor(node) {
1512
- const name2 = JSX8.getElementName(node.openingElement);
1513
- const elementName = getElementRepresentName(node.openingElement, context);
1514
- if (elementName !== "a" && !additionalComponents.some((c) => c.re.test(name2))) {
1515
- return O.none();
1516
- }
1517
- const { attributes } = node.openingElement;
1518
- const initialScope = context.sourceCode.getScope(node);
1519
- const additionalAttributes = additionalComponents.findLast((c) => c.re.test(name2))?.attributes ?? [];
1520
- const [
1521
- targetPropName,
1522
- targetPropDefaultValue
1523
- ] = findAttrInCustomAttributes("target", additionalAttributes);
1524
- const targetProp = JSX8.findPropInAttributes(attributes, initialScope)(targetPropName);
1525
- const targetPropValue = O.isNone(targetProp) ? O.fromNullable(targetPropDefaultValue) : F.pipe(
1526
- targetProp,
1527
- O.flatMap((attr) => JSX8.getPropValue(attr, context.sourceCode.getScope(attr))),
1528
- O.flatMapNullable(
1529
- (v) => match(v).with(P.string, F.identity).with({ [targetPropName]: P.string }, (v2) => v2[targetPropName]).otherwise(F.constNull)
1530
- ),
1531
- O.filter(isString)
1532
- );
1533
- if (!O.exists(targetPropValue, (t) => t === "_blank")) {
1534
- return O.none();
1535
- }
1536
- const [
1537
- hrefPropName,
1538
- hrefPropDefaultValue
1539
- ] = findAttrInCustomAttributes("href", additionalAttributes);
1540
- const hrefProp = JSX8.findPropInAttributes(attributes, initialScope)(hrefPropName);
1541
- const hrefPropValue = O.isNone(hrefProp) ? O.fromNullable(hrefPropDefaultValue) : F.pipe(
1542
- hrefProp,
1543
- O.flatMap((attr) => JSX8.getPropValue(attr, context.sourceCode.getScope(attr))),
1544
- O.flatMapNullable(
1545
- (v) => match(v).with(P.string, F.identity).with({ [hrefPropName]: P.string }, (v2) => v2[hrefPropName]).otherwise(F.constNull)
1546
- ),
1547
- O.filter(isString)
1548
- );
1549
- if (!O.exists(hrefPropValue, isExternalLinkLike)) {
1550
- return O.none();
1551
- }
1552
- const [
1553
- relPropName,
1554
- relPropDefaultValue
1555
- ] = findAttrInCustomAttributes("rel", additionalAttributes);
1556
- const relProp = JSX8.findPropInAttributes(attributes, initialScope)(relPropName);
1557
- const relPropValue = O.isNone(relProp) ? O.fromNullable(relPropDefaultValue) : F.pipe(
1558
- relProp,
1559
- O.flatMap((attr) => JSX8.getPropValue(attr, context.sourceCode.getScope(attr))),
1560
- O.flatMapNullable(
1561
- (v) => match(v).with(P.string, F.identity).with({ [relPropName]: P.string }, (v2) => v2[relPropName]).otherwise(F.constNull)
1562
- ),
1563
- O.filter(isString)
1564
- );
1565
- if (O.exists(relPropValue, isSafeRel)) {
1566
- return O.none();
1567
- }
1568
- return O.some({
1569
- messageId: "noUnsafeTargetBlank",
1570
- node
1571
- });
1572
- }
1573
1532
  return {
1574
- JSXElement: F.flow(getReportDescriptor, O.map(context.report))
1533
+ JSXElement(node) {
1534
+ const [name2, representName] = getElementNameAndRepresentName(
1535
+ node.openingElement,
1536
+ context,
1537
+ settings.polymorphicPropName,
1538
+ settings.additionalComponents
1539
+ );
1540
+ if (representName !== "a") return;
1541
+ const getPropValue3 = (propName) => {
1542
+ return getAttributeStringValue(
1543
+ propName,
1544
+ node,
1545
+ context,
1546
+ getAdditionalAttributes(name2, additionalComponents)
1547
+ );
1548
+ };
1549
+ if (getPropValue3("target") !== "_blank") {
1550
+ return;
1551
+ }
1552
+ if (!isExternalLinkLike(getPropValue3("href"))) {
1553
+ return;
1554
+ }
1555
+ if (!isSafeRel(getPropValue3("rel"))) {
1556
+ context.report({
1557
+ messageId: "noUnsafeTargetBlank",
1558
+ node
1559
+ });
1560
+ }
1561
+ }
1575
1562
  };
1576
1563
  },
1577
1564
  defaultOptions: []
@@ -1614,7 +1601,7 @@ var no_void_elements_with_children_default = createRule({
1614
1601
  create(context) {
1615
1602
  return {
1616
1603
  JSXElement(node) {
1617
- const elementName = JSX8.getElementName(node.openingElement);
1604
+ const elementName = JSX3.getElementName(node.openingElement);
1618
1605
  if (elementName.length === 0 || !voidElements.has(elementName)) {
1619
1606
  return;
1620
1607
  }
@@ -1629,9 +1616,8 @@ var no_void_elements_with_children_default = createRule({
1629
1616
  }
1630
1617
  const { attributes } = node.openingElement;
1631
1618
  const initialScope = context.sourceCode.getScope(node);
1632
- const hasAttr = (name2) => O.isSome(JSX8.findPropInAttributes(attributes, initialScope)(name2));
1633
- const hasChildrenOrDangerAttr = hasAttr("children") || hasAttr("dangerouslySetInnerHTML");
1634
- if (hasChildrenOrDangerAttr) {
1619
+ const hasAttribute = (name2) => JSX3.findPropInAttributes(name2, initialScope, attributes) != null;
1620
+ if (hasAttribute("children") || hasAttribute("dangerouslySetInnerHTML")) {
1635
1621
  context.report({
1636
1622
  messageId: "noVoidElementsWithChildren",
1637
1623
  node,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-react-dom",
3
- "version": "1.23.3-next.8",
3
+ "version": "1.24.0-beta.1",
4
4
  "description": "ESLint React's ESLint plugin for React DOM related rules.",
5
5
  "keywords": [
6
6
  "react",
@@ -10,13 +10,13 @@
10
10
  "eslint-plugin",
11
11
  "eslint-plugin-react-dom"
12
12
  ],
13
- "homepage": "https://github.com/rEl1cx/eslint-react",
13
+ "homepage": "https://github.com/Rel1cx/eslint-react",
14
14
  "bugs": {
15
- "url": "https://github.com/rEl1cx/eslint-react/issues"
15
+ "url": "https://github.com/Rel1cx/eslint-react/issues"
16
16
  },
17
17
  "repository": {
18
18
  "type": "git",
19
- "url": "git+https://github.com/rEl1cx/eslint-react.git",
19
+ "url": "git+https://github.com/Rel1cx/eslint-react.git",
20
20
  "directory": "packages/plugins/eslint-plugin-react-dom"
21
21
  },
22
22
  "license": "MIT",
@@ -47,17 +47,17 @@
47
47
  "compare-versions": "^6.1.1",
48
48
  "string-ts": "^2.2.0",
49
49
  "ts-pattern": "^5.6.0",
50
- "@eslint-react/ast": "1.23.3-next.8",
51
- "@eslint-react/core": "1.23.3-next.8",
52
- "@eslint-react/eff": "1.23.3-next.8",
53
- "@eslint-react/jsx": "1.23.3-next.8",
54
- "@eslint-react/shared": "1.23.3-next.8",
55
- "@eslint-react/types": "1.23.3-next.8",
56
- "@eslint-react/var": "1.23.3-next.8"
50
+ "@eslint-react/ast": "1.24.0-beta.1",
51
+ "@eslint-react/core": "1.24.0-beta.1",
52
+ "@eslint-react/eff": "1.24.0-beta.1",
53
+ "@eslint-react/jsx": "1.24.0-beta.1",
54
+ "@eslint-react/shared": "1.24.0-beta.1",
55
+ "@eslint-react/var": "1.24.0-beta.1",
56
+ "@eslint-react/types": "1.24.0-beta.1"
57
57
  },
58
58
  "devDependencies": {
59
- "@types/react": "^19.0.4",
60
- "@types/react-dom": "^19.0.2",
59
+ "@types/react": "^19.0.5",
60
+ "@types/react-dom": "^19.0.3",
61
61
  "tsup": "^8.3.5",
62
62
  "@workspace/configs": "0.0.0"
63
63
  },