eslint-plugin-react-dom 5.20.1 → 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 +108 -183
  2. package/package.json +7 -6
package/dist/index.js CHANGED
@@ -4,6 +4,7 @@ import "@eslint-react/eslint";
4
4
  import { findAttribute, getAttributeStaticValue, getElementFullType, hasAnyAttribute, hasAttribute, isHostElement, isPaddingWhitespace, resolveAttributeValue } from "@eslint-react/jsx";
5
5
  import * as core from "@eslint-react/core";
6
6
  import { Check, Extract } from "@eslint-react/ast";
7
+ import { createImportLookup } from "@eslint-react/var";
7
8
  import { AST_NODE_TYPES } from "@typescript-eslint/types";
8
9
  import { compare } from "compare-versions";
9
10
 
@@ -26,7 +27,7 @@ var __exportAll = (all, no_symbols) => {
26
27
  //#endregion
27
28
  //#region package.json
28
29
  var name$2 = "eslint-plugin-react-dom";
29
- var version = "5.20.1";
30
+ var version = "5.20.2";
30
31
 
31
32
  //#endregion
32
33
  //#region src/utils/create-rule.ts
@@ -129,41 +130,24 @@ var no_flush_sync_default = createRule({
129
130
  });
130
131
  function create$12(context) {
131
132
  if (!context.sourceCode.text.includes("flushSync")) return {};
132
- const reactDomNames = /* @__PURE__ */ new Set();
133
- const flushSyncNames = /* @__PURE__ */ new Set();
134
- return {
135
- CallExpression(node) {
136
- const callee = Extract.unwrap(node.callee);
137
- switch (true) {
138
- case Check.isIdentifier(callee) && flushSyncNames.has(callee.name):
139
- context.report({
140
- messageId: "default",
141
- node
142
- });
143
- return;
144
- case callee.type === AST_NODE_TYPES.MemberExpression && Check.isIdentifier(callee.object) && Extract.getCalleeName(node) === "flushSync" && reactDomNames.has(callee.object.name):
145
- context.report({
146
- messageId: "default",
147
- node
148
- });
149
- return;
150
- }
151
- },
152
- ImportDeclaration(node) {
153
- const [baseSource] = node.source.value.split("/");
154
- if (baseSource !== "react-dom") return;
155
- for (const specifier of node.specifiers) switch (specifier.type) {
156
- case AST_NODE_TYPES.ImportSpecifier:
157
- if (!Check.isIdentifier(specifier.imported)) continue;
158
- if (specifier.imported.name === "flushSync") flushSyncNames.add(specifier.local.name);
159
- continue;
160
- case AST_NODE_TYPES.ImportDefaultSpecifier:
161
- case AST_NODE_TYPES.ImportNamespaceSpecifier:
162
- reactDomNames.add(specifier.local.name);
163
- continue;
164
- }
133
+ const imports = createImportLookup(context.sourceCode.ast, { source: "react-dom" });
134
+ return { CallExpression(node) {
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;
165
149
  }
166
- };
150
+ } };
167
151
  }
168
152
 
169
153
  //#endregion
@@ -185,43 +169,26 @@ function create$11(context) {
185
169
  if (!context.sourceCode.text.includes("hydrate")) return {};
186
170
  const settings = getSettingsFromContext(context);
187
171
  if (compare(settings.version, "18.0.0", "<")) return {};
188
- const reactDomNames = /* @__PURE__ */ new Set();
189
- const hydrateNames = /* @__PURE__ */ new Set();
190
- return {
191
- CallExpression(node) {
192
- const callee = Extract.unwrap(node.callee);
193
- switch (true) {
194
- case Check.isIdentifier(callee) && hydrateNames.has(callee.name):
195
- context.report({
196
- fix: buildFix$2(context, node),
197
- messageId: "default",
198
- node
199
- });
200
- return;
201
- case callee.type === AST_NODE_TYPES.MemberExpression && Check.isIdentifier(callee.object) && Extract.getCalleeName(node) === "hydrate" && reactDomNames.has(callee.object.name):
202
- context.report({
203
- fix: buildFix$2(context, node),
204
- messageId: "default",
205
- node
206
- });
207
- return;
208
- }
209
- },
210
- ImportDeclaration(node) {
211
- const [baseSource] = node.source.value.split("/");
212
- if (baseSource !== "react-dom") return;
213
- for (const specifier of node.specifiers) switch (specifier.type) {
214
- case AST_NODE_TYPES.ImportSpecifier:
215
- if (!Check.isIdentifier(specifier.imported)) continue;
216
- if (specifier.imported.name === "hydrate") hydrateNames.add(specifier.local.name);
217
- continue;
218
- case AST_NODE_TYPES.ImportDefaultSpecifier:
219
- case AST_NODE_TYPES.ImportNamespaceSpecifier:
220
- reactDomNames.add(specifier.local.name);
221
- continue;
222
- }
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;
223
190
  }
224
- };
191
+ } };
225
192
  }
226
193
  function buildFix$2(context, node) {
227
194
  const getText = (n) => context.sourceCode.getText(n);
@@ -235,15 +202,17 @@ function buildFix$2(context, node) {
235
202
  //#endregion
236
203
  //#region src/utils/create-jsx-element-resolver.ts
237
204
  /**
238
- * Creates a resolver for JSX elements that determines both the JSX element type
239
- * and the underlying DOM element type.
205
+ * Creates a resolver for JSX elements that determines the underlying DOM element type.
240
206
  *
241
207
  * This resolver handles:
242
208
  * 1. Regular HTML elements (div, span, etc.)
243
- * 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.
244
213
  *
245
214
  * @param context The ESLint rule context
246
- * @returns An object with a resolve method to determine element types
215
+ * @returns An object with a resolve method to determine the element type
247
216
  */
248
217
  function createJsxElementResolver(context) {
249
218
  const { polymorphicPropName } = getSettingsFromContext(context);
@@ -260,11 +229,12 @@ resolve(node) {
260
229
  domElementType: elementName,
261
230
  jsxElementType: elementName
262
231
  };
263
- 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;
264
234
  const polyPropValue = getAttributeStaticValue(context, node, polymorphicPropName);
265
235
  if (typeof polyPropValue === "string") return {
266
236
  ...result,
267
- domElementType: polyPropValue
237
+ domElementType: polyPropValue.toLowerCase()
268
238
  };
269
239
  return result;
270
240
  } };
@@ -393,41 +363,27 @@ var no_render_return_value_default = createRule({
393
363
  defaultOptions: []
394
364
  });
395
365
  function create$8(context) {
396
- const reactDomNames = /* @__PURE__ */ new Set(["ReactDOM", "ReactDOM"]);
397
- const renderNames = /* @__PURE__ */ new Set();
398
- return {
399
- CallExpression(node) {
400
- const callee = Extract.unwrap(node.callee);
401
- switch (true) {
402
- case Check.isIdentifier(callee) && renderNames.has(callee.name) && isReturnValueUsed(node):
403
- context.report({
404
- messageId: "default",
405
- node
406
- });
407
- return;
408
- case callee.type === AST_NODE_TYPES.MemberExpression && Check.isIdentifier(callee.object) && Extract.getCalleeName(node) === "render" && reactDomNames.has(callee.object.name) && isReturnValueUsed(node):
409
- context.report({
410
- messageId: "default",
411
- node
412
- });
413
- return;
414
- }
415
- },
416
- ImportDeclaration(node) {
417
- const [baseSource] = node.source.value.split("/");
418
- if (baseSource !== "react-dom") return;
419
- for (const specifier of node.specifiers) switch (specifier.type) {
420
- case AST_NODE_TYPES.ImportSpecifier:
421
- if (!Check.isIdentifier(specifier.imported)) continue;
422
- if (specifier.imported.name === "render") renderNames.add(specifier.local.name);
423
- continue;
424
- case AST_NODE_TYPES.ImportDefaultSpecifier:
425
- case AST_NODE_TYPES.ImportNamespaceSpecifier:
426
- reactDomNames.add(specifier.local.name);
427
- continue;
428
- }
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;
429
385
  }
430
- };
386
+ } };
431
387
  }
432
388
 
433
389
  //#endregion
@@ -449,43 +405,29 @@ function create$7(context) {
449
405
  if (!context.sourceCode.text.includes("render")) return {};
450
406
  const settings = getSettingsFromContext(context);
451
407
  if (compare(settings.version, "18.0.0", "<")) return {};
452
- const reactDomNames = /* @__PURE__ */ new Set(["ReactDOM", "ReactDOM"]);
453
- const renderNames = /* @__PURE__ */ new Set();
454
- return {
455
- CallExpression(node) {
456
- const callee = Extract.unwrap(node.callee);
457
- switch (true) {
458
- case Check.isIdentifier(callee) && renderNames.has(callee.name):
459
- context.report({
460
- fix: buildFix$1(context, node),
461
- messageId: "default",
462
- node
463
- });
464
- return;
465
- case callee.type === AST_NODE_TYPES.MemberExpression && Check.isIdentifier(callee.object) && Extract.getCalleeName(node) === "render" && reactDomNames.has(callee.object.name):
466
- context.report({
467
- fix: buildFix$1(context, node),
468
- messageId: "default",
469
- node
470
- });
471
- return;
472
- }
473
- },
474
- ImportDeclaration(node) {
475
- const [baseSource] = node.source.value.split("/");
476
- if (baseSource !== "react-dom") return;
477
- for (const specifier of node.specifiers) switch (specifier.type) {
478
- case AST_NODE_TYPES.ImportSpecifier:
479
- if (!Check.isIdentifier(specifier.imported)) continue;
480
- if (specifier.imported.name === "render") renderNames.add(specifier.local.name);
481
- continue;
482
- case AST_NODE_TYPES.ImportDefaultSpecifier:
483
- case AST_NODE_TYPES.ImportNamespaceSpecifier:
484
- reactDomNames.add(specifier.local.name);
485
- continue;
486
- }
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;
487
429
  }
488
- };
430
+ } };
489
431
  }
490
432
  /**
491
433
  * Provides a fixer function to replace `render(app, container)` with `createRoot(container).render(app)`
@@ -1858,43 +1800,26 @@ function create$1(context) {
1858
1800
  if (!context.sourceCode.text.includes("useFormState")) return {};
1859
1801
  const settings = getSettingsFromContext(context);
1860
1802
  if (compare(settings.version, "19.0.0", "<")) return {};
1861
- const reactDomNames = /* @__PURE__ */ new Set();
1862
- const useFormStateNames = /* @__PURE__ */ new Set();
1863
- return {
1864
- CallExpression(node) {
1865
- const callee = Extract.unwrap(node.callee);
1866
- switch (true) {
1867
- case Check.isIdentifier(callee) && useFormStateNames.has(callee.name):
1868
- context.report({
1869
- fix: buildFix(context, node),
1870
- messageId: "default",
1871
- node
1872
- });
1873
- return;
1874
- case callee.type === AST_NODE_TYPES.MemberExpression && Check.isIdentifier(callee.object) && Extract.getCalleeName(node) === "useFormState" && reactDomNames.has(callee.object.name):
1875
- context.report({
1876
- fix: buildFix(context, node),
1877
- messageId: "default",
1878
- node
1879
- });
1880
- return;
1881
- }
1882
- },
1883
- ImportDeclaration(node) {
1884
- const [baseSource] = node.source.value.split("/");
1885
- if (baseSource !== "react-dom") return;
1886
- for (const specifier of node.specifiers) switch (specifier.type) {
1887
- case AST_NODE_TYPES.ImportSpecifier:
1888
- if (!Check.isIdentifier(specifier.imported)) continue;
1889
- if (specifier.imported.name === "useFormState") useFormStateNames.add(specifier.local.name);
1890
- continue;
1891
- case AST_NODE_TYPES.ImportDefaultSpecifier:
1892
- case AST_NODE_TYPES.ImportNamespaceSpecifier:
1893
- reactDomNames.add(specifier.local.name);
1894
- continue;
1895
- }
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;
1896
1821
  }
1897
- };
1822
+ } };
1898
1823
  }
1899
1824
  function buildFix(context, node) {
1900
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.1",
3
+ "version": "5.20.2",
4
4
  "description": "ESLint React's ESLint plugin for DOM related rules.",
5
5
  "keywords": [
6
6
  "react",
@@ -37,11 +37,12 @@
37
37
  "dist"
38
38
  ],
39
39
  "dependencies": {
40
- "@eslint-react/ast": "5.20.1",
41
- "@eslint-react/core": "5.20.1",
42
- "@eslint-react/eslint": "5.20.1",
43
- "@eslint-react/jsx": "5.20.1",
44
- "@eslint-react/shared": "5.20.1",
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",
45
46
  "@typescript-eslint/types": "^8.70.0",
46
47
  "@typescript-eslint/utils": "^8.70.0",
47
48
  "compare-versions": "^6.1.1"