@eslint-react/core 1.28.0-next.5 → 1.28.0-next.6
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.js +498 -21
- package/dist/index.mjs +500 -23
- package/package.json +6 -6
package/dist/index.js
CHANGED
|
@@ -35,6 +35,479 @@ var VAR__namespace = /*#__PURE__*/_interopNamespace(VAR);
|
|
|
35
35
|
var birecord__default = /*#__PURE__*/_interopDefault(birecord);
|
|
36
36
|
|
|
37
37
|
// src/component/component-collector.ts
|
|
38
|
+
|
|
39
|
+
// ../../node_modules/.pnpm/valibot@1.0.0-rc.2_typescript@5.8.1-rc/node_modules/valibot/dist/index.js
|
|
40
|
+
var store;
|
|
41
|
+
// @__NO_SIDE_EFFECTS__
|
|
42
|
+
function getGlobalConfig(config2) {
|
|
43
|
+
return {
|
|
44
|
+
lang: config2?.lang ?? store?.lang,
|
|
45
|
+
message: config2?.message,
|
|
46
|
+
abortEarly: config2?.abortEarly ?? store?.abortEarly,
|
|
47
|
+
abortPipeEarly: config2?.abortPipeEarly ?? store?.abortPipeEarly
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
var store2;
|
|
51
|
+
// @__NO_SIDE_EFFECTS__
|
|
52
|
+
function getGlobalMessage(lang) {
|
|
53
|
+
return store2?.get(lang);
|
|
54
|
+
}
|
|
55
|
+
var store3;
|
|
56
|
+
// @__NO_SIDE_EFFECTS__
|
|
57
|
+
function getSchemaMessage(lang) {
|
|
58
|
+
return store3?.get(lang);
|
|
59
|
+
}
|
|
60
|
+
var store4;
|
|
61
|
+
// @__NO_SIDE_EFFECTS__
|
|
62
|
+
function getSpecificMessage(reference, lang) {
|
|
63
|
+
return store4?.get(reference)?.get(lang);
|
|
64
|
+
}
|
|
65
|
+
// @__NO_SIDE_EFFECTS__
|
|
66
|
+
function _stringify(input) {
|
|
67
|
+
const type = typeof input;
|
|
68
|
+
if (type === "string") {
|
|
69
|
+
return `"${input}"`;
|
|
70
|
+
}
|
|
71
|
+
if (type === "number" || type === "bigint" || type === "boolean") {
|
|
72
|
+
return `${input}`;
|
|
73
|
+
}
|
|
74
|
+
if (type === "object" || type === "function") {
|
|
75
|
+
return (input && Object.getPrototypeOf(input)?.constructor?.name) ?? "null";
|
|
76
|
+
}
|
|
77
|
+
return type;
|
|
78
|
+
}
|
|
79
|
+
function _addIssue(context, label, dataset, config2, other) {
|
|
80
|
+
const input = other && "input" in other ? other.input : dataset.value;
|
|
81
|
+
const expected = other?.expected ?? context.expects ?? null;
|
|
82
|
+
const received = other?.received ?? /* @__PURE__ */ _stringify(input);
|
|
83
|
+
const issue = {
|
|
84
|
+
kind: context.kind,
|
|
85
|
+
type: context.type,
|
|
86
|
+
input,
|
|
87
|
+
expected,
|
|
88
|
+
received,
|
|
89
|
+
message: `Invalid ${label}: ${expected ? `Expected ${expected} but r` : "R"}eceived ${received}`,
|
|
90
|
+
requirement: context.requirement,
|
|
91
|
+
path: other?.path,
|
|
92
|
+
issues: other?.issues,
|
|
93
|
+
lang: config2.lang,
|
|
94
|
+
abortEarly: config2.abortEarly,
|
|
95
|
+
abortPipeEarly: config2.abortPipeEarly
|
|
96
|
+
};
|
|
97
|
+
const isSchema = context.kind === "schema";
|
|
98
|
+
const message = other?.message ?? context.message ?? /* @__PURE__ */ getSpecificMessage(context.reference, issue.lang) ?? (isSchema ? /* @__PURE__ */ getSchemaMessage(issue.lang) : null) ?? config2.message ?? /* @__PURE__ */ getGlobalMessage(issue.lang);
|
|
99
|
+
if (message) {
|
|
100
|
+
issue.message = typeof message === "function" ? (
|
|
101
|
+
// @ts-expect-error
|
|
102
|
+
message(issue)
|
|
103
|
+
) : message;
|
|
104
|
+
}
|
|
105
|
+
if (isSchema) {
|
|
106
|
+
dataset.typed = false;
|
|
107
|
+
}
|
|
108
|
+
if (dataset.issues) {
|
|
109
|
+
dataset.issues.push(issue);
|
|
110
|
+
} else {
|
|
111
|
+
dataset.issues = [issue];
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
// @__NO_SIDE_EFFECTS__
|
|
115
|
+
function _getStandardProps(context) {
|
|
116
|
+
return {
|
|
117
|
+
version: 1,
|
|
118
|
+
vendor: "valibot",
|
|
119
|
+
validate(value2) {
|
|
120
|
+
return context["~run"]({ value: value2 }, /* @__PURE__ */ getGlobalConfig());
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
var ValiError = class extends Error {
|
|
125
|
+
/**
|
|
126
|
+
* Creates a Valibot error with useful information.
|
|
127
|
+
*
|
|
128
|
+
* @param issues The error issues.
|
|
129
|
+
*/
|
|
130
|
+
constructor(issues) {
|
|
131
|
+
super(issues[0].message);
|
|
132
|
+
this.name = "ValiError";
|
|
133
|
+
this.issues = issues;
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
// @__NO_SIDE_EFFECTS__
|
|
137
|
+
function getFallback(schema, dataset, config2) {
|
|
138
|
+
return typeof schema.fallback === "function" ? (
|
|
139
|
+
// @ts-expect-error
|
|
140
|
+
schema.fallback(dataset, config2)
|
|
141
|
+
) : (
|
|
142
|
+
// @ts-expect-error
|
|
143
|
+
schema.fallback
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
// @__NO_SIDE_EFFECTS__
|
|
147
|
+
function getDefault(schema, dataset, config2) {
|
|
148
|
+
return typeof schema.default === "function" ? (
|
|
149
|
+
// @ts-expect-error
|
|
150
|
+
schema.default(dataset, config2)
|
|
151
|
+
) : (
|
|
152
|
+
// @ts-expect-error
|
|
153
|
+
schema.default
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
// @__NO_SIDE_EFFECTS__
|
|
157
|
+
function array(item, message) {
|
|
158
|
+
return {
|
|
159
|
+
kind: "schema",
|
|
160
|
+
type: "array",
|
|
161
|
+
reference: array,
|
|
162
|
+
expects: "Array",
|
|
163
|
+
async: false,
|
|
164
|
+
item,
|
|
165
|
+
message,
|
|
166
|
+
get "~standard"() {
|
|
167
|
+
return /* @__PURE__ */ _getStandardProps(this);
|
|
168
|
+
},
|
|
169
|
+
"~run"(dataset, config2) {
|
|
170
|
+
const input = dataset.value;
|
|
171
|
+
if (Array.isArray(input)) {
|
|
172
|
+
dataset.typed = true;
|
|
173
|
+
dataset.value = [];
|
|
174
|
+
for (let key = 0; key < input.length; key++) {
|
|
175
|
+
const value2 = input[key];
|
|
176
|
+
const itemDataset = this.item["~run"]({ value: value2 }, config2);
|
|
177
|
+
if (itemDataset.issues) {
|
|
178
|
+
const pathItem = {
|
|
179
|
+
type: "array",
|
|
180
|
+
origin: "value",
|
|
181
|
+
input,
|
|
182
|
+
key,
|
|
183
|
+
value: value2
|
|
184
|
+
};
|
|
185
|
+
for (const issue of itemDataset.issues) {
|
|
186
|
+
if (issue.path) {
|
|
187
|
+
issue.path.unshift(pathItem);
|
|
188
|
+
} else {
|
|
189
|
+
issue.path = [pathItem];
|
|
190
|
+
}
|
|
191
|
+
dataset.issues?.push(issue);
|
|
192
|
+
}
|
|
193
|
+
if (!dataset.issues) {
|
|
194
|
+
dataset.issues = itemDataset.issues;
|
|
195
|
+
}
|
|
196
|
+
if (config2.abortEarly) {
|
|
197
|
+
dataset.typed = false;
|
|
198
|
+
break;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
if (!itemDataset.typed) {
|
|
202
|
+
dataset.typed = false;
|
|
203
|
+
}
|
|
204
|
+
dataset.value.push(itemDataset.value);
|
|
205
|
+
}
|
|
206
|
+
} else {
|
|
207
|
+
_addIssue(this, "type", dataset, config2);
|
|
208
|
+
}
|
|
209
|
+
return dataset;
|
|
210
|
+
}
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
// @__NO_SIDE_EFFECTS__
|
|
214
|
+
function boolean(message) {
|
|
215
|
+
return {
|
|
216
|
+
kind: "schema",
|
|
217
|
+
type: "boolean",
|
|
218
|
+
reference: boolean,
|
|
219
|
+
expects: "boolean",
|
|
220
|
+
async: false,
|
|
221
|
+
message,
|
|
222
|
+
get "~standard"() {
|
|
223
|
+
return /* @__PURE__ */ _getStandardProps(this);
|
|
224
|
+
},
|
|
225
|
+
"~run"(dataset, config2) {
|
|
226
|
+
if (typeof dataset.value === "boolean") {
|
|
227
|
+
dataset.typed = true;
|
|
228
|
+
} else {
|
|
229
|
+
_addIssue(this, "type", dataset, config2);
|
|
230
|
+
}
|
|
231
|
+
return dataset;
|
|
232
|
+
}
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
// @__NO_SIDE_EFFECTS__
|
|
236
|
+
function object(entries, message) {
|
|
237
|
+
return {
|
|
238
|
+
kind: "schema",
|
|
239
|
+
type: "object",
|
|
240
|
+
reference: object,
|
|
241
|
+
expects: "Object",
|
|
242
|
+
async: false,
|
|
243
|
+
entries,
|
|
244
|
+
message,
|
|
245
|
+
get "~standard"() {
|
|
246
|
+
return /* @__PURE__ */ _getStandardProps(this);
|
|
247
|
+
},
|
|
248
|
+
"~run"(dataset, config2) {
|
|
249
|
+
const input = dataset.value;
|
|
250
|
+
if (input && typeof input === "object") {
|
|
251
|
+
dataset.typed = true;
|
|
252
|
+
dataset.value = {};
|
|
253
|
+
for (const key in this.entries) {
|
|
254
|
+
const valueSchema = this.entries[key];
|
|
255
|
+
if (key in input || (valueSchema.type === "exact_optional" || valueSchema.type === "optional" || valueSchema.type === "nullish") && // @ts-expect-error
|
|
256
|
+
valueSchema.default !== void 0) {
|
|
257
|
+
const value2 = key in input ? (
|
|
258
|
+
// @ts-expect-error
|
|
259
|
+
input[key]
|
|
260
|
+
) : /* @__PURE__ */ getDefault(valueSchema);
|
|
261
|
+
const valueDataset = valueSchema["~run"]({ value: value2 }, config2);
|
|
262
|
+
if (valueDataset.issues) {
|
|
263
|
+
const pathItem = {
|
|
264
|
+
type: "object",
|
|
265
|
+
origin: "value",
|
|
266
|
+
input,
|
|
267
|
+
key,
|
|
268
|
+
value: value2
|
|
269
|
+
};
|
|
270
|
+
for (const issue of valueDataset.issues) {
|
|
271
|
+
if (issue.path) {
|
|
272
|
+
issue.path.unshift(pathItem);
|
|
273
|
+
} else {
|
|
274
|
+
issue.path = [pathItem];
|
|
275
|
+
}
|
|
276
|
+
dataset.issues?.push(issue);
|
|
277
|
+
}
|
|
278
|
+
if (!dataset.issues) {
|
|
279
|
+
dataset.issues = valueDataset.issues;
|
|
280
|
+
}
|
|
281
|
+
if (config2.abortEarly) {
|
|
282
|
+
dataset.typed = false;
|
|
283
|
+
break;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
if (!valueDataset.typed) {
|
|
287
|
+
dataset.typed = false;
|
|
288
|
+
}
|
|
289
|
+
dataset.value[key] = valueDataset.value;
|
|
290
|
+
} else if (valueSchema.fallback !== void 0) {
|
|
291
|
+
dataset.value[key] = /* @__PURE__ */ getFallback(valueSchema);
|
|
292
|
+
} else if (valueSchema.type !== "exact_optional" && valueSchema.type !== "optional" && valueSchema.type !== "nullish") {
|
|
293
|
+
_addIssue(this, "key", dataset, config2, {
|
|
294
|
+
input: void 0,
|
|
295
|
+
expected: `"${key}"`,
|
|
296
|
+
path: [
|
|
297
|
+
{
|
|
298
|
+
type: "object",
|
|
299
|
+
origin: "key",
|
|
300
|
+
input,
|
|
301
|
+
key,
|
|
302
|
+
// @ts-expect-error
|
|
303
|
+
value: input[key]
|
|
304
|
+
}
|
|
305
|
+
]
|
|
306
|
+
});
|
|
307
|
+
if (config2.abortEarly) {
|
|
308
|
+
break;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
} else {
|
|
313
|
+
_addIssue(this, "type", dataset, config2);
|
|
314
|
+
}
|
|
315
|
+
return dataset;
|
|
316
|
+
}
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
// @__NO_SIDE_EFFECTS__
|
|
320
|
+
function optional(wrapped, default_) {
|
|
321
|
+
return {
|
|
322
|
+
kind: "schema",
|
|
323
|
+
type: "optional",
|
|
324
|
+
reference: optional,
|
|
325
|
+
expects: `(${wrapped.expects} | undefined)`,
|
|
326
|
+
async: false,
|
|
327
|
+
wrapped,
|
|
328
|
+
default: default_,
|
|
329
|
+
get "~standard"() {
|
|
330
|
+
return /* @__PURE__ */ _getStandardProps(this);
|
|
331
|
+
},
|
|
332
|
+
"~run"(dataset, config2) {
|
|
333
|
+
if (dataset.value === void 0) {
|
|
334
|
+
if (this.default !== void 0) {
|
|
335
|
+
dataset.value = /* @__PURE__ */ getDefault(this, dataset, config2);
|
|
336
|
+
}
|
|
337
|
+
if (dataset.value === void 0) {
|
|
338
|
+
dataset.typed = true;
|
|
339
|
+
return dataset;
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
return this.wrapped["~run"](dataset, config2);
|
|
343
|
+
}
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
// @__NO_SIDE_EFFECTS__
|
|
347
|
+
function string(message) {
|
|
348
|
+
return {
|
|
349
|
+
kind: "schema",
|
|
350
|
+
type: "string",
|
|
351
|
+
reference: string,
|
|
352
|
+
expects: "string",
|
|
353
|
+
async: false,
|
|
354
|
+
message,
|
|
355
|
+
get "~standard"() {
|
|
356
|
+
return /* @__PURE__ */ _getStandardProps(this);
|
|
357
|
+
},
|
|
358
|
+
"~run"(dataset, config2) {
|
|
359
|
+
if (typeof dataset.value === "string") {
|
|
360
|
+
dataset.typed = true;
|
|
361
|
+
} else {
|
|
362
|
+
_addIssue(this, "type", dataset, config2);
|
|
363
|
+
}
|
|
364
|
+
return dataset;
|
|
365
|
+
}
|
|
366
|
+
};
|
|
367
|
+
}
|
|
368
|
+
function parse(schema, input, config2) {
|
|
369
|
+
const dataset = schema["~run"]({ value: input }, /* @__PURE__ */ getGlobalConfig(config2));
|
|
370
|
+
if (dataset.issues) {
|
|
371
|
+
throw new ValiError(dataset.issues);
|
|
372
|
+
}
|
|
373
|
+
return dataset.value;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
// ../shared/src/schemas.ts
|
|
377
|
+
var CustomComponentPropSchema = object({
|
|
378
|
+
/**
|
|
379
|
+
* The name of the prop in the user-defined component.
|
|
380
|
+
* @example
|
|
381
|
+
* "to"
|
|
382
|
+
*/
|
|
383
|
+
name: string(),
|
|
384
|
+
/**
|
|
385
|
+
* The name of the prop in the built-in component.
|
|
386
|
+
* @example
|
|
387
|
+
* "href"
|
|
388
|
+
*/
|
|
389
|
+
as: optional(string()),
|
|
390
|
+
/**
|
|
391
|
+
* Whether the prop is controlled or not in the user-defined component.
|
|
392
|
+
* @example
|
|
393
|
+
* `true`
|
|
394
|
+
*/
|
|
395
|
+
controlled: optional(boolean()),
|
|
396
|
+
/**
|
|
397
|
+
* The default value of the prop in the user-defined component.
|
|
398
|
+
* @example
|
|
399
|
+
* `"/"`
|
|
400
|
+
*/
|
|
401
|
+
defaultValue: optional(string())
|
|
402
|
+
});
|
|
403
|
+
var CustomComponentSchema = object({
|
|
404
|
+
/**
|
|
405
|
+
* The name of the user-defined component.
|
|
406
|
+
* @example
|
|
407
|
+
* "Link"
|
|
408
|
+
*/
|
|
409
|
+
name: string(),
|
|
410
|
+
/**
|
|
411
|
+
* The name of the built-in component that the user-defined component represents.
|
|
412
|
+
* @example
|
|
413
|
+
* "a"
|
|
414
|
+
*/
|
|
415
|
+
as: optional(string()),
|
|
416
|
+
/**
|
|
417
|
+
* Attributes mapping between the user-defined component and the built-in component.
|
|
418
|
+
* @example
|
|
419
|
+
* `Link` component has a `to` attribute that represents the `href` attribute in the built-in `a` element with a default value of `"/"`.
|
|
420
|
+
*/
|
|
421
|
+
attributes: optional(array(CustomComponentPropSchema)),
|
|
422
|
+
/**
|
|
423
|
+
* The ESQuery selector to select the component precisely.
|
|
424
|
+
* @internal
|
|
425
|
+
* @example
|
|
426
|
+
* `JSXElement:has(JSXAttribute[name.name='component'][value.value='a'])`
|
|
427
|
+
*/
|
|
428
|
+
selector: optional(string())
|
|
429
|
+
});
|
|
430
|
+
var CustomHooksSchema = object({
|
|
431
|
+
use: optional(array(string())),
|
|
432
|
+
useActionState: optional(array(string())),
|
|
433
|
+
useCallback: optional(array(string())),
|
|
434
|
+
useContext: optional(array(string())),
|
|
435
|
+
useDebugValue: optional(array(string())),
|
|
436
|
+
useDeferredValue: optional(array(string())),
|
|
437
|
+
useEffect: optional(array(string())),
|
|
438
|
+
useFormStatus: optional(array(string())),
|
|
439
|
+
useId: optional(array(string())),
|
|
440
|
+
useImperativeHandle: optional(array(string())),
|
|
441
|
+
useInsertionEffect: optional(array(string())),
|
|
442
|
+
useLayoutEffect: optional(array(string())),
|
|
443
|
+
useMemo: optional(array(string())),
|
|
444
|
+
useOptimistic: optional(array(string())),
|
|
445
|
+
useReducer: optional(array(string())),
|
|
446
|
+
useRef: optional(array(string())),
|
|
447
|
+
useState: optional(array(string())),
|
|
448
|
+
useSyncExternalStore: optional(array(string())),
|
|
449
|
+
useTransition: optional(array(string()))
|
|
450
|
+
});
|
|
451
|
+
var ESLintReactSettingsSchema = object({
|
|
452
|
+
/**
|
|
453
|
+
* The source where React is imported from.
|
|
454
|
+
* @description This allows to specify a custom import location for React when not using the official distribution.
|
|
455
|
+
* @default `"react"`
|
|
456
|
+
* @example `"@pika/react"`
|
|
457
|
+
*/
|
|
458
|
+
importSource: optional(string()),
|
|
459
|
+
/**
|
|
460
|
+
* The identifier that’s used for JSX Element creation.
|
|
461
|
+
* @default `"createElement"`
|
|
462
|
+
* @deprecated
|
|
463
|
+
*/
|
|
464
|
+
jsxPragma: optional(string()),
|
|
465
|
+
/**
|
|
466
|
+
* The identifier that’s used for JSX fragment elements.
|
|
467
|
+
* @description This should not be a member expression (i.e. use "Fragment" instead of "React.Fragment").
|
|
468
|
+
* @default `"Fragment"`
|
|
469
|
+
* @deprecated
|
|
470
|
+
*/
|
|
471
|
+
jsxPragmaFrag: optional(string()),
|
|
472
|
+
/**
|
|
473
|
+
* The name of the prop that is used for polymorphic components.
|
|
474
|
+
* @description This is used to determine the type of the component.
|
|
475
|
+
* @example `"as"`
|
|
476
|
+
*/
|
|
477
|
+
polymorphicPropName: optional(string()),
|
|
478
|
+
/**
|
|
479
|
+
* @internal
|
|
480
|
+
*/
|
|
481
|
+
strict: optional(boolean(), false),
|
|
482
|
+
/**
|
|
483
|
+
* Check both the shape and the import to determine if an API is from React.
|
|
484
|
+
* @description This can prevent false positives when using a irrelevant third-party library that has similar APIs to React.
|
|
485
|
+
* @default `true`
|
|
486
|
+
*/
|
|
487
|
+
skipImportCheck: optional(boolean(), true),
|
|
488
|
+
/**
|
|
489
|
+
* React version to use, "detect" means auto detect React version from the project’s dependencies.
|
|
490
|
+
* If `importSource` is specified, an equivalent version of React should be provided here.
|
|
491
|
+
* @example `"18.3.1"`
|
|
492
|
+
* @default `"detect"`
|
|
493
|
+
*/
|
|
494
|
+
version: optional(string()),
|
|
495
|
+
/**
|
|
496
|
+
* A object to define additional hooks that are equivalent to the built-in React Hooks.
|
|
497
|
+
* @description ESLint React will recognize these aliases as equivalent to the built-in hooks in all its rules.
|
|
498
|
+
* @example `{ useEffect: ["useIsomorphicLayoutEffect"] }`
|
|
499
|
+
*/
|
|
500
|
+
additionalHooks: optional(CustomHooksSchema),
|
|
501
|
+
/**
|
|
502
|
+
* An array of user-defined components
|
|
503
|
+
* @description This is used to inform the ESLint React plugins how to treat these components during checks.
|
|
504
|
+
* @example `[{ name: "Link", as: "a", attributes: [{ name: "to", as: "href" }, { name: "rel", defaultValue: "noopener noreferrer" }] }]`
|
|
505
|
+
*/
|
|
506
|
+
additionalComponents: optional(array(CustomComponentSchema))
|
|
507
|
+
});
|
|
508
|
+
var DEFAULT_ESLINT_REACT_SETTINGS = {
|
|
509
|
+
...parse(ESLintReactSettingsSchema, {}),
|
|
510
|
+
importSource: "react"};
|
|
38
511
|
function getRequireExpressionArguments(node) {
|
|
39
512
|
return tsPattern.match(node).with({ type: types.AST_NODE_TYPES.CallExpression, arguments: tsPattern.P.select(), callee: { type: types.AST_NODE_TYPES.Identifier, name: "require" } }, eff.identity).with({ type: types.AST_NODE_TYPES.MemberExpression, object: tsPattern.P.select() }, getRequireExpressionArguments).otherwise(() => eff._);
|
|
40
513
|
}
|
|
@@ -80,37 +553,37 @@ function isFromReactStrict(node, name, importSource, initialScope) {
|
|
|
80
553
|
}
|
|
81
554
|
function isFromReact(name) {
|
|
82
555
|
return (context, node) => {
|
|
83
|
-
const { importSource = defaultImportSource,
|
|
84
|
-
if (
|
|
556
|
+
const { importSource = defaultImportSource, skipImportCheck = true } = shared.unsafeDecodeSettings(context.settings);
|
|
557
|
+
if (skipImportCheck) return isFromReactLoose(node, name);
|
|
85
558
|
return isFromReactStrict(node, name, importSource, context.sourceCode.getScope(node));
|
|
86
559
|
};
|
|
87
560
|
}
|
|
88
561
|
function isFromReactMemberLoose(node, memberName, name) {
|
|
89
|
-
const { object, property } = node;
|
|
562
|
+
const { object: object2, property } = node;
|
|
90
563
|
if (property.type !== types.AST_NODE_TYPES.Identifier || property.name !== name) return false;
|
|
91
|
-
if (
|
|
92
|
-
if (
|
|
93
|
-
return
|
|
564
|
+
if (object2.type === types.AST_NODE_TYPES.Identifier && object2.name === memberName) return true;
|
|
565
|
+
if (object2.type === types.AST_NODE_TYPES.MemberExpression && object2.object.type === types.AST_NODE_TYPES.Identifier && object2.property.type === types.AST_NODE_TYPES.Identifier) {
|
|
566
|
+
return object2.property.name === memberName;
|
|
94
567
|
}
|
|
95
568
|
return false;
|
|
96
569
|
}
|
|
97
570
|
function isFromReactMemberStrict(node, memberName, name, importSource, initialScope) {
|
|
98
|
-
const { object, property } = node;
|
|
571
|
+
const { object: object2, property } = node;
|
|
99
572
|
if (property.type !== types.AST_NODE_TYPES.Identifier || property.name !== name) {
|
|
100
573
|
return false;
|
|
101
574
|
}
|
|
102
|
-
if (
|
|
103
|
-
return isInitializedFromReact(
|
|
575
|
+
if (object2.type === types.AST_NODE_TYPES.Identifier && object2.name === memberName) {
|
|
576
|
+
return isInitializedFromReact(object2.name, importSource, initialScope);
|
|
104
577
|
}
|
|
105
|
-
if (
|
|
106
|
-
return
|
|
578
|
+
if (object2.type === types.AST_NODE_TYPES.MemberExpression && object2.object.type === types.AST_NODE_TYPES.Identifier && isInitializedFromReact(object2.object.name, importSource, initialScope) && object2.property.type === types.AST_NODE_TYPES.Identifier) {
|
|
579
|
+
return object2.property.name === memberName;
|
|
107
580
|
}
|
|
108
581
|
return false;
|
|
109
582
|
}
|
|
110
583
|
function isFromReactMember(memberName, name) {
|
|
111
584
|
return (context, node) => {
|
|
112
|
-
const { importSource = defaultImportSource,
|
|
113
|
-
if (
|
|
585
|
+
const { importSource = defaultImportSource, skipImportCheck = true } = shared.unsafeDecodeSettings(context.settings);
|
|
586
|
+
if (skipImportCheck) return isFromReactMemberLoose(node, memberName, name);
|
|
114
587
|
return isFromReactMemberStrict(node, memberName, name, importSource, context.sourceCode.getScope(node));
|
|
115
588
|
};
|
|
116
589
|
}
|
|
@@ -199,15 +672,17 @@ function isReactHookCall(node) {
|
|
|
199
672
|
}
|
|
200
673
|
function isReactHookCallWithName(context, node) {
|
|
201
674
|
if (node == null) return eff.constFalse;
|
|
202
|
-
const
|
|
203
|
-
|
|
675
|
+
const {
|
|
676
|
+
importSource = DEFAULT_ESLINT_REACT_SETTINGS.importSource,
|
|
677
|
+
skipImportCheck = true
|
|
678
|
+
} = shared.unsafeDecodeSettings(context.settings);
|
|
204
679
|
const initialScope = context.sourceCode.getScope(node);
|
|
205
680
|
return (name) => {
|
|
206
681
|
switch (true) {
|
|
207
682
|
case (node.callee.type === types.AST_NODE_TYPES.Identifier && node.callee.name === name):
|
|
208
|
-
return
|
|
683
|
+
return skipImportCheck || isInitializedFromReact(name, importSource, initialScope);
|
|
209
684
|
case (node.callee.type === types.AST_NODE_TYPES.MemberExpression && node.callee.property.type === types.AST_NODE_TYPES.Identifier && node.callee.property.name === name && "name" in node.callee.object):
|
|
210
|
-
return
|
|
685
|
+
return skipImportCheck || isInitializedFromReact(node.callee.object.name, importSource, initialScope);
|
|
211
686
|
default:
|
|
212
687
|
return false;
|
|
213
688
|
}
|
|
@@ -227,15 +702,17 @@ function isReactHookCallWithNameLoose(node) {
|
|
|
227
702
|
};
|
|
228
703
|
}
|
|
229
704
|
function isReactHookCallWithNameAlias(context, name, alias) {
|
|
230
|
-
const
|
|
231
|
-
|
|
705
|
+
const {
|
|
706
|
+
importSource = DEFAULT_ESLINT_REACT_SETTINGS.importSource,
|
|
707
|
+
skipImportCheck = true
|
|
708
|
+
} = shared.unsafeDecodeSettings(context.settings);
|
|
232
709
|
return (node) => {
|
|
233
710
|
const initialScope = context.sourceCode.getScope(node);
|
|
234
711
|
switch (true) {
|
|
235
712
|
case (node.callee.type === types.AST_NODE_TYPES.Identifier && node.callee.name === name):
|
|
236
|
-
return
|
|
713
|
+
return skipImportCheck || isInitializedFromReact(name, importSource, initialScope);
|
|
237
714
|
case (node.callee.type === types.AST_NODE_TYPES.MemberExpression && node.callee.property.type === types.AST_NODE_TYPES.Identifier && node.callee.property.name === name && "name" in node.callee.object):
|
|
238
|
-
return
|
|
715
|
+
return skipImportCheck || isInitializedFromReact(node.callee.object.name, importSource, initialScope);
|
|
239
716
|
default:
|
|
240
717
|
return alias.some(isReactHookCallWithNameLoose(node));
|
|
241
718
|
}
|
package/dist/index.mjs
CHANGED
|
@@ -1,13 +1,486 @@
|
|
|
1
1
|
import * as AST8 from '@eslint-react/ast';
|
|
2
2
|
import { flip, dual, constFalse, identity, _ } from '@eslint-react/eff';
|
|
3
3
|
import * as JSX3 from '@eslint-react/jsx';
|
|
4
|
-
import { DEFAULT_ESLINT_REACT_SETTINGS, unsafeDecodeSettings, getId } from '@eslint-react/shared';
|
|
4
|
+
import { DEFAULT_ESLINT_REACT_SETTINGS as DEFAULT_ESLINT_REACT_SETTINGS$1, unsafeDecodeSettings, getId } from '@eslint-react/shared';
|
|
5
5
|
import { AST_NODE_TYPES } from '@typescript-eslint/types';
|
|
6
6
|
import * as VAR from '@eslint-react/var';
|
|
7
7
|
import { match, P } from 'ts-pattern';
|
|
8
8
|
import birecord from 'birecord';
|
|
9
9
|
|
|
10
10
|
// src/component/component-collector.ts
|
|
11
|
+
|
|
12
|
+
// ../../node_modules/.pnpm/valibot@1.0.0-rc.2_typescript@5.8.1-rc/node_modules/valibot/dist/index.js
|
|
13
|
+
var store;
|
|
14
|
+
// @__NO_SIDE_EFFECTS__
|
|
15
|
+
function getGlobalConfig(config2) {
|
|
16
|
+
return {
|
|
17
|
+
lang: config2?.lang ?? store?.lang,
|
|
18
|
+
message: config2?.message,
|
|
19
|
+
abortEarly: config2?.abortEarly ?? store?.abortEarly,
|
|
20
|
+
abortPipeEarly: config2?.abortPipeEarly ?? store?.abortPipeEarly
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
var store2;
|
|
24
|
+
// @__NO_SIDE_EFFECTS__
|
|
25
|
+
function getGlobalMessage(lang) {
|
|
26
|
+
return store2?.get(lang);
|
|
27
|
+
}
|
|
28
|
+
var store3;
|
|
29
|
+
// @__NO_SIDE_EFFECTS__
|
|
30
|
+
function getSchemaMessage(lang) {
|
|
31
|
+
return store3?.get(lang);
|
|
32
|
+
}
|
|
33
|
+
var store4;
|
|
34
|
+
// @__NO_SIDE_EFFECTS__
|
|
35
|
+
function getSpecificMessage(reference, lang) {
|
|
36
|
+
return store4?.get(reference)?.get(lang);
|
|
37
|
+
}
|
|
38
|
+
// @__NO_SIDE_EFFECTS__
|
|
39
|
+
function _stringify(input) {
|
|
40
|
+
const type = typeof input;
|
|
41
|
+
if (type === "string") {
|
|
42
|
+
return `"${input}"`;
|
|
43
|
+
}
|
|
44
|
+
if (type === "number" || type === "bigint" || type === "boolean") {
|
|
45
|
+
return `${input}`;
|
|
46
|
+
}
|
|
47
|
+
if (type === "object" || type === "function") {
|
|
48
|
+
return (input && Object.getPrototypeOf(input)?.constructor?.name) ?? "null";
|
|
49
|
+
}
|
|
50
|
+
return type;
|
|
51
|
+
}
|
|
52
|
+
function _addIssue(context, label, dataset, config2, other) {
|
|
53
|
+
const input = other && "input" in other ? other.input : dataset.value;
|
|
54
|
+
const expected = other?.expected ?? context.expects ?? null;
|
|
55
|
+
const received = other?.received ?? /* @__PURE__ */ _stringify(input);
|
|
56
|
+
const issue = {
|
|
57
|
+
kind: context.kind,
|
|
58
|
+
type: context.type,
|
|
59
|
+
input,
|
|
60
|
+
expected,
|
|
61
|
+
received,
|
|
62
|
+
message: `Invalid ${label}: ${expected ? `Expected ${expected} but r` : "R"}eceived ${received}`,
|
|
63
|
+
requirement: context.requirement,
|
|
64
|
+
path: other?.path,
|
|
65
|
+
issues: other?.issues,
|
|
66
|
+
lang: config2.lang,
|
|
67
|
+
abortEarly: config2.abortEarly,
|
|
68
|
+
abortPipeEarly: config2.abortPipeEarly
|
|
69
|
+
};
|
|
70
|
+
const isSchema = context.kind === "schema";
|
|
71
|
+
const message = other?.message ?? context.message ?? /* @__PURE__ */ getSpecificMessage(context.reference, issue.lang) ?? (isSchema ? /* @__PURE__ */ getSchemaMessage(issue.lang) : null) ?? config2.message ?? /* @__PURE__ */ getGlobalMessage(issue.lang);
|
|
72
|
+
if (message) {
|
|
73
|
+
issue.message = typeof message === "function" ? (
|
|
74
|
+
// @ts-expect-error
|
|
75
|
+
message(issue)
|
|
76
|
+
) : message;
|
|
77
|
+
}
|
|
78
|
+
if (isSchema) {
|
|
79
|
+
dataset.typed = false;
|
|
80
|
+
}
|
|
81
|
+
if (dataset.issues) {
|
|
82
|
+
dataset.issues.push(issue);
|
|
83
|
+
} else {
|
|
84
|
+
dataset.issues = [issue];
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
// @__NO_SIDE_EFFECTS__
|
|
88
|
+
function _getStandardProps(context) {
|
|
89
|
+
return {
|
|
90
|
+
version: 1,
|
|
91
|
+
vendor: "valibot",
|
|
92
|
+
validate(value2) {
|
|
93
|
+
return context["~run"]({ value: value2 }, /* @__PURE__ */ getGlobalConfig());
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
var ValiError = class extends Error {
|
|
98
|
+
/**
|
|
99
|
+
* Creates a Valibot error with useful information.
|
|
100
|
+
*
|
|
101
|
+
* @param issues The error issues.
|
|
102
|
+
*/
|
|
103
|
+
constructor(issues) {
|
|
104
|
+
super(issues[0].message);
|
|
105
|
+
this.name = "ValiError";
|
|
106
|
+
this.issues = issues;
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
// @__NO_SIDE_EFFECTS__
|
|
110
|
+
function getFallback(schema, dataset, config2) {
|
|
111
|
+
return typeof schema.fallback === "function" ? (
|
|
112
|
+
// @ts-expect-error
|
|
113
|
+
schema.fallback(dataset, config2)
|
|
114
|
+
) : (
|
|
115
|
+
// @ts-expect-error
|
|
116
|
+
schema.fallback
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
// @__NO_SIDE_EFFECTS__
|
|
120
|
+
function getDefault(schema, dataset, config2) {
|
|
121
|
+
return typeof schema.default === "function" ? (
|
|
122
|
+
// @ts-expect-error
|
|
123
|
+
schema.default(dataset, config2)
|
|
124
|
+
) : (
|
|
125
|
+
// @ts-expect-error
|
|
126
|
+
schema.default
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
// @__NO_SIDE_EFFECTS__
|
|
130
|
+
function array(item, message) {
|
|
131
|
+
return {
|
|
132
|
+
kind: "schema",
|
|
133
|
+
type: "array",
|
|
134
|
+
reference: array,
|
|
135
|
+
expects: "Array",
|
|
136
|
+
async: false,
|
|
137
|
+
item,
|
|
138
|
+
message,
|
|
139
|
+
get "~standard"() {
|
|
140
|
+
return /* @__PURE__ */ _getStandardProps(this);
|
|
141
|
+
},
|
|
142
|
+
"~run"(dataset, config2) {
|
|
143
|
+
const input = dataset.value;
|
|
144
|
+
if (Array.isArray(input)) {
|
|
145
|
+
dataset.typed = true;
|
|
146
|
+
dataset.value = [];
|
|
147
|
+
for (let key = 0; key < input.length; key++) {
|
|
148
|
+
const value2 = input[key];
|
|
149
|
+
const itemDataset = this.item["~run"]({ value: value2 }, config2);
|
|
150
|
+
if (itemDataset.issues) {
|
|
151
|
+
const pathItem = {
|
|
152
|
+
type: "array",
|
|
153
|
+
origin: "value",
|
|
154
|
+
input,
|
|
155
|
+
key,
|
|
156
|
+
value: value2
|
|
157
|
+
};
|
|
158
|
+
for (const issue of itemDataset.issues) {
|
|
159
|
+
if (issue.path) {
|
|
160
|
+
issue.path.unshift(pathItem);
|
|
161
|
+
} else {
|
|
162
|
+
issue.path = [pathItem];
|
|
163
|
+
}
|
|
164
|
+
dataset.issues?.push(issue);
|
|
165
|
+
}
|
|
166
|
+
if (!dataset.issues) {
|
|
167
|
+
dataset.issues = itemDataset.issues;
|
|
168
|
+
}
|
|
169
|
+
if (config2.abortEarly) {
|
|
170
|
+
dataset.typed = false;
|
|
171
|
+
break;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
if (!itemDataset.typed) {
|
|
175
|
+
dataset.typed = false;
|
|
176
|
+
}
|
|
177
|
+
dataset.value.push(itemDataset.value);
|
|
178
|
+
}
|
|
179
|
+
} else {
|
|
180
|
+
_addIssue(this, "type", dataset, config2);
|
|
181
|
+
}
|
|
182
|
+
return dataset;
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
// @__NO_SIDE_EFFECTS__
|
|
187
|
+
function boolean(message) {
|
|
188
|
+
return {
|
|
189
|
+
kind: "schema",
|
|
190
|
+
type: "boolean",
|
|
191
|
+
reference: boolean,
|
|
192
|
+
expects: "boolean",
|
|
193
|
+
async: false,
|
|
194
|
+
message,
|
|
195
|
+
get "~standard"() {
|
|
196
|
+
return /* @__PURE__ */ _getStandardProps(this);
|
|
197
|
+
},
|
|
198
|
+
"~run"(dataset, config2) {
|
|
199
|
+
if (typeof dataset.value === "boolean") {
|
|
200
|
+
dataset.typed = true;
|
|
201
|
+
} else {
|
|
202
|
+
_addIssue(this, "type", dataset, config2);
|
|
203
|
+
}
|
|
204
|
+
return dataset;
|
|
205
|
+
}
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
// @__NO_SIDE_EFFECTS__
|
|
209
|
+
function object(entries, message) {
|
|
210
|
+
return {
|
|
211
|
+
kind: "schema",
|
|
212
|
+
type: "object",
|
|
213
|
+
reference: object,
|
|
214
|
+
expects: "Object",
|
|
215
|
+
async: false,
|
|
216
|
+
entries,
|
|
217
|
+
message,
|
|
218
|
+
get "~standard"() {
|
|
219
|
+
return /* @__PURE__ */ _getStandardProps(this);
|
|
220
|
+
},
|
|
221
|
+
"~run"(dataset, config2) {
|
|
222
|
+
const input = dataset.value;
|
|
223
|
+
if (input && typeof input === "object") {
|
|
224
|
+
dataset.typed = true;
|
|
225
|
+
dataset.value = {};
|
|
226
|
+
for (const key in this.entries) {
|
|
227
|
+
const valueSchema = this.entries[key];
|
|
228
|
+
if (key in input || (valueSchema.type === "exact_optional" || valueSchema.type === "optional" || valueSchema.type === "nullish") && // @ts-expect-error
|
|
229
|
+
valueSchema.default !== void 0) {
|
|
230
|
+
const value2 = key in input ? (
|
|
231
|
+
// @ts-expect-error
|
|
232
|
+
input[key]
|
|
233
|
+
) : /* @__PURE__ */ getDefault(valueSchema);
|
|
234
|
+
const valueDataset = valueSchema["~run"]({ value: value2 }, config2);
|
|
235
|
+
if (valueDataset.issues) {
|
|
236
|
+
const pathItem = {
|
|
237
|
+
type: "object",
|
|
238
|
+
origin: "value",
|
|
239
|
+
input,
|
|
240
|
+
key,
|
|
241
|
+
value: value2
|
|
242
|
+
};
|
|
243
|
+
for (const issue of valueDataset.issues) {
|
|
244
|
+
if (issue.path) {
|
|
245
|
+
issue.path.unshift(pathItem);
|
|
246
|
+
} else {
|
|
247
|
+
issue.path = [pathItem];
|
|
248
|
+
}
|
|
249
|
+
dataset.issues?.push(issue);
|
|
250
|
+
}
|
|
251
|
+
if (!dataset.issues) {
|
|
252
|
+
dataset.issues = valueDataset.issues;
|
|
253
|
+
}
|
|
254
|
+
if (config2.abortEarly) {
|
|
255
|
+
dataset.typed = false;
|
|
256
|
+
break;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
if (!valueDataset.typed) {
|
|
260
|
+
dataset.typed = false;
|
|
261
|
+
}
|
|
262
|
+
dataset.value[key] = valueDataset.value;
|
|
263
|
+
} else if (valueSchema.fallback !== void 0) {
|
|
264
|
+
dataset.value[key] = /* @__PURE__ */ getFallback(valueSchema);
|
|
265
|
+
} else if (valueSchema.type !== "exact_optional" && valueSchema.type !== "optional" && valueSchema.type !== "nullish") {
|
|
266
|
+
_addIssue(this, "key", dataset, config2, {
|
|
267
|
+
input: void 0,
|
|
268
|
+
expected: `"${key}"`,
|
|
269
|
+
path: [
|
|
270
|
+
{
|
|
271
|
+
type: "object",
|
|
272
|
+
origin: "key",
|
|
273
|
+
input,
|
|
274
|
+
key,
|
|
275
|
+
// @ts-expect-error
|
|
276
|
+
value: input[key]
|
|
277
|
+
}
|
|
278
|
+
]
|
|
279
|
+
});
|
|
280
|
+
if (config2.abortEarly) {
|
|
281
|
+
break;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
} else {
|
|
286
|
+
_addIssue(this, "type", dataset, config2);
|
|
287
|
+
}
|
|
288
|
+
return dataset;
|
|
289
|
+
}
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
// @__NO_SIDE_EFFECTS__
|
|
293
|
+
function optional(wrapped, default_) {
|
|
294
|
+
return {
|
|
295
|
+
kind: "schema",
|
|
296
|
+
type: "optional",
|
|
297
|
+
reference: optional,
|
|
298
|
+
expects: `(${wrapped.expects} | undefined)`,
|
|
299
|
+
async: false,
|
|
300
|
+
wrapped,
|
|
301
|
+
default: default_,
|
|
302
|
+
get "~standard"() {
|
|
303
|
+
return /* @__PURE__ */ _getStandardProps(this);
|
|
304
|
+
},
|
|
305
|
+
"~run"(dataset, config2) {
|
|
306
|
+
if (dataset.value === void 0) {
|
|
307
|
+
if (this.default !== void 0) {
|
|
308
|
+
dataset.value = /* @__PURE__ */ getDefault(this, dataset, config2);
|
|
309
|
+
}
|
|
310
|
+
if (dataset.value === void 0) {
|
|
311
|
+
dataset.typed = true;
|
|
312
|
+
return dataset;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
return this.wrapped["~run"](dataset, config2);
|
|
316
|
+
}
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
// @__NO_SIDE_EFFECTS__
|
|
320
|
+
function string(message) {
|
|
321
|
+
return {
|
|
322
|
+
kind: "schema",
|
|
323
|
+
type: "string",
|
|
324
|
+
reference: string,
|
|
325
|
+
expects: "string",
|
|
326
|
+
async: false,
|
|
327
|
+
message,
|
|
328
|
+
get "~standard"() {
|
|
329
|
+
return /* @__PURE__ */ _getStandardProps(this);
|
|
330
|
+
},
|
|
331
|
+
"~run"(dataset, config2) {
|
|
332
|
+
if (typeof dataset.value === "string") {
|
|
333
|
+
dataset.typed = true;
|
|
334
|
+
} else {
|
|
335
|
+
_addIssue(this, "type", dataset, config2);
|
|
336
|
+
}
|
|
337
|
+
return dataset;
|
|
338
|
+
}
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
function parse(schema, input, config2) {
|
|
342
|
+
const dataset = schema["~run"]({ value: input }, /* @__PURE__ */ getGlobalConfig(config2));
|
|
343
|
+
if (dataset.issues) {
|
|
344
|
+
throw new ValiError(dataset.issues);
|
|
345
|
+
}
|
|
346
|
+
return dataset.value;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// ../shared/src/schemas.ts
|
|
350
|
+
var CustomComponentPropSchema = object({
|
|
351
|
+
/**
|
|
352
|
+
* The name of the prop in the user-defined component.
|
|
353
|
+
* @example
|
|
354
|
+
* "to"
|
|
355
|
+
*/
|
|
356
|
+
name: string(),
|
|
357
|
+
/**
|
|
358
|
+
* The name of the prop in the built-in component.
|
|
359
|
+
* @example
|
|
360
|
+
* "href"
|
|
361
|
+
*/
|
|
362
|
+
as: optional(string()),
|
|
363
|
+
/**
|
|
364
|
+
* Whether the prop is controlled or not in the user-defined component.
|
|
365
|
+
* @example
|
|
366
|
+
* `true`
|
|
367
|
+
*/
|
|
368
|
+
controlled: optional(boolean()),
|
|
369
|
+
/**
|
|
370
|
+
* The default value of the prop in the user-defined component.
|
|
371
|
+
* @example
|
|
372
|
+
* `"/"`
|
|
373
|
+
*/
|
|
374
|
+
defaultValue: optional(string())
|
|
375
|
+
});
|
|
376
|
+
var CustomComponentSchema = object({
|
|
377
|
+
/**
|
|
378
|
+
* The name of the user-defined component.
|
|
379
|
+
* @example
|
|
380
|
+
* "Link"
|
|
381
|
+
*/
|
|
382
|
+
name: string(),
|
|
383
|
+
/**
|
|
384
|
+
* The name of the built-in component that the user-defined component represents.
|
|
385
|
+
* @example
|
|
386
|
+
* "a"
|
|
387
|
+
*/
|
|
388
|
+
as: optional(string()),
|
|
389
|
+
/**
|
|
390
|
+
* Attributes mapping between the user-defined component and the built-in component.
|
|
391
|
+
* @example
|
|
392
|
+
* `Link` component has a `to` attribute that represents the `href` attribute in the built-in `a` element with a default value of `"/"`.
|
|
393
|
+
*/
|
|
394
|
+
attributes: optional(array(CustomComponentPropSchema)),
|
|
395
|
+
/**
|
|
396
|
+
* The ESQuery selector to select the component precisely.
|
|
397
|
+
* @internal
|
|
398
|
+
* @example
|
|
399
|
+
* `JSXElement:has(JSXAttribute[name.name='component'][value.value='a'])`
|
|
400
|
+
*/
|
|
401
|
+
selector: optional(string())
|
|
402
|
+
});
|
|
403
|
+
var CustomHooksSchema = object({
|
|
404
|
+
use: optional(array(string())),
|
|
405
|
+
useActionState: optional(array(string())),
|
|
406
|
+
useCallback: optional(array(string())),
|
|
407
|
+
useContext: optional(array(string())),
|
|
408
|
+
useDebugValue: optional(array(string())),
|
|
409
|
+
useDeferredValue: optional(array(string())),
|
|
410
|
+
useEffect: optional(array(string())),
|
|
411
|
+
useFormStatus: optional(array(string())),
|
|
412
|
+
useId: optional(array(string())),
|
|
413
|
+
useImperativeHandle: optional(array(string())),
|
|
414
|
+
useInsertionEffect: optional(array(string())),
|
|
415
|
+
useLayoutEffect: optional(array(string())),
|
|
416
|
+
useMemo: optional(array(string())),
|
|
417
|
+
useOptimistic: optional(array(string())),
|
|
418
|
+
useReducer: optional(array(string())),
|
|
419
|
+
useRef: optional(array(string())),
|
|
420
|
+
useState: optional(array(string())),
|
|
421
|
+
useSyncExternalStore: optional(array(string())),
|
|
422
|
+
useTransition: optional(array(string()))
|
|
423
|
+
});
|
|
424
|
+
var ESLintReactSettingsSchema = object({
|
|
425
|
+
/**
|
|
426
|
+
* The source where React is imported from.
|
|
427
|
+
* @description This allows to specify a custom import location for React when not using the official distribution.
|
|
428
|
+
* @default `"react"`
|
|
429
|
+
* @example `"@pika/react"`
|
|
430
|
+
*/
|
|
431
|
+
importSource: optional(string()),
|
|
432
|
+
/**
|
|
433
|
+
* The identifier that’s used for JSX Element creation.
|
|
434
|
+
* @default `"createElement"`
|
|
435
|
+
* @deprecated
|
|
436
|
+
*/
|
|
437
|
+
jsxPragma: optional(string()),
|
|
438
|
+
/**
|
|
439
|
+
* The identifier that’s used for JSX fragment elements.
|
|
440
|
+
* @description This should not be a member expression (i.e. use "Fragment" instead of "React.Fragment").
|
|
441
|
+
* @default `"Fragment"`
|
|
442
|
+
* @deprecated
|
|
443
|
+
*/
|
|
444
|
+
jsxPragmaFrag: optional(string()),
|
|
445
|
+
/**
|
|
446
|
+
* The name of the prop that is used for polymorphic components.
|
|
447
|
+
* @description This is used to determine the type of the component.
|
|
448
|
+
* @example `"as"`
|
|
449
|
+
*/
|
|
450
|
+
polymorphicPropName: optional(string()),
|
|
451
|
+
/**
|
|
452
|
+
* @internal
|
|
453
|
+
*/
|
|
454
|
+
strict: optional(boolean(), false),
|
|
455
|
+
/**
|
|
456
|
+
* Check both the shape and the import to determine if an API is from React.
|
|
457
|
+
* @description This can prevent false positives when using a irrelevant third-party library that has similar APIs to React.
|
|
458
|
+
* @default `true`
|
|
459
|
+
*/
|
|
460
|
+
skipImportCheck: optional(boolean(), true),
|
|
461
|
+
/**
|
|
462
|
+
* React version to use, "detect" means auto detect React version from the project’s dependencies.
|
|
463
|
+
* If `importSource` is specified, an equivalent version of React should be provided here.
|
|
464
|
+
* @example `"18.3.1"`
|
|
465
|
+
* @default `"detect"`
|
|
466
|
+
*/
|
|
467
|
+
version: optional(string()),
|
|
468
|
+
/**
|
|
469
|
+
* A object to define additional hooks that are equivalent to the built-in React Hooks.
|
|
470
|
+
* @description ESLint React will recognize these aliases as equivalent to the built-in hooks in all its rules.
|
|
471
|
+
* @example `{ useEffect: ["useIsomorphicLayoutEffect"] }`
|
|
472
|
+
*/
|
|
473
|
+
additionalHooks: optional(CustomHooksSchema),
|
|
474
|
+
/**
|
|
475
|
+
* An array of user-defined components
|
|
476
|
+
* @description This is used to inform the ESLint React plugins how to treat these components during checks.
|
|
477
|
+
* @example `[{ name: "Link", as: "a", attributes: [{ name: "to", as: "href" }, { name: "rel", defaultValue: "noopener noreferrer" }] }]`
|
|
478
|
+
*/
|
|
479
|
+
additionalComponents: optional(array(CustomComponentSchema))
|
|
480
|
+
});
|
|
481
|
+
var DEFAULT_ESLINT_REACT_SETTINGS = {
|
|
482
|
+
...parse(ESLintReactSettingsSchema, {}),
|
|
483
|
+
importSource: "react"};
|
|
11
484
|
function getRequireExpressionArguments(node) {
|
|
12
485
|
return match(node).with({ type: AST_NODE_TYPES.CallExpression, arguments: P.select(), callee: { type: AST_NODE_TYPES.Identifier, name: "require" } }, identity).with({ type: AST_NODE_TYPES.MemberExpression, object: P.select() }, getRequireExpressionArguments).otherwise(() => _);
|
|
13
486
|
}
|
|
@@ -35,7 +508,7 @@ function isInitializedFromReact(name, importSource, initialScope) {
|
|
|
35
508
|
}
|
|
36
509
|
|
|
37
510
|
// src/utils/is-from-react.ts
|
|
38
|
-
var defaultImportSource = DEFAULT_ESLINT_REACT_SETTINGS.importSource;
|
|
511
|
+
var defaultImportSource = DEFAULT_ESLINT_REACT_SETTINGS$1.importSource;
|
|
39
512
|
function isFromReactLoose(node, name) {
|
|
40
513
|
if (node.type === AST_NODE_TYPES.MemberExpression) {
|
|
41
514
|
return node.object.type === AST_NODE_TYPES.Identifier && node.property.type === AST_NODE_TYPES.Identifier && node.property.name === name;
|
|
@@ -53,37 +526,37 @@ function isFromReactStrict(node, name, importSource, initialScope) {
|
|
|
53
526
|
}
|
|
54
527
|
function isFromReact(name) {
|
|
55
528
|
return (context, node) => {
|
|
56
|
-
const { importSource = defaultImportSource,
|
|
57
|
-
if (
|
|
529
|
+
const { importSource = defaultImportSource, skipImportCheck = true } = unsafeDecodeSettings(context.settings);
|
|
530
|
+
if (skipImportCheck) return isFromReactLoose(node, name);
|
|
58
531
|
return isFromReactStrict(node, name, importSource, context.sourceCode.getScope(node));
|
|
59
532
|
};
|
|
60
533
|
}
|
|
61
534
|
function isFromReactMemberLoose(node, memberName, name) {
|
|
62
|
-
const { object, property } = node;
|
|
535
|
+
const { object: object2, property } = node;
|
|
63
536
|
if (property.type !== AST_NODE_TYPES.Identifier || property.name !== name) return false;
|
|
64
|
-
if (
|
|
65
|
-
if (
|
|
66
|
-
return
|
|
537
|
+
if (object2.type === AST_NODE_TYPES.Identifier && object2.name === memberName) return true;
|
|
538
|
+
if (object2.type === AST_NODE_TYPES.MemberExpression && object2.object.type === AST_NODE_TYPES.Identifier && object2.property.type === AST_NODE_TYPES.Identifier) {
|
|
539
|
+
return object2.property.name === memberName;
|
|
67
540
|
}
|
|
68
541
|
return false;
|
|
69
542
|
}
|
|
70
543
|
function isFromReactMemberStrict(node, memberName, name, importSource, initialScope) {
|
|
71
|
-
const { object, property } = node;
|
|
544
|
+
const { object: object2, property } = node;
|
|
72
545
|
if (property.type !== AST_NODE_TYPES.Identifier || property.name !== name) {
|
|
73
546
|
return false;
|
|
74
547
|
}
|
|
75
|
-
if (
|
|
76
|
-
return isInitializedFromReact(
|
|
548
|
+
if (object2.type === AST_NODE_TYPES.Identifier && object2.name === memberName) {
|
|
549
|
+
return isInitializedFromReact(object2.name, importSource, initialScope);
|
|
77
550
|
}
|
|
78
|
-
if (
|
|
79
|
-
return
|
|
551
|
+
if (object2.type === AST_NODE_TYPES.MemberExpression && object2.object.type === AST_NODE_TYPES.Identifier && isInitializedFromReact(object2.object.name, importSource, initialScope) && object2.property.type === AST_NODE_TYPES.Identifier) {
|
|
552
|
+
return object2.property.name === memberName;
|
|
80
553
|
}
|
|
81
554
|
return false;
|
|
82
555
|
}
|
|
83
556
|
function isFromReactMember(memberName, name) {
|
|
84
557
|
return (context, node) => {
|
|
85
|
-
const { importSource = defaultImportSource,
|
|
86
|
-
if (
|
|
558
|
+
const { importSource = defaultImportSource, skipImportCheck = true } = unsafeDecodeSettings(context.settings);
|
|
559
|
+
if (skipImportCheck) return isFromReactMemberLoose(node, memberName, name);
|
|
87
560
|
return isFromReactMemberStrict(node, memberName, name, importSource, context.sourceCode.getScope(node));
|
|
88
561
|
};
|
|
89
562
|
}
|
|
@@ -172,15 +645,17 @@ function isReactHookCall(node) {
|
|
|
172
645
|
}
|
|
173
646
|
function isReactHookCallWithName(context, node) {
|
|
174
647
|
if (node == null) return constFalse;
|
|
175
|
-
const
|
|
176
|
-
|
|
648
|
+
const {
|
|
649
|
+
importSource = DEFAULT_ESLINT_REACT_SETTINGS.importSource,
|
|
650
|
+
skipImportCheck = true
|
|
651
|
+
} = unsafeDecodeSettings(context.settings);
|
|
177
652
|
const initialScope = context.sourceCode.getScope(node);
|
|
178
653
|
return (name) => {
|
|
179
654
|
switch (true) {
|
|
180
655
|
case (node.callee.type === AST_NODE_TYPES.Identifier && node.callee.name === name):
|
|
181
|
-
return
|
|
656
|
+
return skipImportCheck || isInitializedFromReact(name, importSource, initialScope);
|
|
182
657
|
case (node.callee.type === AST_NODE_TYPES.MemberExpression && node.callee.property.type === AST_NODE_TYPES.Identifier && node.callee.property.name === name && "name" in node.callee.object):
|
|
183
|
-
return
|
|
658
|
+
return skipImportCheck || isInitializedFromReact(node.callee.object.name, importSource, initialScope);
|
|
184
659
|
default:
|
|
185
660
|
return false;
|
|
186
661
|
}
|
|
@@ -200,15 +675,17 @@ function isReactHookCallWithNameLoose(node) {
|
|
|
200
675
|
};
|
|
201
676
|
}
|
|
202
677
|
function isReactHookCallWithNameAlias(context, name, alias) {
|
|
203
|
-
const
|
|
204
|
-
|
|
678
|
+
const {
|
|
679
|
+
importSource = DEFAULT_ESLINT_REACT_SETTINGS.importSource,
|
|
680
|
+
skipImportCheck = true
|
|
681
|
+
} = unsafeDecodeSettings(context.settings);
|
|
205
682
|
return (node) => {
|
|
206
683
|
const initialScope = context.sourceCode.getScope(node);
|
|
207
684
|
switch (true) {
|
|
208
685
|
case (node.callee.type === AST_NODE_TYPES.Identifier && node.callee.name === name):
|
|
209
|
-
return
|
|
686
|
+
return skipImportCheck || isInitializedFromReact(name, importSource, initialScope);
|
|
210
687
|
case (node.callee.type === AST_NODE_TYPES.MemberExpression && node.callee.property.type === AST_NODE_TYPES.Identifier && node.callee.property.name === name && "name" in node.callee.object):
|
|
211
|
-
return
|
|
688
|
+
return skipImportCheck || isInitializedFromReact(node.callee.object.name, importSource, initialScope);
|
|
212
689
|
default:
|
|
213
690
|
return alias.some(isReactHookCallWithNameLoose(node));
|
|
214
691
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@eslint-react/core",
|
|
3
|
-
"version": "1.28.0-next.
|
|
3
|
+
"version": "1.28.0-next.6",
|
|
4
4
|
"description": "ESLint React's ESLint utility module for static analysis of React core APIs and Patterns.",
|
|
5
5
|
"homepage": "https://github.com/Rel1cx/eslint-react",
|
|
6
6
|
"bugs": {
|
|
@@ -41,11 +41,11 @@
|
|
|
41
41
|
"@typescript-eslint/utils": "^8.25.0",
|
|
42
42
|
"birecord": "^0.1.1",
|
|
43
43
|
"ts-pattern": "^5.6.2",
|
|
44
|
-
"@eslint-react/ast": "1.28.0-next.
|
|
45
|
-
"@eslint-react/eff": "1.28.0-next.
|
|
46
|
-
"@eslint-react/jsx": "1.28.0-next.
|
|
47
|
-
"@eslint-react/shared": "1.28.0-next.
|
|
48
|
-
"@eslint-react/var": "1.28.0-next.
|
|
44
|
+
"@eslint-react/ast": "1.28.0-next.6",
|
|
45
|
+
"@eslint-react/eff": "1.28.0-next.6",
|
|
46
|
+
"@eslint-react/jsx": "1.28.0-next.6",
|
|
47
|
+
"@eslint-react/shared": "1.28.0-next.6",
|
|
48
|
+
"@eslint-react/var": "1.28.0-next.6"
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
51
51
|
"tsup": "^8.4.0",
|