@eslint-react/jsx 5.17.0 → 5.17.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 (3) hide show
  1. package/dist/index.d.ts +194 -209
  2. package/dist/index.js +723 -277
  3. package/package.json +8 -7
package/dist/index.js CHANGED
@@ -2,45 +2,482 @@ import { Traverse } from "@eslint-react/ast";
2
2
  import { resolve } from "@eslint-react/var";
3
3
  import { AST_NODE_TYPES } from "@typescript-eslint/types";
4
4
  import { getStaticValue } from "@typescript-eslint/utils/ast-utils";
5
- import { P, match } from "ts-pattern";
6
5
 
7
- //#region src/collapse-multiline-text.ts
6
+ //#region ../../.pkgs/eff/dist/index.js
8
7
  /**
9
- * Collapse a multiline JSX text string following React's whitespace rules.
8
+ * Applies a `pipe` method's variadic arguments to an initial value from left
9
+ * to right.
10
10
  *
11
- * This mirrors Babel's `cleanJSXElementLiteralChild` algorithm:
12
- * 1. Split the raw text into lines.
13
- * 2. Find the last non-empty line.
14
- * 3. Trim leading spaces on non-first lines and trailing spaces on non-last lines.
15
- * 4. Collapse tabs into spaces.
16
- * 5. Append a single space after each non-last non-empty line.
17
- * @param text The raw JSX text string to collapse.
18
- * @returns The collapsed string, or `null` if the text contains only whitespace.
19
- * @see https://github.com/babel/babel/blob/main/packages/babel-types/src/utils/react/cleanJSXElementLiteralChild.ts
11
+ * **When to use**
12
+ *
13
+ * Use to implement a custom `.pipe(...)` method from JavaScript's `arguments`
14
+ * object.
15
+ *
16
+ * **Details**
17
+ *
18
+ * This helper is intended for implementing `Pipeable.pipe` methods that
19
+ * receive JavaScript's `arguments` object. With no functions it returns the
20
+ * original value; otherwise it feeds each result into the next function.
21
+ *
22
+ * **Example** (Implementing a pipe method)
23
+ *
24
+ * ```ts
25
+ * import { Pipeable } from "effect"
26
+ *
27
+ * class NumberBox {
28
+ * constructor(readonly value: number) {}
29
+ *
30
+ * pipe(..._fns: ReadonlyArray<(value: number) => number>): number {
31
+ * return Pipeable.pipeArguments(this.value, arguments) as number
32
+ * }
33
+ * }
34
+ *
35
+ * const result = new NumberBox(5).pipe(
36
+ * (n) => n + 2,
37
+ * (n) => n * 3
38
+ * )
39
+ * console.log(result) // 21
40
+ * ```
41
+ *
42
+ * @category combinators
43
+ * @since 2.0.0
20
44
  */
21
- function collapseMultilineText(text) {
22
- const lines = text.split(/\r\n|\n|\r/);
23
- let lastNonEmptyLine = 0;
24
- for (let i = 0; i < lines.length; i++) if (/[^ \t]/.exec(lines[i] ?? "") != null) lastNonEmptyLine = i;
25
- let str = "";
26
- for (let i = 0; i < lines.length; i++) {
27
- const line = lines[i] ?? "";
28
- const isFirstLine = i === 0;
29
- const isLastLine = i === lines.length - 1;
30
- const isLastNonEmptyLine = i === lastNonEmptyLine;
31
- let trimmedLine = line.replace(/\t/g, " ");
32
- if (!isFirstLine) trimmedLine = trimmedLine.replace(/^ +/, "");
33
- if (!isLastLine) trimmedLine = trimmedLine.replace(/ +$/, "");
34
- if (trimmedLine.length > 0) {
35
- if (!isLastNonEmptyLine) trimmedLine += " ";
36
- str += trimmedLine;
45
+ const pipeArguments = (self, args) => {
46
+ switch (args.length) {
47
+ case 0: return self;
48
+ case 1: return args[0](self);
49
+ case 2: return args[1](args[0](self));
50
+ case 3: return args[2](args[1](args[0](self)));
51
+ case 4: return args[3](args[2](args[1](args[0](self))));
52
+ case 5: return args[4](args[3](args[2](args[1](args[0](self)))));
53
+ case 6: return args[5](args[4](args[3](args[2](args[1](args[0](self))))));
54
+ case 7: return args[6](args[5](args[4](args[3](args[2](args[1](args[0](self)))))));
55
+ case 8: return args[7](args[6](args[5](args[4](args[3](args[2](args[1](args[0](self))))))));
56
+ case 9: return args[8](args[7](args[6](args[5](args[4](args[3](args[2](args[1](args[0](self)))))))));
57
+ default: {
58
+ let ret = self;
59
+ for (let i = 0, len = args.length; i < len; i++) ret = args[i](ret);
60
+ return ret;
37
61
  }
38
62
  }
39
- return str === "" ? null : str;
40
- }
63
+ };
64
+ /**
65
+ * Reusable prototype that implements `Pipeable.pipe`.
66
+ *
67
+ * **When to use**
68
+ *
69
+ * Use when classes or object prototypes can reuse this value when they need the
70
+ * standard pipe implementation backed by `pipeArguments`.
71
+ *
72
+ * @category prototypes
73
+ * @since 3.15.0
74
+ */
75
+ const Prototype = { pipe() {
76
+ return pipeArguments(this, arguments);
77
+ } };
78
+ /**
79
+ * Provides a base constructor whose instances implement the standard `Pipeable.pipe`
80
+ * method.
81
+ *
82
+ * **When to use**
83
+ *
84
+ * Use when you need to define a class that supports Effect-style method
85
+ * chaining through `.pipe(...)`.
86
+ *
87
+ * @category constructors
88
+ * @since 3.15.0
89
+ */
90
+ const Class = (function() {
91
+ function PipeableBase() {}
92
+ PipeableBase.prototype = Prototype;
93
+ return PipeableBase;
94
+ })();
95
+ /**
96
+ * Provides small helpers for defining and reusing TypeScript functions.
97
+ *
98
+ * The main helpers are `pipe` and `flow` for left-to-right composition and
99
+ * `dual` for APIs that support both direct and pipe-friendly call styles. The
100
+ * module also contains small identity, constant, tuple, type-level, and
101
+ * memoization helpers used across the library.
102
+ *
103
+ * @since 2.0.0
104
+ */
105
+ /**
106
+ * Creates a function that can be called in data-first style or data-last
107
+ * (`pipe`-friendly) style.
108
+ *
109
+ * **When to use**
110
+ *
111
+ * Use to expose one implementation through both direct and `pipe`-friendly
112
+ * call styles.
113
+ *
114
+ * **Details**
115
+ *
116
+ * Pass either the arity of the uncurried function or a predicate that decides
117
+ * whether the current call is data-first. Arity is the common case. Use a
118
+ * predicate when optional arguments make arity ambiguous.
119
+ *
120
+ * **Example** (Selecting data-first or data-last style by arity)
121
+ *
122
+ * ```ts
123
+ * import { Function, pipe } from "effect"
124
+ *
125
+ * const sum = Function.dual<
126
+ * (that: number) => (self: number) => number,
127
+ * (self: number, that: number) => number
128
+ * >(2, (self, that) => self + that)
129
+ *
130
+ * console.log(sum(2, 3)) // 5
131
+ * console.log(pipe(2, sum(3))) // 5
132
+ * ```
133
+ *
134
+ * **Example** (Defining overloads with call signatures)
135
+ *
136
+ * ```ts
137
+ * import { Function, pipe } from "effect"
138
+ *
139
+ * const sum: {
140
+ * (that: number): (self: number) => number
141
+ * (self: number, that: number): number
142
+ * } = Function.dual(2, (self: number, that: number): number => self + that)
143
+ *
144
+ * console.log(sum(2, 3)) // 5
145
+ * console.log(pipe(2, sum(3))) // 5
146
+ * ```
147
+ *
148
+ * **Example** (Selecting data-first or data-last style with a predicate)
149
+ *
150
+ * ```ts
151
+ * import { Function, pipe } from "effect"
152
+ *
153
+ * const sum = Function.dual<
154
+ * (that: number) => (self: number) => number,
155
+ * (self: number, that: number) => number
156
+ * >(
157
+ * (args) => args.length === 2,
158
+ * (self, that) => self + that
159
+ * )
160
+ *
161
+ * console.log(sum(2, 3)) // 5
162
+ * console.log(pipe(2, sum(3))) // 5
163
+ * ```
164
+ *
165
+ * @category combinators
166
+ * @since 2.0.0
167
+ */
168
+ const dual = function(arity, body) {
169
+ if (typeof arity === "function") return function() {
170
+ return arity(arguments) ? body.apply(this, arguments) : ((self) => body(self, ...arguments));
171
+ };
172
+ switch (arity) {
173
+ case 0:
174
+ case 1: throw new RangeError(`Invalid arity ${arity}`);
175
+ case 2: return function(a, b) {
176
+ if (arguments.length >= 2) return body(a, b);
177
+ return function(self) {
178
+ return body(self, a);
179
+ };
180
+ };
181
+ case 3: return function(a, b, c) {
182
+ if (arguments.length >= 3) return body(a, b, c);
183
+ return function(self) {
184
+ return body(self, a, b);
185
+ };
186
+ };
187
+ default: return function() {
188
+ if (arguments.length >= arity) return body.apply(this, arguments);
189
+ const args = arguments;
190
+ return function(self) {
191
+ return body(self, ...args);
192
+ };
193
+ };
194
+ }
195
+ };
196
+ /**
197
+ * Returns its input argument unchanged.
198
+ *
199
+ * **When to use**
200
+ *
201
+ * Use to return a value unchanged where a function is required.
202
+ *
203
+ * **Example** (Returning the same value)
204
+ *
205
+ * ```ts
206
+ * import { identity } from "effect"
207
+ * import * as assert from "node:assert"
208
+ *
209
+ * assert.deepStrictEqual(identity(5), 5)
210
+ * ```
211
+ *
212
+ * @category combinators
213
+ * @since 2.0.0
214
+ */
215
+ const identity = (a) => a;
216
+ /**
217
+ * Returns the input value with a different static type.
218
+ *
219
+ * **When to use**
220
+ *
221
+ * Use when you need an explicit type-level cast and accept that the value is
222
+ * returned unchanged at runtime.
223
+ *
224
+ * **Gotchas**
225
+ *
226
+ * This is a type-level cast only; it performs no runtime validation or
227
+ * conversion.
228
+ *
229
+ * @see {@link satisfies} for checking assignability without changing the resulting type
230
+ *
231
+ * @category utility types
232
+ * @since 4.0.0
233
+ */
234
+ const cast = identity;
235
+ /**
236
+ * Creates a zero-argument function that always returns the provided value.
237
+ *
238
+ * **When to use**
239
+ *
240
+ * Use when you need a thunk or callback that returns the same value on every
241
+ * invocation.
242
+ *
243
+ * **Example** (Creating a constant thunk)
244
+ *
245
+ * ```ts
246
+ * import { Function } from "effect"
247
+ * import * as assert from "node:assert"
248
+ *
249
+ * const constNull = Function.constant(null)
250
+ *
251
+ * assert.deepStrictEqual(constNull(), null)
252
+ * assert.deepStrictEqual(constNull(), null)
253
+ * ```
254
+ *
255
+ * @category constructors
256
+ * @since 2.0.0
257
+ */
258
+ const constant = (value) => () => value;
259
+ /**
260
+ * Returns `true` when called.
261
+ *
262
+ * **When to use**
263
+ *
264
+ * Use when you need a thunk that returns `true` on every invocation.
265
+ *
266
+ * **Example** (Returning true from a thunk)
267
+ *
268
+ * ```ts
269
+ * import { Function } from "effect"
270
+ * import * as assert from "node:assert"
271
+ *
272
+ * assert.deepStrictEqual(Function.constTrue(), true)
273
+ * ```
274
+ *
275
+ * @category constants
276
+ * @since 2.0.0
277
+ */
278
+ const constTrue = constant(true);
279
+ /**
280
+ * Returns `false` when called.
281
+ *
282
+ * **When to use**
283
+ *
284
+ * Use when you need a thunk that returns `false` on every invocation.
285
+ *
286
+ * **Example** (Returning false from a thunk)
287
+ *
288
+ * ```ts
289
+ * import { Function } from "effect"
290
+ * import * as assert from "node:assert"
291
+ *
292
+ * assert.deepStrictEqual(Function.constFalse(), false)
293
+ * ```
294
+ *
295
+ * @category constants
296
+ * @since 2.0.0
297
+ */
298
+ const constFalse = constant(false);
299
+ /**
300
+ * Returns `null` when called.
301
+ *
302
+ * **When to use**
303
+ *
304
+ * Use when you need a thunk that returns `null` on every invocation.
305
+ *
306
+ * **Example** (Returning null from a thunk)
307
+ *
308
+ * ```ts
309
+ * import { Function } from "effect"
310
+ * import * as assert from "node:assert"
311
+ *
312
+ * assert.deepStrictEqual(Function.constNull(), null)
313
+ * ```
314
+ *
315
+ * @category constants
316
+ * @since 2.0.0
317
+ */
318
+ const constNull = constant(null);
319
+ /**
320
+ * Returns `undefined` when called.
321
+ *
322
+ * **When to use**
323
+ *
324
+ * Use when you need a thunk that returns `undefined` on every invocation.
325
+ *
326
+ * **Example** (Returning undefined from a thunk)
327
+ *
328
+ * ```ts
329
+ * import { Function } from "effect"
330
+ * import * as assert from "node:assert"
331
+ *
332
+ * assert.deepStrictEqual(Function.constUndefined(), undefined)
333
+ * ```
334
+ *
335
+ * @category constants
336
+ * @since 2.0.0
337
+ */
338
+ const constUndefined = constant(void 0);
339
+ /**
340
+ * Composes two functions, `ab` and `bc` into a single function that takes in an argument `a` of type `A` and returns a result of type `C`.
341
+ * The result is obtained by first applying the `ab` function to `a` and then applying the `bc` function to the result of `ab`.
342
+ *
343
+ * **When to use**
344
+ *
345
+ * Use to compose exactly two unary functions into a reusable unary function.
346
+ *
347
+ * **Example** (Composing two functions)
348
+ *
349
+ * ```ts
350
+ * import { Function } from "effect"
351
+ * import * as assert from "node:assert"
352
+ *
353
+ * const increment = (n: number) => n + 1
354
+ * const square = (n: number) => n * n
355
+ *
356
+ * assert.strictEqual(Function.compose(increment, square)(2), 9)
357
+ * ```
358
+ *
359
+ * @see {@link flow} for composing a left-to-right sequence of functions
360
+ * @see {@link pipe} for applying a value through a left-to-right sequence immediately
361
+ *
362
+ * @category combinators
363
+ * @since 2.0.0
364
+ */
365
+ const compose = dual(2, (ab, bc) => (a) => bc(ab(a)));
366
+ /**
367
+ * Marks an impossible branch by accepting a `never` value and returning any
368
+ * type.
369
+ *
370
+ * **When to use**
371
+ *
372
+ * Use when you need a return value in a branch that exhaustive checks prove
373
+ * cannot be reached.
374
+ *
375
+ * **Gotchas**
376
+ *
377
+ * Calling `absurd` throws, because a value of type `never` should be
378
+ * impossible at runtime.
379
+ *
380
+ * **Example** (Handling impossible values)
381
+ *
382
+ * ```ts
383
+ * import { absurd } from "effect"
384
+ *
385
+ * const handleNever = (value: never) => {
386
+ * return absurd(value) // This will throw an error if called
387
+ * }
388
+ * ```
389
+ *
390
+ * @category utility types
391
+ * @since 2.0.0
392
+ */
393
+ const absurd = (_) => {
394
+ throw new Error("Called `absurd` function which should be uncallable");
395
+ };
396
+ /**
397
+ * Creates a compile-time placeholder for a value of any type.
398
+ *
399
+ * **When to use**
400
+ *
401
+ * Use as a temporary typed placeholder while developing incomplete code.
402
+ *
403
+ * **Gotchas**
404
+ *
405
+ * `hole` is intended for temporary development use. If the placeholder is
406
+ * evaluated at runtime, it throws.
407
+ *
408
+ * **Example** (Creating a development placeholder)
409
+ *
410
+ * ```ts
411
+ * import { hole } from "effect"
412
+ *
413
+ * // Intentionally not called: `hole` throws if the placeholder is evaluated.
414
+ * const buildUser = (id: number): { readonly id: number; readonly name: string } => ({
415
+ * id,
416
+ * name: hole<string>()
417
+ * })
418
+ *
419
+ * console.log(typeof buildUser) // "function"
420
+ * ```
421
+ *
422
+ * @category utility types
423
+ * @since 2.0.0
424
+ */
425
+ const hole = cast(absurd);
426
+ /**
427
+ * Drops the longest prefix of elements from an array that satisfy the given predicate.
428
+ *
429
+ * Supports both data-first and data-last (`pipe`-friendly) call styles.
430
+ *
431
+ * @param pred - The predicate to test each element with.
432
+ * @returns A new array without the matching prefix.
433
+ * @example
434
+ * ```ts
435
+ * import * as assert from "node:assert"
436
+ * import { dropWhile, pipe } from "@local/eff"
437
+ *
438
+ * // data-first
439
+ * assert.deepStrictEqual(dropWhile([1, 2, 3, 2, 1], (n: number) => n < 3), [3, 2, 1])
440
+ *
441
+ * // data-last
442
+ * assert.deepStrictEqual(pipe([1, 2, 3, 2, 1], dropWhile((n: number) => n < 3)), [3, 2, 1])
443
+ * ```
444
+ * @category array
445
+ */
446
+ const dropWhile = dual(2, (xs, pred) => {
447
+ const len = xs.length;
448
+ let idx = 0;
449
+ while (idx < len && pred(xs[idx])) idx++;
450
+ return xs.slice(idx);
451
+ });
452
+ /**
453
+ * Takes the longest prefix of elements from an array that satisfy the given predicate.
454
+ *
455
+ * Supports both data-first and data-last (`pipe`-friendly) call styles.
456
+ *
457
+ * @param pred - The predicate to test each element with.
458
+ * @returns A new array containing only the matching prefix.
459
+ * @example
460
+ * ```ts
461
+ * import * as assert from "node:assert"
462
+ * import { pipe, takeWhile } from "@local/eff"
463
+ *
464
+ * // data-first
465
+ * assert.deepStrictEqual(takeWhile([1, 2, 3, 2, 1], (n: number) => n < 3), [1, 2])
466
+ *
467
+ * // data-last
468
+ * assert.deepStrictEqual(pipe([1, 2, 3, 2, 1], takeWhile((n: number) => n < 3)), [1, 2])
469
+ * ```
470
+ * @category array
471
+ */
472
+ const takeWhile = dual(2, (xs, pred) => {
473
+ const len = xs.length;
474
+ let idx = 0;
475
+ while (idx < len && pred(xs[idx])) idx++;
476
+ return xs.slice(0, idx);
477
+ });
41
478
 
42
479
  //#endregion
43
- //#region src/get-attribute-name.ts
480
+ //#region src/attribute-name.ts
44
481
  /**
45
482
  * Get the stringified name of a `JSXAttribute` node.
46
483
  *
@@ -55,9 +492,25 @@ function getAttributeName(node) {
55
492
  if (node.name.type === AST_NODE_TYPES.JSXIdentifier) return node.name.name;
56
493
  return node.name.namespace.name + ":" + node.name.name.name;
57
494
  }
495
+ /**
496
+ * Check whether a node is a `JSXAttribute` with the given name.
497
+ *
498
+ * Only plain identifier names are matched (ex: `className`); namespaced
499
+ * attributes (ex: `xml:space`) do not match.
500
+ *
501
+ * Supports both data-first and data-last (curried) call styles:
502
+ * - `isAttribute(node, "className")`
503
+ * - `isAttribute("className")(node)`.
504
+ * @param node The AST node to test.
505
+ * @param name The attribute name to match (ex: "className").
506
+ * @returns `true` when the node is a `JSXAttribute` named `name`.
507
+ */
508
+ const isAttribute = dual(2, (node, name) => {
509
+ return node.type === AST_NODE_TYPES.JSXAttribute && node.name.type === AST_NODE_TYPES.JSXIdentifier && node.name.name === name;
510
+ });
58
511
 
59
512
  //#endregion
60
- //#region src/find-attribute.ts
513
+ //#region src/attribute-find.ts
61
514
  /**
62
515
  * Find a JSX attribute (or spread attribute containing the property) by name on a given element.
63
516
  *
@@ -66,53 +519,19 @@ function getAttributeName(node) {
66
519
  *
67
520
  * Spread attributes are resolved when possible: if the spread argument is an identifier
68
521
  * that resolves to an object expression, the object's properties are searched for a matching key.
69
- * Nested object expressions and nested spread identifiers are also resolved.
522
+ * Nested object expressions and nested spread identifiers are also resolved
523
+ * (see {@link findSpreadProperty}).
70
524
  * @param context The ESLint rule context (needed for variable resolution in spread attributes).
71
525
  * @param element The `JSXElement` node to search.
72
526
  * @param name The attribute name to look for (ex: "className").
73
527
  * @returns The matching `JSXAttribute` or `JSXSpreadAttribute`, or `undefined` when not found.
74
528
  */
75
529
  function findAttribute(context, element, name) {
76
- function findProperty(properties, name, seen = /* @__PURE__ */ new Set()) {
77
- for (const property of properties) {
78
- if (property.type === AST_NODE_TYPES.Property && !property.computed && property.key.type === AST_NODE_TYPES.Identifier && property.key.name === name) return property;
79
- if (property.type !== AST_NODE_TYPES.SpreadElement) continue;
80
- const argument = property.argument;
81
- if (argument.type === AST_NODE_TYPES.Identifier) {
82
- const initNode = resolve(context, argument);
83
- if (initNode?.type === AST_NODE_TYPES.ObjectExpression) {
84
- if (seen.has(initNode)) continue;
85
- seen.add(initNode);
86
- const found = findProperty(initNode.properties, name, seen);
87
- if (found != null) return found;
88
- }
89
- continue;
90
- }
91
- if (argument.type === AST_NODE_TYPES.ObjectExpression) {
92
- if (seen.has(argument)) continue;
93
- seen.add(argument);
94
- const found = findProperty(argument.properties, name, seen);
95
- if (found != null) return found;
96
- }
97
- }
98
- return null;
99
- }
100
530
  return element.openingElement.attributes.findLast((attr) => {
101
531
  if (attr.type === AST_NODE_TYPES.JSXAttribute) return getAttributeName(attr) === name;
102
- switch (attr.argument.type) {
103
- case AST_NODE_TYPES.Identifier: {
104
- const initNode = resolve(context, attr.argument);
105
- if (initNode?.type === AST_NODE_TYPES.ObjectExpression) return findProperty(initNode.properties, name) != null;
106
- return false;
107
- }
108
- case AST_NODE_TYPES.ObjectExpression: return findProperty(attr.argument.properties, name) != null;
109
- }
110
- return false;
532
+ return findSpreadProperty(context, attr.argument, name) != null;
111
533
  });
112
534
  }
113
-
114
- //#endregion
115
- //#region src/find-parent-attribute.ts
116
535
  /**
117
536
  * Walk up the AST from `node` to find the nearest ancestor that is a `JSXAttribute`
118
537
  * and (optionally) passes a predicate.
@@ -121,31 +540,173 @@ function findAttribute(context, element, name) {
121
540
  * inside an expression container) and needs to know which JSX attribute it belongs to.
122
541
  * @param node The starting node for the upward search.
123
542
  * @param test Optional predicate to filter candidate `JSXAttribute` nodes. When omitted every `JSXAttribute` ancestor matches.
124
- * @returns The first matching `JSXAttribute` ancestor, or `null` if none is found before reaching the root.
543
+ * @returns The first matching `JSXAttribute` ancestor, or `undefined` if none is found before reaching the root.
125
544
  */
126
545
  function findParentAttribute(node, test = () => true) {
127
546
  const guard = (n) => {
128
547
  return n.type === AST_NODE_TYPES.JSXAttribute && test(n);
129
548
  };
130
- return Traverse.findParent(node, guard);
549
+ return Traverse.findParent(node, guard) ?? void 0;
550
+ }
551
+ /**
552
+ * Find the `Property` node that provides a given key inside a spread argument.
553
+ *
554
+ * This is the single resolution routine shared by {@link findAttribute} (existence
555
+ * checks) and the `spreadProps` variant of `resolveAttributeValue` (value extraction):
556
+ *
557
+ * - An `Identifier` argument is resolved to its initializer via variable
558
+ * resolution, following alias chains (`const b = a`) like `getStaticValue`
559
+ * does; an `ObjectExpression` argument is searched directly.
560
+ * - Properties are walked **in reverse** so that later entries win, matching
561
+ * JavaScript object semantics (`{ ...a, k: 1 }` -> the literal `k`).
562
+ * - Nested `SpreadElement`s (identifiers or inline object expressions) are
563
+ * searched recursively; a `seen` set guards against circular references.
564
+ * - Plain identifier keys and string literal keys are matched directly;
565
+ * computed keys are matched when they are statically evaluable
566
+ * (ex: `{ ["class" + "Name"]: 1 }`).
567
+ * @param context The ESLint rule context (needed for variable resolution).
568
+ * @param argument The spread argument expression to search.
569
+ * @param name The property name to look for.
570
+ * @param seen Internal set of already-visited nodes (cycle guard).
571
+ * @returns The matching `Property` node, or `undefined` when the key is not found.
572
+ */
573
+ function findSpreadProperty(context, argument, name, seen = /* @__PURE__ */ new Set()) {
574
+ let objectExpression;
575
+ if (argument.type === AST_NODE_TYPES.Identifier) {
576
+ let initNode = resolve(context, argument);
577
+ while (initNode != null && initNode.type === AST_NODE_TYPES.Identifier && !seen.has(initNode)) {
578
+ seen.add(initNode);
579
+ initNode = resolve(context, initNode);
580
+ }
581
+ if (initNode?.type === AST_NODE_TYPES.ObjectExpression) objectExpression = initNode;
582
+ } else if (argument.type === AST_NODE_TYPES.ObjectExpression) objectExpression = argument;
583
+ if (objectExpression == null || seen.has(objectExpression)) return void 0;
584
+ seen.add(objectExpression);
585
+ const { properties } = objectExpression;
586
+ for (let i = properties.length - 1; i >= 0; i--) {
587
+ const property = properties[i];
588
+ if (property == null) continue;
589
+ if (property.type === AST_NODE_TYPES.Property) {
590
+ const { key } = property;
591
+ if (property.computed) {
592
+ if (getStaticValue(key, context.sourceCode.getScope(key))?.value === name) return property;
593
+ continue;
594
+ }
595
+ if (key.type === AST_NODE_TYPES.Identifier && key.name === name) return property;
596
+ if (key.type === AST_NODE_TYPES.Literal && key.value === name) return property;
597
+ continue;
598
+ }
599
+ const found = findSpreadProperty(context, property.argument, name, seen);
600
+ if (found != null) return found;
601
+ }
131
602
  }
132
603
 
133
604
  //#endregion
134
- //#region src/resolve-attribute-value.ts
605
+ //#region src/attribute-has.ts
135
606
  /**
136
- * Resolve the value of a JSX attribute (or spread attribute) into a
137
- * {@link JsxAttributeValue} descriptor that can be inspected further.
607
+ * Check whether a JSX element carries a given attribute (prop).
608
+ *
609
+ * This is a thin convenience wrapper around {@link findAttribute} for the
610
+ * common case where you only need a boolean answer.
611
+ *
612
+ * Spread attributes are taken into account: `<Comp {...{ disabled: true }} />`
613
+ * will report `true` for `"disabled"`.
614
+ * @param context The ESLint rule context (needed for variable resolution in spread attributes).
615
+ * @param element The `JSXElement` node to inspect.
616
+ * @param name The attribute name to look for (ex: "className").
617
+ * @returns `true` when the attribute is present on the element.
618
+ */
619
+ function hasAttribute(context, element, name) {
620
+ return findAttribute(context, element, name) != null;
621
+ }
622
+ /**
623
+ * Check whether a JSX element carries at least one of the given attributes.
624
+ *
625
+ * This is a batch variant of {@link hasAttribute} for the common pattern of
626
+ * short-circuiting on multiple prop names.
627
+ *
628
+ * Spread attributes are taken into account (see {@link findAttribute}).
629
+ * @param context The ESLint rule context (needed for variable resolution in spread attributes).
630
+ * @param element The `JSXElement` node to inspect.
631
+ * @param names The attribute names to look for.
632
+ * @returns `true` when at least one of the attributes is present.
633
+ */
634
+ function hasAnyAttribute(context, element, names) {
635
+ return names.some((name) => findAttribute(context, element, name) != null);
636
+ }
637
+ /**
638
+ * Check whether a JSX element carries all of the given attributes (props).
639
+ *
640
+ * This is a batch variant of {@link hasAttribute} for the common pattern
641
+ * where a rule needs to verify that a set of required props are all present.
642
+ *
643
+ * Spread attributes are taken into account (see {@link findAttribute}).
644
+ * @param context The ESLint rule context (needed for variable resolution in spread attributes).
645
+ * @param element The `JSXElement` node to inspect.
646
+ * @param names The attribute names to look for.
647
+ * @returns `true` when every name in `names` is present on the element.
648
+ */
649
+ function hasEveryAttribute(context, element, names) {
650
+ return names.every((name) => findAttribute(context, element, name) != null);
651
+ }
652
+
653
+ //#endregion
654
+ //#region src/attribute-value.ts
655
+ /**
656
+ * Resolve the value of a JSX attribute (or spread attribute) into an
657
+ * {@link AttributeValue} descriptor that can be inspected further.
138
658
  *
139
659
  * This is the low-level building block; it operates on a single attribute
140
660
  * node that the caller has already located. For the higher-level "find by
141
661
  * name and resolve" combo, see {@link getAttributeValue}.
662
+ *
663
+ * When the attribute is a `JSXSpreadAttribute`, passing `name` (typically the
664
+ * same name the attribute was found by) makes `toStatic()` return the static
665
+ * value of that named property, eliminating the need to branch on
666
+ * `kind === "spreadProps"` at the call site.
142
667
  * @param context The ESLint rule context (needed for scope look-ups).
143
668
  * @param attribute A `JSXAttribute` or `JSXSpreadAttribute` node.
669
+ * @param name Optional property name used to resolve `toStatic()` for spread attributes.
144
670
  * @returns A discriminated-union descriptor of the attribute's value.
145
671
  */
146
- function resolveAttributeValue(context, attribute) {
672
+ function resolveAttributeValue(context, attribute, name) {
147
673
  if (attribute.type === AST_NODE_TYPES.JSXAttribute) return resolveJsxAttribute(context, attribute);
148
- return resolveJsxSpreadAttribute(context, attribute);
674
+ return resolveJsxSpreadAttribute(context, attribute, name);
675
+ }
676
+ /**
677
+ * Find an attribute by name on a JSX element and resolve its value in a single call.
678
+ *
679
+ * This is a convenience composition of {@link findAttribute} and
680
+ * {@link resolveAttributeValue} that eliminates the most common two-step
681
+ * pattern in lint rules.
682
+ * @param context The ESLint rule context.
683
+ * @param element The `JSXElement` node to search.
684
+ * @param name The attribute name to look up (ex: "className").
685
+ * @returns An {@link AttributeValue} descriptor, or `undefined` when the attribute is not present on the element.
686
+ */
687
+ function getAttributeValue(context, element, name) {
688
+ const attr = findAttribute(context, element, name);
689
+ if (attr == null) return void 0;
690
+ return resolveAttributeValue(context, attr, name);
691
+ }
692
+ /**
693
+ * Find an attribute by name on a JSX element and collapse its value to a plain
694
+ * JavaScript value in a single step.
695
+ *
696
+ * This is a convenience composition of {@link findAttribute} ->
697
+ * {@link resolveAttributeValue} -> `toStatic()`, with automatic handling of the
698
+ * `spreadProps` case (extracts the named property from the spread object).
699
+ *
700
+ * Returns `undefined` both when the attribute is absent and when its value
701
+ * cannot be statically determined; use {@link findAttribute} or
702
+ * {@link hasAttribute} when presence itself matters.
703
+ * @param context The ESLint rule context.
704
+ * @param element The `JSXElement` node to inspect.
705
+ * @param name The attribute name to look up (ex: "className").
706
+ * @returns The static value of the attribute, or `undefined` when absent or indeterminate.
707
+ */
708
+ function getAttributeStaticValue(context, element, name) {
709
+ return getAttributeValue(context, element, name)?.toStatic();
149
710
  }
150
711
  function resolveJsxAttribute(context, node) {
151
712
  const scope = context.sourceCode.getScope(node);
@@ -172,9 +733,7 @@ function resolveJsxAttribute(context, node) {
172
733
  if (expr.type === AST_NODE_TYPES.JSXEmptyExpression) return {
173
734
  kind: "missing",
174
735
  node: expr,
175
- toStatic() {
176
- return null;
177
- }
736
+ toStatic() {}
178
737
  };
179
738
  return {
180
739
  kind: "unknown",
@@ -187,84 +746,67 @@ function resolveJsxAttribute(context, node) {
187
746
  case AST_NODE_TYPES.JSXElement: return {
188
747
  kind: "element",
189
748
  node: node.value,
190
- toStatic() {
191
- return null;
192
- }
749
+ toStatic() {}
193
750
  };
194
751
  case AST_NODE_TYPES.JSXSpreadChild: return {
195
752
  kind: "spreadChild",
196
- getChildren() {
197
- return null;
198
- },
199
- node: node.value.expression,
200
- toStatic() {
201
- return null;
202
- }
753
+ node: node.value,
754
+ toStatic() {}
203
755
  };
204
756
  }
205
757
  }
206
- function resolveJsxSpreadAttribute(context, node) {
207
- const scope = context.sourceCode.getScope(node);
758
+ function resolveJsxSpreadAttribute(context, node, name) {
759
+ const getProperty = (propertyName) => {
760
+ const property = findSpreadProperty(context, node.argument, propertyName);
761
+ if (property == null) return void 0;
762
+ const propertyScope = context.sourceCode.getScope(property.value);
763
+ return getStaticValue(property.value, propertyScope)?.value;
764
+ };
208
765
  return {
209
766
  kind: "spreadProps",
210
- getProperty(name) {
211
- return match(getStaticValue(node.argument, scope)?.value).with({ [name]: P.select(P.unknown) }, (v) => v).otherwise(() => null);
212
- },
767
+ getProperty,
213
768
  node: node.argument,
214
769
  toStatic() {
215
- return null;
770
+ return name == null ? void 0 : getProperty(name);
216
771
  }
217
772
  };
218
773
  }
219
774
 
220
775
  //#endregion
221
- //#region src/get-attribute-static-value.ts
776
+ //#region src/text.ts
222
777
  /**
223
- * Find an attribute by name on a JSX element and collapse its value to a plain
224
- * JavaScript value in a single step.
225
- *
226
- * This is a convenience composition of {@link findAttribute} ->
227
- * {@link resolveAttributeValue} -> `toStatic()`, with automatic handling of the
228
- * `spreadProps` case (extracts the named property from the spread object).
229
- *
230
- * Returns `null` when the attribute is absent, `undefined` when the value cannot
231
- * be statically determined (including empty expression containers), and the
232
- * resolved static value otherwise.
233
- * @param context The ESLint rule context.
234
- * @param element The `JSXElement` node to inspect.
235
- * @param name The attribute name to look up (ex: "className").
236
- * @returns The static value of the attribute, `null` when absent, or `undefined` when indeterminate.
237
- */
238
- function getAttributeStaticValue(context, element, name) {
239
- const attr = findAttribute(context, element, name);
240
- if (attr == null) return null;
241
- const resolved = resolveAttributeValue(context, attr);
242
- if (resolved.kind === "spreadProps") return resolved.getProperty(name);
243
- if (resolved.kind === "missing") return;
244
- return resolved.toStatic();
245
- }
246
-
247
- //#endregion
248
- //#region src/get-attribute-value.ts
249
- /**
250
- * Find an attribute by name on a JSX element and resolve its value in a single call.
778
+ * Collapse a multiline JSX text string following React's whitespace rules.
251
779
  *
252
- * This is a convenience composition of {@link findAttribute} and
253
- * {@link resolveAttributeValue} that eliminates the most common two-step
254
- * pattern in lint rules.
255
- * @param context The ESLint rule context.
256
- * @param element The `JSXElement` node to search.
257
- * @param name The attribute name to look up (ex: "className").
258
- * @returns A {@link JsxAttributeValue} descriptor, or `null` when the attribute is not present on the element.
780
+ * This mirrors Babel's `cleanJSXElementLiteralChild` algorithm:
781
+ * 1. Split the raw text into lines.
782
+ * 2. Find the last non-empty line.
783
+ * 3. Trim leading spaces on non-first lines and trailing spaces on non-last lines.
784
+ * 4. Collapse tabs into spaces.
785
+ * 5. Append a single space after each non-last non-empty line.
786
+ * @param text The raw JSX text string to collapse.
787
+ * @returns The collapsed string, or `null` if the text contains only whitespace.
788
+ * @see https://github.com/babel/babel/blob/main/packages/babel-types/src/utils/react/cleanJSXElementLiteralChild.ts
259
789
  */
260
- function getAttributeValue(context, element, name) {
261
- const attr = findAttribute(context, element, name);
262
- if (attr == null) return null;
263
- return resolveAttributeValue(context, attr);
790
+ function collapseMultilineText(text) {
791
+ const lines = text.split(/\r\n|\n|\r/);
792
+ let lastNonEmptyLine = 0;
793
+ for (let i = 0; i < lines.length; i++) if (/[^ \t]/.exec(lines[i] ?? "") != null) lastNonEmptyLine = i;
794
+ let str = "";
795
+ for (let i = 0; i < lines.length; i++) {
796
+ const line = lines[i] ?? "";
797
+ const isFirstLine = i === 0;
798
+ const isLastLine = i === lines.length - 1;
799
+ const isLastNonEmptyLine = i === lastNonEmptyLine;
800
+ let trimmedLine = line.replace(/\t/g, " ");
801
+ if (!isFirstLine) trimmedLine = trimmedLine.replace(/^ +/, "");
802
+ if (!isLastLine) trimmedLine = trimmedLine.replace(/ +$/, "");
803
+ if (trimmedLine.length > 0) {
804
+ if (!isLastNonEmptyLine) trimmedLine += " ";
805
+ str += trimmedLine;
806
+ }
807
+ }
808
+ return str === "" ? null : str;
264
809
  }
265
-
266
- //#endregion
267
- //#region src/is-whitespace.ts
268
810
  /**
269
811
  * Check whether a JSX child node is whitespace padding that React would
270
812
  * trim away during rendering.
@@ -272,19 +814,21 @@ function getAttributeValue(context, element, name) {
272
814
  * A child is considered whitespace padding when it is a `JSXText` node whose
273
815
  * content is empty after applying React's whitespace normalization
274
816
  * (see {@link collapseMultilineText}, modelled after Babel's
275
- * `cleanJSXElementLiteralChild`). This is the whitespace that appears between
276
- * JSX tags purely for formatting.
817
+ * `cleanJSXElementLiteralChild`) **and** it contains a newline. This is the
818
+ * whitespace that appears between JSX tags purely for formatting.
819
+ *
820
+ * For the looser "any whitespace-only text" check, see {@link isWhitespaceText}.
277
821
  * @param node A JSX child node.
278
822
  * @returns `true` when the node is purely formatting whitespace.
279
823
  */
280
- function isWhitespace(node) {
824
+ function isPaddingWhitespace(node) {
281
825
  if (node.type !== AST_NODE_TYPES.JSXText) return false;
282
826
  return collapseMultilineText(node.value) == null && node.value.includes("\n");
283
827
  }
284
828
  /**
285
829
  * Check whether a JSX child node is any whitespace-only text.
286
830
  *
287
- * This is a looser variant of {@link isWhitespace}; it matches every
831
+ * This is a looser variant of {@link isPaddingWhitespace}; it matches every
288
832
  * `JSXText` node whose raw content is empty after trimming, regardless of
289
833
  * whether it contains a newline.
290
834
  * @param node A JSX child node.
@@ -312,7 +856,7 @@ function isEmptyStringExpression(node) {
312
856
  }
313
857
 
314
858
  //#endregion
315
- //#region src/get-children.ts
859
+ //#region src/children.ts
316
860
  /**
317
861
  * Get the meaningful children of a JSX element or fragment.
318
862
  *
@@ -320,39 +864,59 @@ function isEmptyStringExpression(node) {
320
864
  * 1. Iterate over `element.children`.
321
865
  * 2. Skip `JSXText` nodes that clean to nothing (padding whitespace).
322
866
  * 3. Skip `JSXExpressionContainer` nodes whose expression is empty.
323
- * 4. Skip `JSXEmptyExpression` nodes.
867
+ * 4. Skip empty string expressions (`{""}`), which produce no DOM node.
324
868
  * 5. Collect everything else.
325
869
  * @param element A `JSXElement` or `JSXFragment` node.
326
870
  * @returns An array of children nodes that contribute to rendered output.
327
871
  */
328
872
  function getChildren(element) {
329
- const elements = [];
873
+ const children = [];
330
874
  for (const child of element.children) {
331
- if (child.type === AST_NODE_TYPES.JSXText) {
332
- if (collapseMultilineText(child.value) == null && child.value.includes("\n")) continue;
333
- elements.push(child);
334
- continue;
335
- }
875
+ if (isPaddingWhitespace(child)) continue;
336
876
  if (child.type === AST_NODE_TYPES.JSXExpressionContainer) {
337
- const { expression } = child;
338
- if (expression.type === AST_NODE_TYPES.JSXEmptyExpression) continue;
877
+ if (child.expression.type === AST_NODE_TYPES.JSXEmptyExpression) continue;
339
878
  if (isEmptyStringExpression(child)) continue;
340
- elements.push(child);
341
- continue;
342
879
  }
343
- elements.push(child);
880
+ children.push(child);
344
881
  }
345
- return elements;
882
+ return children;
883
+ }
884
+ /**
885
+ * Check whether a JSX element (or fragment) has meaningful children, that is,
886
+ * at least one child that is not purely whitespace text or an empty string expression.
887
+ *
888
+ * A `JSXText` child whose `raw` content is empty after trimming is considered
889
+ * non-meaningful because it is typically a code-formatting artifact
890
+ * (indentation between tags). While React's client renderer preserves these
891
+ * nodes as text nodes, they rarely represent intentionally rendered content.
892
+ *
893
+ * An empty string expression (`children={""}`) is also considered
894
+ * non-meaningful because React's reconciler and SSR renderer explicitly skip
895
+ * empty strings, producing no DOM node.
896
+ *
897
+ * Unlike {@link getChildren} (which only filters whitespace that contains a
898
+ * newline) this check treats any whitespace-only text as non-meaningful
899
+ * (see {@link isWhitespaceText}). As a result `hasChildren(node)` is not
900
+ * always equal to `getChildren(node).length > 0`: they differ for
901
+ * whitespace-only children that have no newline, such as `<div> </div>` or
902
+ * `<div>\t\t</div>`. Choose the API that matches your rule's intent.
903
+ * @param element A `JSXElement` or `JSXFragment` node.
904
+ * @returns `true` when the element has at least one meaningful child.
905
+ */
906
+ function hasChildren(element) {
907
+ if (element.children.length === 0) return false;
908
+ return !element.children.every((child) => isWhitespaceText(child) || isEmptyStringExpression(child));
346
909
  }
347
910
 
348
911
  //#endregion
349
- //#region src/get-element-type.ts
912
+ //#region src/element-type.ts
350
913
  /**
351
914
  * Get the string representation of a JSX element's type.
352
915
  *
353
916
  * - `<div>` -> `"div"`
354
917
  * - `<Foo.Bar>` -> `"Foo.Bar"`
355
918
  * - `<React.Fragment>` -> `"React.Fragment"`
919
+ * - `<xml:space>` -> `"xml:space"`
356
920
  * - `<></>` -> `""`.
357
921
  * @param node A `JSXElement` or `JSXFragment` node.
358
922
  * @returns The fully-qualified element type string.
@@ -382,91 +946,7 @@ function getElementSelfType(node) {
382
946
  }
383
947
 
384
948
  //#endregion
385
- //#region src/has-any-attribute.ts
386
- /**
387
- * Check whether a JSX element carries at least one of the given attributes.
388
- *
389
- * This is a batch variant of {@link hasAttribute} for the common pattern of
390
- * short-circuiting on multiple prop names.
391
- *
392
- * Spread attributes are taken into account (see {@link findAttribute}).
393
- * @param context The ESLint rule context (needed for variable resolution in spread attributes).
394
- * @param element The `JSXElement` node to inspect.
395
- * @param names The attribute names to look for.
396
- * @returns `true` when at least one of the attributes is present.
397
- */
398
- function hasAnyAttribute(context, element, names) {
399
- return names.some((name) => findAttribute(context, element, name) != null);
400
- }
401
-
402
- //#endregion
403
- //#region src/has-attribute.ts
404
- /**
405
- * Check whether a JSX element carries a given attribute (prop).
406
- *
407
- * This is a thin convenience wrapper around {@link findAttribute} for the
408
- * common case where you only need a boolean answer.
409
- *
410
- * Spread attributes are taken into account: `<Comp {...{ disabled: true }} />`
411
- * will report `true` for `"disabled"`.
412
- * @param context The ESLint rule context (needed for variable resolution in spread attributes).
413
- * @param element The `JSXElement` node to inspect.
414
- * @param name The attribute name to look for (ex: "className").
415
- * @returns `true` when the attribute is present on the element.
416
- */
417
- function hasAttribute(context, element, name) {
418
- return findAttribute(context, element, name) != null;
419
- }
420
-
421
- //#endregion
422
- //#region src/has-children.ts
423
- /**
424
- * Check whether a JSX element (or fragment) has meaningful children, that is,
425
- * at least one child that is not purely whitespace text or an empty string expression.
426
- *
427
- * A `JSXText` child whose `raw` content is empty after trimming is considered
428
- * non-meaningful because it is typically a code-formatting artifact
429
- * (indentation between tags). While React's client renderer preserves these
430
- * nodes as text nodes, they rarely represent intentionally rendered content.
431
- *
432
- * An empty string expression (`children={""}`) is also considered
433
- * non-meaningful because React's reconciler and SSR renderer explicitly skip
434
- * empty strings, producing no DOM node.
435
- *
436
- * Unlike {@link getChildren} (which only filters whitespace that contains a
437
- * newline) this check treats any whitespace-only text as non-meaningful
438
- * (see {@link isWhitespaceText}). As a result `hasChildren(node)` is not
439
- * always equal to `getChildren(node).length > 0`: they differ for
440
- * whitespace-only children that have no newline, such as `<div> </div>` or
441
- * `<div>\t\t</div>`. Choose the API that matches your rule's intent.
442
- * @param element A `JSXElement` or `JSXFragment` node.
443
- * @returns `true` when the element has at least one meaningful child.
444
- */
445
- function hasChildren(element) {
446
- if (element.children.length === 0) return false;
447
- return !element.children.every((child) => isWhitespaceText(child) || isEmptyStringExpression(child));
448
- }
449
-
450
- //#endregion
451
- //#region src/has-every-attribute.ts
452
- /**
453
- * Check whether a JSX element carries all of the given attributes (props).
454
- *
455
- * This is a batch variant of {@link hasAttribute} for the common pattern
456
- * where a rule needs to verify that a set of required props are all present.
457
- *
458
- * Spread attributes are taken into account (see {@link findAttribute}).
459
- * @param context The ESLint rule context (needed for variable resolution in spread attributes).
460
- * @param element The `JSXElement` node to inspect.
461
- * @param names The attribute names to look for.
462
- * @returns `true` when every name in `names` is present on the element.
463
- */
464
- function hasEveryAttribute(context, element, names) {
465
- return names.every((name) => findAttribute(context, element, name) != null);
466
- }
467
-
468
- //#endregion
469
- //#region src/is-element.ts
949
+ //#region src/element-is.ts
470
950
  /**
471
951
  * Check whether a node is a `JSXElement` (or `JSXFragment`) and optionally
472
952
  * matches a given test.
@@ -492,9 +972,6 @@ function isElement(node, test) {
492
972
  default: return test.includes(elementType);
493
973
  }
494
974
  }
495
-
496
- //#endregion
497
- //#region src/is-fragment-element.ts
498
975
  /**
499
976
  * Check whether a node is a React Fragment element.
500
977
  *
@@ -514,9 +991,6 @@ function isFragmentElement(node, jsxFragmentFactory = "React.Fragment") {
514
991
  const fragment = jsxFragmentFactory.split(".").at(-1) ?? "Fragment";
515
992
  return getElementFullType(node).split(".").at(-1) === fragment;
516
993
  }
517
-
518
- //#endregion
519
- //#region src/is-host-element.ts
520
994
  /**
521
995
  * Check whether a node is a host (intrinsic / DOM) element.
522
996
  *
@@ -535,32 +1009,4 @@ function isHostElement(node) {
535
1009
  }
536
1010
 
537
1011
  //#endregion
538
- //#region src/jsx-detection-hint.ts
539
- /**
540
- * Hints for JSX detection.
541
- */
542
- const JsxDetectionHint = {
543
- None: 0n,
544
- DoNotIncludeJsxWithNullValue: 1n << 0n,
545
- DoNotIncludeJsxWithNumberValue: 1n << 1n,
546
- DoNotIncludeJsxWithBigIntValue: 1n << 2n,
547
- DoNotIncludeJsxWithStringValue: 1n << 3n,
548
- DoNotIncludeJsxWithBooleanValue: 1n << 4n,
549
- DoNotIncludeJsxWithUndefinedValue: 1n << 5n,
550
- DoNotIncludeJsxWithEmptyArrayValue: 1n << 6n,
551
- DoNotIncludeJsxWithCreateElementValue: 1n << 7n,
552
- RequireAllArrayElementsToBeJsx: 1n << 8n,
553
- RequireBothSidesOfLogicalExpressionToBeJsx: 1n << 9n,
554
- RequireBothBranchesOfConditionalExpressionToBeJsx: 1n << 10n
555
- };
556
- /**
557
- * Default JSX detection hint.
558
- *
559
- * Skips number, bigint, boolean, string, and undefined literals,
560
- * the value types that are commonly returned alongside JSX in React
561
- * components but are not themselves renderable elements.
562
- */
563
- const DEFAULT_JSX_DETECTION_HINT = 0n | JsxDetectionHint.DoNotIncludeJsxWithNumberValue | JsxDetectionHint.DoNotIncludeJsxWithBigIntValue | JsxDetectionHint.DoNotIncludeJsxWithBooleanValue | JsxDetectionHint.DoNotIncludeJsxWithStringValue | JsxDetectionHint.DoNotIncludeJsxWithUndefinedValue;
564
-
565
- //#endregion
566
- export { DEFAULT_JSX_DETECTION_HINT, JsxDetectionHint, collapseMultilineText, findAttribute, findParentAttribute, getAttributeName, getAttributeStaticValue, getAttributeValue, getChildren, getElementFullType, getElementSelfType, hasAnyAttribute, hasAttribute, hasChildren, hasEveryAttribute, isElement, isEmptyStringExpression, isFragmentElement, isHostElement, isWhitespace, isWhitespaceText, resolveAttributeValue };
1012
+ export { collapseMultilineText, findAttribute, findParentAttribute, findSpreadProperty, getAttributeName, getAttributeStaticValue, getAttributeValue, getChildren, getElementFullType, getElementSelfType, hasAnyAttribute, hasAttribute, hasChildren, hasEveryAttribute, isAttribute, isElement, isEmptyStringExpression, isFragmentElement, isHostElement, isPaddingWhitespace, isWhitespaceText, resolveAttributeValue };