eslint-plugin-react-dom 5.20.1 → 5.20.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +157 -231
- package/package.json +7 -6
package/dist/index.js
CHANGED
|
@@ -2,8 +2,8 @@ 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";
|
|
6
5
|
import { Check, Extract } from "@eslint-react/ast";
|
|
6
|
+
import { createImportLookup } from "@eslint-react/var";
|
|
7
7
|
import { AST_NODE_TYPES } from "@typescript-eslint/types";
|
|
8
8
|
import { compare } from "compare-versions";
|
|
9
9
|
|
|
@@ -26,7 +26,7 @@ var __exportAll = (all, no_symbols) => {
|
|
|
26
26
|
//#endregion
|
|
27
27
|
//#region package.json
|
|
28
28
|
var name$2 = "eslint-plugin-react-dom";
|
|
29
|
-
var version = "5.20.
|
|
29
|
+
var version = "5.20.3";
|
|
30
30
|
|
|
31
31
|
//#endregion
|
|
32
32
|
//#region src/utils/create-rule.ts
|
|
@@ -53,11 +53,11 @@ function create$15(context) {
|
|
|
53
53
|
if (!context.sourceCode.text.includes("dangerouslySetInnerHTML")) return {};
|
|
54
54
|
return { JSXElement(node) {
|
|
55
55
|
if (!hasAttribute(context, node, "dangerouslySetInnerHTML")) return;
|
|
56
|
-
const
|
|
57
|
-
if (
|
|
56
|
+
const fact = findAttribute(context, node, "children") ?? node.children.find((child) => !isPaddingWhitespace(child));
|
|
57
|
+
if (fact == null) return;
|
|
58
58
|
context.report({
|
|
59
59
|
messageId: "default",
|
|
60
|
-
node:
|
|
60
|
+
node: fact
|
|
61
61
|
});
|
|
62
62
|
} };
|
|
63
63
|
}
|
|
@@ -79,18 +79,17 @@ var no_dangerously_set_innerhtml_default = createRule({
|
|
|
79
79
|
function create$14(context) {
|
|
80
80
|
if (!context.sourceCode.text.includes("dangerouslySetInnerHTML")) return {};
|
|
81
81
|
return { JSXElement(node) {
|
|
82
|
-
const
|
|
83
|
-
if (
|
|
82
|
+
const prop = findAttribute(context, node, "dangerouslySetInnerHTML");
|
|
83
|
+
if (prop == null) return;
|
|
84
84
|
context.report({
|
|
85
85
|
messageId: "default",
|
|
86
|
-
node:
|
|
86
|
+
node: prop
|
|
87
87
|
});
|
|
88
88
|
} };
|
|
89
89
|
}
|
|
90
90
|
|
|
91
91
|
//#endregion
|
|
92
92
|
//#region src/rules/no-find-dom-node/no-find-dom-node.ts
|
|
93
|
-
const isFindDOMNodeCall = core.isAPICall("findDOMNode");
|
|
94
93
|
const RULE_NAME$13 = "no-find-dom-node";
|
|
95
94
|
var no_find_dom_node_default = createRule({
|
|
96
95
|
meta: {
|
|
@@ -105,11 +104,26 @@ var no_find_dom_node_default = createRule({
|
|
|
105
104
|
});
|
|
106
105
|
function create$13(context) {
|
|
107
106
|
if (!context.sourceCode.text.includes("findDOMNode")) return {};
|
|
107
|
+
const imports = createImportLookup(context.sourceCode.ast, {
|
|
108
|
+
builtinNamespaces: ["React", "ReactDOM"],
|
|
109
|
+
source: "react-dom"
|
|
110
|
+
});
|
|
108
111
|
return { CallExpression(node) {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
112
|
+
const callee = Extract.unwrap(node.callee);
|
|
113
|
+
switch (true) {
|
|
114
|
+
case Check.isIdentifier(callee) && (callee.name === "findDOMNode" || imports.has(callee.name, "findDOMNode")):
|
|
115
|
+
context.report({
|
|
116
|
+
messageId: "default",
|
|
117
|
+
node
|
|
118
|
+
});
|
|
119
|
+
return;
|
|
120
|
+
case callee.type === AST_NODE_TYPES.MemberExpression && Check.isIdentifier(callee.object) && Extract.getCalleeName(node) === "findDOMNode" && imports.hasNamespace(callee.object.name):
|
|
121
|
+
context.report({
|
|
122
|
+
messageId: "default",
|
|
123
|
+
node
|
|
124
|
+
});
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
113
127
|
} };
|
|
114
128
|
}
|
|
115
129
|
|
|
@@ -129,41 +143,24 @@ var no_flush_sync_default = createRule({
|
|
|
129
143
|
});
|
|
130
144
|
function create$12(context) {
|
|
131
145
|
if (!context.sourceCode.text.includes("flushSync")) return {};
|
|
132
|
-
const
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
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
|
-
}
|
|
146
|
+
const imports = createImportLookup(context.sourceCode.ast, { source: "react-dom" });
|
|
147
|
+
return { CallExpression(node) {
|
|
148
|
+
const callee = Extract.unwrap(node.callee);
|
|
149
|
+
switch (true) {
|
|
150
|
+
case Check.isIdentifier(callee) && imports.has(callee.name, "flushSync"):
|
|
151
|
+
context.report({
|
|
152
|
+
messageId: "default",
|
|
153
|
+
node
|
|
154
|
+
});
|
|
155
|
+
return;
|
|
156
|
+
case callee.type === AST_NODE_TYPES.MemberExpression && Check.isIdentifier(callee.object) && Extract.getCalleeName(node) === "flushSync" && imports.hasNamespace(callee.object.name):
|
|
157
|
+
context.report({
|
|
158
|
+
messageId: "default",
|
|
159
|
+
node
|
|
160
|
+
});
|
|
161
|
+
return;
|
|
165
162
|
}
|
|
166
|
-
};
|
|
163
|
+
} };
|
|
167
164
|
}
|
|
168
165
|
|
|
169
166
|
//#endregion
|
|
@@ -185,43 +182,26 @@ function create$11(context) {
|
|
|
185
182
|
if (!context.sourceCode.text.includes("hydrate")) return {};
|
|
186
183
|
const settings = getSettingsFromContext(context);
|
|
187
184
|
if (compare(settings.version, "18.0.0", "<")) return {};
|
|
188
|
-
const
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
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
|
-
}
|
|
185
|
+
const imports = createImportLookup(context.sourceCode.ast, { source: "react-dom" });
|
|
186
|
+
return { CallExpression(node) {
|
|
187
|
+
const callee = Extract.unwrap(node.callee);
|
|
188
|
+
switch (true) {
|
|
189
|
+
case Check.isIdentifier(callee) && imports.has(callee.name, "hydrate"):
|
|
190
|
+
context.report({
|
|
191
|
+
fix: buildFix$2(context, node),
|
|
192
|
+
messageId: "default",
|
|
193
|
+
node
|
|
194
|
+
});
|
|
195
|
+
return;
|
|
196
|
+
case callee.type === AST_NODE_TYPES.MemberExpression && Check.isIdentifier(callee.object) && Extract.getCalleeName(node) === "hydrate" && imports.hasNamespace(callee.object.name):
|
|
197
|
+
context.report({
|
|
198
|
+
fix: buildFix$2(context, node),
|
|
199
|
+
messageId: "default",
|
|
200
|
+
node
|
|
201
|
+
});
|
|
202
|
+
return;
|
|
223
203
|
}
|
|
224
|
-
};
|
|
204
|
+
} };
|
|
225
205
|
}
|
|
226
206
|
function buildFix$2(context, node) {
|
|
227
207
|
const getText = (n) => context.sourceCode.getText(n);
|
|
@@ -235,15 +215,17 @@ function buildFix$2(context, node) {
|
|
|
235
215
|
//#endregion
|
|
236
216
|
//#region src/utils/create-jsx-element-resolver.ts
|
|
237
217
|
/**
|
|
238
|
-
* Creates a resolver for JSX elements that determines
|
|
239
|
-
* and the underlying DOM element type.
|
|
218
|
+
* Creates a resolver for JSX elements that determines the underlying DOM element type.
|
|
240
219
|
*
|
|
241
220
|
* This resolver handles:
|
|
242
221
|
* 1. Regular HTML elements (div, span, etc.)
|
|
243
|
-
* 2. Polymorphic components (components that
|
|
222
|
+
* 2. Polymorphic components (components that render as a different element via a prop,
|
|
223
|
+
* ex: `<Box as="a">`). Only static string values of the polymorphic prop are
|
|
224
|
+
* supported; `<Box as={Component}>` cannot be resolved statically and falls back
|
|
225
|
+
* to the JSX element's own name.
|
|
244
226
|
*
|
|
245
227
|
* @param context The ESLint rule context
|
|
246
|
-
* @returns An object with a resolve method to determine element
|
|
228
|
+
* @returns An object with a resolve method to determine the element type
|
|
247
229
|
*/
|
|
248
230
|
function createJsxElementResolver(context) {
|
|
249
231
|
const { polymorphicPropName } = getSettingsFromContext(context);
|
|
@@ -260,11 +242,12 @@ resolve(node) {
|
|
|
260
242
|
domElementType: elementName,
|
|
261
243
|
jsxElementType: elementName
|
|
262
244
|
};
|
|
263
|
-
|
|
245
|
+
const nameNode = node.openingElement.name;
|
|
246
|
+
if (nameNode.type === AST_NODE_TYPES.JSXIdentifier && nameNode.name.charAt(0) === nameNode.name.charAt(0).toLowerCase()) return result;
|
|
264
247
|
const polyPropValue = getAttributeStaticValue(context, node, polymorphicPropName);
|
|
265
248
|
if (typeof polyPropValue === "string") return {
|
|
266
249
|
...result,
|
|
267
|
-
domElementType: polyPropValue
|
|
250
|
+
domElementType: polyPropValue.toLowerCase()
|
|
268
251
|
};
|
|
269
252
|
return result;
|
|
270
253
|
} };
|
|
@@ -296,14 +279,17 @@ var no_missing_button_type_default = createRule({
|
|
|
296
279
|
function create$10(context) {
|
|
297
280
|
const resolver = createJsxElementResolver(context);
|
|
298
281
|
return { JSXElement(node) {
|
|
299
|
-
|
|
282
|
+
const { domElementType } = resolver.resolve(node);
|
|
283
|
+
if (domElementType !== "button") return;
|
|
300
284
|
if (hasAttribute(context, node, "type")) return;
|
|
301
285
|
context.report({
|
|
302
286
|
messageId: "missingTypeAttribute",
|
|
303
287
|
node: node.openingElement,
|
|
304
288
|
suggest: BUTTON_TYPES.map((type) => ({
|
|
305
289
|
data: { type },
|
|
306
|
-
fix
|
|
290
|
+
fix(fixer) {
|
|
291
|
+
return fixer.insertTextAfter(node.openingElement.name, ` type="${type}"`);
|
|
292
|
+
},
|
|
307
293
|
messageId: "addTypeAttribute"
|
|
308
294
|
}))
|
|
309
295
|
});
|
|
@@ -369,18 +355,13 @@ function create$9(context) {
|
|
|
369
355
|
//#endregion
|
|
370
356
|
//#region src/rules/no-render-return-value/no-render-return-value.ts
|
|
371
357
|
const RULE_NAME$8 = "no-render-return-value";
|
|
372
|
-
const
|
|
358
|
+
const BANNED_PARENT_TYPES = [
|
|
373
359
|
AST_NODE_TYPES.VariableDeclarator,
|
|
374
360
|
AST_NODE_TYPES.Property,
|
|
375
361
|
AST_NODE_TYPES.ReturnStatement,
|
|
376
362
|
AST_NODE_TYPES.ArrowFunctionExpression,
|
|
377
363
|
AST_NODE_TYPES.AssignmentExpression
|
|
378
364
|
];
|
|
379
|
-
function isReturnValueUsed(node) {
|
|
380
|
-
let parent = node.parent;
|
|
381
|
-
while (Check.isTypeExpression(parent)) parent = parent.parent;
|
|
382
|
-
return banParentTypes.includes(parent.type);
|
|
383
|
-
}
|
|
384
365
|
var no_render_return_value_default = createRule({
|
|
385
366
|
meta: {
|
|
386
367
|
type: "problem",
|
|
@@ -393,41 +374,33 @@ var no_render_return_value_default = createRule({
|
|
|
393
374
|
defaultOptions: []
|
|
394
375
|
});
|
|
395
376
|
function create$8(context) {
|
|
396
|
-
|
|
397
|
-
const
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
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
|
-
}
|
|
377
|
+
if (!context.sourceCode.text.includes("render")) return {};
|
|
378
|
+
const imports = createImportLookup(context.sourceCode.ast, {
|
|
379
|
+
builtinNamespaces: ["ReactDOM"],
|
|
380
|
+
source: "react-dom"
|
|
381
|
+
});
|
|
382
|
+
return { CallExpression(node) {
|
|
383
|
+
const callee = Extract.unwrap(node.callee);
|
|
384
|
+
switch (true) {
|
|
385
|
+
case Check.isIdentifier(callee) && imports.has(callee.name, "render") && isReturnValueUsed(node):
|
|
386
|
+
context.report({
|
|
387
|
+
messageId: "default",
|
|
388
|
+
node
|
|
389
|
+
});
|
|
390
|
+
return;
|
|
391
|
+
case callee.type === AST_NODE_TYPES.MemberExpression && Check.isIdentifier(callee.object) && Extract.getCalleeName(node) === "render" && imports.hasNamespace(callee.object.name) && isReturnValueUsed(node):
|
|
392
|
+
context.report({
|
|
393
|
+
messageId: "default",
|
|
394
|
+
node
|
|
395
|
+
});
|
|
396
|
+
return;
|
|
429
397
|
}
|
|
430
|
-
};
|
|
398
|
+
} };
|
|
399
|
+
}
|
|
400
|
+
function isReturnValueUsed(node) {
|
|
401
|
+
let parent = node.parent;
|
|
402
|
+
while (Check.isTypeExpression(parent)) parent = parent.parent;
|
|
403
|
+
return BANNED_PARENT_TYPES.includes(parent.type);
|
|
431
404
|
}
|
|
432
405
|
|
|
433
406
|
//#endregion
|
|
@@ -449,50 +422,30 @@ function create$7(context) {
|
|
|
449
422
|
if (!context.sourceCode.text.includes("render")) return {};
|
|
450
423
|
const settings = getSettingsFromContext(context);
|
|
451
424
|
if (compare(settings.version, "18.0.0", "<")) return {};
|
|
452
|
-
const
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
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
|
-
}
|
|
425
|
+
const imports = createImportLookup(context.sourceCode.ast, {
|
|
426
|
+
builtinNamespaces: ["ReactDOM"],
|
|
427
|
+
source: "react-dom"
|
|
428
|
+
});
|
|
429
|
+
return { CallExpression(node) {
|
|
430
|
+
const callee = Extract.unwrap(node.callee);
|
|
431
|
+
switch (true) {
|
|
432
|
+
case Check.isIdentifier(callee) && imports.has(callee.name, "render"):
|
|
433
|
+
context.report({
|
|
434
|
+
fix: buildFix$1(context, node),
|
|
435
|
+
messageId: "default",
|
|
436
|
+
node
|
|
437
|
+
});
|
|
438
|
+
return;
|
|
439
|
+
case callee.type === AST_NODE_TYPES.MemberExpression && Check.isIdentifier(callee.object) && Extract.getCalleeName(node) === "render" && imports.hasNamespace(callee.object.name):
|
|
440
|
+
context.report({
|
|
441
|
+
fix: buildFix$1(context, node),
|
|
442
|
+
messageId: "default",
|
|
443
|
+
node
|
|
444
|
+
});
|
|
445
|
+
return;
|
|
487
446
|
}
|
|
488
|
-
};
|
|
447
|
+
} };
|
|
489
448
|
}
|
|
490
|
-
/**
|
|
491
|
-
* Provides a fixer function to replace `render(app, container)` with `createRoot(container).render(app)`
|
|
492
|
-
* @param context The rule context
|
|
493
|
-
* @param node The `CallExpression` node to fix
|
|
494
|
-
* @returns A fixer function or null if the fix cannot be applied
|
|
495
|
-
*/
|
|
496
449
|
function buildFix$1(context, node) {
|
|
497
450
|
const getText = (n) => context.sourceCode.getText(n);
|
|
498
451
|
return (fixer) => {
|
|
@@ -547,7 +500,8 @@ function create$5(context) {
|
|
|
547
500
|
const styleProp = findAttribute(context, node, "style");
|
|
548
501
|
if (styleProp == null) return;
|
|
549
502
|
const styleValue = resolveAttributeValue(context, styleProp);
|
|
550
|
-
if (typeof styleValue.toStatic()
|
|
503
|
+
if (typeof styleValue.toStatic() !== "string") return;
|
|
504
|
+
context.report({
|
|
551
505
|
messageId: "default",
|
|
552
506
|
node: styleValue.node ?? styleProp
|
|
553
507
|
});
|
|
@@ -1637,23 +1591,10 @@ var no_unknown_property_default = createRule({
|
|
|
1637
1591
|
create: create$4,
|
|
1638
1592
|
defaultOptions: []
|
|
1639
1593
|
});
|
|
1640
|
-
/**
|
|
1641
|
-
* Create function for the ESLint rule
|
|
1642
|
-
* @param context ESLint rule context
|
|
1643
|
-
* @returns Rule listener
|
|
1644
|
-
*/
|
|
1645
1594
|
function create$4(context) {
|
|
1646
|
-
/**
|
|
1647
|
-
* Gets the ignore configuration from rule options
|
|
1648
|
-
* @returns Array of attribute names to ignore
|
|
1649
|
-
*/
|
|
1650
1595
|
function getIgnoreConfig() {
|
|
1651
1596
|
return context.options[0]?.ignore ?? DEFAULTS.ignore;
|
|
1652
1597
|
}
|
|
1653
|
-
/**
|
|
1654
|
-
* Gets the requireDataLowercase option from rule options
|
|
1655
|
-
* @returns Whether data attributes must be lowercase
|
|
1656
|
-
*/
|
|
1657
1598
|
function getRequireDataLowercase() {
|
|
1658
1599
|
return context.options[0]?.requireDataLowercase ?? DEFAULTS.requireDataLowercase;
|
|
1659
1600
|
}
|
|
@@ -1749,11 +1690,13 @@ var no_unsafe_iframe_sandbox_default = createRule({
|
|
|
1749
1690
|
function create$3(context) {
|
|
1750
1691
|
const resolver = createJsxElementResolver(context);
|
|
1751
1692
|
return { JSXElement(node) {
|
|
1752
|
-
|
|
1693
|
+
const { domElementType } = resolver.resolve(node);
|
|
1694
|
+
if (domElementType !== "iframe") return;
|
|
1753
1695
|
const sandboxProp = findAttribute(context, node, "sandbox");
|
|
1754
1696
|
if (sandboxProp == null) return;
|
|
1755
1697
|
const sandboxValue = resolveAttributeValue(context, sandboxProp, "sandbox");
|
|
1756
|
-
if (isUnsafeSandboxCombination(sandboxValue.toStatic()))
|
|
1698
|
+
if (!isUnsafeSandboxCombination(sandboxValue.toStatic())) return;
|
|
1699
|
+
context.report({
|
|
1757
1700
|
messageId: "default",
|
|
1758
1701
|
node: sandboxValue.node ?? sandboxProp
|
|
1759
1702
|
});
|
|
@@ -1808,8 +1751,8 @@ function create$2(context) {
|
|
|
1808
1751
|
const { domElementType } = resolver.resolve(node);
|
|
1809
1752
|
if (domElementType !== "a") return;
|
|
1810
1753
|
if (getAttributeStaticValue(context, node, "target") !== "_blank") return;
|
|
1811
|
-
const
|
|
1812
|
-
if (!isExternalLinkLike(
|
|
1754
|
+
const hrefValue = getAttributeStaticValue(context, node, "href");
|
|
1755
|
+
if (!isExternalLinkLike(hrefValue)) return;
|
|
1813
1756
|
const relProp = findAttribute(context, node, "rel");
|
|
1814
1757
|
if (relProp == null) {
|
|
1815
1758
|
context.report({
|
|
@@ -1824,8 +1767,8 @@ function create$2(context) {
|
|
|
1824
1767
|
});
|
|
1825
1768
|
return;
|
|
1826
1769
|
}
|
|
1827
|
-
const
|
|
1828
|
-
if (isSafeRel(
|
|
1770
|
+
const relValue = getAttributeStaticValue(context, node, "rel");
|
|
1771
|
+
if (isSafeRel(relValue)) return;
|
|
1829
1772
|
context.report({
|
|
1830
1773
|
messageId: "default",
|
|
1831
1774
|
node: relProp,
|
|
@@ -1858,43 +1801,26 @@ function create$1(context) {
|
|
|
1858
1801
|
if (!context.sourceCode.text.includes("useFormState")) return {};
|
|
1859
1802
|
const settings = getSettingsFromContext(context);
|
|
1860
1803
|
if (compare(settings.version, "19.0.0", "<")) return {};
|
|
1861
|
-
const
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
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
|
-
}
|
|
1804
|
+
const imports = createImportLookup(context.sourceCode.ast, { source: "react-dom" });
|
|
1805
|
+
return { CallExpression(node) {
|
|
1806
|
+
const callee = Extract.unwrap(node.callee);
|
|
1807
|
+
switch (true) {
|
|
1808
|
+
case Check.isIdentifier(callee) && imports.has(callee.name, "useFormState"):
|
|
1809
|
+
context.report({
|
|
1810
|
+
fix: buildFix(context, node),
|
|
1811
|
+
messageId: "default",
|
|
1812
|
+
node
|
|
1813
|
+
});
|
|
1814
|
+
return;
|
|
1815
|
+
case callee.type === AST_NODE_TYPES.MemberExpression && Check.isIdentifier(callee.object) && Extract.getCalleeName(node) === "useFormState" && imports.hasNamespace(callee.object.name):
|
|
1816
|
+
context.report({
|
|
1817
|
+
fix: buildFix(context, node),
|
|
1818
|
+
messageId: "default",
|
|
1819
|
+
node
|
|
1820
|
+
});
|
|
1821
|
+
return;
|
|
1896
1822
|
}
|
|
1897
|
-
};
|
|
1823
|
+
} };
|
|
1898
1824
|
}
|
|
1899
1825
|
function buildFix(context, node) {
|
|
1900
1826
|
const { importSource } = getSettingsFromContext(context);
|
|
@@ -1905,7 +1831,7 @@ function buildFix(context, node) {
|
|
|
1905
1831
|
|
|
1906
1832
|
//#endregion
|
|
1907
1833
|
//#region src/rules/no-void-elements-with-children/lib.ts
|
|
1908
|
-
const
|
|
1834
|
+
const KNOWN_VOID_ELEMENTS = /* @__PURE__ */ new Set([
|
|
1909
1835
|
"area",
|
|
1910
1836
|
"base",
|
|
1911
1837
|
"br",
|
|
@@ -1942,7 +1868,7 @@ function create(context) {
|
|
|
1942
1868
|
const resolver = createJsxElementResolver(context);
|
|
1943
1869
|
return { JSXElement(node) {
|
|
1944
1870
|
const { domElementType } = resolver.resolve(node);
|
|
1945
|
-
if (!
|
|
1871
|
+
if (!KNOWN_VOID_ELEMENTS.has(domElementType)) return;
|
|
1946
1872
|
if (node.children.length > 0 || hasAnyAttribute(context, node, ["children", "dangerouslySetInnerHTML"])) context.report({
|
|
1947
1873
|
data: { elementType: domElementType },
|
|
1948
1874
|
messageId: "default",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eslint-plugin-react-dom",
|
|
3
|
-
"version": "5.20.
|
|
3
|
+
"version": "5.20.3",
|
|
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.
|
|
41
|
-
"@eslint-react/core": "5.20.
|
|
42
|
-
"@eslint-react/eslint": "5.20.
|
|
43
|
-
"@eslint-react/jsx": "5.20.
|
|
44
|
-
"@eslint-react/shared": "5.20.
|
|
40
|
+
"@eslint-react/ast": "5.20.3",
|
|
41
|
+
"@eslint-react/core": "5.20.3",
|
|
42
|
+
"@eslint-react/eslint": "5.20.3",
|
|
43
|
+
"@eslint-react/jsx": "5.20.3",
|
|
44
|
+
"@eslint-react/shared": "5.20.3",
|
|
45
|
+
"@eslint-react/var": "5.20.3",
|
|
45
46
|
"@typescript-eslint/types": "^8.70.0",
|
|
46
47
|
"@typescript-eslint/utils": "^8.70.0",
|
|
47
48
|
"compare-versions": "^6.1.1"
|