eslint-plugin-react-jsx 5.16.0 → 5.16.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.
- package/dist/index.js +81 -144
- package/package.json +6 -6
package/dist/index.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { DEFAULT_ESLINT_REACT_SETTINGS } from "@eslint-react/shared";
|
|
2
|
+
import { Check, Extract } from "@eslint-react/ast";
|
|
3
|
+
import { AST_NODE_TYPES } from "@typescript-eslint/types";
|
|
2
4
|
import { ESLintUtils } from "@typescript-eslint/utils";
|
|
3
5
|
import * as core from "@eslint-react/core";
|
|
4
6
|
import "@eslint-react/eslint";
|
|
5
7
|
import { collapseMultilineText, findAttribute, getChildren, getElementFullType, hasAnyAttribute, hasChildren, isFragmentElement, isHostElement, isWhitespaceText } from "@eslint-react/jsx";
|
|
6
|
-
import { AST_NODE_TYPES } from "@typescript-eslint/types";
|
|
7
|
-
import { Check, Extract } from "@eslint-react/ast";
|
|
8
8
|
import ts from "typescript";
|
|
9
9
|
|
|
10
10
|
//#region \0rolldown/runtime.js
|
|
@@ -26,58 +26,53 @@ var __exportAll = (all, no_symbols) => {
|
|
|
26
26
|
//#endregion
|
|
27
27
|
//#region package.json
|
|
28
28
|
var name$2 = "eslint-plugin-react-jsx";
|
|
29
|
-
var version = "5.16.
|
|
29
|
+
var version = "5.16.1";
|
|
30
30
|
|
|
31
31
|
//#endregion
|
|
32
|
-
//#region src/utils/
|
|
33
|
-
function
|
|
34
|
-
return
|
|
35
|
-
}
|
|
36
|
-
const createRule = ESLintUtils.RuleCreator(getDocsUrl);
|
|
37
|
-
|
|
38
|
-
//#endregion
|
|
39
|
-
//#region src/rules/no-children-prop-with-children/lib.ts
|
|
40
|
-
/**
|
|
41
|
-
* Find a `children` property inside an ObjectExpression.
|
|
42
|
-
* @param node The object expression.
|
|
43
|
-
* @returns The Property node whose key is "children", or null.
|
|
44
|
-
*/
|
|
45
|
-
function findChildrenProperty$1(node) {
|
|
46
|
-
for (const prop of node.properties) {
|
|
47
|
-
if (prop.type !== AST_NODE_TYPES.Property) continue;
|
|
48
|
-
const key = Extract.unwrap(prop.key);
|
|
49
|
-
if (key.type === AST_NODE_TYPES.Identifier && key.name === "children") return prop;
|
|
50
|
-
if (key.type === AST_NODE_TYPES.Literal && key.value === "children") return prop;
|
|
51
|
-
}
|
|
32
|
+
//#region src/utils/common.ts
|
|
33
|
+
function findChildrenProperty(node) {
|
|
34
|
+
for (const prop of node.properties) if (prop.type === AST_NODE_TYPES.Property && Extract.getStaticPropertyName(prop) === "children") return prop;
|
|
52
35
|
return null;
|
|
53
36
|
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
function
|
|
37
|
+
function getChildrenContentRange(node) {
|
|
38
|
+
const first = node.children.at(0);
|
|
39
|
+
const last = node.children.at(-1);
|
|
40
|
+
if (first == null || last == null) return null;
|
|
41
|
+
return [first.range[0], last.range[1]];
|
|
42
|
+
}
|
|
43
|
+
function getChildrenPropText(context, prop) {
|
|
44
|
+
const { sourceCode } = context;
|
|
45
|
+
const { value } = prop;
|
|
46
|
+
if (value == null) return null;
|
|
47
|
+
if (value.type === AST_NODE_TYPES.Literal) return escapeJsxText(String(value.value));
|
|
48
|
+
if (value.type !== AST_NODE_TYPES.JSXExpressionContainer) return null;
|
|
49
|
+
const { expression } = value;
|
|
50
|
+
if (expression.type === AST_NODE_TYPES.JSXEmptyExpression) return null;
|
|
51
|
+
const exprText = sourceCode.getText(expression);
|
|
52
|
+
if (Check.isJSXElementOrFragment(expression)) return exprText;
|
|
53
|
+
return `{${exprText}}`;
|
|
54
|
+
}
|
|
55
|
+
function escapeJsxText(text) {
|
|
56
|
+
return text.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll("{", "{").replaceAll("}", "}");
|
|
57
|
+
}
|
|
58
|
+
function getPropRemovalRange(context, prop) {
|
|
61
59
|
const { sourceCode } = context;
|
|
62
60
|
let start = prop.range[0];
|
|
63
61
|
const end = prop.range[1];
|
|
64
|
-
while (start > 0 && /\s/.test(sourceCode.text[start - 1])) start--;
|
|
62
|
+
while (start > 0 && /\s/.test(sourceCode.text[start - 1] ?? "")) start--;
|
|
65
63
|
return [start, end];
|
|
66
64
|
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
* fragment (from the start of the first child to the end of the last child).
|
|
70
|
-
*
|
|
71
|
-
* Returns `null` when there are no children at all.
|
|
72
|
-
* @param node The JSX element or fragment.
|
|
73
|
-
*/
|
|
74
|
-
function getChildrenContentRange(node) {
|
|
75
|
-
if (node.children.length === 0) return null;
|
|
76
|
-
const first = node.children[0];
|
|
77
|
-
const last = node.children[node.children.length - 1];
|
|
78
|
-
return [first.range[0], last.range[1]];
|
|
65
|
+
function isDirectJsxChild(node) {
|
|
66
|
+
return Check.isJSXElementOrFragment(node.parent);
|
|
79
67
|
}
|
|
80
68
|
|
|
69
|
+
//#endregion
|
|
70
|
+
//#region src/utils/create-rule.ts
|
|
71
|
+
function getDocsUrl(ruleName) {
|
|
72
|
+
return `https://eslint-react.xyz/docs/rules/jsx-${ruleName}`;
|
|
73
|
+
}
|
|
74
|
+
const createRule = ESLintUtils.RuleCreator(getDocsUrl);
|
|
75
|
+
|
|
81
76
|
//#endregion
|
|
82
77
|
//#region src/rules/no-children-prop-with-children/no-children-prop-with-children.ts
|
|
83
78
|
const RULE_NAME$7 = "no-children-prop-with-children";
|
|
@@ -103,8 +98,10 @@ function create$7(context) {
|
|
|
103
98
|
CallExpression(node) {
|
|
104
99
|
if (!core.isCreateElementCall(context, node)) return;
|
|
105
100
|
const [, propsArg, firstExtra] = node.arguments;
|
|
106
|
-
if (propsArg == null
|
|
107
|
-
const
|
|
101
|
+
if (propsArg == null) return;
|
|
102
|
+
const propsObject = Extract.unwrap(propsArg);
|
|
103
|
+
if (propsObject.type !== AST_NODE_TYPES.ObjectExpression) return;
|
|
104
|
+
const childrenProp = findChildrenProperty(propsObject);
|
|
108
105
|
if (childrenProp == null) return;
|
|
109
106
|
if (firstExtra == null) return;
|
|
110
107
|
context.report({
|
|
@@ -128,7 +125,7 @@ function create$7(context) {
|
|
|
128
125
|
node: childrenProp,
|
|
129
126
|
suggest: [{
|
|
130
127
|
fix(fixer) {
|
|
131
|
-
const [start, end] = getPropRemovalRange
|
|
128
|
+
const [start, end] = getPropRemovalRange(context, childrenProp);
|
|
132
129
|
return fixer.removeRange([start, end]);
|
|
133
130
|
},
|
|
134
131
|
messageId: "removeChildrenProp"
|
|
@@ -145,61 +142,6 @@ function create$7(context) {
|
|
|
145
142
|
};
|
|
146
143
|
}
|
|
147
144
|
|
|
148
|
-
//#endregion
|
|
149
|
-
//#region src/rules/no-children-prop/lib.ts
|
|
150
|
-
/**
|
|
151
|
-
* Find a `children` property inside an ObjectExpression.
|
|
152
|
-
* @param node The object expression.
|
|
153
|
-
* @returns The Property node whose key is "children", or null.
|
|
154
|
-
*/
|
|
155
|
-
function findChildrenProperty(node) {
|
|
156
|
-
for (const prop of node.properties) {
|
|
157
|
-
if (prop.type !== AST_NODE_TYPES.Property) continue;
|
|
158
|
-
const key = Extract.unwrap(prop.key);
|
|
159
|
-
if (key.type === AST_NODE_TYPES.Identifier && key.name === "children") return prop;
|
|
160
|
-
if (key.type === AST_NODE_TYPES.Literal && key.value === "children") return prop;
|
|
161
|
-
}
|
|
162
|
-
return null;
|
|
163
|
-
}
|
|
164
|
-
/**
|
|
165
|
-
* Compute the removal range for a JSX attribute, consuming any leading
|
|
166
|
-
* whitespace (spaces, tabs, newlines) so the resulting markup stays clean.
|
|
167
|
-
* @param context The rule context.
|
|
168
|
-
* @param prop The JSX attribute.
|
|
169
|
-
*/
|
|
170
|
-
function getPropRemovalRange(context, prop) {
|
|
171
|
-
const { sourceCode } = context;
|
|
172
|
-
let start = prop.range[0];
|
|
173
|
-
const end = prop.range[1];
|
|
174
|
-
while (start > 0 && /\s/.test(sourceCode.text[start - 1])) start--;
|
|
175
|
-
return [start, end];
|
|
176
|
-
}
|
|
177
|
-
/**
|
|
178
|
-
* Extract the text to use as JSX children content from a `children` prop.
|
|
179
|
-
*
|
|
180
|
-
* - `children="text"` -> `text` (raw string, no quotes)
|
|
181
|
-
* - `children={<div />}` -> `<div />` (JSX element, no braces)
|
|
182
|
-
* - `children={<>…</>}` -> `<>…</>` (JSX fragment, no braces)
|
|
183
|
-
* - `children={expression}` -> `{expression}` (wrapped in braces)
|
|
184
|
-
* - `children` -> `null` (boolean shorthand, cannot extract)
|
|
185
|
-
* @param context The rule context.
|
|
186
|
-
* @param prop The JSX attribute.
|
|
187
|
-
*/
|
|
188
|
-
function getChildrenPropText(context, prop) {
|
|
189
|
-
const { sourceCode } = context;
|
|
190
|
-
const { value } = prop;
|
|
191
|
-
if (value == null) return null;
|
|
192
|
-
if (value.type === AST_NODE_TYPES.Literal) return String(value.value);
|
|
193
|
-
if (value.type === AST_NODE_TYPES.JSXExpressionContainer) {
|
|
194
|
-
const { expression } = value;
|
|
195
|
-
if (expression.type === AST_NODE_TYPES.JSXEmptyExpression) return null;
|
|
196
|
-
const exprText = sourceCode.getText(expression);
|
|
197
|
-
if (Check.isJSXElementOrFragment(expression)) return exprText;
|
|
198
|
-
return `{${exprText}}`;
|
|
199
|
-
}
|
|
200
|
-
return null;
|
|
201
|
-
}
|
|
202
|
-
|
|
203
145
|
//#endregion
|
|
204
146
|
//#region src/rules/no-children-prop/no-children-prop.ts
|
|
205
147
|
const RULE_NAME$6 = "no-children-prop";
|
|
@@ -224,8 +166,10 @@ function create$6(context) {
|
|
|
224
166
|
CallExpression(node) {
|
|
225
167
|
if (!core.isCreateElementCall(context, node)) return;
|
|
226
168
|
const [, propsArg] = node.arguments;
|
|
227
|
-
if (propsArg == null
|
|
228
|
-
const
|
|
169
|
+
if (propsArg == null) return;
|
|
170
|
+
const propsObject = Extract.unwrap(propsArg);
|
|
171
|
+
if (propsObject.type !== AST_NODE_TYPES.ObjectExpression) return;
|
|
172
|
+
const childrenProp = findChildrenProperty(propsObject);
|
|
229
173
|
if (childrenProp == null) return;
|
|
230
174
|
context.report({
|
|
231
175
|
messageId: "default",
|
|
@@ -291,18 +235,14 @@ var no_comment_textnodes_default = createRule({
|
|
|
291
235
|
defaultOptions: []
|
|
292
236
|
});
|
|
293
237
|
function create$5(context) {
|
|
294
|
-
function
|
|
295
|
-
if (
|
|
296
|
-
|
|
297
|
-
}
|
|
298
|
-
const visit = (node) => {
|
|
299
|
-
if (!Check.isJSXElementOrFragment(node.parent)) return;
|
|
300
|
-
if (!hasCommentLike(node)) return;
|
|
238
|
+
function visit(node) {
|
|
239
|
+
if (!isDirectJsxChild(node)) return;
|
|
240
|
+
if (!/^\s*\/(?:\/|\*)/mu.test(context.sourceCode.getText(node))) return;
|
|
301
241
|
context.report({
|
|
302
242
|
messageId: "default",
|
|
303
243
|
node
|
|
304
244
|
});
|
|
305
|
-
}
|
|
245
|
+
}
|
|
306
246
|
return {
|
|
307
247
|
JSXText: visit,
|
|
308
248
|
Literal: visit
|
|
@@ -366,34 +306,30 @@ function create$3(context) {
|
|
|
366
306
|
* Visitor function for JSXElement and JSXFragment nodes
|
|
367
307
|
* @param node The JSXElement or JSXFragment node to be checked
|
|
368
308
|
*/
|
|
369
|
-
|
|
309
|
+
function visit(node) {
|
|
370
310
|
for (const [index, child] of node.children.entries()) {
|
|
371
311
|
if (child.type !== AST_NODE_TYPES.JSXText || !child.value.endsWith("$")) continue;
|
|
372
312
|
if (node.children[index + 1]?.type !== AST_NODE_TYPES.JSXExpressionContainer) continue;
|
|
373
313
|
if (child.value === "$" && node.children.length === 2) continue;
|
|
374
|
-
|
|
314
|
+
if (!context.sourceCode.getText(child).endsWith("$")) continue;
|
|
315
|
+
const dollarStart = child.range[1] - 1;
|
|
316
|
+
const dollarEnd = child.range[1];
|
|
375
317
|
context.report({
|
|
376
318
|
loc: {
|
|
377
|
-
end:
|
|
378
|
-
|
|
379
|
-
line: pos.line
|
|
380
|
-
},
|
|
381
|
-
start: {
|
|
382
|
-
column: pos.column - 1,
|
|
383
|
-
line: pos.line
|
|
384
|
-
}
|
|
319
|
+
end: context.sourceCode.getLocFromIndex(dollarEnd),
|
|
320
|
+
start: context.sourceCode.getLocFromIndex(dollarStart)
|
|
385
321
|
},
|
|
386
322
|
messageId: "default",
|
|
387
323
|
node: child,
|
|
388
324
|
suggest: [{
|
|
389
325
|
fix(fixer) {
|
|
390
|
-
return fixer.removeRange([
|
|
326
|
+
return fixer.removeRange([dollarStart, dollarEnd]);
|
|
391
327
|
},
|
|
392
328
|
messageId: "removeDollarSign"
|
|
393
329
|
}]
|
|
394
330
|
});
|
|
395
331
|
}
|
|
396
|
-
}
|
|
332
|
+
}
|
|
397
333
|
return {
|
|
398
334
|
JSXElement: visit,
|
|
399
335
|
JSXFragment: visit
|
|
@@ -419,23 +355,25 @@ var no_leaked_semicolon_default = createRule({
|
|
|
419
355
|
defaultOptions: []
|
|
420
356
|
});
|
|
421
357
|
function hasLeakedSemicolon(text) {
|
|
422
|
-
return
|
|
358
|
+
return /^;[ \t]*(?:\r\n|\r|\n)/u.test(text);
|
|
423
359
|
}
|
|
424
360
|
function create$2(context) {
|
|
425
|
-
|
|
426
|
-
if (!
|
|
361
|
+
function visit(node) {
|
|
362
|
+
if (!isDirectJsxChild(node)) return;
|
|
427
363
|
if (!hasLeakedSemicolon(context.sourceCode.getText(node))) return;
|
|
364
|
+
const semicolonStart = node.range[0];
|
|
365
|
+
const semicolonEnd = node.range[0] + 1;
|
|
428
366
|
context.report({
|
|
429
367
|
messageId: "default",
|
|
430
368
|
node,
|
|
431
369
|
suggest: [{
|
|
432
370
|
fix(fixer) {
|
|
433
|
-
return fixer.removeRange([
|
|
371
|
+
return fixer.removeRange([semicolonStart, semicolonEnd]);
|
|
434
372
|
},
|
|
435
373
|
messageId: "removeSemicolon"
|
|
436
374
|
}]
|
|
437
375
|
});
|
|
438
|
-
}
|
|
376
|
+
}
|
|
439
377
|
return {
|
|
440
378
|
JSXText: visit,
|
|
441
379
|
Literal: visit
|
|
@@ -459,7 +397,7 @@ var no_namespace_default = createRule({
|
|
|
459
397
|
function create$1(context) {
|
|
460
398
|
return { JSXElement(node) {
|
|
461
399
|
const name = getElementFullType(node);
|
|
462
|
-
if (
|
|
400
|
+
if (!name.includes(":")) return;
|
|
463
401
|
context.report({
|
|
464
402
|
data: { name },
|
|
465
403
|
messageId: "noNamespace",
|
|
@@ -513,30 +451,24 @@ function create(context, [option]) {
|
|
|
513
451
|
function isContentUseless(node) {
|
|
514
452
|
if (node.children.length === 0) return !allowEmptyFragment;
|
|
515
453
|
const insideJsx = Check.isJSXElementOrFragment(node.parent);
|
|
516
|
-
if (!
|
|
517
|
-
if (insideJsx) return true;
|
|
518
|
-
if (node.children.length === 1) return true;
|
|
519
|
-
}
|
|
520
|
-
if (allowExpressions && !insideJsx && node.children.length === 1) {
|
|
521
|
-
const child = node.children[0];
|
|
522
|
-
if (child != null && child.type === AST_NODE_TYPES.JSXText) return false;
|
|
523
|
-
}
|
|
454
|
+
if (allowExpressions && !insideJsx && node.children.length === 1 && node.children[0]?.type === AST_NODE_TYPES.JSXText) return false;
|
|
524
455
|
const meaningful = getChildren(node);
|
|
525
456
|
if (meaningful.length === 0) return true;
|
|
526
|
-
if (meaningful.length
|
|
527
|
-
return
|
|
457
|
+
if (meaningful.length > 1) return false;
|
|
458
|
+
if (meaningful[0]?.type === AST_NODE_TYPES.JSXExpressionContainer) return !allowExpressions;
|
|
459
|
+
return true;
|
|
528
460
|
}
|
|
529
461
|
/**
|
|
530
462
|
* Whether it is safe to auto-fix the fragment by unwrapping it.
|
|
531
463
|
* @param node The fragment node to check.
|
|
532
464
|
*/
|
|
533
465
|
function isSafeToFix(node) {
|
|
466
|
+
if (node.type === AST_NODE_TYPES.JSXElement && node.openingElement.attributes.some((attr) => attr.type === AST_NODE_TYPES.JSXSpreadAttribute)) return false;
|
|
534
467
|
if (Check.isJSXElementOrFragment(node.parent)) return isHostElement(node.parent);
|
|
535
468
|
if (node.children.length === 0) return false;
|
|
536
|
-
return
|
|
537
|
-
if (child
|
|
538
|
-
|
|
539
|
-
return false;
|
|
469
|
+
return node.children.every((child) => {
|
|
470
|
+
if (Check.isJSXElementOrFragment(child)) return true;
|
|
471
|
+
return child.type === AST_NODE_TYPES.JSXText && isWhitespaceText(child);
|
|
540
472
|
});
|
|
541
473
|
}
|
|
542
474
|
/**
|
|
@@ -552,7 +484,12 @@ function create(context, [option]) {
|
|
|
552
484
|
const cleaned = collapseMultilineText(child.value);
|
|
553
485
|
if (cleaned != null) text += cleaned;
|
|
554
486
|
} else text += context.sourceCode.getText(child);
|
|
555
|
-
|
|
487
|
+
let start = node.range[0];
|
|
488
|
+
if (text === "" && node.children.length > 0) {
|
|
489
|
+
const lineStart = Math.max(context.sourceCode.text.lastIndexOf("\n", start - 1), context.sourceCode.text.lastIndexOf("\r", start - 1)) + 1;
|
|
490
|
+
if (/^[ \t]*$/u.test(context.sourceCode.text.slice(lineStart, start))) start = lineStart;
|
|
491
|
+
}
|
|
492
|
+
return fixer.replaceTextRange([start, node.range[1]], text);
|
|
556
493
|
};
|
|
557
494
|
}
|
|
558
495
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eslint-plugin-react-jsx",
|
|
3
|
-
"version": "5.16.
|
|
3
|
+
"version": "5.16.1",
|
|
4
4
|
"description": "ESLint React's ESLint plugin for React Flavored JSX rules.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react",
|
|
@@ -39,11 +39,11 @@
|
|
|
39
39
|
"dependencies": {
|
|
40
40
|
"@typescript-eslint/types": "^8.64.0",
|
|
41
41
|
"@typescript-eslint/utils": "^8.64.0",
|
|
42
|
-
"@eslint-react/
|
|
43
|
-
"@eslint-react/
|
|
44
|
-
"@eslint-react/
|
|
45
|
-
"@eslint-react/shared": "5.16.
|
|
46
|
-
"@eslint-react/
|
|
42
|
+
"@eslint-react/ast": "5.16.1",
|
|
43
|
+
"@eslint-react/eslint": "5.16.1",
|
|
44
|
+
"@eslint-react/jsx": "5.16.1",
|
|
45
|
+
"@eslint-react/shared": "5.16.1",
|
|
46
|
+
"@eslint-react/core": "5.16.1"
|
|
47
47
|
},
|
|
48
48
|
"devDependencies": {
|
|
49
49
|
"@types/react": "^19.2.17",
|