eslint-plugin-react-dom 5.20.0 → 5.20.2

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 (2) hide show
  1. package/dist/index.js +110 -154
  2. package/package.json +8 -6
package/dist/index.js CHANGED
@@ -2,7 +2,9 @@ import { DEFAULT_ESLINT_REACT_SETTINGS, RE_JAVASCRIPT_PROTOCOL, getSettingsFromC
2
2
  import { ESLintUtils } from "@typescript-eslint/utils";
3
3
  import "@eslint-react/eslint";
4
4
  import { findAttribute, getAttributeStaticValue, getElementFullType, hasAnyAttribute, hasAttribute, isHostElement, isPaddingWhitespace, resolveAttributeValue } from "@eslint-react/jsx";
5
+ import * as core from "@eslint-react/core";
5
6
  import { Check, Extract } from "@eslint-react/ast";
7
+ import { createImportLookup } from "@eslint-react/var";
6
8
  import { AST_NODE_TYPES } from "@typescript-eslint/types";
7
9
  import { compare } from "compare-versions";
8
10
 
@@ -25,7 +27,7 @@ var __exportAll = (all, no_symbols) => {
25
27
  //#endregion
26
28
  //#region package.json
27
29
  var name$2 = "eslint-plugin-react-dom";
28
- var version = "5.20.0";
30
+ var version = "5.20.2";
29
31
 
30
32
  //#endregion
31
33
  //#region src/utils/create-rule.ts
@@ -89,6 +91,7 @@ function create$14(context) {
89
91
 
90
92
  //#endregion
91
93
  //#region src/rules/no-find-dom-node/no-find-dom-node.ts
94
+ const isFindDOMNodeCall = core.isAPICall("findDOMNode");
92
95
  const RULE_NAME$13 = "no-find-dom-node";
93
96
  var no_find_dom_node_default = createRule({
94
97
  meta: {
@@ -104,7 +107,7 @@ var no_find_dom_node_default = createRule({
104
107
  function create$13(context) {
105
108
  if (!context.sourceCode.text.includes("findDOMNode")) return {};
106
109
  return { CallExpression(node) {
107
- if (Extract.getCalleeName(node) === "findDOMNode") context.report({
110
+ if (isFindDOMNodeCall(context, node)) context.report({
108
111
  messageId: "default",
109
112
  node
110
113
  });
@@ -127,11 +130,23 @@ var no_flush_sync_default = createRule({
127
130
  });
128
131
  function create$12(context) {
129
132
  if (!context.sourceCode.text.includes("flushSync")) return {};
133
+ const imports = createImportLookup(context.sourceCode.ast, { source: "react-dom" });
130
134
  return { CallExpression(node) {
131
- if (Extract.getCalleeName(node) === "flushSync") context.report({
132
- messageId: "default",
133
- node
134
- });
135
+ const callee = Extract.unwrap(node.callee);
136
+ switch (true) {
137
+ case Check.isIdentifier(callee) && imports.has(callee.name, "flushSync"):
138
+ context.report({
139
+ messageId: "default",
140
+ node
141
+ });
142
+ return;
143
+ case callee.type === AST_NODE_TYPES.MemberExpression && Check.isIdentifier(callee.object) && Extract.getCalleeName(node) === "flushSync" && imports.hasNamespace(callee.object.name):
144
+ context.report({
145
+ messageId: "default",
146
+ node
147
+ });
148
+ return;
149
+ }
135
150
  } };
136
151
  }
137
152
 
@@ -154,43 +169,26 @@ function create$11(context) {
154
169
  if (!context.sourceCode.text.includes("hydrate")) return {};
155
170
  const settings = getSettingsFromContext(context);
156
171
  if (compare(settings.version, "18.0.0", "<")) return {};
157
- const reactDomNames = /* @__PURE__ */ new Set();
158
- const hydrateNames = /* @__PURE__ */ new Set();
159
- return {
160
- CallExpression(node) {
161
- const callee = Extract.unwrap(node.callee);
162
- switch (true) {
163
- case Check.isIdentifier(callee) && hydrateNames.has(callee.name):
164
- context.report({
165
- fix: buildFix$2(context, node),
166
- messageId: "default",
167
- node
168
- });
169
- return;
170
- case callee.type === AST_NODE_TYPES.MemberExpression && Check.isIdentifier(callee.object) && Extract.getCalleeName(node) === "hydrate" && reactDomNames.has(callee.object.name):
171
- context.report({
172
- fix: buildFix$2(context, node),
173
- messageId: "default",
174
- node
175
- });
176
- return;
177
- }
178
- },
179
- ImportDeclaration(node) {
180
- const [baseSource] = node.source.value.split("/");
181
- if (baseSource !== "react-dom") return;
182
- for (const specifier of node.specifiers) switch (specifier.type) {
183
- case AST_NODE_TYPES.ImportSpecifier:
184
- if (!Check.isIdentifier(specifier.imported)) continue;
185
- if (specifier.imported.name === "hydrate") hydrateNames.add(specifier.local.name);
186
- continue;
187
- case AST_NODE_TYPES.ImportDefaultSpecifier:
188
- case AST_NODE_TYPES.ImportNamespaceSpecifier:
189
- reactDomNames.add(specifier.local.name);
190
- continue;
191
- }
172
+ const imports = createImportLookup(context.sourceCode.ast, { source: "react-dom" });
173
+ return { CallExpression(node) {
174
+ const callee = Extract.unwrap(node.callee);
175
+ switch (true) {
176
+ case Check.isIdentifier(callee) && imports.has(callee.name, "hydrate"):
177
+ context.report({
178
+ fix: buildFix$2(context, node),
179
+ messageId: "default",
180
+ node
181
+ });
182
+ return;
183
+ case callee.type === AST_NODE_TYPES.MemberExpression && Check.isIdentifier(callee.object) && Extract.getCalleeName(node) === "hydrate" && imports.hasNamespace(callee.object.name):
184
+ context.report({
185
+ fix: buildFix$2(context, node),
186
+ messageId: "default",
187
+ node
188
+ });
189
+ return;
192
190
  }
193
- };
191
+ } };
194
192
  }
195
193
  function buildFix$2(context, node) {
196
194
  const getText = (n) => context.sourceCode.getText(n);
@@ -204,15 +202,17 @@ function buildFix$2(context, node) {
204
202
  //#endregion
205
203
  //#region src/utils/create-jsx-element-resolver.ts
206
204
  /**
207
- * Creates a resolver for JSX elements that determines both the JSX element type
208
- * and the underlying DOM element type.
205
+ * Creates a resolver for JSX elements that determines the underlying DOM element type.
209
206
  *
210
207
  * This resolver handles:
211
208
  * 1. Regular HTML elements (div, span, etc.)
212
- * 2. Polymorphic components (components that can render as different elements via a prop)
209
+ * 2. Polymorphic components (components that render as a different element via a prop,
210
+ * ex: `<Box as="a">`). Only static string values of the polymorphic prop are
211
+ * supported; `<Box as={Component}>` cannot be resolved statically and falls back
212
+ * to the JSX element's own name.
213
213
  *
214
214
  * @param context The ESLint rule context
215
- * @returns An object with a resolve method to determine element types
215
+ * @returns An object with a resolve method to determine the element type
216
216
  */
217
217
  function createJsxElementResolver(context) {
218
218
  const { polymorphicPropName } = getSettingsFromContext(context);
@@ -229,11 +229,12 @@ resolve(node) {
229
229
  domElementType: elementName,
230
230
  jsxElementType: elementName
231
231
  };
232
- if (elementName === elementName.toLowerCase() || polymorphicPropName == null) return result;
232
+ const nameNode = node.openingElement.name;
233
+ if (nameNode.type === AST_NODE_TYPES.JSXIdentifier && nameNode.name.charAt(0) === nameNode.name.charAt(0).toLowerCase()) return result;
233
234
  const polyPropValue = getAttributeStaticValue(context, node, polymorphicPropName);
234
235
  if (typeof polyPropValue === "string") return {
235
236
  ...result,
236
- domElementType: polyPropValue
237
+ domElementType: polyPropValue.toLowerCase()
237
238
  };
238
239
  return result;
239
240
  } };
@@ -362,41 +363,27 @@ var no_render_return_value_default = createRule({
362
363
  defaultOptions: []
363
364
  });
364
365
  function create$8(context) {
365
- const reactDomNames = /* @__PURE__ */ new Set(["ReactDOM", "ReactDOM"]);
366
- const renderNames = /* @__PURE__ */ new Set();
367
- return {
368
- CallExpression(node) {
369
- const callee = Extract.unwrap(node.callee);
370
- switch (true) {
371
- case Check.isIdentifier(callee) && renderNames.has(callee.name) && isReturnValueUsed(node):
372
- context.report({
373
- messageId: "default",
374
- node
375
- });
376
- return;
377
- case callee.type === AST_NODE_TYPES.MemberExpression && Check.isIdentifier(callee.object) && Extract.getCalleeName(node) === "render" && reactDomNames.has(callee.object.name) && isReturnValueUsed(node):
378
- context.report({
379
- messageId: "default",
380
- node
381
- });
382
- return;
383
- }
384
- },
385
- ImportDeclaration(node) {
386
- const [baseSource] = node.source.value.split("/");
387
- if (baseSource !== "react-dom") return;
388
- for (const specifier of node.specifiers) switch (specifier.type) {
389
- case AST_NODE_TYPES.ImportSpecifier:
390
- if (!Check.isIdentifier(specifier.imported)) continue;
391
- if (specifier.imported.name === "render") renderNames.add(specifier.local.name);
392
- continue;
393
- case AST_NODE_TYPES.ImportDefaultSpecifier:
394
- case AST_NODE_TYPES.ImportNamespaceSpecifier:
395
- reactDomNames.add(specifier.local.name);
396
- continue;
397
- }
366
+ const imports = createImportLookup(context.sourceCode.ast, {
367
+ source: "react-dom",
368
+ builtinNamespaces: ["ReactDOM"]
369
+ });
370
+ return { CallExpression(node) {
371
+ const callee = Extract.unwrap(node.callee);
372
+ switch (true) {
373
+ case Check.isIdentifier(callee) && imports.has(callee.name, "render") && isReturnValueUsed(node):
374
+ context.report({
375
+ messageId: "default",
376
+ node
377
+ });
378
+ return;
379
+ case callee.type === AST_NODE_TYPES.MemberExpression && Check.isIdentifier(callee.object) && Extract.getCalleeName(node) === "render" && imports.hasNamespace(callee.object.name) && isReturnValueUsed(node):
380
+ context.report({
381
+ messageId: "default",
382
+ node
383
+ });
384
+ return;
398
385
  }
399
- };
386
+ } };
400
387
  }
401
388
 
402
389
  //#endregion
@@ -418,43 +405,29 @@ function create$7(context) {
418
405
  if (!context.sourceCode.text.includes("render")) return {};
419
406
  const settings = getSettingsFromContext(context);
420
407
  if (compare(settings.version, "18.0.0", "<")) return {};
421
- const reactDomNames = /* @__PURE__ */ new Set(["ReactDOM", "ReactDOM"]);
422
- const renderNames = /* @__PURE__ */ new Set();
423
- return {
424
- CallExpression(node) {
425
- const callee = Extract.unwrap(node.callee);
426
- switch (true) {
427
- case Check.isIdentifier(callee) && renderNames.has(callee.name):
428
- context.report({
429
- fix: buildFix$1(context, node),
430
- messageId: "default",
431
- node
432
- });
433
- return;
434
- case callee.type === AST_NODE_TYPES.MemberExpression && Check.isIdentifier(callee.object) && Extract.getCalleeName(node) === "render" && reactDomNames.has(callee.object.name):
435
- context.report({
436
- fix: buildFix$1(context, node),
437
- messageId: "default",
438
- node
439
- });
440
- return;
441
- }
442
- },
443
- ImportDeclaration(node) {
444
- const [baseSource] = node.source.value.split("/");
445
- if (baseSource !== "react-dom") return;
446
- for (const specifier of node.specifiers) switch (specifier.type) {
447
- case AST_NODE_TYPES.ImportSpecifier:
448
- if (!Check.isIdentifier(specifier.imported)) continue;
449
- if (specifier.imported.name === "render") renderNames.add(specifier.local.name);
450
- continue;
451
- case AST_NODE_TYPES.ImportDefaultSpecifier:
452
- case AST_NODE_TYPES.ImportNamespaceSpecifier:
453
- reactDomNames.add(specifier.local.name);
454
- continue;
455
- }
408
+ const imports = createImportLookup(context.sourceCode.ast, {
409
+ source: "react-dom",
410
+ builtinNamespaces: ["ReactDOM"]
411
+ });
412
+ return { CallExpression(node) {
413
+ const callee = Extract.unwrap(node.callee);
414
+ switch (true) {
415
+ case Check.isIdentifier(callee) && imports.has(callee.name, "render"):
416
+ context.report({
417
+ fix: buildFix$1(context, node),
418
+ messageId: "default",
419
+ node
420
+ });
421
+ return;
422
+ case callee.type === AST_NODE_TYPES.MemberExpression && Check.isIdentifier(callee.object) && Extract.getCalleeName(node) === "render" && imports.hasNamespace(callee.object.name):
423
+ context.report({
424
+ fix: buildFix$1(context, node),
425
+ messageId: "default",
426
+ node
427
+ });
428
+ return;
456
429
  }
457
- };
430
+ } };
458
431
  }
459
432
  /**
460
433
  * Provides a fixer function to replace `render(app, container)` with `createRoot(container).render(app)`
@@ -1827,43 +1800,26 @@ function create$1(context) {
1827
1800
  if (!context.sourceCode.text.includes("useFormState")) return {};
1828
1801
  const settings = getSettingsFromContext(context);
1829
1802
  if (compare(settings.version, "19.0.0", "<")) return {};
1830
- const reactDomNames = /* @__PURE__ */ new Set();
1831
- const useFormStateNames = /* @__PURE__ */ new Set();
1832
- return {
1833
- CallExpression(node) {
1834
- const callee = Extract.unwrap(node.callee);
1835
- switch (true) {
1836
- case Check.isIdentifier(callee) && useFormStateNames.has(callee.name):
1837
- context.report({
1838
- fix: buildFix(context, node),
1839
- messageId: "default",
1840
- node
1841
- });
1842
- return;
1843
- case callee.type === AST_NODE_TYPES.MemberExpression && Check.isIdentifier(callee.object) && Extract.getCalleeName(node) === "useFormState" && reactDomNames.has(callee.object.name):
1844
- context.report({
1845
- fix: buildFix(context, node),
1846
- messageId: "default",
1847
- node
1848
- });
1849
- return;
1850
- }
1851
- },
1852
- ImportDeclaration(node) {
1853
- const [baseSource] = node.source.value.split("/");
1854
- if (baseSource !== "react-dom") return;
1855
- for (const specifier of node.specifiers) switch (specifier.type) {
1856
- case AST_NODE_TYPES.ImportSpecifier:
1857
- if (!Check.isIdentifier(specifier.imported)) continue;
1858
- if (specifier.imported.name === "useFormState") useFormStateNames.add(specifier.local.name);
1859
- continue;
1860
- case AST_NODE_TYPES.ImportDefaultSpecifier:
1861
- case AST_NODE_TYPES.ImportNamespaceSpecifier:
1862
- reactDomNames.add(specifier.local.name);
1863
- continue;
1864
- }
1803
+ const imports = createImportLookup(context.sourceCode.ast, { source: "react-dom" });
1804
+ return { CallExpression(node) {
1805
+ const callee = Extract.unwrap(node.callee);
1806
+ switch (true) {
1807
+ case Check.isIdentifier(callee) && imports.has(callee.name, "useFormState"):
1808
+ context.report({
1809
+ fix: buildFix(context, node),
1810
+ messageId: "default",
1811
+ node
1812
+ });
1813
+ return;
1814
+ case callee.type === AST_NODE_TYPES.MemberExpression && Check.isIdentifier(callee.object) && Extract.getCalleeName(node) === "useFormState" && imports.hasNamespace(callee.object.name):
1815
+ context.report({
1816
+ fix: buildFix(context, node),
1817
+ messageId: "default",
1818
+ node
1819
+ });
1820
+ return;
1865
1821
  }
1866
- };
1822
+ } };
1867
1823
  }
1868
1824
  function buildFix(context, node) {
1869
1825
  const { importSource } = getSettingsFromContext(context);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-react-dom",
3
- "version": "5.20.0",
3
+ "version": "5.20.2",
4
4
  "description": "ESLint React's ESLint plugin for DOM related rules.",
5
5
  "keywords": [
6
6
  "react",
@@ -37,10 +37,12 @@
37
37
  "dist"
38
38
  ],
39
39
  "dependencies": {
40
- "@eslint-react/ast": "5.20.0",
41
- "@eslint-react/eslint": "5.20.0",
42
- "@eslint-react/jsx": "5.20.0",
43
- "@eslint-react/shared": "5.20.0",
40
+ "@eslint-react/ast": "5.20.2",
41
+ "@eslint-react/core": "5.20.2",
42
+ "@eslint-react/eslint": "5.20.2",
43
+ "@eslint-react/jsx": "5.20.2",
44
+ "@eslint-react/shared": "5.20.2",
45
+ "@eslint-react/var": "5.20.2",
44
46
  "@typescript-eslint/types": "^8.70.0",
45
47
  "@typescript-eslint/utils": "^8.70.0",
46
48
  "compare-versions": "^6.1.1"
@@ -51,7 +53,7 @@
51
53
  "@types/react": "^19.3.0",
52
54
  "@types/react-dom": "^19.3.0",
53
55
  "dedent": "^1.7.2",
54
- "eslint": "^10.10.0",
56
+ "eslint": "^10.11.0",
55
57
  "tsdown": "^0.23.0",
56
58
  "typescript": "6.0.3"
57
59
  },