@eslint-react/core 5.24.1 → 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.
- package/dist/index.d.ts +1 -1
- package/dist/index.js +104 -1320
- package/package.json +5 -5
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"
|
|
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"
|
|
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"
|
|
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"
|
|
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,783 +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
|
-
* 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
|
-
* Creates a function that can be called in data-first style or data-last
|
|
923
|
-
* (`pipe`-friendly) style.
|
|
924
|
-
*
|
|
925
|
-
* **When to use**
|
|
926
|
-
*
|
|
927
|
-
* Use to expose one implementation through both direct and `pipe`-friendly
|
|
928
|
-
* call styles.
|
|
929
|
-
*
|
|
930
|
-
* **Details**
|
|
931
|
-
*
|
|
932
|
-
* Pass either the arity of the uncurried function or a predicate that decides
|
|
933
|
-
* whether the current call is data-first. Arity is the common case. Use a
|
|
934
|
-
* predicate when optional arguments make arity ambiguous.
|
|
935
|
-
*
|
|
936
|
-
* **Example** (Selecting data-first or data-last style by arity)
|
|
937
|
-
*
|
|
938
|
-
* ```ts
|
|
939
|
-
* import { Function, pipe } from "effect"
|
|
940
|
-
*
|
|
941
|
-
* const sum = Function.dual<
|
|
942
|
-
* (that: number) => (self: number) => number,
|
|
943
|
-
* (self: number, that: number) => number
|
|
944
|
-
* >(2, (self, that) => self + that)
|
|
945
|
-
*
|
|
946
|
-
* sum(2, 3) // => 5
|
|
947
|
-
* pipe(2, sum(3)) // => 5
|
|
948
|
-
* ```
|
|
949
|
-
*
|
|
950
|
-
* **Example** (Defining overloads with call signatures)
|
|
951
|
-
*
|
|
952
|
-
* ```ts
|
|
953
|
-
* import { Function, pipe } from "effect"
|
|
954
|
-
*
|
|
955
|
-
* const sum: {
|
|
956
|
-
* (that: number): (self: number) => number
|
|
957
|
-
* (self: number, that: number): number
|
|
958
|
-
* } = Function.dual(2, (self: number, that: number): number => self + that)
|
|
959
|
-
*
|
|
960
|
-
* sum(2, 3) // => 5
|
|
961
|
-
* pipe(2, sum(3)) // => 5
|
|
962
|
-
* ```
|
|
963
|
-
*
|
|
964
|
-
* **Example** (Selecting data-first or data-last style with a predicate)
|
|
965
|
-
*
|
|
966
|
-
* ```ts
|
|
967
|
-
* import { Function, pipe } from "effect"
|
|
968
|
-
*
|
|
969
|
-
* const sum = Function.dual<
|
|
970
|
-
* (that: number) => (self: number) => number,
|
|
971
|
-
* (self: number, that: number) => number
|
|
972
|
-
* >(
|
|
973
|
-
* (args) => args.length === 2,
|
|
974
|
-
* (self, that) => self + that
|
|
975
|
-
* )
|
|
976
|
-
*
|
|
977
|
-
* sum(2, 3) // => 5
|
|
978
|
-
* pipe(2, sum(3)) // => 5
|
|
979
|
-
* ```
|
|
980
|
-
*
|
|
981
|
-
* @category combinators
|
|
982
|
-
* @since 2.0.0
|
|
983
|
-
*/
|
|
984
|
-
const dual = function(arity, body) {
|
|
985
|
-
if (typeof arity === "function") return function() {
|
|
986
|
-
return arity(arguments) ? body.apply(this, arguments) : ((self) => body(self, ...arguments));
|
|
987
|
-
};
|
|
988
|
-
switch (arity) {
|
|
989
|
-
case 0:
|
|
990
|
-
case 1: throw new RangeError(`Invalid arity ${arity}`);
|
|
991
|
-
case 2: return function(a, b) {
|
|
992
|
-
if (arguments.length >= 2) return body(a, b);
|
|
993
|
-
return function(self) {
|
|
994
|
-
return body(self, a);
|
|
995
|
-
};
|
|
996
|
-
};
|
|
997
|
-
case 3: return function(a, b, c) {
|
|
998
|
-
if (arguments.length >= 3) return body(a, b, c);
|
|
999
|
-
return function(self) {
|
|
1000
|
-
return body(self, a, b);
|
|
1001
|
-
};
|
|
1002
|
-
};
|
|
1003
|
-
default: return function() {
|
|
1004
|
-
if (arguments.length >= arity) return body.apply(this, arguments);
|
|
1005
|
-
const args = arguments;
|
|
1006
|
-
return function(self) {
|
|
1007
|
-
return body(self, ...args);
|
|
1008
|
-
};
|
|
1009
|
-
};
|
|
1010
|
-
}
|
|
1011
|
-
};
|
|
1012
|
-
/**
|
|
1013
|
-
* Returns its input argument unchanged.
|
|
1014
|
-
*
|
|
1015
|
-
* **When to use**
|
|
1016
|
-
*
|
|
1017
|
-
* Use to return a value unchanged where a function is required.
|
|
1018
|
-
*
|
|
1019
|
-
* **Example** (Returning the same value)
|
|
1020
|
-
*
|
|
1021
|
-
* ```ts
|
|
1022
|
-
* import { identity } from "effect"
|
|
1023
|
-
*
|
|
1024
|
-
* identity(5) // => 5
|
|
1025
|
-
* ```
|
|
1026
|
-
*
|
|
1027
|
-
* @category combinators
|
|
1028
|
-
* @since 2.0.0
|
|
1029
|
-
*/
|
|
1030
|
-
const identity = (a) => a;
|
|
1031
|
-
/**
|
|
1032
|
-
* Returns the input value with a different static type.
|
|
1033
|
-
*
|
|
1034
|
-
* **When to use**
|
|
1035
|
-
*
|
|
1036
|
-
* Use when you need an explicit type-level cast and accept that the value is
|
|
1037
|
-
* returned unchanged at runtime.
|
|
1038
|
-
*
|
|
1039
|
-
* **Gotchas**
|
|
1040
|
-
*
|
|
1041
|
-
* This is a type-level cast only; it performs no runtime validation or
|
|
1042
|
-
* conversion.
|
|
1043
|
-
*
|
|
1044
|
-
* @see {@link satisfies} for checking assignability without changing the resulting type
|
|
1045
|
-
*
|
|
1046
|
-
* @category utility types
|
|
1047
|
-
* @since 4.0.0
|
|
1048
|
-
*/
|
|
1049
|
-
const cast = identity;
|
|
1050
|
-
/**
|
|
1051
|
-
* Creates a zero-argument function that always returns the provided value.
|
|
1052
|
-
*
|
|
1053
|
-
* **When to use**
|
|
1054
|
-
*
|
|
1055
|
-
* Use when you need a thunk or callback that returns the same value on every
|
|
1056
|
-
* invocation.
|
|
1057
|
-
*
|
|
1058
|
-
* **Example** (Creating a constant thunk)
|
|
1059
|
-
*
|
|
1060
|
-
* ```ts
|
|
1061
|
-
* import { Function } from "effect"
|
|
1062
|
-
*
|
|
1063
|
-
* const constNull = Function.constant(null)
|
|
1064
|
-
*
|
|
1065
|
-
* constNull() // => null
|
|
1066
|
-
* constNull() // => null
|
|
1067
|
-
* ```
|
|
1068
|
-
*
|
|
1069
|
-
* @category constructors
|
|
1070
|
-
* @since 2.0.0
|
|
1071
|
-
*/
|
|
1072
|
-
const constant = (value) => () => value;
|
|
1073
|
-
/**
|
|
1074
|
-
* Returns `true` when called.
|
|
1075
|
-
*
|
|
1076
|
-
* **When to use**
|
|
1077
|
-
*
|
|
1078
|
-
* Use when you need a thunk that returns `true` on every invocation.
|
|
1079
|
-
*
|
|
1080
|
-
* **Example** (Returning true from a thunk)
|
|
1081
|
-
*
|
|
1082
|
-
* ```ts
|
|
1083
|
-
* import { Function } from "effect"
|
|
1084
|
-
*
|
|
1085
|
-
* Function.constTrue() // => true
|
|
1086
|
-
* ```
|
|
1087
|
-
*
|
|
1088
|
-
* @category constants
|
|
1089
|
-
* @since 2.0.0
|
|
1090
|
-
*/
|
|
1091
|
-
const constTrue = constant(true);
|
|
1092
|
-
/**
|
|
1093
|
-
* Returns `false` when called.
|
|
1094
|
-
*
|
|
1095
|
-
* **When to use**
|
|
1096
|
-
*
|
|
1097
|
-
* Use when you need a thunk that returns `false` on every invocation.
|
|
1098
|
-
*
|
|
1099
|
-
* **Example** (Returning false from a thunk)
|
|
1100
|
-
*
|
|
1101
|
-
* ```ts
|
|
1102
|
-
* import { Function } from "effect"
|
|
1103
|
-
*
|
|
1104
|
-
* Function.constFalse() // => false
|
|
1105
|
-
* ```
|
|
1106
|
-
*
|
|
1107
|
-
* @category constants
|
|
1108
|
-
* @since 2.0.0
|
|
1109
|
-
*/
|
|
1110
|
-
const constFalse = constant(false);
|
|
1111
|
-
/**
|
|
1112
|
-
* Returns `null` when called.
|
|
1113
|
-
*
|
|
1114
|
-
* **When to use**
|
|
1115
|
-
*
|
|
1116
|
-
* Use when you need a thunk that returns `null` on every invocation.
|
|
1117
|
-
*
|
|
1118
|
-
* **Example** (Returning null from a thunk)
|
|
1119
|
-
*
|
|
1120
|
-
* ```ts
|
|
1121
|
-
* import { Function } from "effect"
|
|
1122
|
-
*
|
|
1123
|
-
* Function.constNull() // => null
|
|
1124
|
-
* ```
|
|
1125
|
-
*
|
|
1126
|
-
* @category constants
|
|
1127
|
-
* @since 2.0.0
|
|
1128
|
-
*/
|
|
1129
|
-
const constNull = constant(null);
|
|
1130
|
-
/**
|
|
1131
|
-
* Returns `undefined` when called.
|
|
1132
|
-
*
|
|
1133
|
-
* **When to use**
|
|
1134
|
-
*
|
|
1135
|
-
* Use when you need a thunk that returns `undefined` on every invocation.
|
|
1136
|
-
*
|
|
1137
|
-
* **Example** (Returning undefined from a thunk)
|
|
1138
|
-
*
|
|
1139
|
-
* ```ts
|
|
1140
|
-
* import { Function } from "effect"
|
|
1141
|
-
*
|
|
1142
|
-
* Function.constUndefined() // => undefined
|
|
1143
|
-
* ```
|
|
1144
|
-
*
|
|
1145
|
-
* @category constants
|
|
1146
|
-
* @since 2.0.0
|
|
1147
|
-
*/
|
|
1148
|
-
const constUndefined = constant(void 0);
|
|
1149
|
-
/**
|
|
1150
|
-
* 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`.
|
|
1151
|
-
* The result is obtained by first applying the `ab` function to `a` and then applying the `bc` function to the result of `ab`.
|
|
1152
|
-
*
|
|
1153
|
-
* **When to use**
|
|
1154
|
-
*
|
|
1155
|
-
* Use to compose exactly two unary functions into a reusable unary function.
|
|
1156
|
-
*
|
|
1157
|
-
* **Example** (Composing two functions)
|
|
1158
|
-
*
|
|
1159
|
-
* ```ts
|
|
1160
|
-
* import { Function } from "effect"
|
|
1161
|
-
*
|
|
1162
|
-
* const increment = (n: number) => n + 1
|
|
1163
|
-
* const square = (n: number) => n * n
|
|
1164
|
-
*
|
|
1165
|
-
* Function.compose(increment, square)(2) // => 9
|
|
1166
|
-
* ```
|
|
1167
|
-
*
|
|
1168
|
-
* @see {@link flow} for composing a left-to-right sequence of functions
|
|
1169
|
-
* @see {@link pipe} for applying a value through a left-to-right sequence immediately
|
|
1170
|
-
*
|
|
1171
|
-
* @category combinators
|
|
1172
|
-
* @since 2.0.0
|
|
1173
|
-
*/
|
|
1174
|
-
const compose = dual(2, (ab, bc) => (a) => bc(ab(a)));
|
|
1175
|
-
/**
|
|
1176
|
-
* Marks an impossible branch by accepting a `never` value and returning any
|
|
1177
|
-
* type.
|
|
1178
|
-
*
|
|
1179
|
-
* **When to use**
|
|
1180
|
-
*
|
|
1181
|
-
* Use when you need a return value in a branch that exhaustive checks prove
|
|
1182
|
-
* cannot be reached.
|
|
1183
|
-
*
|
|
1184
|
-
* **Gotchas**
|
|
1185
|
-
*
|
|
1186
|
-
* Calling `absurd` throws, because a value of type `never` should be
|
|
1187
|
-
* impossible at runtime.
|
|
1188
|
-
*
|
|
1189
|
-
* **Example** (Handling impossible values)
|
|
1190
|
-
*
|
|
1191
|
-
* ```ts
|
|
1192
|
-
* import { absurd } from "effect"
|
|
1193
|
-
*
|
|
1194
|
-
* const handleNever = (value: never) => {
|
|
1195
|
-
* return absurd(value) // This will throw an error if called
|
|
1196
|
-
* }
|
|
1197
|
-
* ```
|
|
1198
|
-
*
|
|
1199
|
-
* @category utility types
|
|
1200
|
-
* @since 2.0.0
|
|
1201
|
-
*/
|
|
1202
|
-
const absurd = (_) => {
|
|
1203
|
-
throw new Error("Called `absurd` function which should be uncallable");
|
|
1204
|
-
};
|
|
1205
|
-
/**
|
|
1206
|
-
* Creates a compile-time placeholder for a value of any type.
|
|
1207
|
-
*
|
|
1208
|
-
* **When to use**
|
|
1209
|
-
*
|
|
1210
|
-
* Use as a temporary typed placeholder while developing incomplete code.
|
|
1211
|
-
*
|
|
1212
|
-
* **Gotchas**
|
|
1213
|
-
*
|
|
1214
|
-
* `hole` is intended for temporary development use. If the placeholder is
|
|
1215
|
-
* evaluated at runtime, it throws.
|
|
1216
|
-
*
|
|
1217
|
-
* **Example** (Creating a development placeholder)
|
|
1218
|
-
*
|
|
1219
|
-
* ```ts
|
|
1220
|
-
* import { hole } from "effect"
|
|
1221
|
-
*
|
|
1222
|
-
* // Intentionally not called: `hole` throws if the placeholder is evaluated.
|
|
1223
|
-
* const buildUser = (id: number): { readonly id: number; readonly name: string } => ({
|
|
1224
|
-
* id,
|
|
1225
|
-
* name: hole<string>()
|
|
1226
|
-
* })
|
|
1227
|
-
*
|
|
1228
|
-
* ```
|
|
1229
|
-
*
|
|
1230
|
-
* @category utility types
|
|
1231
|
-
* @since 2.0.0
|
|
1232
|
-
*/
|
|
1233
|
-
const hole = cast(absurd);
|
|
1234
|
-
/**
|
|
1235
|
-
* Creates a predicate that returns `true` only if both predicates are `true`.
|
|
1236
|
-
*
|
|
1237
|
-
* **When to use**
|
|
1238
|
-
*
|
|
1239
|
-
* Use when you want to combine `Predicate`s with AND, accepting values that
|
|
1240
|
-
* satisfy multiple conditions, including refinements that narrow to an
|
|
1241
|
-
* intersection.
|
|
1242
|
-
*
|
|
1243
|
-
* **Details**
|
|
1244
|
-
*
|
|
1245
|
-
* Evaluation short-circuits on the first `false`. For refinements, the output
|
|
1246
|
-
* type is an intersection.
|
|
1247
|
-
*
|
|
1248
|
-
* **Example** (Checking both conditions)
|
|
1249
|
-
*
|
|
1250
|
-
* ```ts
|
|
1251
|
-
* import { Predicate } from "effect"
|
|
1252
|
-
*
|
|
1253
|
-
* const hasAAndB = Predicate.and(
|
|
1254
|
-
* Predicate.hasProperty("a"),
|
|
1255
|
-
* Predicate.hasProperty("b")
|
|
1256
|
-
* )
|
|
1257
|
-
*
|
|
1258
|
-
* const input: unknown = JSON.parse(`{"a":1,"b":"ok"}`)
|
|
1259
|
-
* if (hasAAndB(input)) {
|
|
1260
|
-
* // input has both properties at this point
|
|
1261
|
-
* const a = input.a
|
|
1262
|
-
* const b = input.b
|
|
1263
|
-
*
|
|
1264
|
-
* const values = [a, b] // => [1, "ok"]
|
|
1265
|
-
* }
|
|
1266
|
-
* ```
|
|
1267
|
-
*
|
|
1268
|
-
* @see {@link or}
|
|
1269
|
-
* @see {@link not}
|
|
1270
|
-
* @category combinators
|
|
1271
|
-
* @since 2.0.0
|
|
1272
|
-
*/
|
|
1273
|
-
const and = dual(2, (a, b) => (data) => a(data) && b(data));
|
|
1274
|
-
/**
|
|
1275
|
-
* Creates a predicate that returns `true` if either predicate is `true`.
|
|
1276
|
-
*
|
|
1277
|
-
* **When to use**
|
|
1278
|
-
*
|
|
1279
|
-
* Use when you want to combine `Predicate`s with OR, accepting values that
|
|
1280
|
-
* satisfy at least one condition, including refinements that narrow to a union.
|
|
1281
|
-
*
|
|
1282
|
-
* **Details**
|
|
1283
|
-
*
|
|
1284
|
-
* Evaluation short-circuits on the first `true`. For refinements, the output
|
|
1285
|
-
* type is a union.
|
|
1286
|
-
*
|
|
1287
|
-
* **Example** (Checking either condition)
|
|
1288
|
-
*
|
|
1289
|
-
* ```ts
|
|
1290
|
-
* import { Predicate } from "effect"
|
|
1291
|
-
*
|
|
1292
|
-
* const isStringOrNumber = Predicate.or(Predicate.isString, Predicate.isNumber)
|
|
1293
|
-
*
|
|
1294
|
-
* isStringOrNumber("a") // => true
|
|
1295
|
-
* ```
|
|
1296
|
-
*
|
|
1297
|
-
* @see {@link and}
|
|
1298
|
-
* @see {@link xor}
|
|
1299
|
-
* @category combinators
|
|
1300
|
-
* @since 2.0.0
|
|
1301
|
-
*/
|
|
1302
|
-
const or = dual(2, (a, b) => (data) => a(data) || b(data));
|
|
1303
|
-
/**
|
|
1304
|
-
* Creates a predicate that returns `true` if exactly one predicate is `true`.
|
|
1305
|
-
*
|
|
1306
|
-
* **When to use**
|
|
1307
|
-
*
|
|
1308
|
-
* Use when you want to combine two `Predicate`s with exclusive-or semantics.
|
|
1309
|
-
*
|
|
1310
|
-
* **Details**
|
|
1311
|
-
*
|
|
1312
|
-
* Returns `true` when results differ.
|
|
1313
|
-
*
|
|
1314
|
-
* **Example** (Checking exclusive-or conditions)
|
|
1315
|
-
*
|
|
1316
|
-
* ```ts
|
|
1317
|
-
* import { Predicate } from "effect"
|
|
1318
|
-
*
|
|
1319
|
-
* const isEven = (n: number) => n % 2 === 0
|
|
1320
|
-
* const isPositive = (n: number) => n > 0
|
|
1321
|
-
* const either = Predicate.xor(isEven, isPositive)
|
|
1322
|
-
*
|
|
1323
|
-
* either(-2) // => true
|
|
1324
|
-
* ```
|
|
1325
|
-
*
|
|
1326
|
-
* @see {@link or}
|
|
1327
|
-
* @see {@link and}
|
|
1328
|
-
* @category combinators
|
|
1329
|
-
* @since 2.0.0
|
|
1330
|
-
*/
|
|
1331
|
-
const xor = dual(2, (a, b) => (data) => a(data) !== b(data));
|
|
1332
|
-
/**
|
|
1333
|
-
* Creates a predicate that returns `true` when both predicates agree.
|
|
1334
|
-
*
|
|
1335
|
-
* **When to use**
|
|
1336
|
-
*
|
|
1337
|
-
* Use when you want to check equivalence of two `Predicate`s.
|
|
1338
|
-
*
|
|
1339
|
-
* **Details**
|
|
1340
|
-
*
|
|
1341
|
-
* Returns `true` when both results are equal.
|
|
1342
|
-
*
|
|
1343
|
-
* **Example** (Defining equivalence)
|
|
1344
|
-
*
|
|
1345
|
-
* ```ts
|
|
1346
|
-
* import { Predicate } from "effect"
|
|
1347
|
-
*
|
|
1348
|
-
* const isEven = (n: number) => n % 2 === 0
|
|
1349
|
-
* const same = Predicate.eqv(isEven, isEven)
|
|
1350
|
-
*
|
|
1351
|
-
* same(3) // => true
|
|
1352
|
-
* ```
|
|
1353
|
-
*
|
|
1354
|
-
* @see {@link xor}
|
|
1355
|
-
* @category combinators
|
|
1356
|
-
* @since 2.0.0
|
|
1357
|
-
*/
|
|
1358
|
-
const eqv = dual(2, (a, b) => (data) => a(data) === b(data));
|
|
1359
|
-
/**
|
|
1360
|
-
* Creates a predicate representing logical implication: if `antecedent`, then `consequent`.
|
|
1361
|
-
*
|
|
1362
|
-
* **When to use**
|
|
1363
|
-
*
|
|
1364
|
-
* Use when you need to encode logical implication between `Predicate` rules,
|
|
1365
|
-
* where one rule only applies when a precondition holds.
|
|
1366
|
-
*
|
|
1367
|
-
* **Details**
|
|
1368
|
-
*
|
|
1369
|
-
* Models constraints like "if A then B" and returns `true` when the antecedent
|
|
1370
|
-
* is `false`.
|
|
1371
|
-
*
|
|
1372
|
-
* **Example** (Checking implication)
|
|
1373
|
-
*
|
|
1374
|
-
* ```ts
|
|
1375
|
-
* import { Predicate } from "effect"
|
|
1376
|
-
*
|
|
1377
|
-
* const isAdult = (age: number) => age >= 18
|
|
1378
|
-
* const canVote = (age: number) => age >= 18
|
|
1379
|
-
* const implies = Predicate.implies(isAdult, canVote)
|
|
1380
|
-
*
|
|
1381
|
-
* implies(16) // => true
|
|
1382
|
-
* ```
|
|
1383
|
-
*
|
|
1384
|
-
* @see {@link and}
|
|
1385
|
-
* @see {@link or}
|
|
1386
|
-
* @category combinators
|
|
1387
|
-
* @since 2.0.0
|
|
1388
|
-
*/
|
|
1389
|
-
const implies = dual(2, (antecedent, consequent) => (data) => !antecedent(data) || consequent(data));
|
|
1390
|
-
/**
|
|
1391
|
-
* Creates a predicate that returns `true` when neither predicate is `true`.
|
|
1392
|
-
*
|
|
1393
|
-
* **When to use**
|
|
1394
|
-
*
|
|
1395
|
-
* Use when you want to combine two `Predicate`s with logical NOR semantics.
|
|
1396
|
-
*
|
|
1397
|
-
* **Details**
|
|
1398
|
-
*
|
|
1399
|
-
* Returns the negation of `or`.
|
|
1400
|
-
*
|
|
1401
|
-
* **Example** (Checking NOR conditions)
|
|
1402
|
-
*
|
|
1403
|
-
* ```ts
|
|
1404
|
-
* import { Predicate } from "effect"
|
|
1405
|
-
*
|
|
1406
|
-
* const neither = Predicate.nor(Predicate.isString, Predicate.isNumber)
|
|
1407
|
-
*
|
|
1408
|
-
* neither(true) // => true
|
|
1409
|
-
* ```
|
|
1410
|
-
*
|
|
1411
|
-
* @see {@link or}
|
|
1412
|
-
* @see {@link not}
|
|
1413
|
-
* @category combinators
|
|
1414
|
-
* @since 2.0.0
|
|
1415
|
-
*/
|
|
1416
|
-
const nor = dual(2, (a, b) => (data) => !a(data) && !b(data));
|
|
1417
|
-
/**
|
|
1418
|
-
* Creates a predicate that returns `true` unless both predicates are `true`.
|
|
1419
|
-
*
|
|
1420
|
-
* **When to use**
|
|
1421
|
-
*
|
|
1422
|
-
* Use when you want to combine two `Predicate`s with logical NAND semantics.
|
|
1423
|
-
*
|
|
1424
|
-
* **Details**
|
|
1425
|
-
*
|
|
1426
|
-
* Returns the negation of `and`.
|
|
1427
|
-
*
|
|
1428
|
-
* **Example** (Checking NAND conditions)
|
|
1429
|
-
*
|
|
1430
|
-
* ```ts
|
|
1431
|
-
* import { Predicate } from "effect"
|
|
1432
|
-
*
|
|
1433
|
-
* const notBoth = Predicate.nand(Predicate.isString, Predicate.isNumber)
|
|
1434
|
-
*
|
|
1435
|
-
* notBoth("a") // => true
|
|
1436
|
-
* ```
|
|
1437
|
-
*
|
|
1438
|
-
* @see {@link and}
|
|
1439
|
-
* @see {@link not}
|
|
1440
|
-
* @category combinators
|
|
1441
|
-
* @since 2.0.0
|
|
1442
|
-
*/
|
|
1443
|
-
const nand = dual(2, (a, b) => (data) => !a(data) || !b(data));
|
|
1444
|
-
/**
|
|
1445
|
-
* Checks whether a value is a `function`.
|
|
1446
|
-
*
|
|
1447
|
-
* **When to use**
|
|
1448
|
-
*
|
|
1449
|
-
* Use when you need a `Predicate` guard to narrow an `unknown` value to a
|
|
1450
|
-
* callable function.
|
|
1451
|
-
*
|
|
1452
|
-
* **Details**
|
|
1453
|
-
*
|
|
1454
|
-
* Uses `typeof input === "function"`.
|
|
1455
|
-
*
|
|
1456
|
-
* **Example** (Guarding functions)
|
|
1457
|
-
*
|
|
1458
|
-
* ```ts
|
|
1459
|
-
* import { Predicate } from "effect"
|
|
1460
|
-
*
|
|
1461
|
-
* const data: unknown = () => 1
|
|
1462
|
-
*
|
|
1463
|
-
* if (Predicate.isFunction(data)) {
|
|
1464
|
-
* data() // => 1
|
|
1465
|
-
* }
|
|
1466
|
-
* ```
|
|
1467
|
-
*
|
|
1468
|
-
* @see {@link isObjectKeyword}
|
|
1469
|
-
* @category guards
|
|
1470
|
-
* @since 2.0.0
|
|
1471
|
-
*/
|
|
1472
|
-
function isFunction(input) {
|
|
1473
|
-
return typeof input === "function";
|
|
1474
|
-
}
|
|
1475
|
-
/**
|
|
1476
|
-
* Checks whether a value is an `object` in the JavaScript sense (objects, arrays, functions).
|
|
1477
|
-
*
|
|
1478
|
-
* **When to use**
|
|
1479
|
-
*
|
|
1480
|
-
* Use when you need a `Predicate` guard that accepts arrays and functions as
|
|
1481
|
-
* well as objects.
|
|
1482
|
-
*
|
|
1483
|
-
* **Details**
|
|
1484
|
-
*
|
|
1485
|
-
* Returns `true` for arrays and functions, and `false` for `null`.
|
|
1486
|
-
*
|
|
1487
|
-
* **Example** (Checking object keywords)
|
|
1488
|
-
*
|
|
1489
|
-
* ```ts
|
|
1490
|
-
* import { Predicate } from "effect"
|
|
1491
|
-
*
|
|
1492
|
-
* Predicate.isObjectKeyword(() => 1) // => true
|
|
1493
|
-
* Predicate.isObjectKeyword(null) // => false
|
|
1494
|
-
* ```
|
|
1495
|
-
*
|
|
1496
|
-
* @see {@link isObject}
|
|
1497
|
-
* @see {@link isObjectOrArray}
|
|
1498
|
-
* @category guards
|
|
1499
|
-
* @since 4.0.0
|
|
1500
|
-
*/
|
|
1501
|
-
function isObjectKeyword(input) {
|
|
1502
|
-
return typeof input === "object" && input !== null || isFunction(input);
|
|
1503
|
-
}
|
|
1504
|
-
/**
|
|
1505
|
-
* Checks whether a value has a given property key.
|
|
1506
|
-
*
|
|
1507
|
-
* **When to use**
|
|
1508
|
-
*
|
|
1509
|
-
* Use when you need a `Predicate` guard for property access on `unknown`
|
|
1510
|
-
* values with a simple structural object check.
|
|
1511
|
-
*
|
|
1512
|
-
* **Details**
|
|
1513
|
-
*
|
|
1514
|
-
* Uses the `in` operator and `isObjectKeyword`. This does not check property
|
|
1515
|
-
* value types.
|
|
1516
|
-
*
|
|
1517
|
-
* **Example** (Guarding object properties)
|
|
1518
|
-
*
|
|
1519
|
-
* ```ts
|
|
1520
|
-
* import { Predicate } from "effect"
|
|
1521
|
-
*
|
|
1522
|
-
* const hasName = Predicate.hasProperty("name")
|
|
1523
|
-
* const data: unknown = { name: "Ada" }
|
|
1524
|
-
*
|
|
1525
|
-
* if (hasName(data)) {
|
|
1526
|
-
* data.name // => "Ada"
|
|
1527
|
-
* }
|
|
1528
|
-
* ```
|
|
1529
|
-
*
|
|
1530
|
-
* @see {@link isTagged}
|
|
1531
|
-
* @see {@link isObjectKeyword}
|
|
1532
|
-
* @category guards
|
|
1533
|
-
* @since 2.0.0
|
|
1534
|
-
*/
|
|
1535
|
-
const hasProperty = dual(2, (data, property) => isObjectKeyword(data) && property in data);
|
|
1536
|
-
/**
|
|
1537
|
-
* Drops elements from the start while the predicate holds, returning the rest.
|
|
1538
|
-
*
|
|
1539
|
-
* **When to use**
|
|
1540
|
-
*
|
|
1541
|
-
* Use to remove a leading prefix of elements that satisfy a predicate.
|
|
1542
|
-
*
|
|
1543
|
-
* **Details**
|
|
1544
|
-
*
|
|
1545
|
-
* The predicate receives `(element, index)`.
|
|
1546
|
-
*
|
|
1547
|
-
* **Example** (Dropping while condition holds)
|
|
1548
|
-
*
|
|
1549
|
-
* ```ts
|
|
1550
|
-
* import { Array } from "effect"
|
|
1551
|
-
*
|
|
1552
|
-
* Array.dropWhile([1, 2, 3, 4, 5], (x) => x < 4) // => [4, 5]
|
|
1553
|
-
* ```
|
|
1554
|
-
*
|
|
1555
|
-
* @see {@link takeWhile} — keep the matching prefix instead
|
|
1556
|
-
* @see {@link drop} — drop a fixed count
|
|
1557
|
-
*
|
|
1558
|
-
* @category getters
|
|
1559
|
-
* @since 2.0.0
|
|
1560
|
-
*/
|
|
1561
|
-
const dropWhile = dual(2, (self, predicate) => {
|
|
1562
|
-
const input = Array.isArray(self) ? self : Array.from(self);
|
|
1563
|
-
const len = input.length;
|
|
1564
|
-
let idx = 0;
|
|
1565
|
-
while (idx < len && predicate(input[idx], idx)) idx++;
|
|
1566
|
-
return input.slice(idx);
|
|
1567
|
-
});
|
|
1568
|
-
/**
|
|
1569
|
-
* Takes elements from the start while the predicate holds, stopping at the
|
|
1570
|
-
* first element that fails.
|
|
1571
|
-
*
|
|
1572
|
-
* **When to use**
|
|
1573
|
-
*
|
|
1574
|
-
* Use to keep the leading elements of an iterable while each element satisfies
|
|
1575
|
-
* a predicate, returning the retained prefix as an array.
|
|
1576
|
-
*
|
|
1577
|
-
* **Details**
|
|
1578
|
-
*
|
|
1579
|
-
* Supports refinements for type narrowing. The predicate receives
|
|
1580
|
-
* `(element, index)`.
|
|
1581
|
-
*
|
|
1582
|
-
* **Example** (Taking while condition holds)
|
|
1583
|
-
*
|
|
1584
|
-
* ```ts
|
|
1585
|
-
* import { Array } from "effect"
|
|
1586
|
-
*
|
|
1587
|
-
* Array.takeWhile([1, 3, 2, 4, 1, 2], (x) => x < 4) // => [1, 3, 2]
|
|
1588
|
-
* ```
|
|
1589
|
-
*
|
|
1590
|
-
* @see {@link take} for keeping a fixed number of leading elements
|
|
1591
|
-
* @see {@link dropWhile} for removing the matching prefix and keeping the rest
|
|
1592
|
-
* @see {@link span} for splitting the matching prefix from the remaining elements
|
|
1593
|
-
*
|
|
1594
|
-
* @category getters
|
|
1595
|
-
* @since 2.0.0
|
|
1596
|
-
*/
|
|
1597
|
-
const takeWhile = dual(2, (self, predicate) => {
|
|
1598
|
-
const input = Array.isArray(self) ? self : Array.from(self);
|
|
1599
|
-
const len = input.length;
|
|
1600
|
-
let idx = 0;
|
|
1601
|
-
while (idx < len && predicate(input[idx], idx)) idx++;
|
|
1602
|
-
return input.slice(0, idx);
|
|
1603
|
-
});
|
|
1604
|
-
|
|
1605
583
|
//#endregion
|
|
1606
584
|
//#region src/hook.ts
|
|
1607
|
-
/** The names of React's built-in hooks. */
|
|
1608
585
|
const REACT_BUILTIN_HOOK_NAMES = [
|
|
1609
586
|
"use",
|
|
1610
587
|
"useActionState",
|
|
@@ -1626,55 +603,29 @@ const REACT_BUILTIN_HOOK_NAMES = [
|
|
|
1626
603
|
"useSyncExternalStore",
|
|
1627
604
|
"useTransition"
|
|
1628
605
|
];
|
|
1629
|
-
/**
|
|
1630
|
-
* Check if the name is a hook name (starts with `use` followed by an uppercase letter or digit).
|
|
1631
|
-
* @param name The name of the identifier to check.
|
|
1632
|
-
* @returns `true` if the name is a hook name.
|
|
1633
|
-
* @see https://github.com/facebook/react/blob/1d6c8168db1d82713202e842df3167787ffa00ed/packages/eslint-plugin-react-hooks/src/rules/RulesOfHooks.ts#L16
|
|
1634
|
-
*/
|
|
1635
606
|
function isHookName(name) {
|
|
1636
607
|
return name === "use" || /^use[A-Z0-9]/.test(name);
|
|
1637
608
|
}
|
|
1638
|
-
/**
|
|
1639
|
-
* Checks if the given node is a hook identifier.
|
|
1640
|
-
* @param id The AST node to check.
|
|
1641
|
-
* @returns `true` if the node is a hook identifier or member expression with hook name, `false` otherwise.
|
|
1642
|
-
*/
|
|
1643
609
|
function isHookId(id) {
|
|
1644
610
|
switch (id.type) {
|
|
1645
611
|
case AST_NODE_TYPES.Identifier: return isHookName(id.name);
|
|
1646
|
-
case AST_NODE_TYPES.MemberExpression: return "name"
|
|
612
|
+
case AST_NODE_TYPES.MemberExpression: return hasProperty(id.property, "name") && isHookName(id.property.name);
|
|
1647
613
|
default: return false;
|
|
1648
614
|
}
|
|
1649
615
|
}
|
|
1650
|
-
/**
|
|
1651
|
-
* Checks if the given expression is a hook tag (callee / tagged template tag).
|
|
1652
|
-
* @param tag The expression node to check.
|
|
1653
|
-
* @returns `true` if the expression is a hook identifier or member expression with hook name, `false` otherwise.
|
|
1654
|
-
*/
|
|
1655
616
|
function isHookTag(tag) {
|
|
1656
617
|
if (tag == null) return false;
|
|
1657
618
|
return isHookId(Extract.unwrap(tag));
|
|
1658
619
|
}
|
|
1659
|
-
/**
|
|
1660
|
-
* Check if the function node is a hook definition based on its name.
|
|
1661
|
-
* @param node The function node to check.
|
|
1662
|
-
* @returns `true` if the function is a hook definition.
|
|
1663
|
-
*/
|
|
1664
620
|
function isHookDefinition(node) {
|
|
1665
621
|
if (node == null) return false;
|
|
1666
622
|
const id = getFunctionId(node);
|
|
1667
623
|
switch (id?.type) {
|
|
1668
624
|
case AST_NODE_TYPES.Identifier: return isHookName(id.name);
|
|
1669
|
-
case AST_NODE_TYPES.MemberExpression: return "name"
|
|
625
|
+
case AST_NODE_TYPES.MemberExpression: return hasProperty(id.property, "name") && isHookName(id.property.name);
|
|
1670
626
|
default: return false;
|
|
1671
627
|
}
|
|
1672
628
|
}
|
|
1673
|
-
/**
|
|
1674
|
-
* Check if the node is a React Hook call by its name.
|
|
1675
|
-
* @param node The node to check.
|
|
1676
|
-
* @returns `true` if the node is a React Hook call, `false` otherwise.
|
|
1677
|
-
*/
|
|
1678
629
|
function isHookCall(node) {
|
|
1679
630
|
if (node == null) return false;
|
|
1680
631
|
if (node.type !== AST_NODE_TYPES.CallExpression) return false;
|
|
@@ -1682,12 +633,6 @@ function isHookCall(node) {
|
|
|
1682
633
|
if (name == null) return false;
|
|
1683
634
|
return isHookName(name);
|
|
1684
635
|
}
|
|
1685
|
-
/**
|
|
1686
|
-
* Check if the node is a useRef-like call (ex: `useRef` or a custom ref hook).
|
|
1687
|
-
* @param node The AST node to check.
|
|
1688
|
-
* @param additionalRefHooks Regex pattern matching custom hooks that should be treated as ref hooks.
|
|
1689
|
-
* @returns `true` if the node is a useRef-like call.
|
|
1690
|
-
*/
|
|
1691
636
|
function isUseRefLikeCall(node, additionalRefHooks = { test: constFalse }) {
|
|
1692
637
|
if (node == null) return false;
|
|
1693
638
|
if (node.type !== AST_NODE_TYPES.CallExpression) return false;
|
|
@@ -1695,12 +640,6 @@ function isUseRefLikeCall(node, additionalRefHooks = { test: constFalse }) {
|
|
|
1695
640
|
if (name == null) return false;
|
|
1696
641
|
return name === "useRef" || additionalRefHooks.test(name);
|
|
1697
642
|
}
|
|
1698
|
-
/**
|
|
1699
|
-
* Check if the node is a useState-like call (ex: `useState` or a custom state hook).
|
|
1700
|
-
* @param node The AST node to check.
|
|
1701
|
-
* @param additionalStateHooks Regex pattern matching custom hooks that should be treated as state hooks.
|
|
1702
|
-
* @returns `true` if the node is a useState-like call.
|
|
1703
|
-
*/
|
|
1704
643
|
function isUseStateLikeCall(node, additionalStateHooks = { test: constFalse }) {
|
|
1705
644
|
if (node == null) return false;
|
|
1706
645
|
if (node.type !== AST_NODE_TYPES.CallExpression) return false;
|
|
@@ -1708,12 +647,6 @@ function isUseStateLikeCall(node, additionalStateHooks = { test: constFalse }) {
|
|
|
1708
647
|
if (name == null) return false;
|
|
1709
648
|
return name === "useState" || additionalStateHooks.test(name);
|
|
1710
649
|
}
|
|
1711
|
-
/**
|
|
1712
|
-
* Check if the node is a useEffect-like call (ex: `useEffect`, `useLayoutEffect`, or a custom effect hook).
|
|
1713
|
-
* @param node The AST node to check.
|
|
1714
|
-
* @param additionalEffectHooks Regex pattern matching custom hooks that should be treated as effect hooks.
|
|
1715
|
-
* @returns `true` if the node is a useEffect-like call.
|
|
1716
|
-
*/
|
|
1717
650
|
function isUseEffectLikeCall(node, additionalEffectHooks = { test: constFalse }) {
|
|
1718
651
|
if (node == null) return false;
|
|
1719
652
|
if (node.type !== AST_NODE_TYPES.CallExpression) return false;
|
|
@@ -1721,21 +654,11 @@ function isUseEffectLikeCall(node, additionalEffectHooks = { test: constFalse })
|
|
|
1721
654
|
if (name == null) return false;
|
|
1722
655
|
return /^use\w*Effect$/u.test(name) || additionalEffectHooks.test(name);
|
|
1723
656
|
}
|
|
1724
|
-
/**
|
|
1725
|
-
* Check if the node is the setup callback passed to a useEffect-like call.
|
|
1726
|
-
* @param node The AST node to check.
|
|
1727
|
-
* @returns `true` if the node is a useEffect setup callback.
|
|
1728
|
-
*/
|
|
1729
657
|
function isUseEffectSetupCallback(node) {
|
|
1730
658
|
if (node == null) return false;
|
|
1731
659
|
const expr = Extract.unwrap(node);
|
|
1732
660
|
return expr.parent?.type === AST_NODE_TYPES.CallExpression && expr.parent.arguments.at(0) === expr && isUseEffectLikeCall(expr.parent);
|
|
1733
661
|
}
|
|
1734
|
-
/**
|
|
1735
|
-
* Check if the node is the cleanup callback returned by a useEffect-like setup callback.
|
|
1736
|
-
* @param node The AST node to check.
|
|
1737
|
-
* @returns `true` if the node is a useEffect cleanup callback.
|
|
1738
|
-
*/
|
|
1739
662
|
function isUseEffectCleanupCallback(node) {
|
|
1740
663
|
if (node == null) return false;
|
|
1741
664
|
const expr = Extract.unwrap(node);
|
|
@@ -1747,12 +670,6 @@ function isUseEffectCleanupCallback(node) {
|
|
|
1747
670
|
|
|
1748
671
|
//#endregion
|
|
1749
672
|
//#region src/function-component-collector.ts
|
|
1750
|
-
/**
|
|
1751
|
-
* Get an api and visitor object for the rule to collect function components.
|
|
1752
|
-
* @param context The ESLint rule context.
|
|
1753
|
-
* @param options The options to use.
|
|
1754
|
-
* @returns The api and visitor of the collector.
|
|
1755
|
-
*/
|
|
1756
673
|
function getFunctionComponentCollector(context, options = {}) {
|
|
1757
674
|
const { collectDisplayName = false, hint = DEFAULT_COMPONENT_DETECTION_HINT } = options;
|
|
1758
675
|
const functionEntries = [];
|
|
@@ -1848,11 +765,6 @@ function getFunctionComponentCollector(context, options = {}) {
|
|
|
1848
765
|
|
|
1849
766
|
//#endregion
|
|
1850
767
|
//#region src/hook-collector.ts
|
|
1851
|
-
/**
|
|
1852
|
-
* Get an api and visitor object for the rule to collect hooks.
|
|
1853
|
-
* @param context The ESLint rule context.
|
|
1854
|
-
* @returns The api and visitor of the collector.
|
|
1855
|
-
*/
|
|
1856
768
|
function getHookCollector(context) {
|
|
1857
769
|
const functionEntries = [];
|
|
1858
770
|
const hooks = /* @__PURE__ */ new Map();
|
|
@@ -1918,25 +830,8 @@ function getHookCollector(context) {
|
|
|
1918
830
|
|
|
1919
831
|
//#endregion
|
|
1920
832
|
//#region src/jsx-config.ts
|
|
1921
|
-
/**
|
|
1922
|
-
* Weak‑map cache keyed by `sourceCode` so that the (potentially expensive)
|
|
1923
|
-
* pragma‑scanning pass runs at most once per file.
|
|
1924
|
-
*/
|
|
1925
833
|
const annotationCache = /* @__PURE__ */ new WeakMap();
|
|
1926
|
-
/**
|
|
1927
|
-
* Weak‑map cache for the fully‑merged config (compiler options + annotation).
|
|
1928
|
-
*/
|
|
1929
834
|
const mergedCache = /* @__PURE__ */ new WeakMap();
|
|
1930
|
-
/**
|
|
1931
|
-
* Read JSX configuration from the TypeScript compiler options exposed by the
|
|
1932
|
-
* parser services.
|
|
1933
|
-
*
|
|
1934
|
-
* Falls back to sensible React defaults when no compiler options are
|
|
1935
|
-
* available (e.g. when the file is parsed without type information).
|
|
1936
|
-
*
|
|
1937
|
-
* @param context The ESLint rule context.
|
|
1938
|
-
* @returns Fully‑populated `JsxConfig` derived from compiler options.
|
|
1939
|
-
*/
|
|
1940
835
|
function getJsxConfigFromCompilerOptions(context) {
|
|
1941
836
|
const options = context.sourceCode.parserServices?.program?.getCompilerOptions() ?? {};
|
|
1942
837
|
return {
|
|
@@ -1946,16 +841,6 @@ function getJsxConfigFromCompilerOptions(context) {
|
|
|
1946
841
|
jsxImportSource: options.jsxImportSource ?? "react"
|
|
1947
842
|
};
|
|
1948
843
|
}
|
|
1949
|
-
/**
|
|
1950
|
-
* Extract JSX configuration from `@jsx`, `@jsxFrag`, `@jsxRuntime` and
|
|
1951
|
-
* `@jsxImportSource` pragma comments in the source file.
|
|
1952
|
-
*
|
|
1953
|
-
* The result is cached per `sourceCode` instance via a `WeakMap` so that
|
|
1954
|
-
* repeated calls from different rules analysing the same file are free.
|
|
1955
|
-
*
|
|
1956
|
-
* @param context The ESLint rule context.
|
|
1957
|
-
* @returns Partial `JsxConfig` containing only the values found in pragmas.
|
|
1958
|
-
*/
|
|
1959
844
|
function getJsxConfigFromAnnotation(context) {
|
|
1960
845
|
const cached = annotationCache.get(context.sourceCode);
|
|
1961
846
|
if (cached != null) return cached;
|
|
@@ -1979,17 +864,6 @@ function getJsxConfigFromAnnotation(context) {
|
|
|
1979
864
|
annotationCache.set(context.sourceCode, options);
|
|
1980
865
|
return options;
|
|
1981
866
|
}
|
|
1982
|
-
/**
|
|
1983
|
-
* Get the fully‑merged JSX configuration for the current file.
|
|
1984
|
-
*
|
|
1985
|
-
* Compiler options provide the base values; pragma annotations found in the
|
|
1986
|
-
* source override them where present. The result is cached per `sourceCode`.
|
|
1987
|
-
*
|
|
1988
|
-
* This is the main entry‑point most consumers should use.
|
|
1989
|
-
*
|
|
1990
|
-
* @param context The ESLint rule context.
|
|
1991
|
-
* @returns Fully‑populated, merged `JsxConfig`.
|
|
1992
|
-
*/
|
|
1993
867
|
function getJsxConfig(context) {
|
|
1994
868
|
const cached = mergedCache.get(context.sourceCode);
|
|
1995
869
|
if (cached != null) return cached;
|
|
@@ -2010,113 +884,30 @@ function isFlagSetOnObject(obj, flag) {
|
|
|
2010
884
|
return isFlagSet(obj.flags, flag);
|
|
2011
885
|
}
|
|
2012
886
|
const isTypeFlagSet = isFlagSetOnObject;
|
|
2013
|
-
/**
|
|
2014
|
-
* Check if the type is a boolean literal type.
|
|
2015
|
-
* @param type The type to check.
|
|
2016
|
-
* @returns `true` if the type is a boolean literal type.
|
|
2017
|
-
*/
|
|
2018
887
|
function isBooleanLiteralType(type) {
|
|
2019
888
|
return isTypeFlagSet(type, ts.TypeFlags.BooleanLiteral);
|
|
2020
889
|
}
|
|
2021
|
-
/**
|
|
2022
|
-
* Check if the type is the `false` literal type.
|
|
2023
|
-
* @internal
|
|
2024
|
-
*/
|
|
2025
890
|
const isFalseLiteralType = (type) => isBooleanLiteralType(type) && type.intrinsicName === "false";
|
|
2026
|
-
/**
|
|
2027
|
-
* Check if the type is the `true` literal type.
|
|
2028
|
-
* @internal
|
|
2029
|
-
*/
|
|
2030
891
|
const isTrueLiteralType = (type) => isBooleanLiteralType(type) && type.intrinsicName === "true";
|
|
2031
|
-
/**
|
|
2032
|
-
* Check if the type is an any-like type.
|
|
2033
|
-
* @internal
|
|
2034
|
-
*/
|
|
2035
892
|
const isAnyType = (type) => isTypeFlagSet(type, ts.TypeFlags.TypeParameter | ts.TypeFlags.Any);
|
|
2036
|
-
/**
|
|
2037
|
-
* Check if the type is a bigint-like type.
|
|
2038
|
-
* @internal
|
|
2039
|
-
*/
|
|
2040
893
|
const isBigIntType = (type) => isTypeFlagSet(type, ts.TypeFlags.BigIntLike);
|
|
2041
|
-
/**
|
|
2042
|
-
* Check if the type is a boolean-like type.
|
|
2043
|
-
* @internal
|
|
2044
|
-
*/
|
|
2045
894
|
const isBooleanType = (type) => isTypeFlagSet(type, ts.TypeFlags.BooleanLike);
|
|
2046
|
-
/**
|
|
2047
|
-
* Check if the type is an enum-like type.
|
|
2048
|
-
* @internal
|
|
2049
|
-
*/
|
|
2050
895
|
const isEnumType = (type) => isTypeFlagSet(type, ts.TypeFlags.EnumLike);
|
|
2051
|
-
/**
|
|
2052
|
-
* Check if the type is a falsy bigint literal type.
|
|
2053
|
-
* @internal
|
|
2054
|
-
*/
|
|
2055
896
|
const isFalsyBigIntType = (type) => type.isLiteral() && isMatching({ value: { base10Value: "0" } }, type);
|
|
2056
|
-
/**
|
|
2057
|
-
* Check if the type is a falsy number literal type.
|
|
2058
|
-
* @internal
|
|
2059
|
-
*/
|
|
2060
897
|
const isFalsyNumberType = (type) => type.isNumberLiteral() && type.value === 0;
|
|
2061
|
-
/**
|
|
2062
|
-
* Check if the type is a falsy string literal type.
|
|
2063
|
-
* @internal
|
|
2064
|
-
*/
|
|
2065
898
|
const isFalsyStringType = (type) => type.isStringLiteral() && type.value === "";
|
|
2066
|
-
/**
|
|
2067
|
-
* Check if the type is the never type.
|
|
2068
|
-
* @internal
|
|
2069
|
-
*/
|
|
2070
899
|
const isNeverType = (type) => isTypeFlagSet(type, ts.TypeFlags.Never);
|
|
2071
|
-
/**
|
|
2072
|
-
* Check if the type is a nullish type (null, undefined, or void).
|
|
2073
|
-
* @internal
|
|
2074
|
-
*/
|
|
2075
900
|
const isNullishType = (type) => isTypeFlagSet(type, ts.TypeFlags.Null | ts.TypeFlags.Undefined | ts.TypeFlags.VoidLike);
|
|
2076
|
-
/**
|
|
2077
|
-
* Check if the type is a number-like type.
|
|
2078
|
-
* @internal
|
|
2079
|
-
*/
|
|
2080
901
|
const isNumberType = (type) => isTypeFlagSet(type, ts.TypeFlags.NumberLike);
|
|
2081
|
-
/**
|
|
2082
|
-
* Check if the type is an object type.
|
|
2083
|
-
* @internal
|
|
2084
|
-
*/
|
|
2085
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);
|
|
2086
|
-
/**
|
|
2087
|
-
* Check if the type is a string-like type.
|
|
2088
|
-
* @internal
|
|
2089
|
-
*/
|
|
2090
903
|
const isStringType = (type) => isTypeFlagSet(type, ts.TypeFlags.StringLike);
|
|
2091
|
-
/**
|
|
2092
|
-
* Check if the type is a truthy bigint literal type.
|
|
2093
|
-
* @internal
|
|
2094
|
-
*/
|
|
2095
904
|
const isTruthyBigIntType = (type) => type.isLiteral() && isMatching({ value: { base10Value: P.not("0") } }, type);
|
|
2096
|
-
/**
|
|
2097
|
-
* Check if the type is a truthy number literal type.
|
|
2098
|
-
* @internal
|
|
2099
|
-
*/
|
|
2100
905
|
const isTruthyNumberType = (type) => type.isNumberLiteral() && type.value !== 0;
|
|
2101
|
-
/**
|
|
2102
|
-
* Check if the type is a truthy string literal type.
|
|
2103
|
-
* @internal
|
|
2104
|
-
*/
|
|
2105
906
|
const isTruthyStringType = (type) => type.isStringLiteral() && type.value !== "";
|
|
2106
|
-
/**
|
|
2107
|
-
* Check if the type is the unknown type.
|
|
2108
|
-
* @internal
|
|
2109
|
-
*/
|
|
2110
907
|
const isUnknownType = (type) => isTypeFlagSet(type, ts.TypeFlags.Unknown);
|
|
2111
908
|
|
|
2112
909
|
//#endregion
|
|
2113
910
|
//#region src/type-name.ts
|
|
2114
|
-
/**
|
|
2115
|
-
* Get the fully qualified name of a symbol, handling cases that `ts.TypeChecker.getFullyQualifiedName` does not handle (ex: `export as namespace preact`).
|
|
2116
|
-
* @param checker The TypeScript type checker.
|
|
2117
|
-
* @param symbol The symbol to get fully qualified name for.
|
|
2118
|
-
* @returns The fully qualified name of the symbol.
|
|
2119
|
-
*/
|
|
2120
911
|
function getFullyQualifiedNameEx(checker, symbol) {
|
|
2121
912
|
let name = symbol.name;
|
|
2122
913
|
let parent = symbol.declarations?.at(0)?.parent;
|
|
@@ -2163,13 +954,6 @@ function getFullyQualifiedNameEx(checker, symbol) {
|
|
|
2163
954
|
|
|
2164
955
|
//#endregion
|
|
2165
956
|
//#region src/type-variant.ts
|
|
2166
|
-
/**
|
|
2167
|
-
* Get the variants of an array of types.
|
|
2168
|
-
* @param types The types to get the variants of.
|
|
2169
|
-
* @returns The variants of the types.
|
|
2170
|
-
* @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.
|
|
2171
|
-
* @internal
|
|
2172
|
-
*/
|
|
2173
957
|
function getTypeVariants(types) {
|
|
2174
958
|
const variants = /* @__PURE__ */ new Set();
|
|
2175
959
|
if (types.some(isUnknownType)) {
|