@eslint-react/kit 2.1.2-next.1 → 4.0.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +381 -0
- package/dist/index.d.ts +121 -154
- package/dist/index.js +131 -99
- package/package.json +31 -10
package/README.md
ADDED
|
@@ -0,0 +1,381 @@
|
|
|
1
|
+
# @eslint-react/kit
|
|
2
|
+
|
|
3
|
+
ESLint React's toolkit for building custom React lint rules right inside your `eslint.config.ts`.
|
|
4
|
+
|
|
5
|
+
## Index
|
|
6
|
+
|
|
7
|
+
- [Installation](#installation)
|
|
8
|
+
- [Quick Start](#quick-start)
|
|
9
|
+
- [API Reference](#api-reference)
|
|
10
|
+
- [`defineConfig` (default export)](#defineconfig-default-export)
|
|
11
|
+
- [`merge`](#merge)
|
|
12
|
+
- [`Kit` — the toolkit object](#kit--the-toolkit-object)
|
|
13
|
+
- [`kit.collect`](#kitcollect) — Semantic collectors
|
|
14
|
+
- [`kit.is`](#kitis) — Predicates
|
|
15
|
+
- [`kit.hint`](#kithint) — Detection hint bit-flags
|
|
16
|
+
- [`kit.flag`](#kitflag) — Component characteristic flags
|
|
17
|
+
- [Examples](#examples)
|
|
18
|
+
- [Simple: Ban `forwardRef`](#simple-ban-forwardref)
|
|
19
|
+
- [Component: Destructure component props](#component-destructure-component-props)
|
|
20
|
+
- [Hooks: Warn on custom hooks that don't call other hooks](#hooks-warn-on-custom-hooks-that-dont-call-other-hooks)
|
|
21
|
+
- [Multiple Collectors: No component/hook factories](#multiple-collectors-no-componenthook-factories)
|
|
22
|
+
- [More Examples](#more-examples)
|
|
23
|
+
|
|
24
|
+
## Installation
|
|
25
|
+
|
|
26
|
+
```sh
|
|
27
|
+
npm install --save-dev @eslint-react/kit
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Quick Start
|
|
31
|
+
|
|
32
|
+
```ts
|
|
33
|
+
import eslintReact from "@eslint-react/eslint-plugin";
|
|
34
|
+
import eslintReactKit, { merge } from "@eslint-react/kit";
|
|
35
|
+
import eslintJs from "@eslint/js";
|
|
36
|
+
import { defineConfig } from "eslint/config";
|
|
37
|
+
import tseslint from "typescript-eslint";
|
|
38
|
+
|
|
39
|
+
export default defineConfig(
|
|
40
|
+
{
|
|
41
|
+
files: ["**/*.{ts,tsx}"],
|
|
42
|
+
extends: [
|
|
43
|
+
eslintJs.configs.recommended,
|
|
44
|
+
tseslint.configs.recommended,
|
|
45
|
+
eslintReact.configs["recommended-typescript"],
|
|
46
|
+
eslintReactKit(
|
|
47
|
+
{
|
|
48
|
+
name: "function-component-definition",
|
|
49
|
+
make: (context, { collect }) => {
|
|
50
|
+
const { query, visitor } = collect.components(context);
|
|
51
|
+
|
|
52
|
+
return merge(
|
|
53
|
+
visitor,
|
|
54
|
+
{
|
|
55
|
+
"Program:exit"(program) {
|
|
56
|
+
for (const { node } of query.all(program)) {
|
|
57
|
+
if (node.type === "FunctionDeclaration") continue;
|
|
58
|
+
context.report({
|
|
59
|
+
node,
|
|
60
|
+
message: "Function components must be defined with function declarations.",
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
);
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
),
|
|
69
|
+
],
|
|
70
|
+
rules: {
|
|
71
|
+
"@eslint-react/kit/function-component-definition": "error",
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
);
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## API Reference
|
|
78
|
+
|
|
79
|
+
### `defineConfig` (default export)
|
|
80
|
+
|
|
81
|
+
```ts
|
|
82
|
+
import defineReactConfig from "@eslint-react/kit";
|
|
83
|
+
|
|
84
|
+
defineReactConfig(...rules: RuleDefinition[]): Linter.Config
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Creates an ESLint flat-config object from one or more custom rule definitions. Rules are registered under the `@eslint-react/kit` plugin namespace and enabled at `"error"` severity by default.
|
|
88
|
+
|
|
89
|
+
**`RuleDefinition`:**
|
|
90
|
+
|
|
91
|
+
| Field | Type | Description |
|
|
92
|
+
| ------ | -------------------------------- | -------------------------------------------------------------------------------- |
|
|
93
|
+
| `name` | `string` | Unique rule name. Used as `@eslint-react/kit/<name>` in config. |
|
|
94
|
+
| `make` | `(context, kit) => RuleListener` | Rule factory. Receives the ESLint rule context and the structured `Kit` toolkit. |
|
|
95
|
+
|
|
96
|
+
### `merge`
|
|
97
|
+
|
|
98
|
+
```ts
|
|
99
|
+
import { merge } from "@eslint-react/kit";
|
|
100
|
+
|
|
101
|
+
merge(...listeners: RuleListener[]): RuleListener
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Merges multiple `RuleListener` (visitor) objects into a single listener. When two or more listeners define the same visitor key, the handlers are chained and execute in order.
|
|
105
|
+
|
|
106
|
+
This is essential for combining a collector's `visitor` with your own inspection logic.
|
|
107
|
+
|
|
108
|
+
### Kit — the toolkit object
|
|
109
|
+
|
|
110
|
+
The second argument to `make` is a structured `Kit` object:
|
|
111
|
+
|
|
112
|
+
```
|
|
113
|
+
kit
|
|
114
|
+
├── collect -> Semantic collectors (components, hooks)
|
|
115
|
+
├── is -> All predicates (component, hook, React API, import source)
|
|
116
|
+
├── hint -> Detection hint bit-flags
|
|
117
|
+
├── flag -> Component characteristic bit-flags
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
#### `kit.collect`
|
|
123
|
+
|
|
124
|
+
Collector factories create a `{ query, visitor }` pair. The `visitor` must be merged into your rule listener via `merge()`. After traversal completes, `query.all(program)` yields all detected semantic nodes.
|
|
125
|
+
|
|
126
|
+
| Method | Returns | Description |
|
|
127
|
+
| ------------------------------- | ----------------------------------------------------- | --------------------------------------------------------------------------------------- |
|
|
128
|
+
| `components(context, options?)` | `CollectorWithContext<FunctionComponentSemanticNode>` | Detects function components. Options: `{ hint?: bigint, collectDisplayName?: boolean }` |
|
|
129
|
+
| `hooks(context)` | `CollectorWithContext<HookSemanticNode>` | Detects custom hook definitions. |
|
|
130
|
+
|
|
131
|
+
**`CollectorWithContext`** extends `Collector` with contextual queries:
|
|
132
|
+
|
|
133
|
+
| Query | Description |
|
|
134
|
+
| -------------------- | ----------------------------------------- |
|
|
135
|
+
| `query.all(program)` | All collected semantic nodes in the file. |
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
#### `kit.is`
|
|
140
|
+
|
|
141
|
+
All predicates live under `kit.is` — organized into four sub-sections.
|
|
142
|
+
|
|
143
|
+
##### Component
|
|
144
|
+
|
|
145
|
+
| Predicate | Signature | Description |
|
|
146
|
+
| --------------------------- | ------------------------- | --------------------------------------------------------------------------- |
|
|
147
|
+
| `componentDefinition` | `(node, hint) -> boolean` | Whether a function node is a component definition. (context pre-bound) |
|
|
148
|
+
| `componentName` | `(name) -> boolean` | Strict PascalCase component name check. |
|
|
149
|
+
| `componentNameLoose` | `(name) -> boolean` | Loose component name check. |
|
|
150
|
+
| `componentWrapperCall` | `(node) -> boolean` | Whether a node is a `memo(…)` or `forwardRef(…)` call. (context pre-bound) |
|
|
151
|
+
| `componentWrapperCallLoose` | `(node) -> boolean` | Like above, but also matches `useCallback(…)`. (context pre-bound) |
|
|
152
|
+
| `componentWrapperCallback` | `(node) -> boolean` | Whether a function is the callback passed to a wrapper. (context pre-bound) |
|
|
153
|
+
|
|
154
|
+
##### Hook
|
|
155
|
+
|
|
156
|
+
General hook predicates:
|
|
157
|
+
|
|
158
|
+
| Predicate | Signature | Description |
|
|
159
|
+
| -------------------------- | ------------------------------------- | ------------------------------------------------------------ |
|
|
160
|
+
| `hook` | `(node) -> boolean` | Whether a function node is a hook (by name). |
|
|
161
|
+
| `hookCall` | `(node) -> boolean` | Whether a node is a hook call. |
|
|
162
|
+
| `hookName` | `(name) -> boolean` | Whether a string matches the `use[A-Z]` convention. |
|
|
163
|
+
| `useEffectLikeCall` | `(node, additionalHooks?) -> boolean` | Whether a node is a `useEffect`/`useLayoutEffect`-like call. |
|
|
164
|
+
| `useStateLikeCall` | `(node, additionalHooks?) -> boolean` | Whether a node is a `useState`-like call. |
|
|
165
|
+
| `useEffectSetupCallback` | `(node) -> boolean` | Whether a node is a useEffect setup function. |
|
|
166
|
+
| `useEffectCleanupCallback` | `(node) -> boolean` | Whether a node is a useEffect cleanup function. |
|
|
167
|
+
|
|
168
|
+
##### React API
|
|
169
|
+
|
|
170
|
+
Factory functions (context pre-bound):
|
|
171
|
+
|
|
172
|
+
| Predicate | Signature | Description |
|
|
173
|
+
| -------------- | -------------------------------- | ---------------------------------------------------------------------------- |
|
|
174
|
+
| `reactAPI` | `(apiName) -> (node) -> boolean` | Factory: creates a predicate for a React API identifier. (context pre-bound) |
|
|
175
|
+
| `reactAPICall` | `(apiName) -> (node) -> boolean` | Factory: creates a predicate for a React API call. (context pre-bound) |
|
|
176
|
+
|
|
177
|
+
Pre-built identifier predicates (context pre-bound):
|
|
178
|
+
|
|
179
|
+
`captureOwnerStack`, `childrenCount`, `childrenForEach`, `childrenMap`, `childrenOnly`, `childrenToArray`, `cloneElement`, `createContext`, `createElement`, `forwardRef`, `memo`, `lazy`
|
|
180
|
+
|
|
181
|
+
Pre-built call predicates (context pre-bound):
|
|
182
|
+
|
|
183
|
+
`captureOwnerStackCall`, `childrenCountCall`, `childrenForEachCall`, `childrenMapCall`, `childrenOnlyCall`, `childrenToArrayCall`, `cloneElementCall`, `createContextCall`, `createElementCall`, `forwardRefCall`, `memoCall`, `lazyCall`
|
|
184
|
+
|
|
185
|
+
All React API predicates and factories have `context` pre-bound — no need to pass the rule context manually:
|
|
186
|
+
|
|
187
|
+
```ts
|
|
188
|
+
// Direct check
|
|
189
|
+
is.memo(node);
|
|
190
|
+
is.memoCall(node);
|
|
191
|
+
|
|
192
|
+
// Useful in filter/find
|
|
193
|
+
nodes.filter(is.memoCall);
|
|
194
|
+
|
|
195
|
+
// Factory for any API name
|
|
196
|
+
const isCreateRefCall = is.reactAPICall("createRef");
|
|
197
|
+
isCreateRefCall(node);
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
##### Import source
|
|
201
|
+
|
|
202
|
+
| Predicate | Signature | Description |
|
|
203
|
+
| ---------------------------- | ----------------------------------------- | ---------------------------------------------------- |
|
|
204
|
+
| `initializedFromReact` | `(name, scope, importSource?) -> boolean` | Whether a variable comes from a React import. |
|
|
205
|
+
| `initializedFromReactNative` | `(name, scope, importSource?) -> boolean` | Whether a variable comes from a React Native import. |
|
|
206
|
+
|
|
207
|
+
---
|
|
208
|
+
|
|
209
|
+
#### `kit.hint`
|
|
210
|
+
|
|
211
|
+
Bit-flags that control what the component collector considers a "component". Combine with bitwise OR (`|`) and remove with bitwise AND-NOT (`& ~`).
|
|
212
|
+
|
|
213
|
+
```ts
|
|
214
|
+
// The default hint used when none is specified
|
|
215
|
+
hint.component.Default;
|
|
216
|
+
|
|
217
|
+
// All available flags
|
|
218
|
+
hint.component.DoNotIncludeFunctionDefinedAsObjectMethod;
|
|
219
|
+
hint.component.DoNotIncludeFunctionDefinedAsClassMethod;
|
|
220
|
+
hint.component.DoNotIncludeFunctionDefinedAsArrayMapCallback;
|
|
221
|
+
hint.component.DoNotIncludeFunctionDefinedAsArbitraryCallExpressionCallback;
|
|
222
|
+
// … and more (inherits all JsxDetectionHint flags)
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
**Customization example:**
|
|
226
|
+
|
|
227
|
+
```ts
|
|
228
|
+
const { query, visitor } = collect.components(context, {
|
|
229
|
+
// Also treat object methods as components (remove the exclusion flag)
|
|
230
|
+
hint: hint.component.Default & ~hint.component.DoNotIncludeFunctionDefinedAsObjectMethod,
|
|
231
|
+
});
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
---
|
|
235
|
+
|
|
236
|
+
#### `kit.flag`
|
|
237
|
+
|
|
238
|
+
Bit-flags indicating component characteristics. Check with bitwise AND (`&`).
|
|
239
|
+
|
|
240
|
+
```ts
|
|
241
|
+
flag.component.None; // 0n — no flags
|
|
242
|
+
flag.component.Memo; // wrapped in React.memo
|
|
243
|
+
flag.component.ForwardRef; // wrapped in React.forwardRef
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
**Usage:**
|
|
247
|
+
|
|
248
|
+
```ts
|
|
249
|
+
for (const component of query.all(program)) {
|
|
250
|
+
if (component.flag & flag.component.Memo) {
|
|
251
|
+
// This component is memoized
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
## Examples
|
|
257
|
+
|
|
258
|
+
### Simple: Ban `forwardRef`
|
|
259
|
+
|
|
260
|
+
This is a simplified kit reimplementation of the built-in [`react-x/no-forwardRef`](https://eslint-react.xyz/docs/rules/no-forwardRef) rule.
|
|
261
|
+
|
|
262
|
+
```ts
|
|
263
|
+
defineReactConfig({
|
|
264
|
+
name: "no-forward-ref",
|
|
265
|
+
make: (context, { is }) => ({
|
|
266
|
+
CallExpression(node) {
|
|
267
|
+
if (is.forwardRefCall(node)) {
|
|
268
|
+
context.report({ node, message: "forwardRef is deprecated in React 19. Pass ref as a prop instead." });
|
|
269
|
+
}
|
|
270
|
+
},
|
|
271
|
+
}),
|
|
272
|
+
});
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
### Component: Destructure component props
|
|
276
|
+
|
|
277
|
+
This is a simplified kit reimplementation of the built-in [`react-x/prefer-destructuring-assignment`](https://eslint-react.xyz/docs/rules/prefer-destructuring-assignment) rule.
|
|
278
|
+
|
|
279
|
+
```ts
|
|
280
|
+
defineReactConfig({
|
|
281
|
+
name: "destructure-component-props",
|
|
282
|
+
make: (context, { collect }) => {
|
|
283
|
+
const { query, visitor } = collect.components(context);
|
|
284
|
+
|
|
285
|
+
return merge(visitor, {
|
|
286
|
+
"Program:exit"(program) {
|
|
287
|
+
for (const { node } of query.all(program)) {
|
|
288
|
+
const [props] = node.params;
|
|
289
|
+
if (props == null) continue;
|
|
290
|
+
if (props.type !== AST.Identifier) continue;
|
|
291
|
+
const propName = props.name;
|
|
292
|
+
const propVariable = context.sourceCode.getScope(node).variables.find((v) => v.name === propName);
|
|
293
|
+
const propReferences = propVariable?.references ?? [];
|
|
294
|
+
for (const ref of propReferences) {
|
|
295
|
+
const { parent } = ref.identifier;
|
|
296
|
+
if (parent.type !== AST.MemberExpression) continue;
|
|
297
|
+
context.report({
|
|
298
|
+
message: "Use destructuring assignment for component props.",
|
|
299
|
+
node: parent,
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
},
|
|
304
|
+
});
|
|
305
|
+
},
|
|
306
|
+
});
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
### Hooks: Warn on custom hooks that don't call other hooks
|
|
310
|
+
|
|
311
|
+
This is a simplified kit reimplementation of the built-in [`react-x/no-unnecessary-use-prefix`](https://eslint-react.xyz/docs/rules/no-unnecessary-use-prefix) rule.
|
|
312
|
+
|
|
313
|
+
```ts
|
|
314
|
+
defineReactConfig({
|
|
315
|
+
name: "no-unnecessary-use-prefix",
|
|
316
|
+
make: (context, { collect }) => {
|
|
317
|
+
const { query, visitor } = collect.hooks(context);
|
|
318
|
+
|
|
319
|
+
return merge(visitor, {
|
|
320
|
+
"Program:exit"(program) {
|
|
321
|
+
for (const { node, hookCalls } of query.all(program)) {
|
|
322
|
+
if (hookCalls.length === 0) {
|
|
323
|
+
context.report({
|
|
324
|
+
node,
|
|
325
|
+
message: "A custom hook should use at least one hook, otherwise it's just a regular function.",
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
},
|
|
330
|
+
});
|
|
331
|
+
},
|
|
332
|
+
});
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
### Multiple Collectors: No component/hook factories
|
|
336
|
+
|
|
337
|
+
Disallow defining components or hooks inside other functions (factory pattern).
|
|
338
|
+
This is a simplified kit reimplementation of the built-in [`react-x/component-hook-factories`](https://eslint-react.xyz/docs/rules/component-hook-factories) rule.
|
|
339
|
+
|
|
340
|
+
```ts
|
|
341
|
+
defineReactConfig({
|
|
342
|
+
name: "component-hook-factories",
|
|
343
|
+
make: (context, { collect }) => {
|
|
344
|
+
const fc = collect.components(context);
|
|
345
|
+
const hk = collect.hooks(context);
|
|
346
|
+
return merge(
|
|
347
|
+
fc.visitor,
|
|
348
|
+
hk.visitor,
|
|
349
|
+
{
|
|
350
|
+
"Program:exit"(program) {
|
|
351
|
+
const comps = fc.query.all(program);
|
|
352
|
+
const hooks = hk.query.all(program);
|
|
353
|
+
for (const { name, node, kind } of [...comps, ...hooks]) {
|
|
354
|
+
if (name == null) continue;
|
|
355
|
+
if (findParent(node, isFunction) == null) continue;
|
|
356
|
+
context.report({
|
|
357
|
+
node,
|
|
358
|
+
message: `Don't define ${kind} "${name}" inside a function. Move it to the module level.`,
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
},
|
|
362
|
+
},
|
|
363
|
+
);
|
|
364
|
+
},
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
function findParent({ parent }: TSESTree.Node, test: (n: TSESTree.Node) => boolean): TSESTree.Node | null {
|
|
368
|
+
if (parent == null) return null;
|
|
369
|
+
if (test(parent)) return parent;
|
|
370
|
+
if (parent.type === "Program") return null;
|
|
371
|
+
return findParent(parent, test);
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
function isFunction({ type }: TSESTree.Node) {
|
|
375
|
+
return type === "FunctionDeclaration" || type === "FunctionExpression" || type === "ArrowFunctionExpression";
|
|
376
|
+
}
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
## More Examples
|
|
380
|
+
|
|
381
|
+
Please check the [Rule Recipes](https://eslint-react.xyz/docs/configuration/configure-custom-rules#rule-recipes) in the documentation site.
|
package/dist/index.d.ts
CHANGED
|
@@ -1,161 +1,128 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
1
|
+
import * as core from "@eslint-react/core";
|
|
2
|
+
import { TSESTreeFunction } from "@eslint-react/ast";
|
|
3
|
+
import { TSESTree } from "@typescript-eslint/utils";
|
|
4
|
+
import { RuleContext, RuleFix, RuleFixer, RuleListener } from "@typescript-eslint/utils/ts-eslint";
|
|
5
|
+
import { Linter } from "eslint";
|
|
4
6
|
|
|
5
|
-
//#region src/
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
* Regular expression for matching a TypeScript file extension.
|
|
12
|
-
*/
|
|
13
|
-
declare const RE_TS_EXT: RegExp;
|
|
14
|
-
/**
|
|
15
|
-
* Regular expression for matching a JavaScript file extension.
|
|
16
|
-
*/
|
|
17
|
-
declare const RE_JS_EXT: RegExp;
|
|
18
|
-
/**
|
|
19
|
-
* Regular expression for matching a PascalCase string.
|
|
20
|
-
*/
|
|
21
|
-
declare const RE_PASCAL_CASE: RegExp;
|
|
22
|
-
/**
|
|
23
|
-
* Regular expression for matching a camelCase string.
|
|
24
|
-
*/
|
|
25
|
-
declare const RE_CAMEL_CASE: RegExp;
|
|
26
|
-
/**
|
|
27
|
-
* Regular expression for matching a kebab-case string.
|
|
28
|
-
*/
|
|
29
|
-
declare const RE_KEBAB_CASE: RegExp;
|
|
30
|
-
/**
|
|
31
|
-
* Regular expression for matching a snake_case string.
|
|
32
|
-
*/
|
|
33
|
-
declare const RE_SNAKE_CASE: RegExp;
|
|
34
|
-
/**
|
|
35
|
-
* Regular expression for matching a CONSTANT_CASE string.
|
|
36
|
-
*/
|
|
37
|
-
declare const RE_CONSTANT_CASE: RegExp;
|
|
38
|
-
declare const RE_JAVASCRIPT_PROTOCOL: RegExp;
|
|
39
|
-
/**
|
|
40
|
-
* Regular expression for matching a valid JavaScript identifier.
|
|
41
|
-
*/
|
|
42
|
-
declare const RE_JS_IDENTIFIER: RegExp;
|
|
43
|
-
/**
|
|
44
|
-
* Regular expression for matching a RegExp string.
|
|
45
|
-
*/
|
|
46
|
-
declare const RE_REGEXP_STR: RegExp;
|
|
47
|
-
/**
|
|
48
|
-
* Regular expression for matching a `@jsx` annotation comment.
|
|
49
|
-
*/
|
|
50
|
-
declare const RE_ANNOTATION_JSX: RegExp;
|
|
51
|
-
/**
|
|
52
|
-
* Regular expression for matching a `@jsxFrag` annotation comment.
|
|
53
|
-
*/
|
|
54
|
-
declare const RE_ANNOTATION_JSX_FRAG: RegExp;
|
|
55
|
-
/**
|
|
56
|
-
* Regular expression for matching a `@jsxRuntime` annotation comment.
|
|
57
|
-
*/
|
|
58
|
-
declare const RE_ANNOTATION_JSX_RUNTIME: RegExp;
|
|
59
|
-
/**
|
|
60
|
-
* Regular expression for matching a `@jsxImportSource` annotation comment.
|
|
61
|
-
*/
|
|
62
|
-
declare const RE_ANNOTATION_JSX_IMPORT_SOURCE: RegExp;
|
|
63
|
-
/**
|
|
64
|
-
* Regular expression for matching a React component name.
|
|
65
|
-
*/
|
|
66
|
-
declare const RE_COMPONENT_NAME: RegExp;
|
|
67
|
-
/**
|
|
68
|
-
* Regular expression for matching a React component name (loose).
|
|
69
|
-
*/
|
|
70
|
-
declare const RE_COMPONENT_NAME_LOOSE: RegExp;
|
|
71
|
-
/**
|
|
72
|
-
* Regular expression for matching a React Hook name.
|
|
73
|
-
*/
|
|
74
|
-
declare const RE_HOOK_NAME: RegExp;
|
|
75
|
-
//#endregion
|
|
76
|
-
//#region src/types/index.d.ts
|
|
77
|
-
/**
|
|
78
|
-
* Rule severity.
|
|
79
|
-
* @since 0.0.1
|
|
80
|
-
*/
|
|
81
|
-
type SeverityName = "off" | "warn" | "error";
|
|
82
|
-
/**
|
|
83
|
-
* The numeric severity level for a rule.
|
|
84
|
-
*
|
|
85
|
-
* - `0` means off.
|
|
86
|
-
* - `1` means warn.
|
|
87
|
-
* - `2` means error.
|
|
88
|
-
*/
|
|
89
|
-
type SeverityLevel = 0 | 1 | 2;
|
|
90
|
-
/**
|
|
91
|
-
* The severity of a rule in a configuration.
|
|
92
|
-
*/
|
|
93
|
-
type Severity = SeverityName | SeverityLevel;
|
|
94
|
-
/**
|
|
95
|
-
* Rule declaration.
|
|
96
|
-
* @internal
|
|
97
|
-
* @since 0.0.1
|
|
98
|
-
*/
|
|
99
|
-
type RuleConfig<RuleOptions extends unknown[] = unknown[]> = Severity | [Severity, ...Partial<RuleOptions>];
|
|
100
|
-
/**
|
|
101
|
-
* Rule context.
|
|
102
|
-
* @since 0.0.1
|
|
103
|
-
*/
|
|
104
|
-
type RuleContext<MessageIds extends string = string, Options extends readonly unknown[] = readonly unknown[]> = tseslint.RuleContext<MessageIds, Options>;
|
|
105
|
-
/**
|
|
106
|
-
* Rule feature.
|
|
107
|
-
* @since 1.20.0
|
|
108
|
-
*/
|
|
109
|
-
type RuleFeature = "CFG" | "DBG" | "FIX" | "MOD" | "TSC" | "EXP";
|
|
110
|
-
type RulePolicy = number;
|
|
111
|
-
type RuleSuggest<MessageIds extends string = string> = {
|
|
112
|
-
messageId: MessageIds;
|
|
113
|
-
data?: Record<string, unknown>;
|
|
114
|
-
fix: tseslint.ReportFixFunction;
|
|
115
|
-
};
|
|
116
|
-
/**
|
|
117
|
-
* A collection of settings.
|
|
118
|
-
*/
|
|
119
|
-
interface SettingsConfig {
|
|
120
|
-
[key: string]: unknown;
|
|
7
|
+
//#region src/index.d.ts
|
|
8
|
+
interface Collector<T> {
|
|
9
|
+
query: {
|
|
10
|
+
all(program: TSESTree.Program): T[];
|
|
11
|
+
};
|
|
12
|
+
visitor: RuleListener;
|
|
121
13
|
}
|
|
122
|
-
interface
|
|
123
|
-
|
|
124
|
-
|
|
14
|
+
interface CollectorWithContext<T, E> extends Collector<T> {
|
|
15
|
+
query: {
|
|
16
|
+
all(program: TSESTree.Program): T[];
|
|
17
|
+
};
|
|
125
18
|
}
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
19
|
+
declare function components(ctx: RuleContext<string, unknown[]>, options?: {
|
|
20
|
+
collectDisplayName?: boolean;
|
|
21
|
+
hint?: bigint;
|
|
22
|
+
}): CollectorWithContext<core.FunctionComponentSemanticNode, core.FunctionComponentSemanticNode>;
|
|
23
|
+
declare function hooks(ctx: RuleContext<string, unknown[]>): CollectorWithContext<core.HookSemanticNode, {
|
|
24
|
+
key: string;
|
|
25
|
+
node: TSESTree.Node;
|
|
26
|
+
}>;
|
|
27
|
+
interface RuleToolkit {
|
|
28
|
+
is: {
|
|
29
|
+
componentDefinition: (node: TSESTreeFunction, hint: bigint) => boolean;
|
|
30
|
+
componentName: typeof core.isComponentName;
|
|
31
|
+
componentNameLoose: typeof core.isComponentNameLoose;
|
|
32
|
+
componentWrapperCall: (node: TSESTree.Node) => boolean;
|
|
33
|
+
componentWrapperCallLoose: (node: TSESTree.Node) => boolean;
|
|
34
|
+
componentWrapperCallback: (node: TSESTree.Node) => boolean;
|
|
35
|
+
hook: typeof core.isHook;
|
|
36
|
+
hookCall: typeof core.isHookCall;
|
|
37
|
+
hookName: typeof core.isHookName;
|
|
38
|
+
useEffectLikeCall: typeof core.isUseEffectLikeCall;
|
|
39
|
+
useStateLikeCall: typeof core.isUseStateLikeCall;
|
|
40
|
+
useEffectSetupCallback: typeof core.isUseEffectSetupCallback;
|
|
41
|
+
useEffectCleanupCallback: typeof core.isUseEffectCleanupCallback;
|
|
42
|
+
useCall: typeof core.isUseCall;
|
|
43
|
+
useActionStateCall: typeof core.isUseActionStateCall;
|
|
44
|
+
useCallbackCall: typeof core.isUseCallbackCall;
|
|
45
|
+
useContextCall: typeof core.isUseContextCall;
|
|
46
|
+
useDebugValueCall: typeof core.isUseDebugValueCall;
|
|
47
|
+
useDeferredValueCall: typeof core.isUseDeferredValueCall;
|
|
48
|
+
useEffectCall: typeof core.isUseEffectCall;
|
|
49
|
+
useFormStatusCall: typeof core.isUseFormStatusCall;
|
|
50
|
+
useIdCall: typeof core.isUseIdCall;
|
|
51
|
+
useImperativeHandleCall: typeof core.isUseImperativeHandleCall;
|
|
52
|
+
useInsertionEffectCall: typeof core.isUseInsertionEffectCall;
|
|
53
|
+
useLayoutEffectCall: typeof core.isUseLayoutEffectCall;
|
|
54
|
+
useMemoCall: typeof core.isUseMemoCall;
|
|
55
|
+
useOptimisticCall: typeof core.isUseOptimisticCall;
|
|
56
|
+
useReducerCall: typeof core.isUseReducerCall;
|
|
57
|
+
useRefCall: typeof core.isUseRefCall;
|
|
58
|
+
useStateCall: typeof core.isUseStateCall;
|
|
59
|
+
useSyncExternalStoreCall: typeof core.isUseSyncExternalStoreCall;
|
|
60
|
+
useTransitionCall: typeof core.isUseTransitionCall;
|
|
61
|
+
reactAPI: (api: string) => (node: null | TSESTree.Node) => boolean;
|
|
62
|
+
reactAPICall: (api: string) => (node: null | TSESTree.Node) => node is TSESTree.CallExpression;
|
|
63
|
+
captureOwnerStack: (node: null | TSESTree.Node) => boolean;
|
|
64
|
+
childrenCount: (node: null | TSESTree.Node) => boolean;
|
|
65
|
+
childrenForEach: (node: null | TSESTree.Node) => boolean;
|
|
66
|
+
childrenMap: (node: null | TSESTree.Node) => boolean;
|
|
67
|
+
childrenOnly: (node: null | TSESTree.Node) => boolean;
|
|
68
|
+
childrenToArray: (node: null | TSESTree.Node) => boolean;
|
|
69
|
+
cloneElement: (node: null | TSESTree.Node) => boolean;
|
|
70
|
+
createContext: (node: null | TSESTree.Node) => boolean;
|
|
71
|
+
createElement: (node: null | TSESTree.Node) => boolean;
|
|
72
|
+
createRef: (node: null | TSESTree.Node) => boolean;
|
|
73
|
+
forwardRef: (node: null | TSESTree.Node) => boolean;
|
|
74
|
+
memo: (node: null | TSESTree.Node) => boolean;
|
|
75
|
+
lazy: (node: null | TSESTree.Node) => boolean;
|
|
76
|
+
captureOwnerStackCall: (node: null | TSESTree.Node) => node is TSESTree.CallExpression;
|
|
77
|
+
childrenCountCall: (node: null | TSESTree.Node) => node is TSESTree.CallExpression;
|
|
78
|
+
childrenForEachCall: (node: null | TSESTree.Node) => node is TSESTree.CallExpression;
|
|
79
|
+
childrenMapCall: (node: null | TSESTree.Node) => node is TSESTree.CallExpression;
|
|
80
|
+
childrenOnlyCall: (node: null | TSESTree.Node) => node is TSESTree.CallExpression;
|
|
81
|
+
childrenToArrayCall: (node: null | TSESTree.Node) => node is TSESTree.CallExpression;
|
|
82
|
+
cloneElementCall: (node: null | TSESTree.Node) => node is TSESTree.CallExpression;
|
|
83
|
+
createContextCall: (node: null | TSESTree.Node) => node is TSESTree.CallExpression;
|
|
84
|
+
createElementCall: (node: null | TSESTree.Node) => node is TSESTree.CallExpression;
|
|
85
|
+
createRefCall: (node: null | TSESTree.Node) => node is TSESTree.CallExpression;
|
|
86
|
+
forwardRefCall: (node: null | TSESTree.Node) => node is TSESTree.CallExpression;
|
|
87
|
+
memoCall: (node: null | TSESTree.Node) => node is TSESTree.CallExpression;
|
|
88
|
+
lazyCall: (node: null | TSESTree.Node) => node is TSESTree.CallExpression;
|
|
89
|
+
initializedFromReact: typeof core.isInitializedFromReact;
|
|
90
|
+
initializedFromReactNative: typeof core.isInitializedFromReactNative;
|
|
91
|
+
};
|
|
92
|
+
hint: {
|
|
93
|
+
component: typeof core.ComponentDetectionHint & {
|
|
94
|
+
Default: bigint;
|
|
95
|
+
};
|
|
96
|
+
};
|
|
97
|
+
flag: {
|
|
98
|
+
component: typeof core.ComponentFlag;
|
|
99
|
+
};
|
|
100
|
+
collect: {
|
|
101
|
+
components: typeof components;
|
|
102
|
+
hooks: typeof hooks;
|
|
130
103
|
};
|
|
131
|
-
rules: Record<string, CompatibleRule>;
|
|
132
104
|
}
|
|
133
|
-
interface
|
|
134
|
-
name
|
|
135
|
-
|
|
136
|
-
|
|
105
|
+
interface RuleDefinition {
|
|
106
|
+
name: string;
|
|
107
|
+
make(ctx: RuleContext<string, unknown[]>, kit: RuleToolkit): RuleListener;
|
|
108
|
+
}
|
|
109
|
+
declare function defineConfig(...rules: RuleDefinition[]): Linter.Config;
|
|
110
|
+
declare function merge(...listeners: RuleListener[]): RuleListener;
|
|
111
|
+
declare module "@typescript-eslint/utils/ts-eslint" {
|
|
112
|
+
interface RuleContext<MessageIds extends string, Options extends readonly unknown[]> {
|
|
113
|
+
report(descriptor: {
|
|
114
|
+
readonly data?: Readonly<Record<string, unknown>>;
|
|
115
|
+
readonly fix?: ((fixer: RuleFixer) => IterableIterator<RuleFix> | readonly RuleFix[] | RuleFix | null) | null;
|
|
116
|
+
readonly loc?: Readonly<TSESTree.SourceLocation> | Readonly<TSESTree.Position>;
|
|
117
|
+
readonly message: string;
|
|
118
|
+
readonly node: TSESTree.Node;
|
|
119
|
+
readonly suggest?: readonly {
|
|
120
|
+
readonly data?: Readonly<Record<string, unknown>>;
|
|
121
|
+
readonly desc: string;
|
|
122
|
+
readonly fix: (fixer: RuleFixer) => IterableIterator<RuleFix> | readonly RuleFix[] | RuleFix | null;
|
|
123
|
+
}[] | null;
|
|
124
|
+
}): void;
|
|
125
|
+
}
|
|
137
126
|
}
|
|
138
127
|
//#endregion
|
|
139
|
-
|
|
140
|
-
/**
|
|
141
|
-
* Convert a string to the `RegExp`.
|
|
142
|
-
* Normal strings (e.g. `"foo"`) is converted to `/^foo$/` of `RegExp`.
|
|
143
|
-
* Strings like `"/^foo/i"` are converted to `/^foo/i` of `RegExp`.
|
|
144
|
-
* @see https://github.com/sveltejs/eslint-plugin-svelte/blob/main/packages/eslint-plugin-svelte/src/utils/regexp.ts
|
|
145
|
-
* @param string The string to convert.
|
|
146
|
-
* @returns Returns the `RegExp`.
|
|
147
|
-
*/
|
|
148
|
-
declare function toRegExp(string: string): {
|
|
149
|
-
test(s: string): boolean;
|
|
150
|
-
};
|
|
151
|
-
/**
|
|
152
|
-
* Checks whether given string is regexp string
|
|
153
|
-
* @param string The string to check
|
|
154
|
-
* @returns boolean
|
|
155
|
-
*/
|
|
156
|
-
declare function isRegExp(string: string): boolean;
|
|
157
|
-
//#endregion
|
|
158
|
-
//#region src/utils/reporting.d.ts
|
|
159
|
-
declare function report(context: RuleContext): (descriptor: unit | null | ReportDescriptor<string>) => void;
|
|
160
|
-
//#endregion
|
|
161
|
-
export { CompatibleConfig, CompatiblePlugin, CompatibleRule, RE_ANNOTATION_JSX, RE_ANNOTATION_JSX_FRAG, RE_ANNOTATION_JSX_IMPORT_SOURCE, RE_ANNOTATION_JSX_RUNTIME, RE_CAMEL_CASE, RE_COMPONENT_NAME, RE_COMPONENT_NAME_LOOSE, RE_CONSTANT_CASE, RE_HOOK_NAME, RE_HTML_TAG, RE_JAVASCRIPT_PROTOCOL, RE_JS_EXT, RE_JS_IDENTIFIER, RE_KEBAB_CASE, RE_PASCAL_CASE, RE_REGEXP_STR, RE_SNAKE_CASE, RE_TS_EXT, RuleConfig, RuleContext, RuleFeature, RulePolicy, RuleSuggest, SettingsConfig, Severity, SeverityLevel, SeverityName, isRegExp, report, toRegExp };
|
|
128
|
+
export { Collector, CollectorWithContext, type RuleContext, RuleToolkit, defineConfig as default, defineConfig, merge };
|
package/dist/index.js
CHANGED
|
@@ -1,108 +1,140 @@
|
|
|
1
|
-
import "@eslint-react/
|
|
1
|
+
import * as core from "@eslint-react/core";
|
|
2
2
|
|
|
3
|
-
//#region
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
*/
|
|
7
|
-
const RE_HTML_TAG = /^[a-z][^-]*$/u;
|
|
8
|
-
/**
|
|
9
|
-
* Regular expression for matching a TypeScript file extension.
|
|
10
|
-
*/
|
|
11
|
-
const RE_TS_EXT = /^[cm]?tsx?$/u;
|
|
12
|
-
/**
|
|
13
|
-
* Regular expression for matching a JavaScript file extension.
|
|
14
|
-
*/
|
|
15
|
-
const RE_JS_EXT = /^[cm]?jsx?$/u;
|
|
16
|
-
/**
|
|
17
|
-
* Regular expression for matching a PascalCase string.
|
|
18
|
-
*/
|
|
19
|
-
const RE_PASCAL_CASE = /^[A-Z][\dA-Za-z]*$/u;
|
|
20
|
-
/**
|
|
21
|
-
* Regular expression for matching a camelCase string.
|
|
22
|
-
*/
|
|
23
|
-
const RE_CAMEL_CASE = /^[a-z][\dA-Za-z]*$/u;
|
|
24
|
-
/**
|
|
25
|
-
* Regular expression for matching a kebab-case string.
|
|
26
|
-
*/
|
|
27
|
-
const RE_KEBAB_CASE = /^[a-z][\d\-a-z]*$/u;
|
|
28
|
-
/**
|
|
29
|
-
* Regular expression for matching a snake_case string.
|
|
30
|
-
*/
|
|
31
|
-
const RE_SNAKE_CASE = /^[a-z][\d_a-z]*$/u;
|
|
32
|
-
/**
|
|
33
|
-
* Regular expression for matching a CONSTANT_CASE string.
|
|
34
|
-
*/
|
|
35
|
-
const RE_CONSTANT_CASE = /^[A-Z][\d_A-Z]*$/u;
|
|
36
|
-
const RE_JAVASCRIPT_PROTOCOL = /^[\u0000-\u001F ]*j[\t\n\r]*a[\t\n\r]*v[\t\n\r]*a[\t\n\r]*s[\t\n\r]*c[\t\n\r]*r[\t\n\r]*i[\t\n\r]*p[\t\n\r]*t[\t\n\r]*:/iu;
|
|
37
|
-
/**
|
|
38
|
-
* Regular expression for matching a valid JavaScript identifier.
|
|
39
|
-
*/
|
|
40
|
-
const RE_JS_IDENTIFIER = /^[_$a-z][\w$]*$/i;
|
|
41
|
-
/**
|
|
42
|
-
* Regular expression for matching a RegExp string.
|
|
43
|
-
*/
|
|
44
|
-
const RE_REGEXP_STR = /^\/(.+)\/([A-Za-z]*)$/u;
|
|
45
|
-
/**
|
|
46
|
-
* Regular expression for matching a `@jsx` annotation comment.
|
|
47
|
-
*/
|
|
48
|
-
const RE_ANNOTATION_JSX = /@jsx\s+(\S+)/u;
|
|
49
|
-
/**
|
|
50
|
-
* Regular expression for matching a `@jsxFrag` annotation comment.
|
|
51
|
-
*/
|
|
52
|
-
const RE_ANNOTATION_JSX_FRAG = /@jsxFrag\s+(\S+)/u;
|
|
53
|
-
/**
|
|
54
|
-
* Regular expression for matching a `@jsxRuntime` annotation comment.
|
|
55
|
-
*/
|
|
56
|
-
const RE_ANNOTATION_JSX_RUNTIME = /@jsxRuntime\s+(\S+)/u;
|
|
57
|
-
/**
|
|
58
|
-
* Regular expression for matching a `@jsxImportSource` annotation comment.
|
|
59
|
-
*/
|
|
60
|
-
const RE_ANNOTATION_JSX_IMPORT_SOURCE = /@jsxImportSource\s+(\S+)/u;
|
|
61
|
-
/**
|
|
62
|
-
* Regular expression for matching a React component name.
|
|
63
|
-
*/
|
|
64
|
-
const RE_COMPONENT_NAME = /^[A-Z]/u;
|
|
65
|
-
/**
|
|
66
|
-
* Regular expression for matching a React component name (loose).
|
|
67
|
-
*/
|
|
68
|
-
const RE_COMPONENT_NAME_LOOSE = /^_?[A-Z]/u;
|
|
69
|
-
/**
|
|
70
|
-
* Regular expression for matching a React Hook name.
|
|
71
|
-
*/
|
|
72
|
-
const RE_HOOK_NAME = /^use/u;
|
|
3
|
+
//#region package.json
|
|
4
|
+
var name = "@eslint-react/kit";
|
|
5
|
+
var version = "4.0.0-beta.1";
|
|
73
6
|
|
|
74
7
|
//#endregion
|
|
75
|
-
//#region src/
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
* @returns Returns the `RegExp`.
|
|
83
|
-
*/
|
|
84
|
-
function toRegExp(string) {
|
|
85
|
-
const [, pattern, flags = "u"] = RE_REGEXP_STR.exec(string) ?? [];
|
|
86
|
-
if (pattern != null) return new RegExp(pattern, flags);
|
|
87
|
-
return { test: (s) => s === string };
|
|
8
|
+
//#region src/index.ts
|
|
9
|
+
function components(ctx, options) {
|
|
10
|
+
const { api, visitor } = core.getComponentCollector(ctx, options);
|
|
11
|
+
return {
|
|
12
|
+
query: { all: (program) => api.getAllComponents(program) },
|
|
13
|
+
visitor
|
|
14
|
+
};
|
|
88
15
|
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
return RE_REGEXP_STR.test(string);
|
|
16
|
+
function hooks(ctx) {
|
|
17
|
+
const { api, visitor } = core.getHookCollector(ctx);
|
|
18
|
+
return {
|
|
19
|
+
query: { all: (program) => api.getAllHooks(program) },
|
|
20
|
+
visitor
|
|
21
|
+
};
|
|
96
22
|
}
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
23
|
+
function createKit(ctx) {
|
|
24
|
+
return {
|
|
25
|
+
is: {
|
|
26
|
+
componentDefinition: (node, hint) => core.isComponentDefinition(ctx, node, hint),
|
|
27
|
+
componentName: core.isComponentName,
|
|
28
|
+
componentNameLoose: core.isComponentNameLoose,
|
|
29
|
+
componentWrapperCall: (node) => core.isComponentWrapperCall(ctx, node),
|
|
30
|
+
componentWrapperCallLoose: (node) => core.isComponentWrapperCallLoose(ctx, node),
|
|
31
|
+
componentWrapperCallback: (node) => core.isComponentWrapperCallback(ctx, node),
|
|
32
|
+
hook: core.isHook,
|
|
33
|
+
hookCall: core.isHookCall,
|
|
34
|
+
hookName: core.isHookName,
|
|
35
|
+
useEffectLikeCall: core.isUseEffectLikeCall,
|
|
36
|
+
useStateLikeCall: core.isUseStateLikeCall,
|
|
37
|
+
useEffectSetupCallback: core.isUseEffectSetupCallback,
|
|
38
|
+
useEffectCleanupCallback: core.isUseEffectCleanupCallback,
|
|
39
|
+
useCall: core.isUseCall,
|
|
40
|
+
useActionStateCall: core.isUseActionStateCall,
|
|
41
|
+
useCallbackCall: core.isUseCallbackCall,
|
|
42
|
+
useContextCall: core.isUseContextCall,
|
|
43
|
+
useDebugValueCall: core.isUseDebugValueCall,
|
|
44
|
+
useDeferredValueCall: core.isUseDeferredValueCall,
|
|
45
|
+
useEffectCall: core.isUseEffectCall,
|
|
46
|
+
useFormStatusCall: core.isUseFormStatusCall,
|
|
47
|
+
useIdCall: core.isUseIdCall,
|
|
48
|
+
useImperativeHandleCall: core.isUseImperativeHandleCall,
|
|
49
|
+
useInsertionEffectCall: core.isUseInsertionEffectCall,
|
|
50
|
+
useLayoutEffectCall: core.isUseLayoutEffectCall,
|
|
51
|
+
useMemoCall: core.isUseMemoCall,
|
|
52
|
+
useOptimisticCall: core.isUseOptimisticCall,
|
|
53
|
+
useReducerCall: core.isUseReducerCall,
|
|
54
|
+
useRefCall: core.isUseRefCall,
|
|
55
|
+
useStateCall: core.isUseStateCall,
|
|
56
|
+
useSyncExternalStoreCall: core.isUseSyncExternalStoreCall,
|
|
57
|
+
useTransitionCall: core.isUseTransitionCall,
|
|
58
|
+
reactAPI: (api) => core.isReactAPI(api)(ctx),
|
|
59
|
+
reactAPICall: (api) => core.isReactAPICall(api)(ctx),
|
|
60
|
+
captureOwnerStack: core.isCaptureOwnerStack(ctx),
|
|
61
|
+
childrenCount: core.isChildrenCount(ctx),
|
|
62
|
+
childrenForEach: core.isChildrenForEach(ctx),
|
|
63
|
+
childrenMap: core.isChildrenMap(ctx),
|
|
64
|
+
childrenOnly: core.isChildrenOnly(ctx),
|
|
65
|
+
childrenToArray: core.isChildrenToArray(ctx),
|
|
66
|
+
cloneElement: core.isCloneElement(ctx),
|
|
67
|
+
createContext: core.isCreateContext(ctx),
|
|
68
|
+
createElement: core.isCreateElement(ctx),
|
|
69
|
+
createRef: core.isCreateRef(ctx),
|
|
70
|
+
forwardRef: core.isForwardRef(ctx),
|
|
71
|
+
memo: core.isMemo(ctx),
|
|
72
|
+
lazy: core.isLazy(ctx),
|
|
73
|
+
captureOwnerStackCall: core.isCaptureOwnerStackCall(ctx),
|
|
74
|
+
childrenCountCall: core.isChildrenCountCall(ctx),
|
|
75
|
+
childrenForEachCall: core.isChildrenForEachCall(ctx),
|
|
76
|
+
childrenMapCall: core.isChildrenMapCall(ctx),
|
|
77
|
+
childrenOnlyCall: core.isChildrenOnlyCall(ctx),
|
|
78
|
+
childrenToArrayCall: core.isChildrenToArrayCall(ctx),
|
|
79
|
+
cloneElementCall: core.isCloneElementCall(ctx),
|
|
80
|
+
createContextCall: core.isCreateContextCall(ctx),
|
|
81
|
+
createElementCall: core.isCreateElementCall(ctx),
|
|
82
|
+
createRefCall: core.isCreateRefCall(ctx),
|
|
83
|
+
forwardRefCall: core.isForwardRefCall(ctx),
|
|
84
|
+
memoCall: core.isMemoCall(ctx),
|
|
85
|
+
lazyCall: core.isLazyCall(ctx),
|
|
86
|
+
initializedFromReact: core.isInitializedFromReact,
|
|
87
|
+
initializedFromReactNative: core.isInitializedFromReactNative
|
|
88
|
+
},
|
|
89
|
+
hint: { component: {
|
|
90
|
+
...core.ComponentDetectionHint,
|
|
91
|
+
Default: core.DEFAULT_COMPONENT_DETECTION_HINT
|
|
92
|
+
} },
|
|
93
|
+
flag: { component: core.ComponentFlag },
|
|
94
|
+
collect: {
|
|
95
|
+
components,
|
|
96
|
+
hooks
|
|
97
|
+
}
|
|
104
98
|
};
|
|
105
99
|
}
|
|
100
|
+
function defineConfig(...rules) {
|
|
101
|
+
return {
|
|
102
|
+
files: ["**/*.ts", "**/*.tsx"],
|
|
103
|
+
plugins: { [name]: {
|
|
104
|
+
meta: {
|
|
105
|
+
name,
|
|
106
|
+
version
|
|
107
|
+
},
|
|
108
|
+
rules: rules.reduce((acc, { name, make }) => {
|
|
109
|
+
Reflect.set(acc, name, {
|
|
110
|
+
meta: {
|
|
111
|
+
fixable: "code",
|
|
112
|
+
hasSuggestions: true
|
|
113
|
+
},
|
|
114
|
+
create(ctx) {
|
|
115
|
+
return make(ctx, createKit(ctx));
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
return acc;
|
|
119
|
+
}, {})
|
|
120
|
+
} },
|
|
121
|
+
rules: rules.reduce((acc, { name: name$1 }) => {
|
|
122
|
+
acc[`${name}/${name$1}`] = "error";
|
|
123
|
+
return acc;
|
|
124
|
+
}, {})
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
function merge(...listeners) {
|
|
128
|
+
const [base = {}, ...rest] = listeners;
|
|
129
|
+
for (const r of rest) for (const key in r) {
|
|
130
|
+
const existing = base[key];
|
|
131
|
+
base[key] = existing ? (...args) => {
|
|
132
|
+
existing(...args);
|
|
133
|
+
r[key]?.(...args);
|
|
134
|
+
} : r[key];
|
|
135
|
+
}
|
|
136
|
+
return base;
|
|
137
|
+
}
|
|
106
138
|
|
|
107
139
|
//#endregion
|
|
108
|
-
export {
|
|
140
|
+
export { defineConfig as default, defineConfig, merge };
|
package/package.json
CHANGED
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@eslint-react/kit",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "ESLint React's
|
|
3
|
+
"version": "4.0.0-beta.1",
|
|
4
|
+
"description": "ESLint React's utility module for building custom rules.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"react",
|
|
7
|
+
"eslint",
|
|
8
|
+
"eslint-react",
|
|
9
|
+
"@eslint-react/kit"
|
|
10
|
+
],
|
|
5
11
|
"homepage": "https://github.com/Rel1cx/eslint-react",
|
|
6
12
|
"bugs": {
|
|
7
13
|
"url": "https://github.com/Rel1cx/eslint-react/issues"
|
|
@@ -12,7 +18,7 @@
|
|
|
12
18
|
"directory": "packages/utilities/kit"
|
|
13
19
|
},
|
|
14
20
|
"license": "MIT",
|
|
15
|
-
"author": "Rel1cx
|
|
21
|
+
"author": "Rel1cx",
|
|
16
22
|
"sideEffects": false,
|
|
17
23
|
"type": "module",
|
|
18
24
|
"exports": {
|
|
@@ -22,26 +28,41 @@
|
|
|
22
28
|
},
|
|
23
29
|
"./package.json": "./package.json"
|
|
24
30
|
},
|
|
31
|
+
"main": "./dist/index.js",
|
|
32
|
+
"module": "./dist/index.js",
|
|
33
|
+
"types": "./dist/index.d.ts",
|
|
25
34
|
"files": [
|
|
26
35
|
"dist",
|
|
27
36
|
"./package.json"
|
|
28
37
|
],
|
|
29
38
|
"dependencies": {
|
|
30
|
-
"@typescript-eslint/utils": "^8.
|
|
31
|
-
"@eslint-react/
|
|
39
|
+
"@typescript-eslint/utils": "^8.57.2",
|
|
40
|
+
"@eslint-react/ast": "4.0.0-beta.1",
|
|
41
|
+
"@eslint-react/core": "4.0.0-beta.1"
|
|
32
42
|
},
|
|
33
43
|
"devDependencies": {
|
|
34
|
-
"
|
|
35
|
-
"tsdown": "^0.
|
|
44
|
+
"eslint": "^10.1.0",
|
|
45
|
+
"tsdown": "^0.21.4",
|
|
46
|
+
"@local/eff": "3.0.0-beta.72",
|
|
36
47
|
"@local/configs": "0.0.0"
|
|
37
48
|
},
|
|
49
|
+
"peerDependencies": {
|
|
50
|
+
"eslint": "^10.0.0",
|
|
51
|
+
"typescript": "*"
|
|
52
|
+
},
|
|
38
53
|
"engines": {
|
|
39
|
-
"node": ">=
|
|
54
|
+
"node": ">=22.0.0"
|
|
55
|
+
},
|
|
56
|
+
"publishConfig": {
|
|
57
|
+
"access": "public"
|
|
58
|
+
},
|
|
59
|
+
"inlinedDependencies": {
|
|
60
|
+
"@local/eff": "workspace:*"
|
|
40
61
|
},
|
|
41
62
|
"scripts": {
|
|
42
|
-
"build": "tsdown
|
|
63
|
+
"build": "tsdown",
|
|
43
64
|
"build:docs": "typedoc",
|
|
44
65
|
"lint:publish": "publint",
|
|
45
|
-
"lint:ts": "
|
|
66
|
+
"lint:ts": "tsl"
|
|
46
67
|
}
|
|
47
68
|
}
|