@eslint-react/jsx 5.16.1 → 5.17.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.
Files changed (3) hide show
  1. package/dist/index.d.ts +112 -93
  2. package/dist/index.js +575 -82
  3. package/package.json +6 -6
package/dist/index.js CHANGED
@@ -6,7 +6,7 @@ import { P, match } from "ts-pattern";
6
6
 
7
7
  //#region src/collapse-multiline-text.ts
8
8
  /**
9
- * Collapse a multiline JSX text string following React's whitespace rules
9
+ * Collapse a multiline JSX text string following React's whitespace rules.
10
10
  *
11
11
  * This mirrors Babel's `cleanJSXElementLiteralChild` algorithm:
12
12
  * 1. Split the raw text into lines.
@@ -14,8 +14,8 @@ import { P, match } from "ts-pattern";
14
14
  * 3. Trim leading spaces on non-first lines and trailing spaces on non-last lines.
15
15
  * 4. Collapse tabs into spaces.
16
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
17
+ * @param text The raw JSX text string to collapse.
18
+ * @returns The collapsed string, or `null` if the text contains only whitespace.
19
19
  * @see https://github.com/babel/babel/blob/main/packages/babel-types/src/utils/react/cleanJSXElementLiteralChild.ts
20
20
  */
21
21
  function collapseMultilineText(text) {
@@ -42,14 +42,14 @@ function collapseMultilineText(text) {
42
42
  //#endregion
43
43
  //#region src/get-attribute-name.ts
44
44
  /**
45
- * Get the stringified name of a `JSXAttribute` node
45
+ * Get the stringified name of a `JSXAttribute` node.
46
46
  *
47
47
  * Handles both simple identifiers and namespaced names:
48
48
  * - `className` -> `"className"`
49
49
  * - `aria-label` -> `"aria-label"`
50
- * - `xml:space` -> `"xml:space"`
51
- * @param node A `JSXAttribute` AST node
52
- * @returns The attribute name as a plain string
50
+ * - `xml:space` -> `"xml:space"`.
51
+ * @param node A `JSXAttribute` AST node.
52
+ * @returns The attribute name as a plain string.
53
53
  */
54
54
  function getAttributeName(node) {
55
55
  if (node.name.type === AST_NODE_TYPES.JSXIdentifier) return node.name.name;
@@ -59,7 +59,7 @@ function getAttributeName(node) {
59
59
  //#endregion
60
60
  //#region src/find-attribute.ts
61
61
  /**
62
- * Find a JSX attribute (or spread attribute containing the property) by name on a given element
62
+ * Find a JSX attribute (or spread attribute containing the property) by name on a given element.
63
63
  *
64
64
  * Returns the last matching attribute to mirror React's behavior where later props win,
65
65
  * or `undefined` when the attribute is not present.
@@ -67,10 +67,10 @@ function getAttributeName(node) {
67
67
  * Spread attributes are resolved when possible: if the spread argument is an identifier
68
68
  * that resolves to an object expression, the object's properties are searched for a matching key.
69
69
  * Nested object expressions and nested spread identifiers are also resolved.
70
- * @param context The ESLint rule context (needed for variable resolution in spread attributes)
71
- * @param element The `JSXElement` node to search
72
- * @param name The attribute name to look for (ex: "className")
73
- * @returns The matching `JSXAttribute` or `JSXSpreadAttribute`, or `undefined` when not found
70
+ * @param context The ESLint rule context (needed for variable resolution in spread attributes).
71
+ * @param element The `JSXElement` node to search.
72
+ * @param name The attribute name to look for (ex: "className").
73
+ * @returns The matching `JSXAttribute` or `JSXSpreadAttribute`, or `undefined` when not found.
74
74
  */
75
75
  function findAttribute(context, element, name) {
76
76
  function findProperty(properties, name, seen = /* @__PURE__ */ new Set()) {
@@ -115,13 +115,13 @@ function findAttribute(context, element, name) {
115
115
  //#region src/find-parent-attribute.ts
116
116
  /**
117
117
  * Walk up the AST from `node` to find the nearest ancestor that is a `JSXAttribute`
118
- * and (optionally) passes a predicate
118
+ * and (optionally) passes a predicate.
119
119
  *
120
120
  * This is useful when a rule visitor enters a deeply nested node (ex: a `Literal`
121
121
  * inside an expression container) and needs to know which JSX attribute it belongs to.
122
- * @param node The starting node for the upward search
123
- * @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
122
+ * @param node The starting node for the upward search.
123
+ * @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.
125
125
  */
126
126
  function findParentAttribute(node, test = () => true) {
127
127
  const guard = (n) => {
@@ -134,14 +134,14 @@ function findParentAttribute(node, test = () => true) {
134
134
  //#region src/resolve-attribute-value.ts
135
135
  /**
136
136
  * Resolve the value of a JSX attribute (or spread attribute) into a
137
- * {@link JsxAttributeValue} descriptor that can be inspected further
137
+ * {@link JsxAttributeValue} descriptor that can be inspected further.
138
138
  *
139
139
  * This is the low-level building block; it operates on a single attribute
140
140
  * node that the caller has already located. For the higher-level "find by
141
141
  * name and resolve" combo, see {@link getAttributeValue}.
142
- * @param context The ESLint rule context (needed for scope look-ups)
143
- * @param attribute A `JSXAttribute` or `JSXSpreadAttribute` node
144
- * @returns A discriminated-union descriptor of the attribute's value
142
+ * @param context The ESLint rule context (needed for scope look-ups).
143
+ * @param attribute A `JSXAttribute` or `JSXSpreadAttribute` node.
144
+ * @returns A discriminated-union descriptor of the attribute's value.
145
145
  */
146
146
  function resolveAttributeValue(context, attribute) {
147
147
  if (attribute.type === AST_NODE_TYPES.JSXAttribute) return resolveJsxAttribute(context, attribute);
@@ -221,7 +221,7 @@ function resolveJsxSpreadAttribute(context, node) {
221
221
  //#region src/get-attribute-static-value.ts
222
222
  /**
223
223
  * Find an attribute by name on a JSX element and collapse its value to a plain
224
- * JavaScript value in a single step
224
+ * JavaScript value in a single step.
225
225
  *
226
226
  * This is a convenience composition of {@link findAttribute} ->
227
227
  * {@link resolveAttributeValue} -> `toStatic()`, with automatic handling of the
@@ -230,10 +230,10 @@ function resolveJsxSpreadAttribute(context, node) {
230
230
  * Returns `null` when the attribute is absent, `undefined` when the value cannot
231
231
  * be statically determined (including empty expression containers), and the
232
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
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
237
  */
238
238
  function getAttributeStaticValue(context, element, name) {
239
239
  const attr = findAttribute(context, element, name);
@@ -247,15 +247,15 @@ function getAttributeStaticValue(context, element, name) {
247
247
  //#endregion
248
248
  //#region src/get-attribute-value.ts
249
249
  /**
250
- * Find an attribute by name on a JSX element and resolve its value in a single call
250
+ * Find an attribute by name on a JSX element and resolve its value in a single call.
251
251
  *
252
252
  * This is a convenience composition of {@link findAttribute} and
253
253
  * {@link resolveAttributeValue} that eliminates the most common two-step
254
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
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.
259
259
  */
260
260
  function getAttributeValue(context, element, name) {
261
261
  const attr = findAttribute(context, element, name);
@@ -267,42 +267,42 @@ function getAttributeValue(context, element, name) {
267
267
  //#region src/is-whitespace.ts
268
268
  /**
269
269
  * Check whether a JSX child node is whitespace padding that React would
270
- * trim away during rendering
270
+ * trim away during rendering.
271
271
  *
272
272
  * A child is considered whitespace padding when it is a `JSXText` node whose
273
273
  * content is empty after applying React's whitespace normalization
274
274
  * (see {@link collapseMultilineText}, modelled after Babel's
275
275
  * `cleanJSXElementLiteralChild`). This is the whitespace that appears between
276
276
  * JSX tags purely for formatting.
277
- * @param node A JSX child node
278
- * @returns `true` when the node is purely formatting whitespace
277
+ * @param node A JSX child node.
278
+ * @returns `true` when the node is purely formatting whitespace.
279
279
  */
280
280
  function isWhitespace(node) {
281
281
  if (node.type !== AST_NODE_TYPES.JSXText) return false;
282
282
  return collapseMultilineText(node.value) == null && node.value.includes("\n");
283
283
  }
284
284
  /**
285
- * Check whether a JSX child node is any whitespace-only text
285
+ * Check whether a JSX child node is any whitespace-only text.
286
286
  *
287
287
  * This is a looser variant of {@link isWhitespace}; it matches every
288
288
  * `JSXText` node whose raw content is empty after trimming, regardless of
289
289
  * whether it contains a newline.
290
- * @param node A JSX child node
291
- * @returns `true` when the node is a whitespace-only `JSXText`
290
+ * @param node A JSX child node.
291
+ * @returns `true` when the node is a whitespace-only `JSXText`.
292
292
  */
293
293
  function isWhitespaceText(node) {
294
294
  if (node.type !== AST_NODE_TYPES.JSXText) return false;
295
295
  return node.raw.trim() === "";
296
296
  }
297
297
  /**
298
- * Check whether a JSX child node is an empty string expression (`{""}`)
298
+ * Check whether a JSX child node is an empty string expression (`{""}`).
299
299
  *
300
300
  * React's reconciler and SSR renderer explicitly skip empty strings,
301
301
  * producing no DOM node (see `ReactChildFiber.js` and `ReactFizzConfigDOM.js`).
302
302
  * Such expressions are therefore treated as non-rendered children, in the same
303
303
  * way as whitespace padding.
304
- * @param node A JSX child node
305
- * @returns `true` when the node is a `{""}` expression container
304
+ * @param node A JSX child node.
305
+ * @returns `true` when the node is a `{""}` expression container.
306
306
  */
307
307
  function isEmptyStringExpression(node) {
308
308
  if (node.type !== AST_NODE_TYPES.JSXExpressionContainer) return false;
@@ -314,7 +314,7 @@ function isEmptyStringExpression(node) {
314
314
  //#endregion
315
315
  //#region src/get-children.ts
316
316
  /**
317
- * Get the meaningful children of a JSX element or fragment
317
+ * Get the meaningful children of a JSX element or fragment.
318
318
  *
319
319
  * Mirrors Babel's `buildChildren` helper:
320
320
  * 1. Iterate over `element.children`.
@@ -322,8 +322,8 @@ function isEmptyStringExpression(node) {
322
322
  * 3. Skip `JSXExpressionContainer` nodes whose expression is empty.
323
323
  * 4. Skip `JSXEmptyExpression` nodes.
324
324
  * 5. Collect everything else.
325
- * @param element A `JSXElement` or `JSXFragment` node
326
- * @returns An array of children nodes that contribute to rendered output
325
+ * @param element A `JSXElement` or `JSXFragment` node.
326
+ * @returns An array of children nodes that contribute to rendered output.
327
327
  */
328
328
  function getChildren(element) {
329
329
  const elements = [];
@@ -348,14 +348,14 @@ function getChildren(element) {
348
348
  //#endregion
349
349
  //#region src/get-element-type.ts
350
350
  /**
351
- * Get the string representation of a JSX element's type
351
+ * Get the string representation of a JSX element's type.
352
352
  *
353
353
  * - `<div>` -> `"div"`
354
354
  * - `<Foo.Bar>` -> `"Foo.Bar"`
355
355
  * - `<React.Fragment>` -> `"React.Fragment"`
356
- * - `<></>` -> `""`
357
- * @param node A `JSXElement` or `JSXFragment` node
358
- * @returns The fully-qualified element type string
356
+ * - `<></>` -> `""`.
357
+ * @param node A `JSXElement` or `JSXFragment` node.
358
+ * @returns The fully-qualified element type string.
359
359
  */
360
360
  function getElementFullType(node) {
361
361
  if (node.type === AST_NODE_TYPES.JSXFragment) return "";
@@ -369,13 +369,13 @@ function getElementFullType(node) {
369
369
  return getQualifiedName(node.openingElement.name);
370
370
  }
371
371
  /**
372
- * Get the self name (last dot-separated segment) of a JSX element type
372
+ * Get the self name (last dot-separated segment) of a JSX element type.
373
373
  *
374
374
  * - `<Foo.Bar.Baz>` -> `"Baz"`
375
375
  * - `<div>` -> `"div"`
376
- * - `<></>` -> `""`
377
- * @param node A `JSXElement` or `JSXFragment` node
378
- * @returns The last segment of the element type, or `""` for fragments
376
+ * - `<></>` -> `""`.
377
+ * @param node A `JSXElement` or `JSXFragment` node.
378
+ * @returns The last segment of the element type, or `""` for fragments.
379
379
  */
380
380
  function getElementSelfType(node) {
381
381
  return getElementFullType(node).split(".").at(-1) ?? "";
@@ -384,16 +384,16 @@ function getElementSelfType(node) {
384
384
  //#endregion
385
385
  //#region src/has-any-attribute.ts
386
386
  /**
387
- * Check whether a JSX element carries at least one of the given attributes
387
+ * Check whether a JSX element carries at least one of the given attributes.
388
388
  *
389
389
  * This is a batch variant of {@link hasAttribute} for the common pattern of
390
390
  * short-circuiting on multiple prop names.
391
391
  *
392
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
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
397
  */
398
398
  function hasAnyAttribute(context, element, names) {
399
399
  return names.some((name) => findAttribute(context, element, name) != null);
@@ -402,17 +402,17 @@ function hasAnyAttribute(context, element, names) {
402
402
  //#endregion
403
403
  //#region src/has-attribute.ts
404
404
  /**
405
- * Check whether a JSX element carries a given attribute (prop)
405
+ * Check whether a JSX element carries a given attribute (prop).
406
406
  *
407
407
  * This is a thin convenience wrapper around {@link findAttribute} for the
408
408
  * common case where you only need a boolean answer.
409
409
  *
410
410
  * Spread attributes are taken into account: `<Comp {...{ disabled: true }} />`
411
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
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
416
  */
417
417
  function hasAttribute(context, element, name) {
418
418
  return findAttribute(context, element, name) != null;
@@ -422,7 +422,7 @@ function hasAttribute(context, element, name) {
422
422
  //#region src/has-children.ts
423
423
  /**
424
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
425
+ * at least one child that is not purely whitespace text or an empty string expression.
426
426
  *
427
427
  * A `JSXText` child whose `raw` content is empty after trimming is considered
428
428
  * non-meaningful because it is typically a code-formatting artifact
@@ -439,8 +439,8 @@ function hasAttribute(context, element, name) {
439
439
  * always equal to `getChildren(node).length > 0`: they differ for
440
440
  * whitespace-only children that have no newline, such as `<div> </div>` or
441
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
442
+ * @param element A `JSXElement` or `JSXFragment` node.
443
+ * @returns `true` when the element has at least one meaningful child.
444
444
  */
445
445
  function hasChildren(element) {
446
446
  if (element.children.length === 0) return false;
@@ -450,26 +450,519 @@ function hasChildren(element) {
450
450
  //#endregion
451
451
  //#region src/has-every-attribute.ts
452
452
  /**
453
- * Check whether a JSX element carries all of the given attributes (props)
453
+ * Check whether a JSX element carries all of the given attributes (props).
454
454
  *
455
455
  * This is a batch variant of {@link hasAttribute} for the common pattern
456
456
  * where a rule needs to verify that a set of required props are all present.
457
457
  *
458
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
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
463
  */
464
464
  function hasEveryAttribute(context, element, names) {
465
465
  return names.every((name) => findAttribute(context, element, name) != null);
466
466
  }
467
467
 
468
+ //#endregion
469
+ //#region ../../.pkgs/eff/dist/index.js
470
+ /**
471
+ * Applies a `pipe` method's variadic arguments to an initial value from left
472
+ * to right.
473
+ *
474
+ * **When to use**
475
+ *
476
+ * Use to implement a custom `.pipe(...)` method from JavaScript's `arguments`
477
+ * object.
478
+ *
479
+ * **Details**
480
+ *
481
+ * This helper is intended for implementing `Pipeable.pipe` methods that
482
+ * receive JavaScript's `arguments` object. With no functions it returns the
483
+ * original value; otherwise it feeds each result into the next function.
484
+ *
485
+ * **Example** (Implementing a pipe method)
486
+ *
487
+ * ```ts
488
+ * import { Pipeable } from "effect"
489
+ *
490
+ * class NumberBox {
491
+ * constructor(readonly value: number) {}
492
+ *
493
+ * pipe(..._fns: ReadonlyArray<(value: number) => number>): number {
494
+ * return Pipeable.pipeArguments(this.value, arguments) as number
495
+ * }
496
+ * }
497
+ *
498
+ * const result = new NumberBox(5).pipe(
499
+ * (n) => n + 2,
500
+ * (n) => n * 3
501
+ * )
502
+ * console.log(result) // 21
503
+ * ```
504
+ *
505
+ * @category combinators
506
+ * @since 2.0.0
507
+ */
508
+ const pipeArguments = (self, args) => {
509
+ switch (args.length) {
510
+ case 0: return self;
511
+ case 1: return args[0](self);
512
+ case 2: return args[1](args[0](self));
513
+ case 3: return args[2](args[1](args[0](self)));
514
+ case 4: return args[3](args[2](args[1](args[0](self))));
515
+ case 5: return args[4](args[3](args[2](args[1](args[0](self)))));
516
+ case 6: return args[5](args[4](args[3](args[2](args[1](args[0](self))))));
517
+ case 7: return args[6](args[5](args[4](args[3](args[2](args[1](args[0](self)))))));
518
+ case 8: return args[7](args[6](args[5](args[4](args[3](args[2](args[1](args[0](self))))))));
519
+ case 9: return args[8](args[7](args[6](args[5](args[4](args[3](args[2](args[1](args[0](self)))))))));
520
+ default: {
521
+ let ret = self;
522
+ for (let i = 0, len = args.length; i < len; i++) ret = args[i](ret);
523
+ return ret;
524
+ }
525
+ }
526
+ };
527
+ /**
528
+ * Reusable prototype that implements `Pipeable.pipe`.
529
+ *
530
+ * **When to use**
531
+ *
532
+ * Use when classes or object prototypes can reuse this value when they need the
533
+ * standard pipe implementation backed by `pipeArguments`.
534
+ *
535
+ * @category prototypes
536
+ * @since 3.15.0
537
+ */
538
+ const Prototype = { pipe() {
539
+ return pipeArguments(this, arguments);
540
+ } };
541
+ /**
542
+ * Provides a base constructor whose instances implement the standard `Pipeable.pipe`
543
+ * method.
544
+ *
545
+ * **When to use**
546
+ *
547
+ * Use when you need to define a class that supports Effect-style method
548
+ * chaining through `.pipe(...)`.
549
+ *
550
+ * @category constructors
551
+ * @since 3.15.0
552
+ */
553
+ const Class = (function() {
554
+ function PipeableBase() {}
555
+ PipeableBase.prototype = Prototype;
556
+ return PipeableBase;
557
+ })();
558
+ /**
559
+ * Provides small helpers for defining and reusing TypeScript functions.
560
+ *
561
+ * The main helpers are `pipe` and `flow` for left-to-right composition and
562
+ * `dual` for APIs that support both direct and pipe-friendly call styles. The
563
+ * module also contains small identity, constant, tuple, type-level, and
564
+ * memoization helpers used across the library.
565
+ *
566
+ * @since 2.0.0
567
+ */
568
+ /**
569
+ * Creates a function that can be called in data-first style or data-last
570
+ * (`pipe`-friendly) style.
571
+ *
572
+ * **When to use**
573
+ *
574
+ * Use to expose one implementation through both direct and `pipe`-friendly
575
+ * call styles.
576
+ *
577
+ * **Details**
578
+ *
579
+ * Pass either the arity of the uncurried function or a predicate that decides
580
+ * whether the current call is data-first. Arity is the common case. Use a
581
+ * predicate when optional arguments make arity ambiguous.
582
+ *
583
+ * **Example** (Selecting data-first or data-last style by arity)
584
+ *
585
+ * ```ts
586
+ * import { Function, pipe } from "effect"
587
+ *
588
+ * const sum = Function.dual<
589
+ * (that: number) => (self: number) => number,
590
+ * (self: number, that: number) => number
591
+ * >(2, (self, that) => self + that)
592
+ *
593
+ * console.log(sum(2, 3)) // 5
594
+ * console.log(pipe(2, sum(3))) // 5
595
+ * ```
596
+ *
597
+ * **Example** (Defining overloads with call signatures)
598
+ *
599
+ * ```ts
600
+ * import { Function, pipe } from "effect"
601
+ *
602
+ * const sum: {
603
+ * (that: number): (self: number) => number
604
+ * (self: number, that: number): number
605
+ * } = Function.dual(2, (self: number, that: number): number => self + that)
606
+ *
607
+ * console.log(sum(2, 3)) // 5
608
+ * console.log(pipe(2, sum(3))) // 5
609
+ * ```
610
+ *
611
+ * **Example** (Selecting data-first or data-last style with a predicate)
612
+ *
613
+ * ```ts
614
+ * import { Function, pipe } from "effect"
615
+ *
616
+ * const sum = Function.dual<
617
+ * (that: number) => (self: number) => number,
618
+ * (self: number, that: number) => number
619
+ * >(
620
+ * (args) => args.length === 2,
621
+ * (self, that) => self + that
622
+ * )
623
+ *
624
+ * console.log(sum(2, 3)) // 5
625
+ * console.log(pipe(2, sum(3))) // 5
626
+ * ```
627
+ *
628
+ * @category combinators
629
+ * @since 2.0.0
630
+ */
631
+ const dual = function(arity, body) {
632
+ if (typeof arity === "function") return function() {
633
+ return arity(arguments) ? body.apply(this, arguments) : ((self) => body(self, ...arguments));
634
+ };
635
+ switch (arity) {
636
+ case 0:
637
+ case 1: throw new RangeError(`Invalid arity ${arity}`);
638
+ case 2: return function(a, b) {
639
+ if (arguments.length >= 2) return body(a, b);
640
+ return function(self) {
641
+ return body(self, a);
642
+ };
643
+ };
644
+ case 3: return function(a, b, c) {
645
+ if (arguments.length >= 3) return body(a, b, c);
646
+ return function(self) {
647
+ return body(self, a, b);
648
+ };
649
+ };
650
+ default: return function() {
651
+ if (arguments.length >= arity) return body.apply(this, arguments);
652
+ const args = arguments;
653
+ return function(self) {
654
+ return body(self, ...args);
655
+ };
656
+ };
657
+ }
658
+ };
659
+ /**
660
+ * Returns its input argument unchanged.
661
+ *
662
+ * **When to use**
663
+ *
664
+ * Use to return a value unchanged where a function is required.
665
+ *
666
+ * **Example** (Returning the same value)
667
+ *
668
+ * ```ts
669
+ * import { identity } from "effect"
670
+ * import * as assert from "node:assert"
671
+ *
672
+ * assert.deepStrictEqual(identity(5), 5)
673
+ * ```
674
+ *
675
+ * @category combinators
676
+ * @since 2.0.0
677
+ */
678
+ const identity = (a) => a;
679
+ /**
680
+ * Returns the input value with a different static type.
681
+ *
682
+ * **When to use**
683
+ *
684
+ * Use when you need an explicit type-level cast and accept that the value is
685
+ * returned unchanged at runtime.
686
+ *
687
+ * **Gotchas**
688
+ *
689
+ * This is a type-level cast only; it performs no runtime validation or
690
+ * conversion.
691
+ *
692
+ * @see {@link satisfies} for checking assignability without changing the resulting type
693
+ *
694
+ * @category utility types
695
+ * @since 4.0.0
696
+ */
697
+ const cast = identity;
698
+ /**
699
+ * Creates a zero-argument function that always returns the provided value.
700
+ *
701
+ * **When to use**
702
+ *
703
+ * Use when you need a thunk or callback that returns the same value on every
704
+ * invocation.
705
+ *
706
+ * **Example** (Creating a constant thunk)
707
+ *
708
+ * ```ts
709
+ * import { Function } from "effect"
710
+ * import * as assert from "node:assert"
711
+ *
712
+ * const constNull = Function.constant(null)
713
+ *
714
+ * assert.deepStrictEqual(constNull(), null)
715
+ * assert.deepStrictEqual(constNull(), null)
716
+ * ```
717
+ *
718
+ * @category constructors
719
+ * @since 2.0.0
720
+ */
721
+ const constant = (value) => () => value;
722
+ /**
723
+ * Returns `true` when called.
724
+ *
725
+ * **When to use**
726
+ *
727
+ * Use when you need a thunk that returns `true` on every invocation.
728
+ *
729
+ * **Example** (Returning true from a thunk)
730
+ *
731
+ * ```ts
732
+ * import { Function } from "effect"
733
+ * import * as assert from "node:assert"
734
+ *
735
+ * assert.deepStrictEqual(Function.constTrue(), true)
736
+ * ```
737
+ *
738
+ * @category constants
739
+ * @since 2.0.0
740
+ */
741
+ const constTrue = constant(true);
742
+ /**
743
+ * Returns `false` when called.
744
+ *
745
+ * **When to use**
746
+ *
747
+ * Use when you need a thunk that returns `false` on every invocation.
748
+ *
749
+ * **Example** (Returning false from a thunk)
750
+ *
751
+ * ```ts
752
+ * import { Function } from "effect"
753
+ * import * as assert from "node:assert"
754
+ *
755
+ * assert.deepStrictEqual(Function.constFalse(), false)
756
+ * ```
757
+ *
758
+ * @category constants
759
+ * @since 2.0.0
760
+ */
761
+ const constFalse = constant(false);
762
+ /**
763
+ * Returns `null` when called.
764
+ *
765
+ * **When to use**
766
+ *
767
+ * Use when you need a thunk that returns `null` on every invocation.
768
+ *
769
+ * **Example** (Returning null from a thunk)
770
+ *
771
+ * ```ts
772
+ * import { Function } from "effect"
773
+ * import * as assert from "node:assert"
774
+ *
775
+ * assert.deepStrictEqual(Function.constNull(), null)
776
+ * ```
777
+ *
778
+ * @category constants
779
+ * @since 2.0.0
780
+ */
781
+ const constNull = constant(null);
782
+ /**
783
+ * Returns `undefined` when called.
784
+ *
785
+ * **When to use**
786
+ *
787
+ * Use when you need a thunk that returns `undefined` on every invocation.
788
+ *
789
+ * **Example** (Returning undefined from a thunk)
790
+ *
791
+ * ```ts
792
+ * import { Function } from "effect"
793
+ * import * as assert from "node:assert"
794
+ *
795
+ * assert.deepStrictEqual(Function.constUndefined(), undefined)
796
+ * ```
797
+ *
798
+ * @category constants
799
+ * @since 2.0.0
800
+ */
801
+ const constUndefined = constant(void 0);
802
+ /**
803
+ * 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`.
804
+ * The result is obtained by first applying the `ab` function to `a` and then applying the `bc` function to the result of `ab`.
805
+ *
806
+ * **When to use**
807
+ *
808
+ * Use to compose exactly two unary functions into a reusable unary function.
809
+ *
810
+ * **Example** (Composing two functions)
811
+ *
812
+ * ```ts
813
+ * import { Function } from "effect"
814
+ * import * as assert from "node:assert"
815
+ *
816
+ * const increment = (n: number) => n + 1
817
+ * const square = (n: number) => n * n
818
+ *
819
+ * assert.strictEqual(Function.compose(increment, square)(2), 9)
820
+ * ```
821
+ *
822
+ * @see {@link flow} for composing a left-to-right sequence of functions
823
+ * @see {@link pipe} for applying a value through a left-to-right sequence immediately
824
+ *
825
+ * @category combinators
826
+ * @since 2.0.0
827
+ */
828
+ const compose = dual(2, (ab, bc) => (a) => bc(ab(a)));
829
+ /**
830
+ * Marks an impossible branch by accepting a `never` value and returning any
831
+ * type.
832
+ *
833
+ * **When to use**
834
+ *
835
+ * Use when you need a return value in a branch that exhaustive checks prove
836
+ * cannot be reached.
837
+ *
838
+ * **Gotchas**
839
+ *
840
+ * Calling `absurd` throws, because a value of type `never` should be
841
+ * impossible at runtime.
842
+ *
843
+ * **Example** (Handling impossible values)
844
+ *
845
+ * ```ts
846
+ * import { absurd } from "effect"
847
+ *
848
+ * const handleNever = (value: never) => {
849
+ * return absurd(value) // This will throw an error if called
850
+ * }
851
+ * ```
852
+ *
853
+ * @category utility types
854
+ * @since 2.0.0
855
+ */
856
+ const absurd = (_) => {
857
+ throw new Error("Called `absurd` function which should be uncallable");
858
+ };
859
+ /**
860
+ * Creates a compile-time placeholder for a value of any type.
861
+ *
862
+ * **When to use**
863
+ *
864
+ * Use as a temporary typed placeholder while developing incomplete code.
865
+ *
866
+ * **Gotchas**
867
+ *
868
+ * `hole` is intended for temporary development use. If the placeholder is
869
+ * evaluated at runtime, it throws.
870
+ *
871
+ * **Example** (Creating a development placeholder)
872
+ *
873
+ * ```ts
874
+ * import { hole } from "effect"
875
+ *
876
+ * // Intentionally not called: `hole` throws if the placeholder is evaluated.
877
+ * const buildUser = (id: number): { readonly id: number; readonly name: string } => ({
878
+ * id,
879
+ * name: hole<string>()
880
+ * })
881
+ *
882
+ * console.log(typeof buildUser) // "function"
883
+ * ```
884
+ *
885
+ * @category utility types
886
+ * @since 2.0.0
887
+ */
888
+ const hole = cast(absurd);
889
+ /**
890
+ * Drops the longest prefix of elements from an array that satisfy the given predicate.
891
+ *
892
+ * Supports both data-first and data-last (`pipe`-friendly) call styles.
893
+ *
894
+ * @param pred - The predicate to test each element with.
895
+ * @returns A new array without the matching prefix.
896
+ * @example
897
+ * ```ts
898
+ * import * as assert from "node:assert"
899
+ * import { dropWhile, pipe } from "@local/eff"
900
+ *
901
+ * // data-first
902
+ * assert.deepStrictEqual(dropWhile([1, 2, 3, 2, 1], (n: number) => n < 3), [3, 2, 1])
903
+ *
904
+ * // data-last
905
+ * assert.deepStrictEqual(pipe([1, 2, 3, 2, 1], dropWhile((n: number) => n < 3)), [3, 2, 1])
906
+ * ```
907
+ * @category array
908
+ */
909
+ const dropWhile = dual(2, (xs, pred) => {
910
+ const len = xs.length;
911
+ let idx = 0;
912
+ while (idx < len && pred(xs[idx])) idx++;
913
+ return xs.slice(idx);
914
+ });
915
+ /**
916
+ * Takes the longest prefix of elements from an array that satisfy the given predicate.
917
+ *
918
+ * Supports both data-first and data-last (`pipe`-friendly) call styles.
919
+ *
920
+ * @param pred - The predicate to test each element with.
921
+ * @returns A new array containing only the matching prefix.
922
+ * @example
923
+ * ```ts
924
+ * import * as assert from "node:assert"
925
+ * import { pipe, takeWhile } from "@local/eff"
926
+ *
927
+ * // data-first
928
+ * assert.deepStrictEqual(takeWhile([1, 2, 3, 2, 1], (n: number) => n < 3), [1, 2])
929
+ *
930
+ * // data-last
931
+ * assert.deepStrictEqual(pipe([1, 2, 3, 2, 1], takeWhile((n: number) => n < 3)), [1, 2])
932
+ * ```
933
+ * @category array
934
+ */
935
+ const takeWhile = dual(2, (xs, pred) => {
936
+ const len = xs.length;
937
+ let idx = 0;
938
+ while (idx < len && pred(xs[idx])) idx++;
939
+ return xs.slice(0, idx);
940
+ });
941
+
942
+ //#endregion
943
+ //#region src/is-attribute.ts
944
+ /**
945
+ * Check whether a node is a `JSXAttribute` with the given name.
946
+ *
947
+ * Only plain identifier names are matched (ex: `className`); namespaced
948
+ * attributes (ex: `xml:space`) do not match.
949
+ *
950
+ * Supports both data-first and data-last (curried) call styles:
951
+ * - `isAttribute(node, "className")`
952
+ * - `isAttribute("className")(node)`.
953
+ * @param node The AST node to test.
954
+ * @param name The attribute name to match (ex: "className").
955
+ * @returns `true` when the node is a `JSXAttribute` named `name`.
956
+ */
957
+ const isAttribute = dual(2, (node, name) => {
958
+ return node.type === AST_NODE_TYPES.JSXAttribute && node.name.type === AST_NODE_TYPES.JSXIdentifier && node.name.name === name;
959
+ });
960
+
468
961
  //#endregion
469
962
  //#region src/is-element.ts
470
963
  /**
471
964
  * Check whether a node is a `JSXElement` (or `JSXFragment`) and optionally
472
- * matches a given test
965
+ * matches a given test.
473
966
  *
474
967
  * Modelled after
475
968
  * [`hast-util-is-element`](https://github.com/syntax-tree/hast-util-is-element):
@@ -477,9 +970,9 @@ function hasEveryAttribute(context, element, names) {
477
970
  *
478
971
  * When called without a test, the function acts as a simple type-guard
479
972
  * for `JSXElement | JSXFragment`.
480
- * @param node The AST node to test
481
- * @param test Optional test to match the element type against
482
- * @returns `true` when the node is a matching JSX element
973
+ * @param node The AST node to test.
974
+ * @param test Optional test to match the element type against.
975
+ * @returns `true` when the node is a matching JSX element.
483
976
  */
484
977
  function isElement(node, test) {
485
978
  if (node == null) return false;
@@ -496,7 +989,7 @@ function isElement(node, test) {
496
989
  //#endregion
497
990
  //#region src/is-fragment-element.ts
498
991
  /**
499
- * Check whether a node is a React Fragment element
992
+ * Check whether a node is a React Fragment element.
500
993
  *
501
994
  * Recognizes both the shorthand `<>...</>` syntax (`JSXFragment`) and the
502
995
  * explicit `<Fragment>` / `<React.Fragment>` form (`JSXElement`).
@@ -504,9 +997,9 @@ function isElement(node, test) {
504
997
  * The comparison is performed against the self name (last dot-separated
505
998
  * segment) of both the node and the configured factory, so `<React.Fragment>`
506
999
  * matches `"React.Fragment"` and `<Fragment>` matches `"Fragment"`.
507
- * @param node The AST node to test
508
- * @param jsxFragmentFactory The configured fragment factory string (ex: "React.Fragment")
509
- * @returns `true` when the node represents a React Fragment
1000
+ * @param node The AST node to test.
1001
+ * @param jsxFragmentFactory The configured fragment factory string (ex: "React.Fragment").
1002
+ * @returns `true` when the node represents a React Fragment.
510
1003
  */
511
1004
  function isFragmentElement(node, jsxFragmentFactory = "React.Fragment") {
512
1005
  if (node.type === AST_NODE_TYPES.JSXFragment) return true;
@@ -518,13 +1011,13 @@ function isFragmentElement(node, jsxFragmentFactory = "React.Fragment") {
518
1011
  //#endregion
519
1012
  //#region src/is-host-element.ts
520
1013
  /**
521
- * Check whether a node is a host (intrinsic / DOM) element
1014
+ * Check whether a node is a host (intrinsic / DOM) element.
522
1015
  *
523
1016
  * A host element is a `JSXElement` whose tag name is a plain `JSXIdentifier`
524
1017
  * starting with a lowercase letter, the same heuristic React uses to
525
1018
  * distinguish `<div>` from `<MyComponent>`.
526
- * @param node The AST node to test
527
- * @returns `true` when the node is a `JSXElement` with a lowercase tag name
1019
+ * @param node The AST node to test.
1020
+ * @returns `true` when the node is a `JSXElement` with a lowercase tag name.
528
1021
  */
529
1022
  function isHostElement(node) {
530
1023
  if (node.type !== AST_NODE_TYPES.JSXElement) return false;
@@ -537,7 +1030,7 @@ function isHostElement(node) {
537
1030
  //#endregion
538
1031
  //#region src/jsx-detection-hint.ts
539
1032
  /**
540
- * Hints for JSX detection
1033
+ * Hints for JSX detection.
541
1034
  */
542
1035
  const JsxDetectionHint = {
543
1036
  None: 0n,
@@ -554,7 +1047,7 @@ const JsxDetectionHint = {
554
1047
  RequireBothBranchesOfConditionalExpressionToBeJsx: 1n << 10n
555
1048
  };
556
1049
  /**
557
- * Default JSX detection hint
1050
+ * Default JSX detection hint.
558
1051
  *
559
1052
  * Skips number, bigint, boolean, string, and undefined literals,
560
1053
  * the value types that are commonly returned alongside JSX in React
@@ -563,4 +1056,4 @@ const JsxDetectionHint = {
563
1056
  const DEFAULT_JSX_DETECTION_HINT = 0n | JsxDetectionHint.DoNotIncludeJsxWithNumberValue | JsxDetectionHint.DoNotIncludeJsxWithBigIntValue | JsxDetectionHint.DoNotIncludeJsxWithBooleanValue | JsxDetectionHint.DoNotIncludeJsxWithStringValue | JsxDetectionHint.DoNotIncludeJsxWithUndefinedValue;
564
1057
 
565
1058
  //#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 };
1059
+ export { DEFAULT_JSX_DETECTION_HINT, JsxDetectionHint, collapseMultilineText, findAttribute, findParentAttribute, getAttributeName, getAttributeStaticValue, getAttributeValue, getChildren, getElementFullType, getElementSelfType, hasAnyAttribute, hasAttribute, hasChildren, hasEveryAttribute, isAttribute, isElement, isEmptyStringExpression, isFragmentElement, isHostElement, isWhitespace, isWhitespaceText, resolveAttributeValue };