eslint-plugin-react-dom 2.0.0-next.12 → 2.0.0-next.121

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.d.ts +19 -6
  2. package/dist/index.js +151 -123
  3. package/package.json +15 -16
package/dist/index.d.ts CHANGED
@@ -1,6 +1,19 @@
1
+ import { CamelCase } from 'string-ts';
1
2
  import * as _typescript_eslint_utils_ts_eslint from '@typescript-eslint/utils/ts-eslint';
2
3
  import { RulePreset } from '@eslint-react/kit';
3
4
 
5
+ declare const RULE_NAME$2 = "no-unsafe-target-blank";
6
+ type MessageID$2 = CamelCase<typeof RULE_NAME$2> | RuleSuggestMessageID$2;
7
+ type RuleSuggestMessageID$2 = "addRelNoreferrerNoopener";
8
+
9
+ declare const RULE_NAME$1 = "no-missing-iframe-sandbox";
10
+ type MessageID$1 = CamelCase<typeof RULE_NAME$1> | RuleSuggestMessageID$1;
11
+ type RuleSuggestMessageID$1 = "addIframeSandbox";
12
+
13
+ declare const RULE_NAME = "no-missing-button-type";
14
+ type MessageID = CamelCase<typeof RULE_NAME> | RuleSuggestMessageID;
15
+ type RuleSuggestMessageID = "addButtonType";
16
+
4
17
  declare const _default: {
5
18
  configs: {
6
19
  recommended: {
@@ -16,15 +29,15 @@ declare const _default: {
16
29
  readonly "no-find-dom-node": _typescript_eslint_utils_ts_eslint.RuleModule<"noFindDomNode", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
17
30
  readonly "no-flush-sync": _typescript_eslint_utils_ts_eslint.RuleModule<"noFlushSync", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
18
31
  readonly "no-hydrate": _typescript_eslint_utils_ts_eslint.RuleModule<"noHydrate", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
19
- readonly "no-missing-button-type": _typescript_eslint_utils_ts_eslint.RuleModule<"noMissingButtonType", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
20
- readonly "no-missing-iframe-sandbox": _typescript_eslint_utils_ts_eslint.RuleModule<"noMissingIframeSandbox", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
32
+ readonly "no-missing-button-type": _typescript_eslint_utils_ts_eslint.RuleModule<MessageID, [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
33
+ readonly "no-missing-iframe-sandbox": _typescript_eslint_utils_ts_eslint.RuleModule<MessageID$1, [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
21
34
  readonly "no-namespace": _typescript_eslint_utils_ts_eslint.RuleModule<"noNamespace", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
22
35
  readonly "no-render": _typescript_eslint_utils_ts_eslint.RuleModule<"noRender", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
23
36
  readonly "no-render-return-value": _typescript_eslint_utils_ts_eslint.RuleModule<"noRenderReturnValue", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
24
37
  readonly "no-script-url": _typescript_eslint_utils_ts_eslint.RuleModule<"noScriptUrl", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
25
38
  readonly "no-unknown-property": _typescript_eslint_utils_ts_eslint.RuleModule<"dataLowercaseRequired" | "invalidPropOnTag" | "unknownProp" | "unknownPropWithStandardName", unknown[], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
26
39
  readonly "no-unsafe-iframe-sandbox": _typescript_eslint_utils_ts_eslint.RuleModule<"noUnsafeIframeSandbox", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
27
- readonly "no-unsafe-target-blank": _typescript_eslint_utils_ts_eslint.RuleModule<"noUnsafeTargetBlank", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
40
+ readonly "no-unsafe-target-blank": _typescript_eslint_utils_ts_eslint.RuleModule<MessageID$2, [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
28
41
  readonly "no-use-form-state": _typescript_eslint_utils_ts_eslint.RuleModule<"noUseFormState", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
29
42
  readonly "no-void-elements-with-children": _typescript_eslint_utils_ts_eslint.RuleModule<"noVoidElementsWithChildren", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
30
43
  };
@@ -48,15 +61,15 @@ declare const _default: {
48
61
  readonly "no-find-dom-node": _typescript_eslint_utils_ts_eslint.RuleModule<"noFindDomNode", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
49
62
  readonly "no-flush-sync": _typescript_eslint_utils_ts_eslint.RuleModule<"noFlushSync", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
50
63
  readonly "no-hydrate": _typescript_eslint_utils_ts_eslint.RuleModule<"noHydrate", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
51
- readonly "no-missing-button-type": _typescript_eslint_utils_ts_eslint.RuleModule<"noMissingButtonType", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
52
- readonly "no-missing-iframe-sandbox": _typescript_eslint_utils_ts_eslint.RuleModule<"noMissingIframeSandbox", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
64
+ readonly "no-missing-button-type": _typescript_eslint_utils_ts_eslint.RuleModule<MessageID, [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
65
+ readonly "no-missing-iframe-sandbox": _typescript_eslint_utils_ts_eslint.RuleModule<MessageID$1, [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
53
66
  readonly "no-namespace": _typescript_eslint_utils_ts_eslint.RuleModule<"noNamespace", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
54
67
  readonly "no-render": _typescript_eslint_utils_ts_eslint.RuleModule<"noRender", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
55
68
  readonly "no-render-return-value": _typescript_eslint_utils_ts_eslint.RuleModule<"noRenderReturnValue", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
56
69
  readonly "no-script-url": _typescript_eslint_utils_ts_eslint.RuleModule<"noScriptUrl", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
57
70
  readonly "no-unknown-property": _typescript_eslint_utils_ts_eslint.RuleModule<"dataLowercaseRequired" | "invalidPropOnTag" | "unknownProp" | "unknownPropWithStandardName", unknown[], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
58
71
  readonly "no-unsafe-iframe-sandbox": _typescript_eslint_utils_ts_eslint.RuleModule<"noUnsafeIframeSandbox", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
59
- readonly "no-unsafe-target-blank": _typescript_eslint_utils_ts_eslint.RuleModule<"noUnsafeTargetBlank", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
72
+ readonly "no-unsafe-target-blank": _typescript_eslint_utils_ts_eslint.RuleModule<MessageID$2, [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
60
73
  readonly "no-use-form-state": _typescript_eslint_utils_ts_eslint.RuleModule<"noUseFormState", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
61
74
  readonly "no-void-elements-with-children": _typescript_eslint_utils_ts_eslint.RuleModule<"noVoidElementsWithChildren", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
62
75
  };
package/dist/index.js CHANGED
@@ -1,11 +1,10 @@
1
1
  import { getDocsUrl, getSettingsFromContext, DEFAULT_ESLINT_REACT_SETTINGS } from '@eslint-react/shared';
2
2
  import * as ER from '@eslint-react/core';
3
3
  import { ESLintUtils } from '@typescript-eslint/utils';
4
- import * as AST from '@eslint-react/ast';
4
+ import { unit } from '@eslint-react/eff';
5
5
  import { AST_NODE_TYPES } from '@typescript-eslint/types';
6
6
  import { compare } from 'compare-versions';
7
7
  import { RegExp, Reporter } from '@eslint-react/kit';
8
- import { _ } from '@eslint-react/eff';
9
8
 
10
9
  var __defProp = Object.defineProperty;
11
10
  var __export = (target, all) => {
@@ -44,7 +43,7 @@ var settings = {
44
43
 
45
44
  // package.json
46
45
  var name2 = "eslint-plugin-react-dom";
47
- var version = "2.0.0-next.12";
46
+ var version = "2.0.0-next.121";
48
47
  function createJsxElementResolver(context) {
49
48
  const { components, polymorphicPropName } = getSettingsFromContext(context);
50
49
  return {
@@ -89,6 +88,39 @@ var createRule = ESLintUtils.RuleCreator(getDocsUrl("dom"));
89
88
  function findCustomComponentProp(name3, props) {
90
89
  return props.findLast((a) => a.as === name3);
91
90
  }
91
+ function resolveAttribute(context, attributes, elementNode, attributeName) {
92
+ const customComponentProp = findCustomComponentProp(attributeName, attributes);
93
+ const propNameOnJsx = customComponentProp?.name ?? attributeName;
94
+ const attribute = ER.getAttribute(
95
+ context,
96
+ propNameOnJsx,
97
+ elementNode.openingElement.attributes,
98
+ context.sourceCode.getScope(elementNode)
99
+ );
100
+ if (attribute == null) {
101
+ return {
102
+ attribute: unit,
103
+ attributeName: propNameOnJsx,
104
+ attributeValue: unit,
105
+ attributeValueString: customComponentProp?.defaultValue
106
+ };
107
+ }
108
+ const attributeValue = ER.getAttributeValue(context, attribute, propNameOnJsx);
109
+ if (attributeValue.kind === "some" && typeof attributeValue.value === "string") {
110
+ return {
111
+ attribute,
112
+ attributeName: propNameOnJsx,
113
+ attributeValue,
114
+ attributeValueString: attributeValue.value
115
+ };
116
+ }
117
+ return {
118
+ attribute,
119
+ attributeName: propNameOnJsx,
120
+ attributeValue: unit,
121
+ attributeValueString: customComponentProp?.defaultValue ?? unit
122
+ };
123
+ }
92
124
 
93
125
  // src/rules/no-dangerously-set-innerhtml.ts
94
126
  var RULE_NAME = "no-dangerously-set-innerhtml";
@@ -153,7 +185,7 @@ function create2(context) {
153
185
  JSXElement(node) {
154
186
  const attributes = node.openingElement.attributes;
155
187
  const initialScope = context.sourceCode.getScope(node);
156
- const hasChildren = hasChildrenWithin(node) || ER.hasAttribute(context, "children", attributes, initialScope);
188
+ const hasChildren = node.children.some(isSignificantChildren) || ER.hasAttribute(context, "children", attributes, initialScope);
157
189
  if (hasChildren && ER.hasAttribute(context, dangerouslySetInnerHTML2, attributes, initialScope)) {
158
190
  context.report({
159
191
  messageId: "noDangerouslySetInnerhtmlWithChildren",
@@ -163,8 +195,14 @@ function create2(context) {
163
195
  }
164
196
  };
165
197
  }
166
- function hasChildrenWithin(node) {
167
- return node.children.length > 0 && node.children[0] != null && !AST.isLineBreak(node.children[0]);
198
+ function isWhiteSpace(node) {
199
+ return typeof node.value === "string" && node.raw.trim() === "";
200
+ }
201
+ function isPaddingSpaces(node) {
202
+ return ER.isJsxText(node) && isWhiteSpace(node) && node.raw.includes("\n");
203
+ }
204
+ function isSignificantChildren(node) {
205
+ return node.type !== AST_NODE_TYPES.JSXText || !isPaddingSpaces(node);
168
206
  }
169
207
  var RULE_NAME3 = "no-find-dom-node";
170
208
  var RULE_FEATURES3 = [];
@@ -322,8 +360,13 @@ function getFix(context, node) {
322
360
  ];
323
361
  };
324
362
  }
363
+
364
+ // src/rules/no-missing-button-type.ts
325
365
  var RULE_NAME6 = "no-missing-button-type";
326
- var RULE_FEATURES6 = [];
366
+ var RULE_FEATURES6 = [
367
+ "FIX"
368
+ ];
369
+ var BUTTON_TYPES = ["button", "submit", "reset"];
327
370
  var no_missing_button_type_default = createRule({
328
371
  meta: {
329
372
  type: "problem",
@@ -331,7 +374,9 @@ var no_missing_button_type_default = createRule({
331
374
  description: "Enforces explicit `type` attribute for `button` elements.",
332
375
  [Symbol.for("rule_features")]: RULE_FEATURES6
333
376
  },
377
+ hasSuggestions: true,
334
378
  messages: {
379
+ addButtonType: "Add 'type' attribute with value '{{type}}'.",
335
380
  noMissingButtonType: "Add missing 'type' attribute on 'button' component."
336
381
  },
337
382
  schema: []
@@ -346,60 +391,42 @@ function create6(context) {
346
391
  JSXElement(node) {
347
392
  const { attributes, domElementType } = resolver.resolve(node);
348
393
  if (domElementType !== "button") return;
349
- const customComponentProp = findCustomComponentProp("type", attributes);
350
- const propNameOnJsx = customComponentProp?.name ?? "type";
351
- const attributeNode = ER.getAttribute(
352
- context,
353
- propNameOnJsx,
354
- node.openingElement.attributes,
355
- context.sourceCode.getScope(node)
356
- );
357
- if (attributeNode != null) {
358
- const attributeValue = ER.getAttributeValue(
359
- context,
360
- attributeNode,
361
- propNameOnJsx
362
- );
363
- if (attributeValue.kind === "some" && typeof attributeValue.value !== "string") {
364
- context.report({
365
- messageId: "noMissingButtonType",
366
- node: attributeNode
367
- });
368
- }
369
- return;
370
- }
371
- if (typeof customComponentProp?.defaultValue !== "string") {
394
+ const typeAttribute = resolveAttribute(context, attributes, node, "type");
395
+ if (typeAttribute.attributeValueString != null) return;
396
+ if (typeAttribute.attribute == null) {
372
397
  context.report({
373
398
  messageId: "noMissingButtonType",
374
- node
399
+ node: node.openingElement,
400
+ suggest: getSuggest((type) => (fixer) => {
401
+ return fixer.insertTextAfter(node.openingElement.name, ` ${typeAttribute.attributeName}="${type}"`);
402
+ })
375
403
  });
404
+ return;
376
405
  }
406
+ context.report({
407
+ messageId: "noMissingButtonType",
408
+ node: typeAttribute.attributeValue?.node ?? typeAttribute.attribute,
409
+ suggest: getSuggest((type) => (fixer) => {
410
+ if (typeAttribute.attribute == null) return null;
411
+ return fixer.replaceText(typeAttribute.attribute, `${typeAttribute.attributeName}="${type}"`);
412
+ })
413
+ });
377
414
  }
378
415
  };
379
416
  }
417
+ function getSuggest(getFix4) {
418
+ return BUTTON_TYPES.map((type) => ({
419
+ messageId: "addButtonType",
420
+ data: { type },
421
+ fix: getFix4(type)
422
+ }));
423
+ }
424
+
425
+ // src/rules/no-missing-iframe-sandbox.ts
380
426
  var RULE_NAME7 = "no-missing-iframe-sandbox";
381
- var RULE_FEATURES7 = [];
382
- var validTypes = [
383
- "",
384
- "allow-downloads",
385
- "allow-downloads-without-user-activation",
386
- "allow-forms",
387
- "allow-modals",
388
- "allow-orientation-lock",
389
- "allow-pointer-lock",
390
- "allow-popups",
391
- "allow-popups-to-escape-sandbox",
392
- "allow-presentation",
393
- "allow-same-origin",
394
- "allow-scripts",
395
- "allow-storage-access-by-user-activation",
396
- "allow-top-navigation",
397
- "allow-top-navigation-by-user-activation",
398
- "allow-top-navigation-to-custom-protocols"
427
+ var RULE_FEATURES7 = [
428
+ "FIX"
399
429
  ];
400
- function hasValidSandBox(value) {
401
- return typeof value === "string" && value.split(" ").every((value2) => validTypes.some((valid) => valid === value2));
402
- }
403
430
  var no_missing_iframe_sandbox_default = createRule({
404
431
  meta: {
405
432
  type: "problem",
@@ -407,7 +434,10 @@ var no_missing_iframe_sandbox_default = createRule({
407
434
  description: "Enforces explicit `sandbox` attribute for `iframe` elements.",
408
435
  [Symbol.for("rule_features")]: RULE_FEATURES7
409
436
  },
437
+ fixable: "code",
438
+ hasSuggestions: true,
410
439
  messages: {
440
+ addIframeSandbox: "Add 'sandbox' attribute with value '{{value}}'.",
411
441
  noMissingIframeSandbox: "Add missing 'sandbox' attribute on 'iframe' component."
412
442
  },
413
443
  schema: []
@@ -422,33 +452,34 @@ function create7(context) {
422
452
  JSXElement(node) {
423
453
  const { attributes, domElementType } = resolver.resolve(node);
424
454
  if (domElementType !== "iframe") return;
425
- const customComponentProp = findCustomComponentProp("sandbox", attributes);
426
- const propNameOnJsx = customComponentProp?.name ?? "sandbox";
427
- const attributeNode = ER.getAttribute(
428
- context,
429
- propNameOnJsx,
430
- node.openingElement.attributes,
431
- context.sourceCode.getScope(node)
432
- );
433
- if (attributeNode != null) {
434
- const attributeValue = ER.getAttributeValue(
435
- context,
436
- attributeNode,
437
- propNameOnJsx
438
- );
439
- if (attributeValue.kind === "some" && hasValidSandBox(attributeValue.value)) return;
455
+ const sandboxAttribute = resolveAttribute(context, attributes, node, "sandbox");
456
+ if (sandboxAttribute.attributeValueString != null) return;
457
+ if (sandboxAttribute.attribute == null) {
440
458
  context.report({
441
459
  messageId: "noMissingIframeSandbox",
442
- node: attributeNode
460
+ node: node.openingElement,
461
+ suggest: [{
462
+ messageId: "addIframeSandbox",
463
+ data: { value: "" },
464
+ fix(fixer) {
465
+ return fixer.insertTextAfter(node.openingElement.name, ` ${sandboxAttribute.attributeName}=""`);
466
+ }
467
+ }]
443
468
  });
444
469
  return;
445
470
  }
446
- if (!hasValidSandBox(customComponentProp?.defaultValue)) {
447
- context.report({
448
- messageId: "noMissingIframeSandbox",
449
- node
450
- });
451
- }
471
+ context.report({
472
+ messageId: "noMissingIframeSandbox",
473
+ node: sandboxAttribute.attributeValue?.node ?? sandboxAttribute.attribute,
474
+ suggest: [{
475
+ messageId: "addIframeSandbox",
476
+ data: { value: "" },
477
+ fix(fixer) {
478
+ if (sandboxAttribute.attribute == null) return null;
479
+ return fixer.replaceText(sandboxAttribute.attribute, `${sandboxAttribute.attributeName}=""`);
480
+ }
481
+ }]
482
+ });
452
483
  }
453
484
  };
454
485
  }
@@ -1722,12 +1753,14 @@ function testReactVersion(context, comparator, version2) {
1722
1753
  const { version: localVersion } = getSettingsFromContext(context);
1723
1754
  return compare(localVersion, version2, comparator);
1724
1755
  }
1756
+
1757
+ // src/rules/no-unsafe-iframe-sandbox.ts
1725
1758
  var RULE_NAME13 = "no-unsafe-iframe-sandbox";
1726
1759
  var RULE_FEATURES12 = [];
1727
1760
  var unsafeSandboxValues = [
1728
1761
  ["allow-scripts", "allow-same-origin"]
1729
1762
  ];
1730
- function hasSafeSandbox(value) {
1763
+ function isSafeSandbox(value) {
1731
1764
  if (typeof value !== "string") return false;
1732
1765
  return !unsafeSandboxValues.some((values) => {
1733
1766
  return values.every((v) => value.includes(v));
@@ -1755,40 +1788,22 @@ function create13(context) {
1755
1788
  JSXElement(node) {
1756
1789
  const { attributes, domElementType } = resolver.resolve(node);
1757
1790
  if (domElementType !== "iframe") return;
1758
- const customComponentProp = findCustomComponentProp("sandbox", attributes);
1759
- const propNameOnJsx = customComponentProp?.name ?? "sandbox";
1760
- const attributeNode = ER.getAttribute(
1761
- context,
1762
- propNameOnJsx,
1763
- node.openingElement.attributes,
1764
- context.sourceCode.getScope(node)
1765
- );
1766
- if (attributeNode != null) {
1767
- const attributeValue = ER.getAttributeValue(
1768
- context,
1769
- attributeNode,
1770
- propNameOnJsx
1771
- );
1772
- if (attributeValue.kind === "some" && !hasSafeSandbox(attributeValue.value)) {
1773
- context.report({
1774
- messageId: "noUnsafeIframeSandbox",
1775
- node: attributeNode
1776
- });
1777
- return;
1778
- }
1779
- }
1780
- if (customComponentProp?.defaultValue == null) return;
1781
- if (!hasSafeSandbox(customComponentProp.defaultValue)) {
1791
+ const sandboxAttribute = resolveAttribute(context, attributes, node, "sandbox");
1792
+ if (!isSafeSandbox(sandboxAttribute.attributeValueString)) {
1782
1793
  context.report({
1783
1794
  messageId: "noUnsafeIframeSandbox",
1784
- node
1795
+ node: sandboxAttribute.attributeValue?.node ?? sandboxAttribute.attribute ?? node
1785
1796
  });
1786
1797
  }
1787
1798
  }
1788
1799
  };
1789
1800
  }
1801
+
1802
+ // src/rules/no-unsafe-target-blank.ts
1790
1803
  var RULE_NAME14 = "no-unsafe-target-blank";
1791
- var RULE_FEATURES13 = [];
1804
+ var RULE_FEATURES13 = [
1805
+ "FIX"
1806
+ ];
1792
1807
  function isExternalLinkLike(value) {
1793
1808
  if (value == null) return false;
1794
1809
  return value.startsWith("https://") || /^(?:\w+:|\/\/)/u.test(value);
@@ -1804,7 +1819,10 @@ var no_unsafe_target_blank_default = createRule({
1804
1819
  description: 'Disallow `target="_blank"` without `rel="noreferrer noopener"`.',
1805
1820
  [Symbol.for("rule_features")]: RULE_FEATURES13
1806
1821
  },
1822
+ fixable: "code",
1823
+ hasSuggestions: true,
1807
1824
  messages: {
1825
+ addRelNoreferrerNoopener: `Add 'rel="noreferrer noopener"' to the link to prevent security risks.`,
1808
1826
  noUnsafeTargetBlank: `Using 'target="_blank"' on an external link without 'rel="noreferrer noopener"' is a security risk.`
1809
1827
  },
1810
1828
  schema: []
@@ -1819,35 +1837,44 @@ function create14(context) {
1819
1837
  JSXElement(node) {
1820
1838
  const { attributes, domElementType } = resolver.resolve(node);
1821
1839
  if (domElementType !== "a") return;
1822
- const elementScope = context.sourceCode.getScope(node);
1823
- const getAttributeStringValue = (name3) => {
1824
- const customComponentProp = findCustomComponentProp(name3, attributes);
1825
- const propNameOnJsx = customComponentProp?.name ?? name3;
1826
- const attributeNode = ER.getAttribute(
1827
- context,
1828
- propNameOnJsx,
1829
- node.openingElement.attributes,
1830
- elementScope
1831
- );
1832
- if (attributeNode == null) return customComponentProp?.defaultValue;
1833
- const attributeValue = ER.getAttributeValue(context, attributeNode, propNameOnJsx);
1834
- if (attributeValue.kind === "some" && typeof attributeValue.value === "string") {
1835
- return attributeValue.value;
1836
- }
1837
- return _;
1838
- };
1839
- if (getAttributeStringValue("target") !== "_blank") {
1840
+ const targetAttribute = resolveAttribute(context, attributes, node, "target");
1841
+ if (targetAttribute.attributeValueString !== "_blank") {
1840
1842
  return;
1841
1843
  }
1842
- if (!isExternalLinkLike(getAttributeStringValue("href"))) {
1844
+ const hrefAttribute = resolveAttribute(context, attributes, node, "href");
1845
+ if (!isExternalLinkLike(hrefAttribute.attributeValueString)) {
1843
1846
  return;
1844
1847
  }
1845
- if (isSafeRel(getAttributeStringValue("rel"))) {
1848
+ const relAttribute = resolveAttribute(context, attributes, node, "rel");
1849
+ if (isSafeRel(relAttribute.attributeValueString)) {
1850
+ return;
1851
+ }
1852
+ if (relAttribute.attribute == null) {
1853
+ context.report({
1854
+ messageId: "noUnsafeTargetBlank",
1855
+ node: node.openingElement,
1856
+ suggest: [{
1857
+ messageId: "addRelNoreferrerNoopener",
1858
+ fix(fixer) {
1859
+ return fixer.insertTextAfter(
1860
+ node.openingElement.name,
1861
+ ` ${relAttribute.attributeName}="noreferrer noopener"`
1862
+ );
1863
+ }
1864
+ }]
1865
+ });
1846
1866
  return;
1847
1867
  }
1848
1868
  context.report({
1849
1869
  messageId: "noUnsafeTargetBlank",
1850
- node
1870
+ node: relAttribute.attributeValue?.node ?? relAttribute.attribute,
1871
+ suggest: [{
1872
+ messageId: "addRelNoreferrerNoopener",
1873
+ fix(fixer) {
1874
+ if (relAttribute.attribute == null) return null;
1875
+ return fixer.replaceText(relAttribute.attribute, `${relAttribute.attributeName}="noreferrer noopener"`);
1876
+ }
1877
+ }]
1851
1878
  });
1852
1879
  }
1853
1880
  };
@@ -1965,9 +1992,10 @@ var no_void_elements_with_children_default = createRule({
1965
1992
  defaultOptions: []
1966
1993
  });
1967
1994
  function create16(context) {
1995
+ const resolver = createJsxElementResolver(context);
1968
1996
  return {
1969
1997
  JSXElement(node) {
1970
- const elementName = ER.getElementType(context, node);
1998
+ const { domElementType: elementName } = resolver.resolve(node);
1971
1999
  if (elementName.length === 0 || !voidElements.has(elementName)) {
1972
2000
  return;
1973
2001
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-react-dom",
3
- "version": "2.0.0-next.12",
3
+ "version": "2.0.0-next.121",
4
4
  "description": "ESLint React's ESLint plugin for React DOM related rules.",
5
5
  "keywords": [
6
6
  "react",
@@ -35,27 +35,27 @@
35
35
  "./package.json"
36
36
  ],
37
37
  "dependencies": {
38
- "@typescript-eslint/scope-manager": "^8.31.1",
39
- "@typescript-eslint/types": "^8.31.1",
40
- "@typescript-eslint/utils": "^8.31.1",
38
+ "@typescript-eslint/scope-manager": "^8.38.0",
39
+ "@typescript-eslint/types": "^8.38.0",
40
+ "@typescript-eslint/utils": "^8.38.0",
41
41
  "compare-versions": "^6.1.1",
42
42
  "string-ts": "^2.2.1",
43
- "ts-pattern": "^5.7.0",
44
- "@eslint-react/eff": "2.0.0-next.12",
45
- "@eslint-react/ast": "2.0.0-next.12",
46
- "@eslint-react/kit": "2.0.0-next.12",
47
- "@eslint-react/var": "2.0.0-next.12",
48
- "@eslint-react/core": "2.0.0-next.12",
49
- "@eslint-react/shared": "2.0.0-next.12"
43
+ "ts-pattern": "^5.8.0",
44
+ "@eslint-react/ast": "2.0.0-next.121",
45
+ "@eslint-react/core": "2.0.0-next.121",
46
+ "@eslint-react/kit": "2.0.0-next.121",
47
+ "@eslint-react/eff": "2.0.0-next.121",
48
+ "@eslint-react/shared": "2.0.0-next.121",
49
+ "@eslint-react/var": "2.0.0-next.121"
50
50
  },
51
51
  "devDependencies": {
52
- "@types/react": "^19.1.2",
53
- "@types/react-dom": "^19.1.3",
54
- "tsup": "^8.4.0",
52
+ "@types/react": "^19.1.9",
53
+ "@types/react-dom": "^19.1.7",
54
+ "tsup": "^8.5.0",
55
55
  "@local/configs": "0.0.0"
56
56
  },
57
57
  "peerDependencies": {
58
- "eslint": "^9.26.0",
58
+ "eslint": "^9.31.0",
59
59
  "typescript": "^4.9.5 || ^5.4.5"
60
60
  },
61
61
  "peerDependenciesMeta": {
@@ -67,7 +67,6 @@
67
67
  }
68
68
  },
69
69
  "engines": {
70
- "bun": ">=1.0.15",
71
70
  "node": ">=20.19.0"
72
71
  },
73
72
  "publishConfig": {