@eslint-react/kit 4.0.0-beta.2 → 4.0.1-beta.0

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 CHANGED
@@ -7,7 +7,9 @@ ESLint React's toolkit for building custom React lint rules right inside your `e
7
7
  - [Installation](#installation)
8
8
  - [Quick Start](#quick-start)
9
9
  - [API Reference](#api-reference)
10
- - [`defineConfig` (default export)](#defineconfig-default-export)
10
+ - [`eslintReactKit` (default export)](#eslintreactkit-default-export)
11
+ - [`RuleDefinition`](#ruledefinition)
12
+ - [`KitBuilder`](#kitbuilder)
11
13
  - [`merge`](#merge)
12
14
  - [`Kit` — the toolkit object](#kit--the-toolkit-object)
13
15
  - [`kit.collect`](#kitcollect) — Semantic collectors
@@ -25,7 +27,7 @@ ESLint React's toolkit for building custom React lint rules right inside your `e
25
27
  ## Installation
26
28
 
27
29
  ```sh
28
- npm install --save-dev @eslint-react/kit
30
+ npm install --save-dev @eslint-react/kit@beta
29
31
  ```
30
32
 
31
33
  ## Quick Start
@@ -33,10 +35,32 @@ npm install --save-dev @eslint-react/kit
33
35
  ```ts
34
36
  import eslintReact from "@eslint-react/eslint-plugin";
35
37
  import eslintReactKit, { merge } from "@eslint-react/kit";
38
+ import type { RuleDefinition } from "@eslint-react/kit";
36
39
  import eslintJs from "@eslint/js";
37
40
  import { defineConfig } from "eslint/config";
38
41
  import tseslint from "typescript-eslint";
39
42
 
43
+ /** Enforce function declarations for function components. */
44
+ function functionComponentDefinition(): RuleDefinition {
45
+ return (context, { collect }) => {
46
+ const { query, visitor } = collect.components(context);
47
+ return merge(
48
+ visitor,
49
+ {
50
+ "Program:exit"(program) {
51
+ for (const { node } of query.all(program)) {
52
+ if (node.type === "FunctionDeclaration") continue;
53
+ context.report({
54
+ node,
55
+ message: "Function components must be defined with function declarations.",
56
+ });
57
+ }
58
+ },
59
+ },
60
+ );
61
+ };
62
+ }
63
+
40
64
  export default defineConfig(
41
65
  {
42
66
  files: ["**/*.{ts,tsx}"],
@@ -44,55 +68,75 @@ export default defineConfig(
44
68
  eslintJs.configs.recommended,
45
69
  tseslint.configs.recommended,
46
70
  eslintReact.configs["recommended-typescript"],
47
- eslintReactKit(
48
- {
49
- name: "function-component-definition",
50
- make: (context, { collect }) => {
51
- const { query, visitor } = collect.components(context);
52
-
53
- return merge(
54
- visitor,
55
- {
56
- "Program:exit"(program) {
57
- for (const { node } of query.all(program)) {
58
- if (node.type === "FunctionDeclaration") continue;
59
- context.report({
60
- node,
61
- message: "Function components must be defined with function declarations.",
62
- });
63
- }
64
- },
65
- },
66
- );
67
- },
68
- },
69
- ),
71
+ eslintReactKit()
72
+ .use(functionComponentDefinition)
73
+ .getConfig(),
70
74
  ],
71
- rules: {
72
- "@eslint-react/kit/function-component-definition": "error",
73
- },
74
75
  },
75
76
  );
76
77
  ```
77
78
 
79
+ The rule name is derived automatically from the function name (`functionComponentDefinition` → `function-component-definition`), and registered as `@eslint-react/kit/function-component-definition` at `"error"` severity.
80
+
78
81
  ## API Reference
79
82
 
80
- ### `defineConfig` (default export)
83
+ ### `eslintReactKit` (default export)
84
+
85
+ ```ts
86
+ import eslintReactKit from "@eslint-react/kit";
87
+
88
+ eslintReactKit(): KitBuilder
89
+ ```
90
+
91
+ Creates a `KitBuilder` instance for registering custom rules via the chainable `.use()` API.
92
+
93
+ ### `RuleDefinition`
94
+
95
+ ```ts
96
+ import type { RuleDefinition } from "@eslint-react/kit";
97
+
98
+ type RuleDefinition = (ctx: RuleContext, kit: RuleToolkit) => RuleListener;
99
+ ```
100
+
101
+ A rule definition is a function that receives the ESLint rule context and the structured `Kit` toolkit, and returns a `RuleListener` (AST visitor object).
102
+
103
+ Rules are defined as **named functions** that return a `RuleDefinition`. The function name is automatically converted to kebab-case and used as the rule name under the `@eslint-react/kit` plugin namespace.
81
104
 
82
105
  ```ts
83
- import defineReactConfig from "@eslint-react/kit";
106
+ // Function name `noForwardRef` → rule name `no-forward-ref`
107
+ // Registered as `@eslint-react/kit/no-forward-ref`
108
+ function noForwardRef(): RuleDefinition {
109
+ return (context, { is }) => ({ ... });
110
+ }
84
111
 
85
- defineReactConfig(...rules: RuleDefinition[]): Linter.Config
112
+ // Functions that accept options work the same way
113
+ function forbidElements({ forbidden }: ForbidElementsOptions): RuleDefinition {
114
+ return (context) => ({ ... });
115
+ }
86
116
  ```
87
117
 
88
- 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.
118
+ ### `KitBuilder`
89
119
 
90
- **`RuleDefinition`:**
120
+ ```ts
121
+ interface KitBuilder {
122
+ use<F extends (...args: any[]) => RuleDefinition>(factory: F, ...args: Parameters<F>): KitBuilder;
123
+ getConfig(): Linter.Config;
124
+ }
125
+ ```
126
+
127
+ A chainable builder for registering custom rules.
91
128
 
92
- | Field | Type | Description |
93
- | ------ | -------------------------------- | -------------------------------------------------------------------------------- |
94
- | `name` | `string` | Unique rule name. Used as `@eslint-react/kit/<name>` in config. |
95
- | `make` | `(context, kit) => RuleListener` | Rule factory. Receives the ESLint rule context and the structured `Kit` toolkit. |
129
+ | Method | Description |
130
+ | ----------- | -------------------------------------------------------------------------------------------------------------------------- |
131
+ | `use` | Registers a rule factory. The rule name is `kebabCase(factory.name)`. Options type is inferred from the factory signature. |
132
+ | `getConfig` | Returns a `Linter.Config` with all registered rules enabled at `"error"` severity. |
133
+
134
+ ```ts
135
+ eslintReactKit()
136
+ .use(noForwardRef) // no-arg factory
137
+ .use(forbidElements, { forbidden: new Map() }) // factory with inferred options
138
+ .getConfig();
139
+ ```
96
140
 
97
141
  ### `merge`
98
142
 
@@ -108,7 +152,7 @@ This is essential for combining a collector's `visitor` with your own inspection
108
152
 
109
153
  ### Kit — the toolkit object
110
154
 
111
- The second argument to `make` is a structured `Kit` object:
155
+ The second argument passed to the `RuleDefinition` function is a structured `Kit` object:
112
156
 
113
157
  ```
114
158
  kit
@@ -273,9 +317,8 @@ Exposes the normalized `react-x` settings from the ESLint shared configuration (
273
317
  **Usage:**
274
318
 
275
319
  ```ts
276
- defineReactConfig({
277
- name: "require-react-19",
278
- make: (context, { settings }) => ({
320
+ function requireReact19(): RuleDefinition {
321
+ return (context, { settings }) => ({
279
322
  Program(program) {
280
323
  if (!settings.version.startsWith("19.")) {
281
324
  context.report({
@@ -284,8 +327,8 @@ defineReactConfig({
284
327
  });
285
328
  }
286
329
  },
287
- }),
288
- });
330
+ });
331
+ }
289
332
  ```
290
333
 
291
334
  ---
@@ -294,29 +337,32 @@ defineReactConfig({
294
337
 
295
338
  ### Simple: Ban `forwardRef`
296
339
 
297
- This is a simplified kit reimplementation of the built-in [`react-x/no-forwardRef`](https://eslint-react.xyz/docs/rules/no-forwardRef) rule.
340
+ This is a simplified kit reimplementation of the built-in [`react-x/no-forwardRef`](https://beta.eslint-react.xyz/docs/rules/no-forward-ref) rule.
298
341
 
299
342
  ```ts
300
- defineReactConfig({
301
- name: "no-forward-ref",
302
- make: (context, { is }) => ({
343
+ function noForwardRef(): RuleDefinition {
344
+ return (context, { is }) => ({
303
345
  CallExpression(node) {
304
346
  if (is.forwardRefCall(node)) {
305
347
  context.report({ node, message: "forwardRef is deprecated in React 19. Pass ref as a prop instead." });
306
348
  }
307
349
  },
308
- }),
309
- });
350
+ });
351
+ }
352
+
353
+ // Usage
354
+ eslintReactKit()
355
+ .use(noForwardRef)
356
+ .getConfig();
310
357
  ```
311
358
 
312
359
  ### Component: Destructure component props
313
360
 
314
- 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.
361
+ This is a simplified kit reimplementation of the built-in [`react-x/prefer-destructuring-assignment`](https://beta.eslint-react.xyz/docs/rules/prefer-destructuring-assignment) rule.
315
362
 
316
363
  ```ts
317
- defineReactConfig({
318
- name: "destructure-component-props",
319
- make: (context, { collect }) => {
364
+ function destructureComponentProps(): RuleDefinition {
365
+ return (context, { collect }) => {
320
366
  const { query, visitor } = collect.components(context);
321
367
 
322
368
  return merge(visitor, {
@@ -339,18 +385,22 @@ defineReactConfig({
339
385
  }
340
386
  },
341
387
  });
342
- },
343
- });
388
+ };
389
+ }
390
+
391
+ // Usage
392
+ eslintReactKit()
393
+ .use(destructureComponentProps)
394
+ .getConfig();
344
395
  ```
345
396
 
346
397
  ### Hooks: Warn on custom hooks that don't call other hooks
347
398
 
348
- 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.
399
+ This is a simplified kit reimplementation of the built-in [`react-x/no-unnecessary-use-prefix`](https://beta.eslint-react.xyz/docs/rules/no-unnecessary-use-prefix) rule.
349
400
 
350
401
  ```ts
351
- defineReactConfig({
352
- name: "no-unnecessary-use-prefix",
353
- make: (context, { collect }) => {
402
+ function noUnnecessaryUsePrefix(): RuleDefinition {
403
+ return (context, { collect }) => {
354
404
  const { query, visitor } = collect.hooks(context);
355
405
 
356
406
  return merge(visitor, {
@@ -365,19 +415,34 @@ defineReactConfig({
365
415
  }
366
416
  },
367
417
  });
368
- },
369
- });
418
+ };
419
+ }
420
+
421
+ // Usage
422
+ eslintReactKit()
423
+ .use(noUnnecessaryUsePrefix)
424
+ .getConfig();
370
425
  ```
371
426
 
372
427
  ### Multiple Collectors: No component/hook factories
373
428
 
374
429
  Disallow defining components or hooks inside other functions (factory pattern).
375
- 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.
430
+ This is a simplified kit reimplementation of the built-in [`react-x/component-hook-factories`](https://beta.eslint-react.xyz/docs/rules/component-hook-factories) rule.
376
431
 
377
432
  ```ts
378
- defineReactConfig({
379
- name: "component-hook-factories",
380
- make: (context, { collect }) => {
433
+ function findParent({ parent }: TSESTree.Node, test: (n: TSESTree.Node) => boolean): TSESTree.Node | null {
434
+ if (parent == null) return null;
435
+ if (test(parent)) return parent;
436
+ if (parent.type === "Program") return null;
437
+ return findParent(parent, test);
438
+ }
439
+
440
+ function isFunction({ type }: TSESTree.Node) {
441
+ return type === "FunctionDeclaration" || type === "FunctionExpression" || type === "ArrowFunctionExpression";
442
+ }
443
+
444
+ function componentHookFactories(): RuleDefinition {
445
+ return (context, { collect }) => {
381
446
  const fc = collect.components(context);
382
447
  const hk = collect.hooks(context);
383
448
  return merge(
@@ -398,21 +463,15 @@ defineReactConfig({
398
463
  },
399
464
  },
400
465
  );
401
- },
402
- });
403
-
404
- function findParent({ parent }: TSESTree.Node, test: (n: TSESTree.Node) => boolean): TSESTree.Node | null {
405
- if (parent == null) return null;
406
- if (test(parent)) return parent;
407
- if (parent.type === "Program") return null;
408
- return findParent(parent, test);
466
+ };
409
467
  }
410
468
 
411
- function isFunction({ type }: TSESTree.Node) {
412
- return type === "FunctionDeclaration" || type === "FunctionExpression" || type === "ArrowFunctionExpression";
413
- }
469
+ // Usage
470
+ eslintReactKit()
471
+ .use(componentHookFactories)
472
+ .getConfig();
414
473
  ```
415
474
 
416
475
  ## More Examples
417
476
 
418
- Please check the [Rule Recipes](https://eslint-react.xyz/docs/configuration/configure-custom-rules#rule-recipes) in the documentation site.
477
+ Please check the [Rule Recipes](https://beta.eslint-react.xyz/docs/configuration/configure-custom-rules#rule-recipes) in the documentation site.
package/dist/index.d.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  import * as core from "@eslint-react/core";
2
- import { ESLintReactSettingsNormalized } from "@eslint-react/shared";
2
+ import { ESLintReactSettingsNormalized, RuleFix, RuleFixer, RuleListener, defineRuleListener as merge } from "@eslint-react/shared";
3
3
  import { TSESTreeFunction } from "@eslint-react/ast";
4
4
  import { TSESTree } from "@typescript-eslint/utils";
5
- import { RuleContext, RuleFix, RuleFixer, RuleListener } from "@typescript-eslint/utils/ts-eslint";
5
+ import { RuleContext } from "@typescript-eslint/utils/ts-eslint";
6
6
  import { Linter } from "eslint";
7
7
 
8
8
  //#region src/index.d.ts
@@ -17,33 +17,79 @@ interface CollectorWithContext<T> extends Collector<T> {
17
17
  all(program: TSESTree.Program): T[];
18
18
  };
19
19
  }
20
- declare function components(ctx: RuleContext<string, unknown[]>, options?: {
21
- collectDisplayName?: boolean;
22
- hint?: bigint;
23
- }): CollectorWithContext<core.FunctionComponentSemanticNode>;
24
- declare function hooks(ctx: RuleContext<string, unknown[]>): CollectorWithContext<core.HookSemanticNode>;
20
+ type RuleDefinition = (ctx: RuleContext, kit: RuleToolkit) => RuleListener;
21
+ interface KitBuilder {
22
+ getConfig(args?: {
23
+ files?: string[];
24
+ }): Linter.Config;
25
+ use<F extends (...args: any[]) => RuleDefinition>(factory: F, ...args: Parameters<F>): KitBuilder;
26
+ }
25
27
  interface RuleToolkit {
28
+ collect: {
29
+ components(ctx: RuleContext, options?: {
30
+ collectDisplayName?: boolean;
31
+ hint?: bigint;
32
+ }): CollectorWithContext<core.FunctionComponentSemanticNode>;
33
+ hooks(ctx: RuleContext): CollectorWithContext<core.HookSemanticNode>;
34
+ };
35
+ flag: {
36
+ component: typeof core.ComponentFlag;
37
+ };
38
+ hint: {
39
+ component: typeof core.ComponentDetectionHint & {
40
+ Default: bigint;
41
+ };
42
+ };
26
43
  is: {
44
+ captureOwnerStack: (node: null | TSESTree.Node) => boolean;
45
+ captureOwnerStackCall: (node: null | TSESTree.Node) => node is TSESTree.CallExpression;
46
+ childrenCount: (node: null | TSESTree.Node) => boolean;
47
+ childrenCountCall: (node: null | TSESTree.Node) => node is TSESTree.CallExpression;
48
+ childrenForEach: (node: null | TSESTree.Node) => boolean;
49
+ childrenForEachCall: (node: null | TSESTree.Node) => node is TSESTree.CallExpression;
50
+ childrenMap: (node: null | TSESTree.Node) => boolean;
51
+ childrenMapCall: (node: null | TSESTree.Node) => node is TSESTree.CallExpression;
52
+ childrenOnly: (node: null | TSESTree.Node) => boolean;
53
+ childrenOnlyCall: (node: null | TSESTree.Node) => node is TSESTree.CallExpression;
54
+ childrenToArray: (node: null | TSESTree.Node) => boolean;
55
+ childrenToArrayCall: (node: null | TSESTree.Node) => node is TSESTree.CallExpression;
56
+ cloneElement: (node: null | TSESTree.Node) => boolean;
57
+ cloneElementCall: (node: null | TSESTree.Node) => node is TSESTree.CallExpression;
27
58
  componentDefinition: (node: TSESTreeFunction, hint: bigint) => boolean;
28
59
  componentName: typeof core.isComponentName;
29
60
  componentNameLoose: typeof core.isComponentNameLoose;
30
61
  componentWrapperCall: (node: TSESTree.Node) => boolean;
31
- componentWrapperCallLoose: (node: TSESTree.Node) => boolean;
32
62
  componentWrapperCallback: (node: TSESTree.Node) => boolean;
63
+ componentWrapperCallLoose: (node: TSESTree.Node) => boolean;
64
+ createContext: (node: null | TSESTree.Node) => boolean;
65
+ createContextCall: (node: null | TSESTree.Node) => node is TSESTree.CallExpression;
66
+ createElement: (node: null | TSESTree.Node) => boolean;
67
+ createElementCall: (node: null | TSESTree.Node) => node is TSESTree.CallExpression;
68
+ createRef: (node: null | TSESTree.Node) => boolean;
69
+ createRefCall: (node: null | TSESTree.Node) => node is TSESTree.CallExpression;
70
+ forwardRef: (node: null | TSESTree.Node) => boolean;
71
+ forwardRefCall: (node: null | TSESTree.Node) => node is TSESTree.CallExpression;
33
72
  hook: typeof core.isHook;
34
73
  hookCall: typeof core.isHookCall;
35
74
  hookName: typeof core.isHookName;
36
- useEffectLikeCall: typeof core.isUseEffectLikeCall;
37
- useStateLikeCall: typeof core.isUseStateLikeCall;
38
- useEffectSetupCallback: typeof core.isUseEffectSetupCallback;
39
- useEffectCleanupCallback: typeof core.isUseEffectCleanupCallback;
40
- useCall: typeof core.isUseCall;
75
+ initializedFromReact: typeof core.isInitializedFromReact;
76
+ initializedFromReactNative: typeof core.isInitializedFromReactNative;
77
+ lazy: (node: null | TSESTree.Node) => boolean;
78
+ lazyCall: (node: null | TSESTree.Node) => node is TSESTree.CallExpression;
79
+ memo: (node: null | TSESTree.Node) => boolean;
80
+ memoCall: (node: null | TSESTree.Node) => node is TSESTree.CallExpression;
81
+ reactAPI: (api: string) => (node: null | TSESTree.Node) => boolean;
82
+ reactAPICall: (api: string) => (node: null | TSESTree.Node) => node is TSESTree.CallExpression;
41
83
  useActionStateCall: typeof core.isUseActionStateCall;
84
+ useCall: typeof core.isUseCall;
42
85
  useCallbackCall: typeof core.isUseCallbackCall;
43
86
  useContextCall: typeof core.isUseContextCall;
44
87
  useDebugValueCall: typeof core.isUseDebugValueCall;
45
88
  useDeferredValueCall: typeof core.isUseDeferredValueCall;
46
89
  useEffectCall: typeof core.isUseEffectCall;
90
+ useEffectCleanupCallback: typeof core.isUseEffectCleanupCallback;
91
+ useEffectLikeCall: typeof core.isUseEffectLikeCall;
92
+ useEffectSetupCallback: typeof core.isUseEffectSetupCallback;
47
93
  useFormStatusCall: typeof core.isUseFormStatusCall;
48
94
  useIdCall: typeof core.isUseIdCall;
49
95
  useImperativeHandleCall: typeof core.isUseImperativeHandleCall;
@@ -54,61 +100,15 @@ interface RuleToolkit {
54
100
  useReducerCall: typeof core.isUseReducerCall;
55
101
  useRefCall: typeof core.isUseRefCall;
56
102
  useStateCall: typeof core.isUseStateCall;
103
+ useStateLikeCall: typeof core.isUseStateLikeCall;
57
104
  useSyncExternalStoreCall: typeof core.isUseSyncExternalStoreCall;
58
105
  useTransitionCall: typeof core.isUseTransitionCall;
59
- reactAPI: (api: string) => (node: null | TSESTree.Node) => boolean;
60
- reactAPICall: (api: string) => (node: null | TSESTree.Node) => node is TSESTree.CallExpression;
61
- captureOwnerStack: (node: null | TSESTree.Node) => boolean;
62
- childrenCount: (node: null | TSESTree.Node) => boolean;
63
- childrenForEach: (node: null | TSESTree.Node) => boolean;
64
- childrenMap: (node: null | TSESTree.Node) => boolean;
65
- childrenOnly: (node: null | TSESTree.Node) => boolean;
66
- childrenToArray: (node: null | TSESTree.Node) => boolean;
67
- cloneElement: (node: null | TSESTree.Node) => boolean;
68
- createContext: (node: null | TSESTree.Node) => boolean;
69
- createElement: (node: null | TSESTree.Node) => boolean;
70
- createRef: (node: null | TSESTree.Node) => boolean;
71
- forwardRef: (node: null | TSESTree.Node) => boolean;
72
- memo: (node: null | TSESTree.Node) => boolean;
73
- lazy: (node: null | TSESTree.Node) => boolean;
74
- captureOwnerStackCall: (node: null | TSESTree.Node) => node is TSESTree.CallExpression;
75
- childrenCountCall: (node: null | TSESTree.Node) => node is TSESTree.CallExpression;
76
- childrenForEachCall: (node: null | TSESTree.Node) => node is TSESTree.CallExpression;
77
- childrenMapCall: (node: null | TSESTree.Node) => node is TSESTree.CallExpression;
78
- childrenOnlyCall: (node: null | TSESTree.Node) => node is TSESTree.CallExpression;
79
- childrenToArrayCall: (node: null | TSESTree.Node) => node is TSESTree.CallExpression;
80
- cloneElementCall: (node: null | TSESTree.Node) => node is TSESTree.CallExpression;
81
- createContextCall: (node: null | TSESTree.Node) => node is TSESTree.CallExpression;
82
- createElementCall: (node: null | TSESTree.Node) => node is TSESTree.CallExpression;
83
- createRefCall: (node: null | TSESTree.Node) => node is TSESTree.CallExpression;
84
- forwardRefCall: (node: null | TSESTree.Node) => node is TSESTree.CallExpression;
85
- memoCall: (node: null | TSESTree.Node) => node is TSESTree.CallExpression;
86
- lazyCall: (node: null | TSESTree.Node) => node is TSESTree.CallExpression;
87
- initializedFromReact: typeof core.isInitializedFromReact;
88
- initializedFromReactNative: typeof core.isInitializedFromReactNative;
89
- };
90
- hint: {
91
- component: typeof core.ComponentDetectionHint & {
92
- Default: bigint;
93
- };
94
- };
95
- flag: {
96
- component: typeof core.ComponentFlag;
97
- };
98
- collect: {
99
- components: typeof components;
100
- hooks: typeof hooks;
101
106
  };
102
107
  settings: ESLintReactSettingsNormalized;
103
108
  }
104
- interface RuleDefinition {
105
- name: string;
106
- make(ctx: RuleContext<string, unknown[]>, kit: RuleToolkit): RuleListener;
107
- }
108
- declare function defineConfig(...rules: RuleDefinition[]): Linter.Config;
109
- declare function merge(...listeners: RuleListener[]): RuleListener;
109
+ declare function eslintReactKit(): KitBuilder;
110
110
  declare module "@typescript-eslint/utils/ts-eslint" {
111
- interface RuleContext<MessageIds extends string, Options extends readonly unknown[]> {
111
+ interface RuleContext<MessageIds extends string = string, Options extends readonly unknown[] = readonly unknown[]> {
112
112
  report(descriptor: {
113
113
  readonly data?: Readonly<Record<string, unknown>>;
114
114
  readonly fix?: ((fixer: RuleFixer) => IterableIterator<RuleFix> | readonly RuleFix[] | RuleFix | null) | null;
@@ -124,4 +124,4 @@ declare module "@typescript-eslint/utils/ts-eslint" {
124
124
  }
125
125
  }
126
126
  //#endregion
127
- export { Collector, CollectorWithContext, type RuleContext, RuleToolkit, defineConfig as default, defineConfig, merge };
127
+ export { Collector, CollectorWithContext, KitBuilder, RuleDefinition, type RuleFix, type RuleFixer, type RuleListener, eslintReactKit as default, merge };
package/dist/index.js CHANGED
@@ -1,49 +1,90 @@
1
1
  import * as core from "@eslint-react/core";
2
- import { getSettingsFromContext } from "@eslint-react/shared";
2
+ import { IdGenerator, defineRuleListener as merge, getSettingsFromContext } from "@eslint-react/shared";
3
+ import { kebabCase } from "string-ts";
3
4
 
4
5
  //#region package.json
5
6
  var name = "@eslint-react/kit";
6
- var version = "4.0.0-beta.2";
7
+ var version = "4.0.1-beta.0";
7
8
 
8
9
  //#endregion
9
10
  //#region src/index.ts
10
- function components(ctx, options) {
11
- const { api, visitor } = core.getComponentCollector(ctx, options);
12
- return {
13
- query: { all: (program) => api.getAllComponents(program) },
14
- visitor
15
- };
16
- }
17
- function hooks(ctx) {
18
- const { api, visitor } = core.getHookCollector(ctx);
19
- return {
20
- query: { all: (program) => api.getAllHooks(program) },
21
- visitor
22
- };
23
- }
24
11
  function createKit(ctx) {
25
12
  return {
13
+ collect: {
14
+ components(ctx, options) {
15
+ const { api, visitor } = core.getComponentCollector(ctx, options);
16
+ return {
17
+ query: { all(program) {
18
+ return api.getAllComponents(program);
19
+ } },
20
+ visitor
21
+ };
22
+ },
23
+ hooks(ctx) {
24
+ const { api, visitor } = core.getHookCollector(ctx);
25
+ return {
26
+ query: { all(program) {
27
+ return api.getAllHooks(program);
28
+ } },
29
+ visitor
30
+ };
31
+ }
32
+ },
33
+ flag: { component: core.ComponentFlag },
34
+ hint: { component: {
35
+ ...core.ComponentDetectionHint,
36
+ Default: core.DEFAULT_COMPONENT_DETECTION_HINT
37
+ } },
26
38
  is: {
39
+ captureOwnerStack: core.isCaptureOwnerStack(ctx),
40
+ captureOwnerStackCall: core.isCaptureOwnerStackCall(ctx),
41
+ childrenCount: core.isChildrenCount(ctx),
42
+ childrenCountCall: core.isChildrenCountCall(ctx),
43
+ childrenForEach: core.isChildrenForEach(ctx),
44
+ childrenForEachCall: core.isChildrenForEachCall(ctx),
45
+ childrenMap: core.isChildrenMap(ctx),
46
+ childrenMapCall: core.isChildrenMapCall(ctx),
47
+ childrenOnly: core.isChildrenOnly(ctx),
48
+ childrenOnlyCall: core.isChildrenOnlyCall(ctx),
49
+ childrenToArray: core.isChildrenToArray(ctx),
50
+ childrenToArrayCall: core.isChildrenToArrayCall(ctx),
51
+ cloneElement: core.isCloneElement(ctx),
52
+ cloneElementCall: core.isCloneElementCall(ctx),
27
53
  componentDefinition: (node, hint) => core.isComponentDefinition(ctx, node, hint),
28
54
  componentName: core.isComponentName,
29
55
  componentNameLoose: core.isComponentNameLoose,
30
56
  componentWrapperCall: (node) => core.isComponentWrapperCall(ctx, node),
31
- componentWrapperCallLoose: (node) => core.isComponentWrapperCallLoose(ctx, node),
32
57
  componentWrapperCallback: (node) => core.isComponentWrapperCallback(ctx, node),
58
+ componentWrapperCallLoose: (node) => core.isComponentWrapperCallLoose(ctx, node),
59
+ createContext: core.isCreateContext(ctx),
60
+ createContextCall: core.isCreateContextCall(ctx),
61
+ createElement: core.isCreateElement(ctx),
62
+ createElementCall: core.isCreateElementCall(ctx),
63
+ createRef: core.isCreateRef(ctx),
64
+ createRefCall: core.isCreateRefCall(ctx),
65
+ forwardRef: core.isForwardRef(ctx),
66
+ forwardRefCall: core.isForwardRefCall(ctx),
33
67
  hook: core.isHook,
34
68
  hookCall: core.isHookCall,
35
69
  hookName: core.isHookName,
36
- useEffectLikeCall: core.isUseEffectLikeCall,
37
- useStateLikeCall: core.isUseStateLikeCall,
38
- useEffectSetupCallback: core.isUseEffectSetupCallback,
39
- useEffectCleanupCallback: core.isUseEffectCleanupCallback,
40
- useCall: core.isUseCall,
70
+ initializedFromReact: core.isInitializedFromReact,
71
+ initializedFromReactNative: core.isInitializedFromReactNative,
72
+ lazy: core.isLazy(ctx),
73
+ lazyCall: core.isLazyCall(ctx),
74
+ memo: core.isMemo(ctx),
75
+ memoCall: core.isMemoCall(ctx),
76
+ reactAPI: (api) => core.isReactAPI(api)(ctx),
77
+ reactAPICall: (api) => core.isReactAPICall(api)(ctx),
41
78
  useActionStateCall: core.isUseActionStateCall,
79
+ useCall: core.isUseCall,
42
80
  useCallbackCall: core.isUseCallbackCall,
43
81
  useContextCall: core.isUseContextCall,
44
82
  useDebugValueCall: core.isUseDebugValueCall,
45
83
  useDeferredValueCall: core.isUseDeferredValueCall,
46
84
  useEffectCall: core.isUseEffectCall,
85
+ useEffectCleanupCallback: core.isUseEffectCleanupCallback,
86
+ useEffectLikeCall: core.isUseEffectLikeCall,
87
+ useEffectSetupCallback: core.isUseEffectSetupCallback,
47
88
  useFormStatusCall: core.isUseFormStatusCall,
48
89
  useIdCall: core.isUseIdCall,
49
90
  useImperativeHandleCall: core.isUseImperativeHandleCall,
@@ -54,89 +95,55 @@ function createKit(ctx) {
54
95
  useReducerCall: core.isUseReducerCall,
55
96
  useRefCall: core.isUseRefCall,
56
97
  useStateCall: core.isUseStateCall,
98
+ useStateLikeCall: core.isUseStateLikeCall,
57
99
  useSyncExternalStoreCall: core.isUseSyncExternalStoreCall,
58
- useTransitionCall: core.isUseTransitionCall,
59
- reactAPI: (api) => core.isReactAPI(api)(ctx),
60
- reactAPICall: (api) => core.isReactAPICall(api)(ctx),
61
- captureOwnerStack: core.isCaptureOwnerStack(ctx),
62
- childrenCount: core.isChildrenCount(ctx),
63
- childrenForEach: core.isChildrenForEach(ctx),
64
- childrenMap: core.isChildrenMap(ctx),
65
- childrenOnly: core.isChildrenOnly(ctx),
66
- childrenToArray: core.isChildrenToArray(ctx),
67
- cloneElement: core.isCloneElement(ctx),
68
- createContext: core.isCreateContext(ctx),
69
- createElement: core.isCreateElement(ctx),
70
- createRef: core.isCreateRef(ctx),
71
- forwardRef: core.isForwardRef(ctx),
72
- memo: core.isMemo(ctx),
73
- lazy: core.isLazy(ctx),
74
- captureOwnerStackCall: core.isCaptureOwnerStackCall(ctx),
75
- childrenCountCall: core.isChildrenCountCall(ctx),
76
- childrenForEachCall: core.isChildrenForEachCall(ctx),
77
- childrenMapCall: core.isChildrenMapCall(ctx),
78
- childrenOnlyCall: core.isChildrenOnlyCall(ctx),
79
- childrenToArrayCall: core.isChildrenToArrayCall(ctx),
80
- cloneElementCall: core.isCloneElementCall(ctx),
81
- createContextCall: core.isCreateContextCall(ctx),
82
- createElementCall: core.isCreateElementCall(ctx),
83
- createRefCall: core.isCreateRefCall(ctx),
84
- forwardRefCall: core.isForwardRefCall(ctx),
85
- memoCall: core.isMemoCall(ctx),
86
- lazyCall: core.isLazyCall(ctx),
87
- initializedFromReact: core.isInitializedFromReact,
88
- initializedFromReactNative: core.isInitializedFromReactNative
89
- },
90
- hint: { component: {
91
- ...core.ComponentDetectionHint,
92
- Default: core.DEFAULT_COMPONENT_DETECTION_HINT
93
- } },
94
- flag: { component: core.ComponentFlag },
95
- collect: {
96
- components,
97
- hooks
100
+ useTransitionCall: core.isUseTransitionCall
98
101
  },
99
102
  settings: getSettingsFromContext(ctx)
100
103
  };
101
104
  }
102
- function defineConfig(...rules) {
103
- return {
104
- files: ["**/*.ts", "**/*.tsx"],
105
- plugins: { [name]: {
106
- meta: {
107
- name,
108
- version
109
- },
110
- rules: rules.reduce((acc, { name, make }) => {
111
- Reflect.set(acc, name, {
105
+ function eslintReactKit() {
106
+ const idGen = new IdGenerator();
107
+ const rules = [];
108
+ const builder = {
109
+ getConfig({ files = ["**/*.ts", "**/*.tsx"] } = {}) {
110
+ return {
111
+ files,
112
+ plugins: { [name]: {
112
113
  meta: {
113
- fixable: "code",
114
- hasSuggestions: true
114
+ name,
115
+ version
115
116
  },
116
- create(ctx) {
117
- return make(ctx, createKit(ctx));
118
- }
119
- });
120
- return acc;
121
- }, {})
122
- } },
123
- rules: rules.reduce((acc, { name: name$1 }) => {
124
- acc[`${name}/${name$1}`] = "error";
125
- return acc;
126
- }, {})
117
+ rules: rules.reduce((acc, { name, make }) => {
118
+ Reflect.set(acc, name, {
119
+ meta: {
120
+ fixable: "code",
121
+ hasSuggestions: true
122
+ },
123
+ create(ctx) {
124
+ return make(ctx, createKit(ctx));
125
+ }
126
+ });
127
+ return acc;
128
+ }, {})
129
+ } },
130
+ rules: rules.reduce((acc, { name: name$1 }) => {
131
+ acc[`${name}/${name$1}`] = "error";
132
+ return acc;
133
+ }, {})
134
+ };
135
+ },
136
+ use(factory, ...args) {
137
+ const name = kebabCase(factory.name === "" ? idGen.next() : factory.name);
138
+ rules.push({
139
+ name,
140
+ make: factory(...args)
141
+ });
142
+ return builder;
143
+ }
127
144
  };
128
- }
129
- function merge(...listeners) {
130
- const [base = {}, ...rest] = listeners;
131
- for (const r of rest) for (const key in r) {
132
- const existing = base[key];
133
- base[key] = existing ? (...args) => {
134
- existing(...args);
135
- r[key]?.(...args);
136
- } : r[key];
137
- }
138
- return base;
145
+ return builder;
139
146
  }
140
147
 
141
148
  //#endregion
142
- export { defineConfig as default, defineConfig, merge };
149
+ export { eslintReactKit as default, merge };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eslint-react/kit",
3
- "version": "4.0.0-beta.2",
3
+ "version": "4.0.1-beta.0",
4
4
  "description": "ESLint React's utility module for building custom rules.",
5
5
  "keywords": [
6
6
  "react",
@@ -37,13 +37,14 @@
37
37
  ],
38
38
  "dependencies": {
39
39
  "@typescript-eslint/utils": "^8.57.2",
40
- "@eslint-react/ast": "4.0.0-beta.2",
41
- "@eslint-react/shared": "4.0.0-beta.2",
42
- "@eslint-react/core": "4.0.0-beta.2"
40
+ "string-ts": "^2.3.1",
41
+ "@eslint-react/core": "4.0.1-beta.0",
42
+ "@eslint-react/shared": "4.0.1-beta.0",
43
+ "@eslint-react/ast": "4.0.1-beta.0"
43
44
  },
44
45
  "devDependencies": {
45
46
  "eslint": "^10.1.0",
46
- "tsdown": "^0.21.5",
47
+ "tsdown": "^0.21.6",
47
48
  "@local/configs": "0.0.0",
48
49
  "@local/eff": "3.0.0-beta.72"
49
50
  },