@eslint-react/core 5.24.0 → 5.24.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +104 -1020
  2. package/package.json +6 -6
package/dist/index.js CHANGED
@@ -7,11 +7,6 @@ import { P, isMatching, match } from "ts-pattern";
7
7
  import ts from "typescript";
8
8
 
9
9
  //#region src/api.ts
10
- /**
11
- * Check if the node is a React API identifier or member expression.
12
- * @param api The React API name to check against (ex: "useState", "React.memo").
13
- * @returns A predicate function to check if a node matches the API.
14
- */
15
10
  function isAPI(api) {
16
11
  const func = (context, node) => {
17
12
  if (node == null) return false;
@@ -25,11 +20,6 @@ function isAPI(api) {
25
20
  }
26
21
  return dual;
27
22
  }
28
- /**
29
- * Check if the node is a call expression to a specific React API.
30
- * @param api The React API name to check against.
31
- * @returns A predicate function to check if a node is a call to the API.
32
- */
33
23
  function isAPICall(api) {
34
24
  const func = (context, node) => {
35
25
  if (node == null) return false;
@@ -41,157 +31,181 @@ function isAPICall(api) {
41
31
  }
42
32
  return dual;
43
33
  }
44
- /** Check if the node is a React `captureOwnerStack` API identifier or member expression. */
45
34
  const isCaptureOwnerStack = isAPI("captureOwnerStack");
46
- /** Check if the node is a React `Children.count` API identifier or member expression. */
47
35
  const isChildrenCount = isAPI("Children.count");
48
- /** Check if the node is a React `Children.forEach` API identifier or member expression. */
49
36
  const isChildrenForEach = isAPI("Children.forEach");
50
- /** Check if the node is a React `Children.map` API identifier or member expression. */
51
37
  const isChildrenMap = isAPI("Children.map");
52
- /** Check if the node is a React `Children.only` API identifier or member expression. */
53
38
  const isChildrenOnly = isAPI("Children.only");
54
- /** Check if the node is a React `Children.toArray` API identifier or member expression. */
55
39
  const isChildrenToArray = isAPI("Children.toArray");
56
- /** Check if the node is a React `cloneElement` API identifier or member expression. */
57
40
  const isCloneElement = isAPI("cloneElement");
58
- /** Check if the node is a React `createContext` API identifier or member expression. */
59
41
  const isCreateContext = isAPI("createContext");
60
- /** Check if the node is a React `createElement` API identifier or member expression. */
61
42
  const isCreateElement = isAPI("createElement");
62
- /** Check if the node is a React `createRef` API identifier or member expression. */
63
43
  const isCreateRef = isAPI("createRef");
64
- /** Check if the node is a React `forwardRef` API identifier or member expression. */
65
44
  const isForwardRef = isAPI("forwardRef");
66
- /** Check if the node is a React `memo` API identifier or member expression. */
67
45
  const isMemo = isAPI("memo");
68
- /** Check if the node is a React `lazy` API identifier or member expression. */
69
46
  const isLazy = isAPI("lazy");
70
- /** Check if the node is a call expression to the React `captureOwnerStack` API. */
71
47
  const isCaptureOwnerStackCall = isAPICall("captureOwnerStack");
72
- /** Check if the node is a call expression to the React `Children.count` API. */
73
48
  const isChildrenCountCall = isAPICall("Children.count");
74
- /** Check if the node is a call expression to the React `Children.forEach` API. */
75
49
  const isChildrenForEachCall = isAPICall("Children.forEach");
76
- /** Check if the node is a call expression to the React `Children.map` API. */
77
50
  const isChildrenMapCall = isAPICall("Children.map");
78
- /** Check if the node is a call expression to the React `Children.only` API. */
79
51
  const isChildrenOnlyCall = isAPICall("Children.only");
80
- /** Check if the node is a call expression to the React `Children.toArray` API. */
81
52
  const isChildrenToArrayCall = isAPICall("Children.toArray");
82
- /** Check if the node is a call expression to the React `cloneElement` API. */
83
53
  const isCloneElementCall = isAPICall("cloneElement");
84
- /** Check if the node is a call expression to the React `createContext` API. */
85
54
  const isCreateContextCall = isAPICall("createContext");
86
- /** Check if the node is a call expression to the React `createElement` API. */
87
55
  const isCreateElementCall = isAPICall("createElement");
88
- /** Check if the node is a call expression to the React `createRef` API. */
89
56
  const isCreateRefCall = isAPICall("createRef");
90
- /** Check if the node is a call expression to the React `forwardRef` API. */
91
57
  const isForwardRefCall = isAPICall("forwardRef");
92
- /** Check if the node is a call expression to the React `memo` API. */
93
58
  const isMemoCall = isAPICall("memo");
94
- /** Check if the node is a call expression to the React `lazy` API. */
95
59
  const isLazyCall = isAPICall("lazy");
96
- /** Check if the node is a React `use` API identifier or member expression. */
97
60
  const isUse = isAPI("use");
98
- /** Check if the node is a React `useActionState` API identifier or member expression. */
99
61
  const isUseActionState = isAPI("useActionState");
100
- /** Check if the node is a React `useCallback` API identifier or member expression. */
101
62
  const isUseCallback = isAPI("useCallback");
102
- /** Check if the node is a React `useContext` API identifier or member expression. */
103
63
  const isUseContext = isAPI("useContext");
104
- /** Check if the node is a React `useDebugValue` API identifier or member expression. */
105
64
  const isUseDebugValue = isAPI("useDebugValue");
106
- /** Check if the node is a React `useDeferredValue` API identifier or member expression. */
107
65
  const isUseDeferredValue = isAPI("useDeferredValue");
108
- /** Check if the node is a React `useEffect` API identifier or member expression. */
109
66
  const isUseEffect = isAPI("useEffect");
110
- /** Check if the node is a React `useFormStatus` API identifier or member expression. */
111
67
  const isUseFormStatus = isAPI("useFormStatus");
112
- /** Check if the node is a React `useId` API identifier or member expression. */
113
68
  const isUseId = isAPI("useId");
114
- /** Check if the node is a React `useImperativeHandle` API identifier or member expression. */
115
69
  const isUseImperativeHandle = isAPI("useImperativeHandle");
116
- /** Check if the node is a React `useInsertionEffect` API identifier or member expression. */
117
70
  const isUseInsertionEffect = isAPI("useInsertionEffect");
118
- /** Check if the node is a React `useLayoutEffect` API identifier or member expression. */
119
71
  const isUseLayoutEffect = isAPI("useLayoutEffect");
120
- /** Check if the node is a React `useMemo` API identifier or member expression. */
121
72
  const isUseMemo = isAPI("useMemo");
122
- /** Check if the node is a React `useOptimistic` API identifier or member expression. */
123
73
  const isUseOptimistic = isAPI("useOptimistic");
124
- /** Check if the node is a React `useReducer` API identifier or member expression. */
125
74
  const isUseReducer = isAPI("useReducer");
126
- /** Check if the node is a React `useRef` API identifier or member expression. */
127
75
  const isUseRef = isAPI("useRef");
128
- /** Check if the node is a React `useState` API identifier or member expression. */
129
76
  const isUseState = isAPI("useState");
130
- /** Check if the node is a React `useSyncExternalStore` API identifier or member expression. */
131
77
  const isUseSyncExternalStore = isAPI("useSyncExternalStore");
132
- /** Check if the node is a React `useTransition` API identifier or member expression. */
133
78
  const isUseTransition = isAPI("useTransition");
134
- /** Check if the node is a call expression to the React `use` API. */
135
79
  const isUseCall = isAPICall("use");
136
- /** Check if the node is a call expression to the React `useActionState` API. */
137
80
  const isUseActionStateCall = isAPICall("useActionState");
138
- /** Check if the node is a call expression to the React `useCallback` API. */
139
81
  const isUseCallbackCall = isAPICall("useCallback");
140
- /** Check if the node is a call expression to the React `useContext` API. */
141
82
  const isUseContextCall = isAPICall("useContext");
142
- /** Check if the node is a call expression to the React `useDebugValue` API. */
143
83
  const isUseDebugValueCall = isAPICall("useDebugValue");
144
- /** Check if the node is a call expression to the React `useDeferredValue` API. */
145
84
  const isUseDeferredValueCall = isAPICall("useDeferredValue");
146
- /** Check if the node is a call expression to the React `useEffect` API. */
147
85
  const isUseEffectCall = isAPICall("useEffect");
148
- /** Check if the node is a call expression to the React `useFormStatus` API. */
149
86
  const isUseFormStatusCall = isAPICall("useFormStatus");
150
- /** Check if the node is a call expression to the React `useId` API. */
151
87
  const isUseIdCall = isAPICall("useId");
152
- /** Check if the node is a call expression to the React `useImperativeHandle` API. */
153
88
  const isUseImperativeHandleCall = isAPICall("useImperativeHandle");
154
- /** Check if the node is a call expression to the React `useInsertionEffect` API. */
155
89
  const isUseInsertionEffectCall = isAPICall("useInsertionEffect");
156
- /** Check if the node is a call expression to the React `useLayoutEffect` API. */
157
90
  const isUseLayoutEffectCall = isAPICall("useLayoutEffect");
158
- /** Check if the node is a call expression to the React `useMemo` API. */
159
91
  const isUseMemoCall = isAPICall("useMemo");
160
- /** Check if the node is a call expression to the React `useOptimistic` API. */
161
92
  const isUseOptimisticCall = isAPICall("useOptimistic");
162
- /** Check if the node is a call expression to the React `useReducer` API. */
163
93
  const isUseReducerCall = isAPICall("useReducer");
164
- /** Check if the node is a call expression to the React `useRef` API. */
165
94
  const isUseRefCall = isAPICall("useRef");
166
- /** Check if the node is a call expression to the React `useState` API. */
167
95
  const isUseStateCall = isAPICall("useState");
168
- /** Check if the node is a call expression to the React `useSyncExternalStore` API. */
169
96
  const isUseSyncExternalStoreCall = isAPICall("useSyncExternalStore");
170
- /** Check if the node is a call expression to the React `useTransition` API. */
171
97
  const isUseTransitionCall = isAPICall("useTransition");
172
98
 
173
99
  //#endregion
174
100
  //#region src/class.ts
175
- /**
176
- * Get the class identifier of a class node.
177
- * @param node The class node to get the identifier from.
178
- * @returns The class identifier or `null` if not found.
179
- */
180
101
  function getClassId(node) {
181
102
  if (node.id != null) return node.id;
182
103
  if (node.parent.type === AST_NODE_TYPES.VariableDeclarator) return node.parent.id;
183
104
  return null;
184
105
  }
185
106
 
107
+ //#endregion
108
+ //#region ../../.pkgs/eff/dist/index.js
109
+ const pipeArguments = (self, args) => {
110
+ switch (args.length) {
111
+ case 0: return self;
112
+ case 1: return args[0](self);
113
+ case 2: return args[1](args[0](self));
114
+ case 3: return args[2](args[1](args[0](self)));
115
+ case 4: return args[3](args[2](args[1](args[0](self))));
116
+ case 5: return args[4](args[3](args[2](args[1](args[0](self)))));
117
+ case 6: return args[5](args[4](args[3](args[2](args[1](args[0](self))))));
118
+ case 7: return args[6](args[5](args[4](args[3](args[2](args[1](args[0](self)))))));
119
+ case 8: return args[7](args[6](args[5](args[4](args[3](args[2](args[1](args[0](self))))))));
120
+ case 9: return args[8](args[7](args[6](args[5](args[4](args[3](args[2](args[1](args[0](self)))))))));
121
+ default: {
122
+ let ret = self;
123
+ for (let i = 0, len = args.length; i < len; i++) ret = args[i](ret);
124
+ return ret;
125
+ }
126
+ }
127
+ };
128
+ const Prototype = { pipe() {
129
+ return pipeArguments(this, arguments);
130
+ } };
131
+ const Class = (function() {
132
+ function PipeableBase() {}
133
+ PipeableBase.prototype = Prototype;
134
+ return PipeableBase;
135
+ })();
136
+ const dual = function(arity, body) {
137
+ if (typeof arity === "function") return function() {
138
+ return arity(arguments) ? body.apply(this, arguments) : ((self) => body(self, ...arguments));
139
+ };
140
+ switch (arity) {
141
+ case 0:
142
+ case 1: throw new RangeError(`Invalid arity ${arity}`);
143
+ case 2: return function(a, b) {
144
+ if (arguments.length >= 2) return body(a, b);
145
+ return function(self) {
146
+ return body(self, a);
147
+ };
148
+ };
149
+ case 3: return function(a, b, c) {
150
+ if (arguments.length >= 3) return body(a, b, c);
151
+ return function(self) {
152
+ return body(self, a, b);
153
+ };
154
+ };
155
+ default: return function() {
156
+ if (arguments.length >= arity) return body.apply(this, arguments);
157
+ const args = arguments;
158
+ return function(self) {
159
+ return body(self, ...args);
160
+ };
161
+ };
162
+ }
163
+ };
164
+ const identity = (a) => a;
165
+ const cast = identity;
166
+ const constant = (value) => () => value;
167
+ const constTrue = constant(true);
168
+ const constFalse = constant(false);
169
+ const constNull = constant(null);
170
+ const constUndefined = constant(void 0);
171
+ const compose = dual(2, (ab, bc) => (a) => bc(ab(a)));
172
+ const absurd = (_) => {
173
+ throw new Error("Called `absurd` function which should be uncallable");
174
+ };
175
+ const hole = cast(absurd);
176
+ const and = dual(2, (a, b) => (data) => a(data) && b(data));
177
+ const or = dual(2, (a, b) => (data) => a(data) || b(data));
178
+ const xor = dual(2, (a, b) => (data) => a(data) !== b(data));
179
+ const eqv = dual(2, (a, b) => (data) => a(data) === b(data));
180
+ const implies = dual(2, (antecedent, consequent) => (data) => !antecedent(data) || consequent(data));
181
+ const nor = dual(2, (a, b) => (data) => !a(data) && !b(data));
182
+ const nand = dual(2, (a, b) => (data) => !a(data) || !b(data));
183
+ function isFunction(input) {
184
+ return typeof input === "function";
185
+ }
186
+ function isObjectKeyword(input) {
187
+ return typeof input === "object" && input !== null || isFunction(input);
188
+ }
189
+ const hasProperty = dual(2, (data, property) => isObjectKeyword(data) && property in data);
190
+ const dropWhile = dual(2, (self, predicate) => {
191
+ const input = Array.isArray(self) ? self : Array.from(self);
192
+ const len = input.length;
193
+ let idx = 0;
194
+ while (idx < len && predicate(input[idx], idx)) idx++;
195
+ return input.slice(idx);
196
+ });
197
+ const takeWhile = dual(2, (self, predicate) => {
198
+ const input = Array.isArray(self) ? self : Array.from(self);
199
+ const len = input.length;
200
+ let idx = 0;
201
+ while (idx < len && predicate(input[idx], idx)) idx++;
202
+ return input.slice(0, idx);
203
+ });
204
+
186
205
  //#endregion
187
206
  //#region src/class-component.ts
188
- /**
189
- * Check if the node is a class component (extends `Component` or `PureComponent`).
190
- * @param node The node to check.
191
- * @returns `true` if the node is a class component.
192
- */
193
207
  function isClassComponent(node) {
194
- if ("superClass" in node && node.superClass != null) {
208
+ if (hasProperty(node, "superClass") && node.superClass != null) {
195
209
  const re = /^(?:Pure)?Component$/u;
196
210
  switch (true) {
197
211
  case Check.isIdentifier(node.superClass): return re.test(node.superClass.name);
@@ -200,14 +214,8 @@ function isClassComponent(node) {
200
214
  }
201
215
  return false;
202
216
  }
203
- /**
204
- * Check if the node is a pure component (extends `PureComponent`).
205
- * @param node The AST node to check.
206
- * @returns `true` if the node is a pure component.
207
- * @deprecated Class components are legacy. This function exists only to support legacy rules.
208
- */
209
217
  function isPureComponent(node) {
210
- if ("superClass" in node && node.superClass != null) {
218
+ if (hasProperty(node, "superClass") && node.superClass != null) {
211
219
  const re = /^PureComponent$/u;
212
220
  switch (true) {
213
221
  case Check.isIdentifier(node.superClass): return re.test(node.superClass.name);
@@ -219,68 +227,33 @@ function isPureComponent(node) {
219
227
  function createLifecycleChecker(methodName, isStatic = false) {
220
228
  return (node) => Check.isPropertyOrMethod(node) && node.static === isStatic && Check.isIdentifier(node.key, methodName);
221
229
  }
222
- /** @deprecated Class components are legacy. */
223
230
  const isRender = createLifecycleChecker("render");
224
- /** @deprecated Class components are legacy. */
225
231
  const isComponentDidCatch = createLifecycleChecker("componentDidCatch");
226
- /** @deprecated Class components are legacy. */
227
232
  const isComponentDidMount = createLifecycleChecker("componentDidMount");
228
- /** @deprecated Class components are legacy. */
229
233
  const isComponentDidUpdate = createLifecycleChecker("componentDidUpdate");
230
- /** @deprecated Class components are legacy. */
231
234
  const isComponentWillMount = createLifecycleChecker("componentWillMount");
232
- /** @deprecated Class components are legacy. */
233
235
  const isComponentWillReceiveProps = createLifecycleChecker("componentWillReceiveProps");
234
- /** @deprecated Class components are legacy. */
235
236
  const isComponentWillUnmount = createLifecycleChecker("componentWillUnmount");
236
- /** @deprecated Class components are legacy. */
237
237
  const isComponentWillUpdate = createLifecycleChecker("componentWillUpdate");
238
- /** @deprecated Class components are legacy. */
239
238
  const isGetChildContext = createLifecycleChecker("getChildContext");
240
- /** @deprecated Class components are legacy. */
241
239
  const isGetInitialState = createLifecycleChecker("getInitialState");
242
- /** @deprecated Class components are legacy. */
243
240
  const isGetSnapshotBeforeUpdate = createLifecycleChecker("getSnapshotBeforeUpdate");
244
- /** @deprecated Class components are legacy. */
245
241
  const isShouldComponentUpdate = createLifecycleChecker("shouldComponentUpdate");
246
- /** @deprecated Class components are legacy. */
247
242
  const isUnsafeComponentWillMount = createLifecycleChecker("UNSAFE_componentWillMount");
248
- /** @deprecated Class components are legacy. */
249
243
  const isUnsafeComponentWillReceiveProps = createLifecycleChecker("UNSAFE_componentWillReceiveProps");
250
- /** @deprecated Class components are legacy. */
251
244
  const isUnsafeComponentWillUpdate = createLifecycleChecker("UNSAFE_componentWillUpdate");
252
- /** @deprecated Class components are legacy. */
253
245
  const isGetDefaultProps = createLifecycleChecker("getDefaultProps", true);
254
- /** @deprecated Class components are legacy. */
255
246
  const isGetDerivedStateFromProps = createLifecycleChecker("getDerivedStateFromProps", true);
256
- /** @deprecated Class components are legacy. */
257
247
  const isGetDerivedStateFromError = createLifecycleChecker("getDerivedStateFromError", true);
258
- /**
259
- * Check if the node is a render-like method of a class component.
260
- * @param node The AST node to check.
261
- * @returns `true` if the node is a render-like method.
262
- * @deprecated Class components are legacy. This function exists only to support legacy rules.
263
- */
264
248
  function isRenderMethodLike(node) {
265
249
  return Check.isPropertyOrMethod(node) && Check.isIdentifier(node.key) && node.key.name.startsWith("render") && Check.isOneOf([AST_NODE_TYPES.ClassDeclaration, AST_NODE_TYPES.ClassExpression])(node.parent.parent);
266
250
  }
267
- /**
268
- * Check if the function is a callback passed to a class component's render method.
269
- * @param node The function node to check.
270
- * @returns `true` if the function is a render method callback.
271
- */
272
251
  function isRenderMethodCallback(node) {
273
252
  const parent = node.parent;
274
253
  const greatGrandparent = parent.parent?.parent;
275
254
  if (greatGrandparent == null) return false;
276
255
  return isRenderMethodLike(parent) && isClassComponent(greatGrandparent);
277
256
  }
278
- /**
279
- * Check if the call expression is a `this.setState(...)` call.
280
- * @param node The call expression node to check.
281
- * @returns `true` if the node is a `this.setState(...)` call.
282
- * @deprecated Class components are legacy. This function exists only to support legacy rules.
283
- */
284
257
  function isThisSetStateCall(node) {
285
258
  const callee = Extract.unwrap(node.callee);
286
259
  return callee.type === AST_NODE_TYPES.MemberExpression && Extract.unwrap(callee.object).type === AST_NODE_TYPES.ThisExpression && Extract.getCalleeName(node) === "setState";
@@ -288,11 +261,6 @@ function isThisSetStateCall(node) {
288
261
 
289
262
  //#endregion
290
263
  //#region src/class-component-collector.ts
291
- /**
292
- * Get an api and visitor object for the rule to collect class components.
293
- * @param context The rule context.
294
- * @deprecated Class components are legacy. This function exists only to support legacy rules.
295
- */
296
264
  function getClassComponentCollector(context) {
297
265
  const components = /* @__PURE__ */ new Map();
298
266
  const getText = (n) => context.sourceCode.getText(n);
@@ -326,27 +294,10 @@ function getClassComponentCollector(context) {
326
294
 
327
295
  //#endregion
328
296
  //#region src/create-element.ts
329
- /**
330
- * Get the type argument (the first argument) of a `createElement` call.
331
- * @param context The ESLint rule context.
332
- * @param node The node to inspect.
333
- * @returns The type argument, or `null` when the node is not a `createElement` call or has no arguments.
334
- */
335
297
  function getCreateElementTypeArgument(context, node) {
336
298
  if (!isCreateElementCall(context, node)) return null;
337
299
  return node.arguments[0] ?? null;
338
300
  }
339
- /**
340
- * Get the props object (the second argument) of a `createElement` call.
341
- *
342
- * Type expressions and chain expressions wrapping the argument are unwrapped
343
- * before the object check; `null`, spread, or otherwise non-object props
344
- * arguments yield `null`.
345
- *
346
- * @param context The ESLint rule context.
347
- * @param node The node to inspect.
348
- * @returns The props `ObjectExpression`, or `null` when absent or not statically an object literal.
349
- */
350
301
  function getCreateElementPropsObject(context, node) {
351
302
  if (!isCreateElementCall(context, node)) return null;
352
303
  const propsArg = node.arguments[1];
@@ -354,57 +305,21 @@ function getCreateElementPropsObject(context, node) {
354
305
  const expr = Extract.unwrap(propsArg);
355
306
  return expr.type === AST_NODE_TYPES.ObjectExpression ? expr : null;
356
307
  }
357
- /**
358
- * Get the children arguments (the arguments after the props object) of a `createElement` call.
359
- * @param context The ESLint rule context.
360
- * @param node The node to inspect.
361
- * @returns The children arguments, or an empty array when the node is not a `createElement` call.
362
- */
363
308
  function getCreateElementChildrenArguments(context, node) {
364
309
  if (!isCreateElementCall(context, node)) return [];
365
310
  return node.arguments.slice(2);
366
311
  }
367
- /**
368
- * Find a statically named property in the props object of a `createElement` call.
369
- *
370
- * Statically resolvable names include plain identifier keys as well as
371
- * string-literal and simple template-literal keys (computed or not).
372
- * @param context The ESLint rule context.
373
- * @param node The node to inspect.
374
- * @param name The property name to look for (ex: `"children"`, `"key"`).
375
- * @returns The matching `Property` node, or `null` when the call has no static property with that name.
376
- *
377
- * @example
378
- * ```ts
379
- * import { getCreateElementProp } from "@eslint-react/core";
380
- *
381
- * const childrenProp = getCreateElementProp(context, node, "children");
382
- * ```
383
- */
384
312
  function getCreateElementProp(context, node, name) {
385
313
  const propsObject = getCreateElementPropsObject(context, node);
386
314
  if (propsObject == null) return null;
387
315
  for (const prop of propsObject.properties) if (prop.type === AST_NODE_TYPES.Property && Extract.getPropertyName(prop, "max") === name) return prop;
388
316
  return null;
389
317
  }
390
- /**
391
- * Check if the node is passed as a children argument (the third argument or
392
- * later) of a `createElement` call.
393
- * @param context The ESLint rule context.
394
- * @param node The node to check.
395
- * @returns `true` if the node is a direct children argument of a `createElement` call.
396
- */
397
318
  function isCreateElementChildrenArgument(context, node) {
398
319
  let parent = node.parent;
399
320
  while (Check.isTypeExpression(parent)) parent = parent.parent;
400
321
  return parent?.type === AST_NODE_TYPES.CallExpression && isCreateElementCall(context, parent) && parent.arguments.slice(2).some((arg) => Extract.unwrap(arg) === node);
401
322
  }
402
- /**
403
- * Check if the node is inside the props object (the second argument) of a `createElement` call.
404
- * @param context The ESLint rule context.
405
- * @param node The node to check.
406
- * @returns `true` if the node is inside `createElement`'s props object.
407
- */
408
323
  function isInsideCreateElementProps(context, node) {
409
324
  const call = Traverse.findParent(node, isCreateElementCall(context));
410
325
  if (call == null) return false;
@@ -415,23 +330,9 @@ function isInsideCreateElementProps(context, node) {
415
330
 
416
331
  //#endregion
417
332
  //#region src/function.ts
418
- /**
419
- * Get the static identifier of a function AST node.
420
- *
421
- * @remarks
422
- * For function declarations this is straightforward. For anonymous function
423
- * expressions it is more complex. This function roughly detects the same AST
424
- * nodes as the ECMAScript spec's `IsAnonymousFunctionDefinition()` with some
425
- * exceptions to better fit our use case.
426
- *
427
- * Ported from {@link https://github.com/facebook/react/blob/bb8a76c6cc77ea2976d690ea09f5a1b3d9b1792a/packages/eslint-plugin-react-hooks/src/rules/RulesOfHooks.ts#L860 | RulesOfHooks.ts}
428
- *
429
- * @param node The function node to analyze.
430
- * @returns The identifier node if found, `null` otherwise.
431
- */
432
333
  function getFunctionId(node) {
433
334
  switch (true) {
434
- case "id" in node && node.id != null: return node.id;
335
+ case hasProperty(node, "id") && node.id != null: return node.id;
435
336
  case node.parent.type === AST_NODE_TYPES.VariableDeclarator && node.parent.init === node: return node.parent.id;
436
337
  case node.parent.type === AST_NODE_TYPES.AssignmentExpression && node.parent.right === node && node.parent.operator === "=": return node.parent.left;
437
338
  case node.parent.type === AST_NODE_TYPES.Property && node.parent.value === node && !node.parent.computed: return node.parent.key;
@@ -442,12 +343,6 @@ function getFunctionId(node) {
442
343
  }
443
344
  return null;
444
345
  }
445
- /**
446
- * Get the initialization path of a function node in the AST.
447
- *
448
- * @param node The function node to analyze.
449
- * @returns The function initialization path or `null` if not identifiable.
450
- */
451
346
  function getFunctionInitPath(node) {
452
347
  if (node.type === AST_NODE_TYPES.FunctionDeclaration) return [node];
453
348
  let parent = node.parent;
@@ -493,36 +388,18 @@ function getFunctionInitPath(node) {
493
388
  }
494
389
  return null;
495
390
  }
496
- /**
497
- * Check if a specific function call exists in the function initialization path.
498
- *
499
- * @param callName The name of the call to check for (e.g., "memo", "forwardRef").
500
- * @param initPath The function initialization path to search in.
501
- * @returns `true` if the call exists in the path, `false` otherwise.
502
- */
503
391
  function isFunctionHasCallInInitPath(callName, initPath) {
504
392
  return initPath.some((node) => {
505
393
  if (node.type !== AST_NODE_TYPES.CallExpression) return false;
506
394
  const callee = Extract.unwrap(node.callee);
507
395
  if (Check.isIdentifier(callee)) return callee.name === callName;
508
- if (callee.type === AST_NODE_TYPES.MemberExpression && "name" in callee.property) return callee.property.name === callName;
396
+ if (callee.type === AST_NODE_TYPES.MemberExpression && hasProperty(callee.property, "name")) return callee.property.name === callName;
509
397
  return false;
510
398
  });
511
399
  }
512
- /**
513
- * Check if a function is empty.
514
- *
515
- * @param node The function node to check.
516
- * @returns `true` if the function is empty, `false` otherwise.
517
- */
518
400
  function isFunctionEmpty(node) {
519
401
  return node.body.type === AST_NODE_TYPES.BlockStatement && node.body.body.length === 0;
520
402
  }
521
- /**
522
- * Get the directives of a function (ex: "use strict", "use client", "use server").
523
- * @param node The function node to get the directives from.
524
- * @returns The directives of the function.
525
- */
526
403
  function getFunctionDirectives(node) {
527
404
  const directives = [];
528
405
  if (node.body.type !== AST_NODE_TYPES.BlockStatement) return directives;
@@ -535,17 +412,9 @@ function getFunctionDirectives(node) {
535
412
  }
536
413
  return directives;
537
414
  }
538
- /**
539
- * Check if a directive with the given name exists in the function directives.
540
- *
541
- * @param node The function AST node.
542
- * @param name The directive name to check (e.g., "use memo", "use no memo").
543
- * @returns `true` if the directive exists, `false` otherwise.
544
- */
545
415
  function isFunctionHasDirective(node, name) {
546
416
  return getFunctionDirectives(node).some((d) => d.directive === name);
547
417
  }
548
- /** The esquery selector matching `displayName` assignment expressions. */
549
418
  const SEL_FUNCTION_DISPLAY_NAME_ASSIGNMENT = [
550
419
  "AssignmentExpression",
551
420
  "[operator='=']",
@@ -555,67 +424,21 @@ const SEL_FUNCTION_DISPLAY_NAME_ASSIGNMENT = [
555
424
 
556
425
  //#endregion
557
426
  //#region src/jsx.ts
558
- /**
559
- * Hints for JSX detection.
560
- */
561
427
  const JsxDetectionHint = {
562
- /** No hints set. */
563
428
  None: 0n,
564
- /** Do not treat `null` values as JSX-like. */
565
429
  DoNotIncludeJsxWithNullValue: 1n << 0n,
566
- /** Do not treat number values as JSX-like. */
567
430
  DoNotIncludeJsxWithNumberValue: 1n << 1n,
568
- /** Do not treat bigint values as JSX-like. */
569
431
  DoNotIncludeJsxWithBigIntValue: 1n << 2n,
570
- /** Do not treat string values as JSX-like. */
571
432
  DoNotIncludeJsxWithStringValue: 1n << 3n,
572
- /** Do not treat boolean values as JSX-like. */
573
433
  DoNotIncludeJsxWithBooleanValue: 1n << 4n,
574
- /** Do not treat undefined values as JSX-like. */
575
434
  DoNotIncludeJsxWithUndefinedValue: 1n << 5n,
576
- /** Do not treat empty array values as JSX-like. */
577
435
  DoNotIncludeJsxWithEmptyArrayValue: 1n << 6n,
578
- /** Do not treat `createElement` calls as JSX-like. */
579
436
  DoNotIncludeJsxWithCreateElementValue: 1n << 7n,
580
- /** Require all array elements to be JSX-like for the array to be JSX-like. */
581
437
  RequireAllArrayElementsToBeJsx: 1n << 8n,
582
- /** Require both sides of a logical expression to be JSX-like. */
583
438
  RequireBothSidesOfLogicalExpressionToBeJsx: 1n << 9n,
584
- /** Require both branches of a conditional expression to be JSX-like. */
585
439
  RequireBothBranchesOfConditionalExpressionToBeJsx: 1n << 10n
586
440
  };
587
- /**
588
- * Default JSX detection hint.
589
- *
590
- * Skips number, bigint, boolean, string, and undefined literals,
591
- * the value types that are commonly returned alongside JSX in React
592
- * components but are not themselves renderable elements.
593
- */
594
441
  const DEFAULT_JSX_DETECTION_HINT = 0n | JsxDetectionHint.DoNotIncludeJsxWithNumberValue | JsxDetectionHint.DoNotIncludeJsxWithBigIntValue | JsxDetectionHint.DoNotIncludeJsxWithBooleanValue | JsxDetectionHint.DoNotIncludeJsxWithStringValue | JsxDetectionHint.DoNotIncludeJsxWithUndefinedValue;
595
- /**
596
- * Check if the node represents JSX-like content based on heuristics.
597
- *
598
- * The detection behavior is configurable through {@link JsxDetectionHint}
599
- * bit-flags so that callers can opt individual value kinds in or out.
600
- *
601
- * Identifiers are resolved to their definitions via scope analysis;
602
- * circular definitions (e.g. `var a = b; var b = a;`) are detected and
603
- * treated as not JSX-like instead of recursing indefinitely.
604
- *
605
- * @param context The ESLint rule context (needed for variable resolution).
606
- * @param node The AST node to analyze.
607
- * @param hint Optional bit-flags to adjust detection behavior. Defaults to {@link DEFAULT_JSX_DETECTION_HINT}.
608
- * @returns Whether the node is considered JSX-like.
609
- *
610
- * @example
611
- * ```ts
612
- * import { isJsxLike } from "@eslint-react/core";
613
- *
614
- * if (isJsxLike(context, node)) {
615
- * // node looks like it evaluates to a React element
616
- * }
617
- * ```
618
- */
619
442
  function isJsxLike(context, node, hint = DEFAULT_JSX_DETECTION_HINT) {
620
443
  const seen = /* @__PURE__ */ new Set();
621
444
  function visit(node) {
@@ -655,49 +478,23 @@ function isJsxLike(context, node, hint = DEFAULT_JSX_DETECTION_HINT) {
655
478
 
656
479
  //#endregion
657
480
  //#region src/function-component.ts
658
- /**
659
- * Component flag constants.
660
- */
661
481
  const FunctionComponentFlag = {
662
- /** No flags set. */
663
482
  None: 0n,
664
- /** Indicates the component is a pure component (ex: extends PureComponent). */
665
483
  PureComponent: 1n << 0n,
666
- /** Indicates the component creates elements using `createElement` instead of JSX. */
667
484
  CreateElement: 1n << 1n,
668
- /** Indicates the component is memoized (ex: React.memo). */
669
485
  Memo: 1n << 2n,
670
- /** Indicates the component forwards a ref (ex: React.forwardRef). */
671
486
  ForwardRef: 1n << 3n
672
487
  };
673
- /**
674
- * Get component flag from init path.
675
- * @param initPath The init path of the function component.
676
- * @returns The component flag.
677
- * @internal
678
- */
679
488
  function getFunctionComponentFlagFromInitPath(initPath) {
680
489
  let flag = FunctionComponentFlag.None;
681
490
  if (initPath != null && isFunctionHasCallInInitPath("memo", initPath)) flag |= FunctionComponentFlag.Memo;
682
491
  if (initPath != null && isFunctionHasCallInInitPath("forwardRef", initPath)) flag |= FunctionComponentFlag.ForwardRef;
683
492
  return flag;
684
493
  }
685
- /**
686
- * Check if the node is a call expression for a component wrapper.
687
- * @param context The ESLint rule context.
688
- * @param node The node to check.
689
- * @returns `true` if the node is a call expression for a component wrapper.
690
- */
691
494
  function isFunctionComponentWrapperCall(context, node) {
692
495
  if (node.type !== AST_NODE_TYPES.CallExpression) return false;
693
496
  return isMemoCall(context, node) || isForwardRefCall(context, node);
694
497
  }
695
- /**
696
- * Check if the node is a callback function passed to a component wrapper.
697
- * @param context The ESLint rule context.
698
- * @param node The node to check.
699
- * @returns `true` if the node is a callback function passed to a component wrapper.
700
- */
701
498
  function isFunctionComponentWrapperCallback(context, node) {
702
499
  if (!Check.isFunction(node)) return false;
703
500
  let parent = node.parent;
@@ -705,12 +502,6 @@ function isFunctionComponentWrapperCallback(context, node) {
705
502
  if (parent.type !== AST_NODE_TYPES.CallExpression) return false;
706
503
  return isFunctionComponentWrapperCall(context, parent);
707
504
  }
708
- /**
709
- * Get function component identifier from `const Component = memo(() => {});`.
710
- * @param context The rule context.
711
- * @param node The AST node to get the function component identifier from.
712
- * @internal
713
- */
714
505
  function getFunctionComponentId(context, node) {
715
506
  const functionId = getFunctionId(node);
716
507
  if (functionId != null) return functionId;
@@ -722,29 +513,12 @@ function getFunctionComponentId(context, node) {
722
513
  default: return null;
723
514
  }
724
515
  }
725
- /**
726
- * Check if a string matches the strict component name pattern.
727
- * @param name The name to check.
728
- * @returns `true` if the name matches the strict component name pattern.
729
- */
730
516
  function isFunctionComponentName(name) {
731
517
  return RE_COMPONENT_NAME.test(name);
732
518
  }
733
- /**
734
- * Check if a string matches the loose component name pattern.
735
- * @param name The name to check.
736
- * @returns `true` if the name matches the loose component name pattern.
737
- */
738
519
  function isFunctionComponentNameLoose(name) {
739
520
  return RE_COMPONENT_NAME_LOOSE.test(name);
740
521
  }
741
- /**
742
- * Check if a function has a loose component name.
743
- * @param context The rule context.
744
- * @param fn The function to check.
745
- * @param allowNone Whether to allow no name.
746
- * @returns `true` if the function has a loose component name.
747
- */
748
522
  function isFunctionWithLooseComponentName(context, fn, allowNone = false) {
749
523
  const id = getFunctionComponentId(context, fn);
750
524
  if (id == null) return allowNone;
@@ -752,40 +526,18 @@ function isFunctionWithLooseComponentName(context, fn, allowNone = false) {
752
526
  if (id.type === AST_NODE_TYPES.MemberExpression && Check.isIdentifier(id.property)) return isFunctionComponentNameLoose(id.property.name);
753
527
  return false;
754
528
  }
755
- /**
756
- * Hints for component collector.
757
- */
758
529
  const FunctionComponentDetectionHint = {
759
530
  ...JsxDetectionHint,
760
- /** Exclude functions defined as class methods from component detection. */
761
531
  DoNotIncludeFunctionDefinedAsClassMethod: 1n << 11n,
762
- /** Exclude functions defined as class properties from component detection. */
763
532
  DoNotIncludeFunctionDefinedAsClassProperty: 1n << 12n,
764
- /** Exclude functions defined as object methods from component detection. */
765
533
  DoNotIncludeFunctionDefinedAsObjectMethod: 1n << 13n,
766
- /** Exclude functions defined as array expression elements from component detection. */
767
534
  DoNotIncludeFunctionDefinedAsArrayExpressionElement: 1n << 14n,
768
- /** Exclude functions defined as array pattern elements from component detection. */
769
535
  DoNotIncludeFunctionDefinedAsArrayPatternElement: 1n << 15n,
770
- /** Exclude functions defined as array flatMap callbacks from component detection. */
771
536
  DoNotIncludeFunctionDefinedAsArrayFlatMapCallback: 1n << 16n,
772
- /** Exclude functions defined as array map callbacks from component detection. */
773
537
  DoNotIncludeFunctionDefinedAsArrayMapCallback: 1n << 17n,
774
- /** Exclude functions defined as arbitrary call expression callbacks from component detection. */
775
538
  DoNotIncludeFunctionDefinedAsArbitraryCallExpressionCallback: 1n << 18n
776
539
  };
777
- /**
778
- * Default component detection hint.
779
- */
780
540
  const DEFAULT_COMPONENT_DETECTION_HINT = 0n | FunctionComponentDetectionHint.DoNotIncludeJsxWithBigIntValue | FunctionComponentDetectionHint.DoNotIncludeJsxWithBooleanValue | FunctionComponentDetectionHint.DoNotIncludeJsxWithNumberValue | FunctionComponentDetectionHint.DoNotIncludeJsxWithStringValue | FunctionComponentDetectionHint.DoNotIncludeJsxWithUndefinedValue | FunctionComponentDetectionHint.DoNotIncludeFunctionDefinedAsArbitraryCallExpressionCallback | FunctionComponentDetectionHint.DoNotIncludeFunctionDefinedAsArrayExpressionElement | FunctionComponentDetectionHint.DoNotIncludeFunctionDefinedAsArrayFlatMapCallback | FunctionComponentDetectionHint.DoNotIncludeFunctionDefinedAsArrayMapCallback | FunctionComponentDetectionHint.DoNotIncludeFunctionDefinedAsArrayPatternElement | FunctionComponentDetectionHint.RequireAllArrayElementsToBeJsx | FunctionComponentDetectionHint.RequireBothBranchesOfConditionalExpressionToBeJsx | FunctionComponentDetectionHint.RequireBothSidesOfLogicalExpressionToBeJsx;
781
- /**
782
- * Check if the function node is a valid React component definition.
783
- *
784
- * @param context The rule context.
785
- * @param node The function node to analyze.
786
- * @param hint Component detection hints (bit flags) to customize detection logic.
787
- * @returns `true` if the node is considered a component definition.
788
- */
789
541
  function isFunctionComponentDefinition(context, node, hint) {
790
542
  if (!isFunctionWithLooseComponentName(context, node, true)) return false;
791
543
  let parent = node.parent;
@@ -828,483 +580,8 @@ function isFunctionComponentDefinition(context, node, hint) {
828
580
  return significantParent.type !== AST_NODE_TYPES.JSXExpressionContainer;
829
581
  }
830
582
 
831
- //#endregion
832
- //#region ../../.pkgs/eff/dist/index.js
833
- /**
834
- * Applies a `pipe` method's variadic arguments to an initial value from left
835
- * to right.
836
- *
837
- * **When to use**
838
- *
839
- * Use to implement a custom `.pipe(...)` method from JavaScript's `arguments`
840
- * object.
841
- *
842
- * **Details**
843
- *
844
- * This helper is intended for implementing `Pipeable.pipe` methods that
845
- * receive JavaScript's `arguments` object. With no functions it returns the
846
- * original value; otherwise it feeds each result into the next function.
847
- *
848
- * **Example** (Implementing a pipe method)
849
- *
850
- * ```ts
851
- * import { Pipeable } from "effect"
852
- *
853
- * class NumberBox {
854
- * constructor(readonly value: number) {}
855
- *
856
- * pipe(..._fns: ReadonlyArray<(value: number) => number>): number {
857
- * return Pipeable.pipeArguments(this.value, arguments) as number
858
- * }
859
- * }
860
- *
861
- * const result = new NumberBox(5).pipe(
862
- * (n) => n + 2,
863
- * (n) => n * 3
864
- * )
865
- * console.log(result) // 21
866
- * ```
867
- *
868
- * @category combinators
869
- * @since 2.0.0
870
- */
871
- const pipeArguments = (self, args) => {
872
- switch (args.length) {
873
- case 0: return self;
874
- case 1: return args[0](self);
875
- case 2: return args[1](args[0](self));
876
- case 3: return args[2](args[1](args[0](self)));
877
- case 4: return args[3](args[2](args[1](args[0](self))));
878
- case 5: return args[4](args[3](args[2](args[1](args[0](self)))));
879
- case 6: return args[5](args[4](args[3](args[2](args[1](args[0](self))))));
880
- case 7: return args[6](args[5](args[4](args[3](args[2](args[1](args[0](self)))))));
881
- case 8: return args[7](args[6](args[5](args[4](args[3](args[2](args[1](args[0](self))))))));
882
- case 9: return args[8](args[7](args[6](args[5](args[4](args[3](args[2](args[1](args[0](self)))))))));
883
- default: {
884
- let ret = self;
885
- for (let i = 0, len = args.length; i < len; i++) ret = args[i](ret);
886
- return ret;
887
- }
888
- }
889
- };
890
- /**
891
- * Reusable prototype that implements `Pipeable.pipe`.
892
- *
893
- * **When to use**
894
- *
895
- * Use when classes or object prototypes can reuse this value when they need the
896
- * standard pipe implementation backed by `pipeArguments`.
897
- *
898
- * @category prototypes
899
- * @since 3.15.0
900
- */
901
- const Prototype = { pipe() {
902
- return pipeArguments(this, arguments);
903
- } };
904
- /**
905
- * Provides a base constructor whose instances implement the standard `Pipeable.pipe`
906
- * method.
907
- *
908
- * **When to use**
909
- *
910
- * Use when you need to define a class that supports Effect-style method
911
- * chaining through `.pipe(...)`.
912
- *
913
- * @category constructors
914
- * @since 3.15.0
915
- */
916
- const Class = (function() {
917
- function PipeableBase() {}
918
- PipeableBase.prototype = Prototype;
919
- return PipeableBase;
920
- })();
921
- /**
922
- * Provides small helpers for defining and reusing TypeScript functions.
923
- *
924
- * The main helpers are `pipe` and `flow` for left-to-right composition and
925
- * `dual` for APIs that support both direct and pipe-friendly call styles. The
926
- * module also contains small identity, constant, tuple, type-level, and
927
- * memoization helpers used across the library.
928
- *
929
- * @since 2.0.0
930
- */
931
- /**
932
- * Creates a function that can be called in data-first style or data-last
933
- * (`pipe`-friendly) style.
934
- *
935
- * **When to use**
936
- *
937
- * Use to expose one implementation through both direct and `pipe`-friendly
938
- * call styles.
939
- *
940
- * **Details**
941
- *
942
- * Pass either the arity of the uncurried function or a predicate that decides
943
- * whether the current call is data-first. Arity is the common case. Use a
944
- * predicate when optional arguments make arity ambiguous.
945
- *
946
- * **Example** (Selecting data-first or data-last style by arity)
947
- *
948
- * ```ts
949
- * import { Function, pipe } from "effect"
950
- *
951
- * const sum = Function.dual<
952
- * (that: number) => (self: number) => number,
953
- * (self: number, that: number) => number
954
- * >(2, (self, that) => self + that)
955
- *
956
- * console.log(sum(2, 3)) // 5
957
- * console.log(pipe(2, sum(3))) // 5
958
- * ```
959
- *
960
- * **Example** (Defining overloads with call signatures)
961
- *
962
- * ```ts
963
- * import { Function, pipe } from "effect"
964
- *
965
- * const sum: {
966
- * (that: number): (self: number) => number
967
- * (self: number, that: number): number
968
- * } = Function.dual(2, (self: number, that: number): number => self + that)
969
- *
970
- * console.log(sum(2, 3)) // 5
971
- * console.log(pipe(2, sum(3))) // 5
972
- * ```
973
- *
974
- * **Example** (Selecting data-first or data-last style with a predicate)
975
- *
976
- * ```ts
977
- * import { Function, pipe } from "effect"
978
- *
979
- * const sum = Function.dual<
980
- * (that: number) => (self: number) => number,
981
- * (self: number, that: number) => number
982
- * >(
983
- * (args) => args.length === 2,
984
- * (self, that) => self + that
985
- * )
986
- *
987
- * console.log(sum(2, 3)) // 5
988
- * console.log(pipe(2, sum(3))) // 5
989
- * ```
990
- *
991
- * @category combinators
992
- * @since 2.0.0
993
- */
994
- const dual = function(arity, body) {
995
- if (typeof arity === "function") return function() {
996
- return arity(arguments) ? body.apply(this, arguments) : ((self) => body(self, ...arguments));
997
- };
998
- switch (arity) {
999
- case 0:
1000
- case 1: throw new RangeError(`Invalid arity ${arity}`);
1001
- case 2: return function(a, b) {
1002
- if (arguments.length >= 2) return body(a, b);
1003
- return function(self) {
1004
- return body(self, a);
1005
- };
1006
- };
1007
- case 3: return function(a, b, c) {
1008
- if (arguments.length >= 3) return body(a, b, c);
1009
- return function(self) {
1010
- return body(self, a, b);
1011
- };
1012
- };
1013
- default: return function() {
1014
- if (arguments.length >= arity) return body.apply(this, arguments);
1015
- const args = arguments;
1016
- return function(self) {
1017
- return body(self, ...args);
1018
- };
1019
- };
1020
- }
1021
- };
1022
- /**
1023
- * Returns its input argument unchanged.
1024
- *
1025
- * **When to use**
1026
- *
1027
- * Use to return a value unchanged where a function is required.
1028
- *
1029
- * **Example** (Returning the same value)
1030
- *
1031
- * ```ts
1032
- * import { identity } from "effect"
1033
- * import * as assert from "node:assert"
1034
- *
1035
- * assert.deepStrictEqual(identity(5), 5)
1036
- * ```
1037
- *
1038
- * @category combinators
1039
- * @since 2.0.0
1040
- */
1041
- const identity = (a) => a;
1042
- /**
1043
- * Returns the input value with a different static type.
1044
- *
1045
- * **When to use**
1046
- *
1047
- * Use when you need an explicit type-level cast and accept that the value is
1048
- * returned unchanged at runtime.
1049
- *
1050
- * **Gotchas**
1051
- *
1052
- * This is a type-level cast only; it performs no runtime validation or
1053
- * conversion.
1054
- *
1055
- * @see {@link satisfies} for checking assignability without changing the resulting type
1056
- *
1057
- * @category utility types
1058
- * @since 4.0.0
1059
- */
1060
- const cast = identity;
1061
- /**
1062
- * Creates a zero-argument function that always returns the provided value.
1063
- *
1064
- * **When to use**
1065
- *
1066
- * Use when you need a thunk or callback that returns the same value on every
1067
- * invocation.
1068
- *
1069
- * **Example** (Creating a constant thunk)
1070
- *
1071
- * ```ts
1072
- * import { Function } from "effect"
1073
- * import * as assert from "node:assert"
1074
- *
1075
- * const constNull = Function.constant(null)
1076
- *
1077
- * assert.deepStrictEqual(constNull(), null)
1078
- * assert.deepStrictEqual(constNull(), null)
1079
- * ```
1080
- *
1081
- * @category constructors
1082
- * @since 2.0.0
1083
- */
1084
- const constant = (value) => () => value;
1085
- /**
1086
- * Returns `true` when called.
1087
- *
1088
- * **When to use**
1089
- *
1090
- * Use when you need a thunk that returns `true` on every invocation.
1091
- *
1092
- * **Example** (Returning true from a thunk)
1093
- *
1094
- * ```ts
1095
- * import { Function } from "effect"
1096
- * import * as assert from "node:assert"
1097
- *
1098
- * assert.deepStrictEqual(Function.constTrue(), true)
1099
- * ```
1100
- *
1101
- * @category constants
1102
- * @since 2.0.0
1103
- */
1104
- const constTrue = constant(true);
1105
- /**
1106
- * Returns `false` when called.
1107
- *
1108
- * **When to use**
1109
- *
1110
- * Use when you need a thunk that returns `false` on every invocation.
1111
- *
1112
- * **Example** (Returning false from a thunk)
1113
- *
1114
- * ```ts
1115
- * import { Function } from "effect"
1116
- * import * as assert from "node:assert"
1117
- *
1118
- * assert.deepStrictEqual(Function.constFalse(), false)
1119
- * ```
1120
- *
1121
- * @category constants
1122
- * @since 2.0.0
1123
- */
1124
- const constFalse = constant(false);
1125
- /**
1126
- * Returns `null` when called.
1127
- *
1128
- * **When to use**
1129
- *
1130
- * Use when you need a thunk that returns `null` on every invocation.
1131
- *
1132
- * **Example** (Returning null from a thunk)
1133
- *
1134
- * ```ts
1135
- * import { Function } from "effect"
1136
- * import * as assert from "node:assert"
1137
- *
1138
- * assert.deepStrictEqual(Function.constNull(), null)
1139
- * ```
1140
- *
1141
- * @category constants
1142
- * @since 2.0.0
1143
- */
1144
- const constNull = constant(null);
1145
- /**
1146
- * Returns `undefined` when called.
1147
- *
1148
- * **When to use**
1149
- *
1150
- * Use when you need a thunk that returns `undefined` on every invocation.
1151
- *
1152
- * **Example** (Returning undefined from a thunk)
1153
- *
1154
- * ```ts
1155
- * import { Function } from "effect"
1156
- * import * as assert from "node:assert"
1157
- *
1158
- * assert.deepStrictEqual(Function.constUndefined(), undefined)
1159
- * ```
1160
- *
1161
- * @category constants
1162
- * @since 2.0.0
1163
- */
1164
- const constUndefined = constant(void 0);
1165
- /**
1166
- * 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`.
1167
- * The result is obtained by first applying the `ab` function to `a` and then applying the `bc` function to the result of `ab`.
1168
- *
1169
- * **When to use**
1170
- *
1171
- * Use to compose exactly two unary functions into a reusable unary function.
1172
- *
1173
- * **Example** (Composing two functions)
1174
- *
1175
- * ```ts
1176
- * import { Function } from "effect"
1177
- * import * as assert from "node:assert"
1178
- *
1179
- * const increment = (n: number) => n + 1
1180
- * const square = (n: number) => n * n
1181
- *
1182
- * assert.strictEqual(Function.compose(increment, square)(2), 9)
1183
- * ```
1184
- *
1185
- * @see {@link flow} for composing a left-to-right sequence of functions
1186
- * @see {@link pipe} for applying a value through a left-to-right sequence immediately
1187
- *
1188
- * @category combinators
1189
- * @since 2.0.0
1190
- */
1191
- const compose = dual(2, (ab, bc) => (a) => bc(ab(a)));
1192
- /**
1193
- * Marks an impossible branch by accepting a `never` value and returning any
1194
- * type.
1195
- *
1196
- * **When to use**
1197
- *
1198
- * Use when you need a return value in a branch that exhaustive checks prove
1199
- * cannot be reached.
1200
- *
1201
- * **Gotchas**
1202
- *
1203
- * Calling `absurd` throws, because a value of type `never` should be
1204
- * impossible at runtime.
1205
- *
1206
- * **Example** (Handling impossible values)
1207
- *
1208
- * ```ts
1209
- * import { absurd } from "effect"
1210
- *
1211
- * const handleNever = (value: never) => {
1212
- * return absurd(value) // This will throw an error if called
1213
- * }
1214
- * ```
1215
- *
1216
- * @category utility types
1217
- * @since 2.0.0
1218
- */
1219
- const absurd = (_) => {
1220
- throw new Error("Called `absurd` function which should be uncallable");
1221
- };
1222
- /**
1223
- * Creates a compile-time placeholder for a value of any type.
1224
- *
1225
- * **When to use**
1226
- *
1227
- * Use as a temporary typed placeholder while developing incomplete code.
1228
- *
1229
- * **Gotchas**
1230
- *
1231
- * `hole` is intended for temporary development use. If the placeholder is
1232
- * evaluated at runtime, it throws.
1233
- *
1234
- * **Example** (Creating a development placeholder)
1235
- *
1236
- * ```ts
1237
- * import { hole } from "effect"
1238
- *
1239
- * // Intentionally not called: `hole` throws if the placeholder is evaluated.
1240
- * const buildUser = (id: number): { readonly id: number; readonly name: string } => ({
1241
- * id,
1242
- * name: hole<string>()
1243
- * })
1244
- *
1245
- * console.log(typeof buildUser) // "function"
1246
- * ```
1247
- *
1248
- * @category utility types
1249
- * @since 2.0.0
1250
- */
1251
- const hole = cast(absurd);
1252
- /**
1253
- * Drops the longest prefix of elements from an array that satisfy the given predicate.
1254
- *
1255
- * Supports both data-first and data-last (`pipe`-friendly) call styles.
1256
- *
1257
- * @param pred - The predicate to test each element with.
1258
- * @returns A new array without the matching prefix.
1259
- * @example
1260
- * ```ts
1261
- * import * as assert from "node:assert"
1262
- * import { dropWhile, pipe } from "@local/eff"
1263
- *
1264
- * // data-first
1265
- * assert.deepStrictEqual(dropWhile([1, 2, 3, 2, 1], (n: number) => n < 3), [3, 2, 1])
1266
- *
1267
- * // data-last
1268
- * assert.deepStrictEqual(pipe([1, 2, 3, 2, 1], dropWhile((n: number) => n < 3)), [3, 2, 1])
1269
- * ```
1270
- * @category array
1271
- */
1272
- const dropWhile = dual(2, (xs, pred) => {
1273
- const len = xs.length;
1274
- let idx = 0;
1275
- while (idx < len && pred(xs[idx])) idx++;
1276
- return xs.slice(idx);
1277
- });
1278
- /**
1279
- * Takes the longest prefix of elements from an array that satisfy the given predicate.
1280
- *
1281
- * Supports both data-first and data-last (`pipe`-friendly) call styles.
1282
- *
1283
- * @param pred - The predicate to test each element with.
1284
- * @returns A new array containing only the matching prefix.
1285
- * @example
1286
- * ```ts
1287
- * import * as assert from "node:assert"
1288
- * import { pipe, takeWhile } from "@local/eff"
1289
- *
1290
- * // data-first
1291
- * assert.deepStrictEqual(takeWhile([1, 2, 3, 2, 1], (n: number) => n < 3), [1, 2])
1292
- *
1293
- * // data-last
1294
- * assert.deepStrictEqual(pipe([1, 2, 3, 2, 1], takeWhile((n: number) => n < 3)), [1, 2])
1295
- * ```
1296
- * @category array
1297
- */
1298
- const takeWhile = dual(2, (xs, pred) => {
1299
- const len = xs.length;
1300
- let idx = 0;
1301
- while (idx < len && pred(xs[idx])) idx++;
1302
- return xs.slice(0, idx);
1303
- });
1304
-
1305
583
  //#endregion
1306
584
  //#region src/hook.ts
1307
- /** The names of React's built-in hooks. */
1308
585
  const REACT_BUILTIN_HOOK_NAMES = [
1309
586
  "use",
1310
587
  "useActionState",
@@ -1326,55 +603,29 @@ const REACT_BUILTIN_HOOK_NAMES = [
1326
603
  "useSyncExternalStore",
1327
604
  "useTransition"
1328
605
  ];
1329
- /**
1330
- * Check if the name is a hook name (starts with `use` followed by an uppercase letter or digit).
1331
- * @param name The name of the identifier to check.
1332
- * @returns `true` if the name is a hook name.
1333
- * @see https://github.com/facebook/react/blob/1d6c8168db1d82713202e842df3167787ffa00ed/packages/eslint-plugin-react-hooks/src/rules/RulesOfHooks.ts#L16
1334
- */
1335
606
  function isHookName(name) {
1336
607
  return name === "use" || /^use[A-Z0-9]/.test(name);
1337
608
  }
1338
- /**
1339
- * Checks if the given node is a hook identifier.
1340
- * @param id The AST node to check.
1341
- * @returns `true` if the node is a hook identifier or member expression with hook name, `false` otherwise.
1342
- */
1343
609
  function isHookId(id) {
1344
610
  switch (id.type) {
1345
611
  case AST_NODE_TYPES.Identifier: return isHookName(id.name);
1346
- case AST_NODE_TYPES.MemberExpression: return "name" in id.property && isHookName(id.property.name);
612
+ case AST_NODE_TYPES.MemberExpression: return hasProperty(id.property, "name") && isHookName(id.property.name);
1347
613
  default: return false;
1348
614
  }
1349
615
  }
1350
- /**
1351
- * Checks if the given expression is a hook tag (callee / tagged template tag).
1352
- * @param tag The expression node to check.
1353
- * @returns `true` if the expression is a hook identifier or member expression with hook name, `false` otherwise.
1354
- */
1355
616
  function isHookTag(tag) {
1356
617
  if (tag == null) return false;
1357
618
  return isHookId(Extract.unwrap(tag));
1358
619
  }
1359
- /**
1360
- * Check if the function node is a hook definition based on its name.
1361
- * @param node The function node to check.
1362
- * @returns `true` if the function is a hook definition.
1363
- */
1364
620
  function isHookDefinition(node) {
1365
621
  if (node == null) return false;
1366
622
  const id = getFunctionId(node);
1367
623
  switch (id?.type) {
1368
624
  case AST_NODE_TYPES.Identifier: return isHookName(id.name);
1369
- case AST_NODE_TYPES.MemberExpression: return "name" in id.property && isHookName(id.property.name);
625
+ case AST_NODE_TYPES.MemberExpression: return hasProperty(id.property, "name") && isHookName(id.property.name);
1370
626
  default: return false;
1371
627
  }
1372
628
  }
1373
- /**
1374
- * Check if the node is a React Hook call by its name.
1375
- * @param node The node to check.
1376
- * @returns `true` if the node is a React Hook call, `false` otherwise.
1377
- */
1378
629
  function isHookCall(node) {
1379
630
  if (node == null) return false;
1380
631
  if (node.type !== AST_NODE_TYPES.CallExpression) return false;
@@ -1382,12 +633,6 @@ function isHookCall(node) {
1382
633
  if (name == null) return false;
1383
634
  return isHookName(name);
1384
635
  }
1385
- /**
1386
- * Check if the node is a useRef-like call (ex: `useRef` or a custom ref hook).
1387
- * @param node The AST node to check.
1388
- * @param additionalRefHooks Regex pattern matching custom hooks that should be treated as ref hooks.
1389
- * @returns `true` if the node is a useRef-like call.
1390
- */
1391
636
  function isUseRefLikeCall(node, additionalRefHooks = { test: constFalse }) {
1392
637
  if (node == null) return false;
1393
638
  if (node.type !== AST_NODE_TYPES.CallExpression) return false;
@@ -1395,12 +640,6 @@ function isUseRefLikeCall(node, additionalRefHooks = { test: constFalse }) {
1395
640
  if (name == null) return false;
1396
641
  return name === "useRef" || additionalRefHooks.test(name);
1397
642
  }
1398
- /**
1399
- * Check if the node is a useState-like call (ex: `useState` or a custom state hook).
1400
- * @param node The AST node to check.
1401
- * @param additionalStateHooks Regex pattern matching custom hooks that should be treated as state hooks.
1402
- * @returns `true` if the node is a useState-like call.
1403
- */
1404
643
  function isUseStateLikeCall(node, additionalStateHooks = { test: constFalse }) {
1405
644
  if (node == null) return false;
1406
645
  if (node.type !== AST_NODE_TYPES.CallExpression) return false;
@@ -1408,12 +647,6 @@ function isUseStateLikeCall(node, additionalStateHooks = { test: constFalse }) {
1408
647
  if (name == null) return false;
1409
648
  return name === "useState" || additionalStateHooks.test(name);
1410
649
  }
1411
- /**
1412
- * Check if the node is a useEffect-like call (ex: `useEffect`, `useLayoutEffect`, or a custom effect hook).
1413
- * @param node The AST node to check.
1414
- * @param additionalEffectHooks Regex pattern matching custom hooks that should be treated as effect hooks.
1415
- * @returns `true` if the node is a useEffect-like call.
1416
- */
1417
650
  function isUseEffectLikeCall(node, additionalEffectHooks = { test: constFalse }) {
1418
651
  if (node == null) return false;
1419
652
  if (node.type !== AST_NODE_TYPES.CallExpression) return false;
@@ -1421,21 +654,11 @@ function isUseEffectLikeCall(node, additionalEffectHooks = { test: constFalse })
1421
654
  if (name == null) return false;
1422
655
  return /^use\w*Effect$/u.test(name) || additionalEffectHooks.test(name);
1423
656
  }
1424
- /**
1425
- * Check if the node is the setup callback passed to a useEffect-like call.
1426
- * @param node The AST node to check.
1427
- * @returns `true` if the node is a useEffect setup callback.
1428
- */
1429
657
  function isUseEffectSetupCallback(node) {
1430
658
  if (node == null) return false;
1431
659
  const expr = Extract.unwrap(node);
1432
660
  return expr.parent?.type === AST_NODE_TYPES.CallExpression && expr.parent.arguments.at(0) === expr && isUseEffectLikeCall(expr.parent);
1433
661
  }
1434
- /**
1435
- * Check if the node is the cleanup callback returned by a useEffect-like setup callback.
1436
- * @param node The AST node to check.
1437
- * @returns `true` if the node is a useEffect cleanup callback.
1438
- */
1439
662
  function isUseEffectCleanupCallback(node) {
1440
663
  if (node == null) return false;
1441
664
  const expr = Extract.unwrap(node);
@@ -1447,12 +670,6 @@ function isUseEffectCleanupCallback(node) {
1447
670
 
1448
671
  //#endregion
1449
672
  //#region src/function-component-collector.ts
1450
- /**
1451
- * Get an api and visitor object for the rule to collect function components.
1452
- * @param context The ESLint rule context.
1453
- * @param options The options to use.
1454
- * @returns The api and visitor of the collector.
1455
- */
1456
673
  function getFunctionComponentCollector(context, options = {}) {
1457
674
  const { collectDisplayName = false, hint = DEFAULT_COMPONENT_DETECTION_HINT } = options;
1458
675
  const functionEntries = [];
@@ -1548,11 +765,6 @@ function getFunctionComponentCollector(context, options = {}) {
1548
765
 
1549
766
  //#endregion
1550
767
  //#region src/hook-collector.ts
1551
- /**
1552
- * Get an api and visitor object for the rule to collect hooks.
1553
- * @param context The ESLint rule context.
1554
- * @returns The api and visitor of the collector.
1555
- */
1556
768
  function getHookCollector(context) {
1557
769
  const functionEntries = [];
1558
770
  const hooks = /* @__PURE__ */ new Map();
@@ -1618,25 +830,8 @@ function getHookCollector(context) {
1618
830
 
1619
831
  //#endregion
1620
832
  //#region src/jsx-config.ts
1621
- /**
1622
- * Weak‑map cache keyed by `sourceCode` so that the (potentially expensive)
1623
- * pragma‑scanning pass runs at most once per file.
1624
- */
1625
833
  const annotationCache = /* @__PURE__ */ new WeakMap();
1626
- /**
1627
- * Weak‑map cache for the fully‑merged config (compiler options + annotation).
1628
- */
1629
834
  const mergedCache = /* @__PURE__ */ new WeakMap();
1630
- /**
1631
- * Read JSX configuration from the TypeScript compiler options exposed by the
1632
- * parser services.
1633
- *
1634
- * Falls back to sensible React defaults when no compiler options are
1635
- * available (e.g. when the file is parsed without type information).
1636
- *
1637
- * @param context The ESLint rule context.
1638
- * @returns Fully‑populated `JsxConfig` derived from compiler options.
1639
- */
1640
835
  function getJsxConfigFromCompilerOptions(context) {
1641
836
  const options = context.sourceCode.parserServices?.program?.getCompilerOptions() ?? {};
1642
837
  return {
@@ -1646,16 +841,6 @@ function getJsxConfigFromCompilerOptions(context) {
1646
841
  jsxImportSource: options.jsxImportSource ?? "react"
1647
842
  };
1648
843
  }
1649
- /**
1650
- * Extract JSX configuration from `@jsx`, `@jsxFrag`, `@jsxRuntime` and
1651
- * `@jsxImportSource` pragma comments in the source file.
1652
- *
1653
- * The result is cached per `sourceCode` instance via a `WeakMap` so that
1654
- * repeated calls from different rules analysing the same file are free.
1655
- *
1656
- * @param context The ESLint rule context.
1657
- * @returns Partial `JsxConfig` containing only the values found in pragmas.
1658
- */
1659
844
  function getJsxConfigFromAnnotation(context) {
1660
845
  const cached = annotationCache.get(context.sourceCode);
1661
846
  if (cached != null) return cached;
@@ -1679,17 +864,6 @@ function getJsxConfigFromAnnotation(context) {
1679
864
  annotationCache.set(context.sourceCode, options);
1680
865
  return options;
1681
866
  }
1682
- /**
1683
- * Get the fully‑merged JSX configuration for the current file.
1684
- *
1685
- * Compiler options provide the base values; pragma annotations found in the
1686
- * source override them where present. The result is cached per `sourceCode`.
1687
- *
1688
- * This is the main entry‑point most consumers should use.
1689
- *
1690
- * @param context The ESLint rule context.
1691
- * @returns Fully‑populated, merged `JsxConfig`.
1692
- */
1693
867
  function getJsxConfig(context) {
1694
868
  const cached = mergedCache.get(context.sourceCode);
1695
869
  if (cached != null) return cached;
@@ -1710,113 +884,30 @@ function isFlagSetOnObject(obj, flag) {
1710
884
  return isFlagSet(obj.flags, flag);
1711
885
  }
1712
886
  const isTypeFlagSet = isFlagSetOnObject;
1713
- /**
1714
- * Check if the type is a boolean literal type.
1715
- * @param type The type to check.
1716
- * @returns `true` if the type is a boolean literal type.
1717
- */
1718
887
  function isBooleanLiteralType(type) {
1719
888
  return isTypeFlagSet(type, ts.TypeFlags.BooleanLiteral);
1720
889
  }
1721
- /**
1722
- * Check if the type is the `false` literal type.
1723
- * @internal
1724
- */
1725
890
  const isFalseLiteralType = (type) => isBooleanLiteralType(type) && type.intrinsicName === "false";
1726
- /**
1727
- * Check if the type is the `true` literal type.
1728
- * @internal
1729
- */
1730
891
  const isTrueLiteralType = (type) => isBooleanLiteralType(type) && type.intrinsicName === "true";
1731
- /**
1732
- * Check if the type is an any-like type.
1733
- * @internal
1734
- */
1735
892
  const isAnyType = (type) => isTypeFlagSet(type, ts.TypeFlags.TypeParameter | ts.TypeFlags.Any);
1736
- /**
1737
- * Check if the type is a bigint-like type.
1738
- * @internal
1739
- */
1740
893
  const isBigIntType = (type) => isTypeFlagSet(type, ts.TypeFlags.BigIntLike);
1741
- /**
1742
- * Check if the type is a boolean-like type.
1743
- * @internal
1744
- */
1745
894
  const isBooleanType = (type) => isTypeFlagSet(type, ts.TypeFlags.BooleanLike);
1746
- /**
1747
- * Check if the type is an enum-like type.
1748
- * @internal
1749
- */
1750
895
  const isEnumType = (type) => isTypeFlagSet(type, ts.TypeFlags.EnumLike);
1751
- /**
1752
- * Check if the type is a falsy bigint literal type.
1753
- * @internal
1754
- */
1755
896
  const isFalsyBigIntType = (type) => type.isLiteral() && isMatching({ value: { base10Value: "0" } }, type);
1756
- /**
1757
- * Check if the type is a falsy number literal type.
1758
- * @internal
1759
- */
1760
897
  const isFalsyNumberType = (type) => type.isNumberLiteral() && type.value === 0;
1761
- /**
1762
- * Check if the type is a falsy string literal type.
1763
- * @internal
1764
- */
1765
898
  const isFalsyStringType = (type) => type.isStringLiteral() && type.value === "";
1766
- /**
1767
- * Check if the type is the never type.
1768
- * @internal
1769
- */
1770
899
  const isNeverType = (type) => isTypeFlagSet(type, ts.TypeFlags.Never);
1771
- /**
1772
- * Check if the type is a nullish type (null, undefined, or void).
1773
- * @internal
1774
- */
1775
900
  const isNullishType = (type) => isTypeFlagSet(type, ts.TypeFlags.Null | ts.TypeFlags.Undefined | ts.TypeFlags.VoidLike);
1776
- /**
1777
- * Check if the type is a number-like type.
1778
- * @internal
1779
- */
1780
901
  const isNumberType = (type) => isTypeFlagSet(type, ts.TypeFlags.NumberLike);
1781
- /**
1782
- * Check if the type is an object type.
1783
- * @internal
1784
- */
1785
902
  const isObjectType = (type) => !isTypeFlagSet(type, ts.TypeFlags.Null | ts.TypeFlags.Undefined | ts.TypeFlags.VoidLike | ts.TypeFlags.BooleanLike | ts.TypeFlags.StringLike | ts.TypeFlags.NumberLike | ts.TypeFlags.BigIntLike | ts.TypeFlags.TypeParameter | ts.TypeFlags.Any | ts.TypeFlags.Unknown | ts.TypeFlags.Never);
1786
- /**
1787
- * Check if the type is a string-like type.
1788
- * @internal
1789
- */
1790
903
  const isStringType = (type) => isTypeFlagSet(type, ts.TypeFlags.StringLike);
1791
- /**
1792
- * Check if the type is a truthy bigint literal type.
1793
- * @internal
1794
- */
1795
904
  const isTruthyBigIntType = (type) => type.isLiteral() && isMatching({ value: { base10Value: P.not("0") } }, type);
1796
- /**
1797
- * Check if the type is a truthy number literal type.
1798
- * @internal
1799
- */
1800
905
  const isTruthyNumberType = (type) => type.isNumberLiteral() && type.value !== 0;
1801
- /**
1802
- * Check if the type is a truthy string literal type.
1803
- * @internal
1804
- */
1805
906
  const isTruthyStringType = (type) => type.isStringLiteral() && type.value !== "";
1806
- /**
1807
- * Check if the type is the unknown type.
1808
- * @internal
1809
- */
1810
907
  const isUnknownType = (type) => isTypeFlagSet(type, ts.TypeFlags.Unknown);
1811
908
 
1812
909
  //#endregion
1813
910
  //#region src/type-name.ts
1814
- /**
1815
- * Get the fully qualified name of a symbol, handling cases that `ts.TypeChecker.getFullyQualifiedName` does not handle (ex: `export as namespace preact`).
1816
- * @param checker The TypeScript type checker.
1817
- * @param symbol The symbol to get fully qualified name for.
1818
- * @returns The fully qualified name of the symbol.
1819
- */
1820
911
  function getFullyQualifiedNameEx(checker, symbol) {
1821
912
  let name = symbol.name;
1822
913
  let parent = symbol.declarations?.at(0)?.parent;
@@ -1863,13 +954,6 @@ function getFullyQualifiedNameEx(checker, symbol) {
1863
954
 
1864
955
  //#endregion
1865
956
  //#region src/type-variant.ts
1866
- /**
1867
- * Get the variants of an array of types.
1868
- * @param types The types to get the variants of.
1869
- * @returns The variants of the types.
1870
- * @remarks Ported from https://github.com/typescript-eslint/typescript-eslint/blob/eb736bbfc22554694400e6a4f97051d845d32e0b/packages/eslint-plugin/src/rules/strict-boolean-expressions.ts#L826 with some enhancements.
1871
- * @internal
1872
- */
1873
957
  function getTypeVariants(types) {
1874
958
  const variants = /* @__PURE__ */ new Set();
1875
959
  if (types.some(isUnknownType)) {