@eslint-react/kit 5.0.0-next.0 → 5.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
@@ -24,7 +24,7 @@ ESLint React's toolkit for building custom React rules with JavasSript functions
24
24
  - [Component: Destructure component props](#component-destructure-component-props)
25
25
  - [Hooks: Warn on custom hooks that don't call other hooks](#hooks-warn-on-custom-hooks-that-dont-call-other-hooks)
26
26
  - [Multiple Collectors: No component/hook factories](#multiple-collectors-no-componenthook-factories)
27
- - [Override Config: Using spread syntax with `getConfig`](#Override-config-using-spread-syntax-with-getconfig)
27
+ - [Override Config: Using spread syntax with `getConfig`](#override-config-using-spread-syntax-with-getconfig)
28
28
  - [Advanced Config: Using `getPlugin` for custom plugin namespace](#advanced-config-using-getplugin-for-custom-plugin-namespace)
29
29
  - [More Examples](#more-examples)
30
30
 
@@ -102,7 +102,7 @@ import type { RuleFunction } from "@eslint-react/kit";
102
102
  type RuleFunction = (ctx: RuleContext, kit: RuleToolkit) => RuleListener;
103
103
  ```
104
104
 
105
- A rule definition is a function that receives the ESLint rule context and the structured `Kit` toolkit, and returns a `RuleListener` (AST visitor object).
105
+ A function that receives the ESLint rule context and the structured `Kit` toolkit, and returns a `RuleListener` (AST visitor object).
106
106
 
107
107
  Rules are defined as **named functions** that return a `RuleFunction`. The function name is automatically converted to kebab-case and used as the rule name under the `@eslint-react/kit` plugin namespace.
108
108
 
@@ -119,6 +119,75 @@ function forbidElements({ forbidden }: ForbidElementsOptions): RuleFunction {
119
119
  }
120
120
  ```
121
121
 
122
+ #### Anonymous Rules
123
+
124
+ When you use an **anonymous function** (arrow function without a name) with `.use()`, a random ULID is automatically generated as the rule name:
125
+
126
+ ```ts
127
+ // Anonymous rule → random ULID name like "01KNE2WSJ8011D2HXE3A6H717C"
128
+ // Registered as `@eslint-react/kit/01KNE2WSJ8011D2HXE3A6H717C`
129
+ esslintReactKit().use(() => (context) => ({
130
+ JSXOpeningElement(node) {
131
+ // Critical check that cannot be easily disabled
132
+ },
133
+ }));
134
+ ```
135
+
136
+ > **Note:** The rule names are ULIDs generated randomly on each ESLint run. The examples above illustrate the format — actual values will differ every time.
137
+
138
+ ##### Characteristics of Anonymous Rules
139
+
140
+ | Feature | Description |
141
+ | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------ |
142
+ | **Random name** | A unique ULID is generated for each rule on every lint run |
143
+ | **Non-disablable** | Cannot be disabled via `{ rules: { <rule-name>: "off" } }` or `// eslint-disable-next-line <rule-name>` because the name changes each time |
144
+ | **Use case** | Critical checks that must never be bypassed |
145
+
146
+ Anonymous rules are ideal for checks that are **critical to code quality or security** and should never be bypassed via disable comments:
147
+
148
+ ```ts
149
+ // This critical security check cannot be easily disabled by developers
150
+ eslintReactKit().use(() => (context, { is }) => ({
151
+ CallExpression(node) {
152
+ // Prevent dangerous API calls that could lead to XSS
153
+ if (is.createElement(node) && isUnsafeArguments(node.arguments)) {
154
+ context.report({
155
+ node,
156
+ message: "Potential XSS vulnerability detected. This issue must be fixed.",
157
+ });
158
+ }
159
+ },
160
+ }));
161
+ ```
162
+
163
+ **Note:** Since the rule name is random and changes on every ESLint run, developers cannot use standard rules configs or disable comments like:
164
+
165
+ ```ts
166
+ // This will NOT work - the rule name is random!
167
+ { rules: { "01KNE2WSJ8011D2HXE3A6H717C": "off" } }
168
+
169
+ // This will NOT work - the rule name is random!
170
+ // eslint-disable-next-line 01KNE2WSJ8011D2HXE3A6H717C
171
+ ```
172
+
173
+ To disable an anonymous rule, developers must modify the ESLint configuration file directly, which provides an audit trail for policy violations.
174
+
175
+ ##### Debugging Anonymous Rules
176
+
177
+ For debugging purposes, you can [set a `displayName`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/displayName#setting_a_displayname) on an anonymous function. The display name will be used as a label in various places. This helps identify the rule, while the actual registered rule name remains a random ULID:
178
+
179
+ ```ts
180
+ import { defineRule } from "@eslint-react/kit";
181
+
182
+ const myRule = defineRule(() => (context) => ({}));
183
+
184
+ myRule.displayName = "my-rule";
185
+
186
+ eslintReactKit().use(myRule);
187
+ ```
188
+
189
+ > **Note:** This feature is currently under development and may not be available in the current release.
190
+
122
191
  ### `Builder`
123
192
 
124
193
  ```ts
@@ -257,21 +326,6 @@ Factory functions (context pre-bound):
257
326
  | `API` | `(apiName) -> (node) -> boolean` | Factory: creates a predicate for a React API identifier. (context pre-bound) |
258
327
  | `APICall` | `(apiName) -> (node) -> boolean` | Factory: creates a predicate for a React API call. (context pre-bound) |
259
328
 
260
- Pre-built identifier predicates (context pre-bound):
261
-
262
- - `captureOwnerStack`
263
- - `childrenCount`
264
- - `childrenForEach`
265
- - `childrenMap`
266
- - `childrenOnly`
267
- - `childrenToArray`
268
- - `cloneElement`
269
- - `createContext`
270
- - `createElement`
271
- - `forwardRef`
272
- - `memo`
273
- - `lazy`
274
-
275
329
  Pre-built call predicates (context pre-bound):
276
330
 
277
331
  - `captureOwnerStackCall`
@@ -291,7 +345,6 @@ All React API predicates and factories have `context` pre-bound — no need to p
291
345
 
292
346
  ```ts
293
347
  // Direct check
294
- is.memo(node);
295
348
  is.memoCall(node);
296
349
 
297
350
  // Useful in filter/find
@@ -304,10 +357,10 @@ isCreateRefCall(node);
304
357
 
305
358
  ##### Import source
306
359
 
307
- | Predicate | Signature | Description |
308
- | ---------------------------- | ----------------------------------------- | ---------------------------------------------------- |
309
- | `initializedFromReact` | `(name, scope, importSource?) -> boolean` | Whether a variable comes from a React import. |
310
- | `initializedFromReactNative` | `(name, scope, importSource?) -> boolean` | Whether a variable comes from a React Native import. |
360
+ | Predicate | Signature | Description |
361
+ | -------------------- | ----------------------------------------- | ---------------------------------------------------- |
362
+ | `APIFromReact` | `(name, scope, importSource?) -> boolean` | Whether a variable comes from a React import. |
363
+ | `APIFromReactNative` | `(name, scope, importSource?) -> boolean` | Whether a variable comes from a React Native import. |
311
364
 
312
365
  ---
313
366
 
package/dist/index.d.ts CHANGED
@@ -2362,41 +2362,28 @@ interface RuleToolkit {
2362
2362
  is: {
2363
2363
  API: (api: string) => (node: null | Node) => boolean;
2364
2364
  APICall: (api: string) => (node: null | Node) => node is CallExpression;
2365
- captureOwnerStack: (node: null | Node) => boolean;
2365
+ APIFromReact: typeof core.isAPIFromReact;
2366
+ APIFromReactNative: typeof core.isAPIFromReactNative;
2366
2367
  captureOwnerStackCall: (node: null | Node) => node is CallExpression;
2367
- childrenCount: (node: null | Node) => boolean;
2368
2368
  childrenCountCall: (node: null | Node) => node is CallExpression;
2369
- childrenForEach: (node: null | Node) => boolean;
2370
2369
  childrenForEachCall: (node: null | Node) => node is CallExpression;
2371
- childrenMap: (node: null | Node) => boolean;
2372
2370
  childrenMapCall: (node: null | Node) => node is CallExpression;
2373
- childrenOnly: (node: null | Node) => boolean;
2374
2371
  childrenOnlyCall: (node: null | Node) => node is CallExpression;
2375
- childrenToArray: (node: null | Node) => boolean;
2376
2372
  childrenToArrayCall: (node: null | Node) => node is CallExpression;
2377
- cloneElement: (node: null | Node) => boolean;
2378
2373
  cloneElementCall: (node: null | Node) => node is CallExpression;
2379
2374
  componentDecl: (node: TSESTreeFunction, hint: bigint) => boolean;
2380
2375
  componentName: typeof core.isFunctionComponentName;
2381
2376
  componentNameLoose: typeof core.isFunctionComponentNameLoose;
2382
2377
  componentWrapperCall: (node: Node) => boolean;
2383
2378
  componentWrapperCallback: (node: Node) => boolean;
2384
- createContext: (node: null | Node) => boolean;
2385
2379
  createContextCall: (node: null | Node) => node is CallExpression;
2386
- createElement: (node: null | Node) => boolean;
2387
2380
  createElementCall: (node: null | Node) => node is CallExpression;
2388
- createRef: (node: null | Node) => boolean;
2389
2381
  createRefCall: (node: null | Node) => node is CallExpression;
2390
- forwardRef: (node: null | Node) => boolean;
2391
2382
  forwardRefCall: (node: null | Node) => node is CallExpression;
2392
2383
  hookCall: typeof core.isHookCall;
2393
2384
  hookDecl: typeof core.isHookDefinition;
2394
2385
  hookName: typeof core.isHookName;
2395
- initializedFromReact: typeof core.isAPIFromReact;
2396
- initializedFromReactNative: typeof core.isAPIFromReactNative;
2397
- lazy: (node: null | Node) => boolean;
2398
2386
  lazyCall: (node: null | Node) => node is CallExpression;
2399
- memo: (node: null | Node) => boolean;
2400
2387
  memoCall: (node: null | Node) => node is CallExpression;
2401
2388
  useActionStateCall: (node: null | Node) => node is CallExpression;
2402
2389
  useCall: (node: null | Node) => node is CallExpression;
@@ -2425,10 +2412,6 @@ interface RuleToolkit {
2425
2412
  settings: ESLintReactSettingsNormalized;
2426
2413
  }
2427
2414
  type RuleFunction = (context: RuleContext, toolkit: RuleToolkit) => RuleListener;
2428
- /**
2429
- * @deprecated Use `RuleFunction` instead.
2430
- */
2431
- type RuleDefinition = RuleFunction;
2432
2415
  interface Builder {
2433
2416
  getConfig(): Linter.Config;
2434
2417
  getPlugin(): ESLint.Plugin;
@@ -2452,4 +2435,4 @@ declare module "@typescript-eslint/utils/ts-eslint" {
2452
2435
  }
2453
2436
  }
2454
2437
  //#endregion
2455
- export { Builder, Collector, CollectorWithContext, RuleDefinition, type RuleFix, type RuleFixer, RuleFunction, type RuleListener, build as default, merge };
2438
+ export { Builder, Collector, CollectorWithContext, type RuleFix, type RuleFixer, RuleFunction, type RuleListener, build as default, merge };
package/dist/index.js CHANGED
@@ -1,10 +1,11 @@
1
1
  import * as core from "@eslint-react/core";
2
- import { IdGenerator, defineRuleListener as merge, getSettingsFromContext } from "@eslint-react/shared";
2
+ import { defineRuleListener as merge, getSettingsFromContext } from "@eslint-react/shared";
3
3
  import { kebabCase } from "string-ts";
4
+ import { ulid } from "ulid";
4
5
 
5
6
  //#region package.json
6
7
  var name = "@eslint-react/kit";
7
- var version = "5.0.0-next.0";
8
+ var version = "5.0.1-beta.0";
8
9
 
9
10
  //#endregion
10
11
  //#region src/index.ts
@@ -38,41 +39,28 @@ function makeRuleToolkit(context) {
38
39
  is: {
39
40
  API: (api) => core.isAPI(api)(context),
40
41
  APICall: (api) => core.isAPICall(api)(context),
41
- captureOwnerStack: core.isCaptureOwnerStack(context),
42
+ APIFromReact: core.isAPIFromReact,
43
+ APIFromReactNative: core.isAPIFromReactNative,
42
44
  captureOwnerStackCall: core.isCaptureOwnerStackCall(context),
43
- childrenCount: core.isChildrenCount(context),
44
45
  childrenCountCall: core.isChildrenCountCall(context),
45
- childrenForEach: core.isChildrenForEach(context),
46
46
  childrenForEachCall: core.isChildrenForEachCall(context),
47
- childrenMap: core.isChildrenMap(context),
48
47
  childrenMapCall: core.isChildrenMapCall(context),
49
- childrenOnly: core.isChildrenOnly(context),
50
48
  childrenOnlyCall: core.isChildrenOnlyCall(context),
51
- childrenToArray: core.isChildrenToArray(context),
52
49
  childrenToArrayCall: core.isChildrenToArrayCall(context),
53
- cloneElement: core.isCloneElement(context),
54
50
  cloneElementCall: core.isCloneElementCall(context),
55
51
  componentDecl: (node, hint) => core.isFunctionComponentDefinition(context, node, hint),
56
52
  componentName: core.isFunctionComponentName,
57
53
  componentNameLoose: core.isFunctionComponentNameLoose,
58
54
  componentWrapperCall: (node) => core.isFunctionComponentWrapperCall(context, node),
59
55
  componentWrapperCallback: (node) => core.isFunctionComponentWrapperCallback(context, node),
60
- createContext: core.isCreateContext(context),
61
56
  createContextCall: core.isCreateContextCall(context),
62
- createElement: core.isCreateElement(context),
63
57
  createElementCall: core.isCreateElementCall(context),
64
- createRef: core.isCreateRef(context),
65
58
  createRefCall: core.isCreateRefCall(context),
66
- forwardRef: core.isForwardRef(context),
67
59
  forwardRefCall: core.isForwardRefCall(context),
68
60
  hookCall: core.isHookCall,
69
61
  hookDecl: core.isHookDefinition,
70
62
  hookName: core.isHookName,
71
- initializedFromReact: core.isAPIFromReact,
72
- initializedFromReactNative: core.isAPIFromReactNative,
73
- lazy: core.isLazy(context),
74
63
  lazyCall: core.isLazyCall(context),
75
- memo: core.isMemo(context),
76
64
  memoCall: core.isMemoCall(context),
77
65
  useActionStateCall: core.isUseActionStateCall(context),
78
66
  useCall: core.isUseCall(context),
@@ -102,7 +90,6 @@ function makeRuleToolkit(context) {
102
90
  };
103
91
  }
104
92
  function build() {
105
- const idGen = new IdGenerator();
106
93
  const rules = {};
107
94
  const builder = {
108
95
  getConfig() {
@@ -126,7 +113,7 @@ function build() {
126
113
  };
127
114
  },
128
115
  use(make, ...args) {
129
- const name = kebabCase(make.name === "" ? idGen.next() : make.name);
116
+ const name = make.name === "" ? ulid() : kebabCase(make.name);
130
117
  Reflect.set(rules, name, {
131
118
  meta: {
132
119
  fixable: "code",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eslint-react/kit",
3
- "version": "5.0.0-next.0",
3
+ "version": "5.0.1-beta.0",
4
4
  "description": "ESLint React's utility module for building custom React rules with JavasSript functions.",
5
5
  "keywords": [
6
6
  "react",
@@ -38,9 +38,10 @@
38
38
  "dependencies": {
39
39
  "@typescript-eslint/utils": "^8.58.0",
40
40
  "string-ts": "^2.3.1",
41
- "@eslint-react/ast": "5.0.0-next.0",
42
- "@eslint-react/shared": "5.0.0-next.0",
43
- "@eslint-react/core": "5.0.0-next.0"
41
+ "ulid": "^3.0.2",
42
+ "@eslint-react/ast": "5.0.1-beta.0",
43
+ "@eslint-react/shared": "5.0.1-beta.0",
44
+ "@eslint-react/core": "5.0.1-beta.0"
44
45
  },
45
46
  "devDependencies": {
46
47
  "eslint": "^10.2.0",