eslint-plugin-flawless 0.1.10 → 0.1.12

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.
@@ -0,0 +1,4226 @@
1
+ import { ASTUtils, AST_NODE_TYPES, ESLintUtils, TSESLint } from "@typescript-eslint/utils";
2
+ import { existsSync } from "node:fs";
3
+ import path from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ import { createSyncFn } from "synckit";
6
+ import { RuleCreator, getParserServices } from "@typescript-eslint/utils/eslint-utils";
7
+ import { DefinitionType, ImplicitLibVariable, PatternVisitor, ScopeType, Visitor } from "@typescript-eslint/scope-manager";
8
+ import { requiresQuoting } from "@typescript-eslint/type-utils";
9
+ import assert from "node:assert";
10
+ import * as core from "@eslint-react/core";
11
+ import { findVariable } from "@typescript-eslint/utils/ast-utils";
12
+ import ts9 from "typescript";
13
+ import { getStaticTOMLValue } from "toml-eslint-parser";
14
+ //#region package.json
15
+ var name = "eslint-plugin-flawless";
16
+ var version = "0.1.12";
17
+ var repository = {
18
+ "url": "git+https://github.com/christopher-buss/eslint-plugin-flawless.git",
19
+ "type": "git"
20
+ };
21
+ //#endregion
22
+ //#region src/util.ts
23
+ const createRule = RuleCreator((name) => {
24
+ return `${repository.url.replace(/^git\+/, "").replace(/\.git$/, "")}/blob/v${version}/src/rules/${name}/documentation.md`;
25
+ });
26
+ /**
27
+ * Creates a rule with a docs URL, allowing a rule's `create` to receive a
28
+ * context with a custom `sourceCode` type (such as a YAML source code). The
29
+ * `SourceCode` type parameter defaults to ESLint's `SourceCode`, so standard
30
+ * rules are unaffected.
31
+ *
32
+ * @template Options - The rule's options tuple.
33
+ * @template MessageIds - The rule's message identifiers.
34
+ * @template SourceCode - The source code type exposed on `context.sourceCode`.
35
+ * @param rule - The rule definition (meta, name, defaultOptions, create).
36
+ * @returns The created rule module.
37
+ */
38
+ function createEslintRule(rule) {
39
+ return createRule(rule);
40
+ }
41
+ /**
42
+ * Creates a dual-runtime rule from oxlint's `createOnce` alternative API.
43
+ *
44
+ * The rule is authored once against `createOnce` (oxlint's per-run entry point,
45
+ * where per-file state is created in a `before` hook). The returned module
46
+ * also exposes an ESLint-compatible `create` that delegates to `createOnce` on
47
+ * each file, so the same object works with ESLint's `RuleTester` and the oxlint
48
+ * `jsPlugins` loader alike. Oxlint ignores `create` when `createOnce` is
49
+ * present; ESLint ignores `createOnce`.
50
+ *
51
+ * Rule bodies keep `@typescript-eslint` node/context types at the boundary
52
+ * (oxlint's AST is structurally ESTree at runtime); `@eslint-react/core` helpers
53
+ * are typed against those contexts, so a full switch to oxlint's `ESTree` types
54
+ * is intentionally avoided.
55
+ *
56
+ * IMPORTANT: `context.sourceCode`, `context.options`, and `context.report` must
57
+ * not be read in the `createOnce` body — only inside `before` or a visitor. In
58
+ * oxlint's runtime they throw at setup time.
59
+ *
60
+ * @template Options - The rule's options tuple.
61
+ * @template MessageIds - The rule's message identifiers.
62
+ * @template SourceCode - The source code type exposed on `context.sourceCode`.
63
+ * @param rule - The rule definition (meta, name, defaultOptions, createOnce).
64
+ * @returns A rule module usable by both ESLint and oxlint.
65
+ */
66
+ function createFlawlessRule({ createOnce, ...meta }) {
67
+ function create(context) {
68
+ const { after, before, ...visitors } = createOnce(context);
69
+ if (before !== void 0 && before() === false) return {};
70
+ if (after === void 0) return visitors;
71
+ const existing = visitors["Program:exit"];
72
+ visitors["Program:exit"] = (node) => {
73
+ existing?.(node);
74
+ after();
75
+ };
76
+ return visitors;
77
+ }
78
+ const module = createRule({
79
+ ...meta,
80
+ create
81
+ });
82
+ return Object.assign(module, { createOnce });
83
+ }
84
+ //#endregion
85
+ //#region src/rules/arrow-return-style/rule.ts
86
+ const RULE_NAME$12 = "arrow-return-style";
87
+ const IMPLICIT = "useImplicitReturn";
88
+ const EXPLICIT = "useExplicitReturn";
89
+ const COMPLEX_EXPLICIT = "useExplicitReturnComplex";
90
+ const DEFAULTS = {
91
+ jsxAlwaysUseExplicitReturn: false,
92
+ maxLen: 80,
93
+ maxObjectProperties: 4,
94
+ namedExportsAlwaysUseExplicitReturn: true,
95
+ objectReturnStyle: "complex-explicit",
96
+ tabWidth: 4,
97
+ useOxfmt: true
98
+ };
99
+ /** `undefined` = not initialized yet; `null` = initialization failed. */
100
+ let formatSync;
101
+ function getFormatSync() {
102
+ if (formatSync !== void 0) return formatSync;
103
+ try {
104
+ const directory = path.dirname(fileURLToPath(import.meta.url));
105
+ const workerPath = [
106
+ path.join(directory, "worker.mjs"),
107
+ path.join(directory, "rules", "arrow-return-style", "worker.mjs"),
108
+ path.join(directory, "worker.ts")
109
+ ].find((candidate) => existsSync(candidate));
110
+ if (workerPath === void 0) formatSync = null;
111
+ else formatSync = createSyncFn(workerPath, {
112
+ timeout: 15e3,
113
+ ...workerPath.endsWith(".ts") ? { tsRunner: "tsx" } : {}
114
+ });
115
+ } catch {
116
+ formatSync = null;
117
+ }
118
+ return formatSync;
119
+ }
120
+ /**
121
+ * Worker verdicts cached across files and fix passes: a snippet formats the
122
+ * same way regardless of which file (or autofix iteration) asked. Bounded LRU
123
+ * so long sessions (editors, watch mode) cannot grow it without limit.
124
+ */
125
+ const formatCache = /* @__PURE__ */ new Map();
126
+ const FORMAT_CACHE_MAX_ENTRIES = 1024;
127
+ function formatCacheGet(key) {
128
+ const value = formatCache.get(key);
129
+ if (value !== void 0) {
130
+ formatCache.delete(key);
131
+ formatCache.set(key, value);
132
+ }
133
+ return value;
134
+ }
135
+ function formatCacheSet(key, value) {
136
+ formatCache.delete(key);
137
+ formatCache.set(key, value);
138
+ if (formatCache.size > FORMAT_CACHE_MAX_ENTRIES) {
139
+ const oldest = formatCache.keys().next().value;
140
+ if (oldest !== void 0) formatCache.delete(oldest);
141
+ }
142
+ }
143
+ /**
144
+ * Visual width of `text` with tabs expanded to `tabWidth`-column tab stops.
145
+ *
146
+ * @param text - The text to measure.
147
+ * @param tabWidth - Columns a tab occupies.
148
+ * @returns The tab-expanded width in columns.
149
+ */
150
+ function expandedWidth(text, tabWidth) {
151
+ let width = 0;
152
+ for (const character of text) width += character === " " ? tabWidth - width % tabWidth : 1;
153
+ return width;
154
+ }
155
+ function leadingWhitespace(line) {
156
+ return /^[\t ]*/.exec(line)?.[0] ?? "";
157
+ }
158
+ function isJsx(node) {
159
+ return node.type === AST_NODE_TYPES.JSXElement || node.type === AST_NODE_TYPES.JSXFragment;
160
+ }
161
+ function isNamedExportArrow(node) {
162
+ if (node.parent.type !== AST_NODE_TYPES.VariableDeclarator) return false;
163
+ return node.parent.parent.parent.type === AST_NODE_TYPES.ExportNamedDeclaration;
164
+ }
165
+ /**
166
+ * Does the collapsed body need wrapping parens to stay an expression body?
167
+ *
168
+ * @param node - The would-be implicit body expression.
169
+ * @returns Whether the fixer must wrap the body in parentheses.
170
+ */
171
+ function needsParens(node) {
172
+ return node.type === AST_NODE_TYPES.ObjectExpression || node.type === AST_NODE_TYPES.SequenceExpression;
173
+ }
174
+ function countObjectComplexity(node) {
175
+ let features = 0;
176
+ for (const property of node.properties) {
177
+ if (property.type === AST_NODE_TYPES.SpreadElement) {
178
+ features += 1;
179
+ continue;
180
+ }
181
+ if (property.computed) features += 1;
182
+ if (property.value.type === AST_NODE_TYPES.CallExpression) features += 1;
183
+ }
184
+ return features;
185
+ }
186
+ function isComplexLiteral(node, maxObjectProperties) {
187
+ if (node.type === AST_NODE_TYPES.ObjectExpression) {
188
+ if (node.properties.length > maxObjectProperties) return true;
189
+ return countObjectComplexity(node) >= 2;
190
+ }
191
+ if (node.type === AST_NODE_TYPES.ArrayExpression) {
192
+ const spreads = node.elements.filter((element) => element?.type === AST_NODE_TYPES.SpreadElement).length;
193
+ const calls = node.elements.filter((element) => element?.type === AST_NODE_TYPES.CallExpression).length;
194
+ return spreads >= 1 && node.elements.length > 1 || calls >= 2 || spreads + calls >= 2;
195
+ }
196
+ return false;
197
+ }
198
+ function isLiteralBody(node) {
199
+ return node.type === AST_NODE_TYPES.ObjectExpression || node.type === AST_NODE_TYPES.ArrayExpression;
200
+ }
201
+ /**
202
+ * Walks up to the outermost node whose parent is the Program.
203
+ *
204
+ * @param node - The node to walk up from.
205
+ * @returns The top-level statement containing `node`.
206
+ */
207
+ function statementOf(node) {
208
+ let current = node;
209
+ while (current.parent !== void 0 && current.parent.type !== AST_NODE_TYPES.Program) current = current.parent;
210
+ return current;
211
+ }
212
+ function pushChildNodes$1(stack, value) {
213
+ const candidates = Array.isArray(value) ? value : [value];
214
+ for (const item of candidates) if (typeof item === "object" && item !== null && "type" in item) stack.push(item);
215
+ }
216
+ /**
217
+ * Collects arrow functions in `root`'s subtree, ordered by source position.
218
+ *
219
+ * @param root - The subtree to search.
220
+ * @returns All arrow function nodes, sorted by range start.
221
+ */
222
+ function collectArrows(root) {
223
+ const arrows = [];
224
+ const stack = [root];
225
+ while (stack.length > 0) {
226
+ const current = stack.pop();
227
+ if (current === void 0) continue;
228
+ if (current.type === AST_NODE_TYPES.ArrowFunctionExpression) arrows.push(current);
229
+ for (const [key, value] of Object.entries(current)) if (key !== "parent" && key !== "loc" && key !== "range") pushChildNodes$1(stack, value);
230
+ }
231
+ return arrows.sort((a, b) => a.range[0] - b.range[0]);
232
+ }
233
+ const arrowReturnStyle = createFlawlessRule({
234
+ name: RULE_NAME$12,
235
+ createOnce(context) {
236
+ let config = DEFAULTS;
237
+ let sourceCode;
238
+ let lines;
239
+ let pendingConsults;
240
+ /**
241
+ * Effective line-length limit for emitted lines: the fixer must never
242
+ * produce a line the formatter would immediately rewrap.
243
+ *
244
+ * @returns The maximum permitted tab-expanded line width.
245
+ */
246
+ function limit() {
247
+ const { maxLen, useOxfmt } = config;
248
+ return useOxfmt === false ? maxLen : Math.min(maxLen, printWidth());
249
+ }
250
+ function printWidth() {
251
+ const { maxLen, useOxfmt } = config;
252
+ if (typeof useOxfmt === "object" && typeof useOxfmt.printWidth === "number") return useOxfmt.printWidth;
253
+ return maxLen;
254
+ }
255
+ function width(text) {
256
+ return expandedWidth(text, config.tabWidth);
257
+ }
258
+ function lineOf(lineNumber) {
259
+ return lines[lineNumber - 1] ?? "";
260
+ }
261
+ function objectStyleWantsExplicit(body) {
262
+ if (!isLiteralBody(body) || config.objectReturnStyle === "off") return false;
263
+ if (config.objectReturnStyle === "always-explicit") return true;
264
+ return isComplexLiteral(body, config.maxObjectProperties);
265
+ }
266
+ function arrowTokenOf(node) {
267
+ let token = sourceCode.getTokenBefore(node.body);
268
+ while (token !== null && token.value === "(") token = sourceCode.getTokenBefore(token);
269
+ if (token?.value !== "=>") throw new Error("arrow-return-style: could not locate the => token");
270
+ return token;
271
+ }
272
+ /**
273
+ * The `(`/`)` pair directly wrapping the body, if any.
274
+ *
275
+ * @param node - Function whose body may be parenthesized.
276
+ * @param arrowToken - Token of the `=>` operator, used to distinguish
277
+ * body parens from parameter-list parens.
278
+ * @returns The wrapping paren tokens, or `null` when not parenthesized.
279
+ */
280
+ function bodyParens(node, arrowToken) {
281
+ const before = sourceCode.getTokenBefore(node.body);
282
+ if (before?.value !== "(" || before.range[0] < arrowToken.range[1]) return null;
283
+ const after = sourceCode.getTokenAfter(node.body);
284
+ if (after?.value !== ")") return null;
285
+ return {
286
+ close: after,
287
+ open: before
288
+ };
289
+ }
290
+ /**
291
+ * Resolves every deferred oxfmt consult with a single worker round-trip:
292
+ * would the formatter render each arrow (params through body) on a single
293
+ * line that fits `maxLen`? Arrows the formatter cannot keep on one fitting
294
+ * line are reported. Fails open (no report) when the worker or oxfmt is
295
+ * unavailable so that an unavailable formatter can never introduce reports
296
+ * it would have to fight over.
297
+ */
298
+ function resolvePendingConsults() {
299
+ if (pendingConsults.length === 0) return;
300
+ const nodes = pendingConsults;
301
+ pendingConsults = [];
302
+ const worker = getFormatSync();
303
+ if (worker === null) return;
304
+ const consults = nodes.flatMap((node) => {
305
+ const statement = statementOf(node);
306
+ const snippet = sourceCode.getText(statement);
307
+ const baseIndent = leadingWhitespace(lineOf(statement.loc.start.line));
308
+ const arrowIndex = collectArrows(statement).findIndex((arrow) => arrow.range[0] === node.range[0]);
309
+ if (arrowIndex === -1) return [];
310
+ const request = {
311
+ arrowIndex,
312
+ code: `${snippet}\n`,
313
+ printWidth: printWidth(),
314
+ tabWidth: config.tabWidth
315
+ };
316
+ return [{
317
+ baseIndent,
318
+ cacheKey: `${arrowIndex}${request.printWidth}${request.tabWidth}${snippet}`,
319
+ node,
320
+ request
321
+ }];
322
+ });
323
+ const misses = /* @__PURE__ */ new Map();
324
+ for (const consult of consults) if (formatCacheGet(consult.cacheKey) === void 0) misses.set(consult.cacheKey, consult.request);
325
+ if (misses.size > 0) {
326
+ let responses = [];
327
+ try {
328
+ responses = worker([...misses.values()]);
329
+ } catch {
330
+ responses = [];
331
+ }
332
+ for (const [index, cacheKey] of [...misses.keys()].entries()) {
333
+ const response = responses[index];
334
+ if (response !== void 0) formatCacheSet(cacheKey, response);
335
+ }
336
+ }
337
+ for (const consult of consults) {
338
+ const response = formatCacheGet(consult.cacheKey);
339
+ if (response === void 0) continue;
340
+ if (response.lineText === null) continue;
341
+ if (!(response.singleLine && width(consult.baseIndent) + width(response.lineText) <= config.maxLen)) reportExplicit(consult.node, EXPLICIT);
342
+ }
343
+ }
344
+ /**
345
+ * Derives the one-level indent unit for an inserted block: prefer the
346
+ * offset between the arrow's line and the body's (or its continuation's)
347
+ * deeper indentation; otherwise infer from the indent character in use.
348
+ *
349
+ * @param arrowIndent - Leading whitespace of the arrow's line.
350
+ * @param node - The arrow function being fixed.
351
+ * @param bodyStart - The body's first token or node (including parens).
352
+ * @param bodyEnd - The body's last token or node (including parens).
353
+ * @returns The whitespace string for one indentation level.
354
+ */
355
+ function indentUnit(arrowIndent, node, bodyStart, bodyEnd) {
356
+ const referenceLines = [];
357
+ const arrowLine = bodyStart.loc.start.line;
358
+ if (bodyStart.loc.start.line !== node.loc.start.line) referenceLines.push(bodyStart.loc.start.line);
359
+ else if (bodyEnd.loc.end.line > arrowLine) referenceLines.push(arrowLine + 1);
360
+ for (const lineNumber of referenceLines) {
361
+ const reference = leadingWhitespace(lineOf(lineNumber));
362
+ if (reference.length > arrowIndent.length && reference.startsWith(arrowIndent)) return reference.slice(arrowIndent.length);
363
+ }
364
+ if (arrowIndent.includes(" ")) return " ";
365
+ return arrowIndent.length > 0 ? " " : " ";
366
+ }
367
+ /**
368
+ * Reports and fixes an implicit arrow into an explicit block body.
369
+ *
370
+ * @param node - The arrow function to convert.
371
+ * @param messageId - The violation to report.
372
+ */
373
+ function reportExplicit(node, messageId) {
374
+ const arrowToken = arrowTokenOf(node);
375
+ const parens = bodyParens(node, arrowToken);
376
+ const bodyStart = parens?.open ?? node.body;
377
+ const bodyEnd = parens?.close ?? node.body;
378
+ const comments = sourceCode.getCommentsBefore(bodyStart);
379
+ const arrowIndent = leadingWhitespace(lineOf(node.loc.start.line));
380
+ const targetIndent = arrowIndent + indentUnit(arrowIndent, node, bodyStart, bodyEnd);
381
+ const bodyLines = sourceCode.getText(node.body).split("\n");
382
+ const [firstLine, ...restLines] = bodyLines;
383
+ const lastLineIndent = leadingWhitespace(bodyLines.at(-1) ?? "");
384
+ let shifted = restLines;
385
+ if (restLines.length > 0) {
386
+ if (targetIndent.startsWith(lastLineIndent)) {
387
+ const prefix = targetIndent.slice(lastLineIndent.length);
388
+ shifted = restLines.map((line) => prefix + line);
389
+ } else if (lastLineIndent.startsWith(targetIndent)) {
390
+ const strip = lastLineIndent.length - targetIndent.length;
391
+ shifted = restLines.map((line) => {
392
+ return leadingWhitespace(line).length >= strip ? line.slice(strip) : line;
393
+ });
394
+ }
395
+ }
396
+ const commentLines = comments.map((comment) => targetIndent + sourceCode.getText(comment));
397
+ const returnLine = `${targetIndent}return ${[firstLine, ...shifted].join("\n")};`;
398
+ const replacement = ` {\n${[...commentLines, returnLine].join("\n")}\n${arrowIndent}}`;
399
+ context.report({
400
+ fix: (fixer) => fixer.replaceTextRange([arrowToken.range[1], bodyEnd.range[1]], replacement),
401
+ messageId,
402
+ node
403
+ });
404
+ }
405
+ /**
406
+ * Reports and fixes a single-`return` block into an implicit body.
407
+ *
408
+ * @param node - The arrow function to convert.
409
+ * @param block - The block body being replaced.
410
+ * @param replacement - The implicit body text.
411
+ */
412
+ function reportImplicit(node, block, replacement) {
413
+ context.report({
414
+ fix: (fixer) => fixer.replaceTextRange(block.range, replacement),
415
+ messageId: IMPLICIT,
416
+ node
417
+ });
418
+ }
419
+ function checkBlockBody(node) {
420
+ const block = node.body;
421
+ if (block.body.length !== 1) return;
422
+ const [statement] = block.body;
423
+ if (statement?.type !== AST_NODE_TYPES.ReturnStatement || statement.argument === null) return;
424
+ if (sourceCode.getCommentsInside(block).length > 0) return;
425
+ const { argument } = statement;
426
+ if (isJsx(argument) && config.jsxAlwaysUseExplicitReturn) return;
427
+ if (isNamedExportArrow(node) && config.namedExportsAlwaysUseExplicitReturn) return;
428
+ if (objectStyleWantsExplicit(argument)) return;
429
+ if (argument.loc.start.line !== argument.loc.end.line) return;
430
+ const argumentText = sourceCode.getText(argument);
431
+ const collapsed = needsParens(argument) ? `(${argumentText})` : argumentText;
432
+ const prefix = lineOf(block.loc.start.line).slice(0, block.loc.start.column);
433
+ const suffix = lineOf(block.loc.end.line).slice(block.loc.end.column);
434
+ if (width(prefix + collapsed + suffix) > limit()) return;
435
+ reportImplicit(node, block, collapsed);
436
+ }
437
+ function checkExpressionBody(node) {
438
+ const body = node.body;
439
+ const arrowToken = arrowTokenOf(node);
440
+ const parens = bodyParens(node, arrowToken);
441
+ const bodyStart = parens?.open ?? body;
442
+ const bodyEnd = parens?.close ?? body;
443
+ if (sourceCode.getCommentsBefore(bodyStart).length > 0) {
444
+ reportExplicit(node, EXPLICIT);
445
+ return;
446
+ }
447
+ if (isJsx(body) && config.jsxAlwaysUseExplicitReturn) {
448
+ reportExplicit(node, EXPLICIT);
449
+ return;
450
+ }
451
+ if (isNamedExportArrow(node) && config.namedExportsAlwaysUseExplicitReturn) {
452
+ reportExplicit(node, EXPLICIT);
453
+ return;
454
+ }
455
+ if (bodyStart.loc.start.line !== arrowToken.loc.end.line) {
456
+ reportExplicit(node, EXPLICIT);
457
+ return;
458
+ }
459
+ if (bodyEnd.loc.end.line !== bodyStart.loc.start.line) {
460
+ if (body.type === AST_NODE_TYPES.ObjectExpression) reportExplicit(node, EXPLICIT);
461
+ return;
462
+ }
463
+ if (width(lineOf(bodyStart.loc.start.line)) > limit()) {
464
+ if (config.useOxfmt !== false) pendingConsults.push(node);
465
+ else reportExplicit(node, EXPLICIT);
466
+ return;
467
+ }
468
+ if (objectStyleWantsExplicit(body)) reportExplicit(node, COMPLEX_EXPLICIT);
469
+ }
470
+ return {
471
+ "ArrowFunctionExpression": function(node) {
472
+ if (node.body.type === AST_NODE_TYPES.BlockStatement) checkBlockBody(node);
473
+ else checkExpressionBody(node);
474
+ },
475
+ "before": function() {
476
+ config = {
477
+ ...DEFAULTS,
478
+ ...context.options[0]
479
+ };
480
+ ({sourceCode} = context);
481
+ lines = [...sourceCode.lines];
482
+ pendingConsults = [];
483
+ },
484
+ "Program:exit": function() {
485
+ resolvePendingConsults();
486
+ }
487
+ };
488
+ },
489
+ defaultOptions: [DEFAULTS],
490
+ meta: {
491
+ docs: {
492
+ description: "Enforce arrow function return style based on line length",
493
+ requiresTypeChecking: false
494
+ },
495
+ fixable: "code",
496
+ messages: {
497
+ [COMPLEX_EXPLICIT]: "Use an explicit return block for complex object or array bodies.",
498
+ [EXPLICIT]: "Use an explicit return block for this arrow function body.",
499
+ [IMPLICIT]: "Use an implicit return for this arrow function body."
500
+ },
501
+ schema: [{
502
+ additionalProperties: false,
503
+ properties: {
504
+ jsxAlwaysUseExplicitReturn: { type: "boolean" },
505
+ maxLen: {
506
+ minimum: 0,
507
+ type: "integer"
508
+ },
509
+ maxObjectProperties: {
510
+ minimum: 0,
511
+ type: "integer"
512
+ },
513
+ namedExportsAlwaysUseExplicitReturn: { type: "boolean" },
514
+ objectReturnStyle: {
515
+ enum: [
516
+ "always-explicit",
517
+ "complex-explicit",
518
+ "off"
519
+ ],
520
+ type: "string"
521
+ },
522
+ tabWidth: {
523
+ minimum: 1,
524
+ type: "integer"
525
+ },
526
+ useOxfmt: { oneOf: [{ type: "boolean" }, {
527
+ additionalProperties: false,
528
+ properties: { printWidth: {
529
+ minimum: 0,
530
+ type: "integer"
531
+ } },
532
+ type: "object"
533
+ }] }
534
+ },
535
+ type: "object"
536
+ }],
537
+ type: "suggestion"
538
+ }
539
+ });
540
+ //#endregion
541
+ //#region src/rules/jsx-shorthand-boolean/rule.ts
542
+ const RULE_NAME$11 = "jsx-shorthand-boolean";
543
+ const MESSAGE_ID$5 = "setAttributeValue";
544
+ const messages$11 = { [MESSAGE_ID$5]: "Set an explicit value for boolean attribute '{{name}}'." };
545
+ function createOnce$7(context) {
546
+ return { JSXAttribute(node) {
547
+ if (node.value !== null) return;
548
+ context.report({
549
+ data: { name: context.sourceCode.getText(node.name) },
550
+ fix: (fixer) => fixer.insertTextAfter(node.name, "={true}"),
551
+ messageId: MESSAGE_ID$5,
552
+ node
553
+ });
554
+ } };
555
+ }
556
+ const jsxShorthandBoolean = createFlawlessRule({
557
+ name: RULE_NAME$11,
558
+ createOnce: createOnce$7,
559
+ defaultOptions: [],
560
+ meta: {
561
+ docs: {
562
+ description: "Disallow shorthand boolean JSX attributes",
563
+ recommended: false,
564
+ requiresTypeChecking: false
565
+ },
566
+ fixable: "code",
567
+ hasSuggestions: false,
568
+ messages: messages$11,
569
+ schema: [],
570
+ type: "suggestion"
571
+ }
572
+ });
573
+ //#endregion
574
+ //#region src/rules/jsx-shorthand-fragment/rule.ts
575
+ const RULE_NAME$10 = "jsx-shorthand-fragment";
576
+ const MESSAGE_ID_NAMED = "useNamedFragment";
577
+ const MESSAGE_ID_SHORTHAND = "useShorthandFragment";
578
+ const DEFAULT_MODE = "syntax";
579
+ const DEFAULT_FRAGMENT_NAME = "Fragment";
580
+ const messages$10 = {
581
+ [MESSAGE_ID_NAMED]: "Use the '{{name}}' component instead of fragment shorthand syntax.",
582
+ [MESSAGE_ID_SHORTHAND]: "Use the fragment shorthand syntax '<>...</>' instead of the '{{name}}' component."
583
+ };
584
+ const schema$2 = [{
585
+ additionalProperties: false,
586
+ properties: {
587
+ fragmentName: {
588
+ description: "The identifier to use for the named fragment element in \"element\" mode.",
589
+ type: "string"
590
+ },
591
+ mode: {
592
+ description: "Which form to enforce: \"syntax\" (shorthand `<>...</>`, default) or \"element\" (a named fragment).",
593
+ enum: ["element", "syntax"],
594
+ type: "string"
595
+ }
596
+ },
597
+ type: "object"
598
+ }];
599
+ /**
600
+ * Flattens a JSX element name into a dotted string, e.g. `Fragment` or
601
+ * `React.Fragment`. Returns `null` for namespaced names (`<a:b>`) which cannot
602
+ * be a fragment.
603
+ *
604
+ * @param node - The JSX element name node.
605
+ * @returns The dotted name, or `null` when it is not a plain identifier chain.
606
+ */
607
+ function jsxNameToString(node) {
608
+ if (node.type === AST_NODE_TYPES.JSXIdentifier) return node.name;
609
+ if (node.type === AST_NODE_TYPES.JSXMemberExpression) {
610
+ const object = jsxNameToString(node.object);
611
+ if (object === null) return null;
612
+ return `${object}.${node.property.name}`;
613
+ }
614
+ return null;
615
+ }
616
+ function createOnce$6(context) {
617
+ let mode;
618
+ let fragmentName;
619
+ let namedFragments;
620
+ return {
621
+ before() {
622
+ const options = context.options[0] ?? {};
623
+ mode = options.mode ?? DEFAULT_MODE;
624
+ fragmentName = options.fragmentName ?? DEFAULT_FRAGMENT_NAME;
625
+ namedFragments = /* @__PURE__ */ new Set([
626
+ "Fragment",
627
+ fragmentName,
628
+ "React.Fragment"
629
+ ]);
630
+ },
631
+ JSXElement(node) {
632
+ if (mode !== "syntax") return;
633
+ const { openingElement } = node;
634
+ const name = jsxNameToString(openingElement.name);
635
+ if (name === null || !namedFragments.has(name)) return;
636
+ if (openingElement.attributes.length > 0) return;
637
+ context.report({
638
+ data: { name },
639
+ fix: (fixer) => {
640
+ const { closingElement } = node;
641
+ if (closingElement === null) return fixer.replaceText(node, "<></>");
642
+ return [fixer.replaceText(openingElement, "<>"), fixer.replaceText(closingElement, "</>")];
643
+ },
644
+ messageId: MESSAGE_ID_SHORTHAND,
645
+ node
646
+ });
647
+ },
648
+ JSXFragment(node) {
649
+ if (mode !== "element") return;
650
+ const { closingFragment, openingFragment } = node;
651
+ context.report({
652
+ data: { name: fragmentName },
653
+ fix: (fixer) => {
654
+ return [fixer.replaceText(openingFragment, `<${fragmentName}>`), fixer.replaceText(closingFragment, `</${fragmentName}>`)];
655
+ },
656
+ messageId: MESSAGE_ID_NAMED,
657
+ node
658
+ });
659
+ }
660
+ };
661
+ }
662
+ const jsxShorthandFragment = createFlawlessRule({
663
+ name: RULE_NAME$10,
664
+ createOnce: createOnce$6,
665
+ defaultOptions: [{
666
+ fragmentName: DEFAULT_FRAGMENT_NAME,
667
+ mode: DEFAULT_MODE
668
+ }],
669
+ meta: {
670
+ defaultOptions: [{
671
+ fragmentName: DEFAULT_FRAGMENT_NAME,
672
+ mode: DEFAULT_MODE
673
+ }],
674
+ docs: {
675
+ description: "Enforce a consistent fragment form: the shorthand `<>...</>` or a named fragment",
676
+ recommended: false,
677
+ requiresTypeChecking: false
678
+ },
679
+ fixable: "code",
680
+ hasSuggestions: false,
681
+ messages: messages$10,
682
+ schema: schema$2,
683
+ type: "suggestion"
684
+ }
685
+ });
686
+ //#endregion
687
+ //#region src/utils/is-type-import.ts
688
+ /**
689
+ * Determine whether a variable definition is a type import. E.g.:.
690
+ *
691
+ * ```ts
692
+ * import type { Foo } from 'foo';
693
+ * import { type Bar } from 'bar';
694
+ * ```
695
+ *
696
+ * @param definition - The variable definition to check.
697
+ * @returns True if the definition is a type import, false otherwise.
698
+ */
699
+ function isTypeImport(definition) {
700
+ return definition?.type === DefinitionType.ImportBinding && (definition.parent.importKind === "type" || definition.node.type === AST_NODE_TYPES.ImportSpecifier && definition.node.importKind === "type");
701
+ }
702
+ //#endregion
703
+ //#region src/utils/reference-contains-type-query.ts
704
+ /**
705
+ * Recursively checks whether a given reference has a type query declaration among its parents.
706
+ * @param node - The AST node to check.
707
+ * @returns True if a TSTypeQuery is found in the parent chain, false otherwise.
708
+ */
709
+ function referenceContainsTypeQuery(node) {
710
+ switch (node.type) {
711
+ case AST_NODE_TYPES.Identifier:
712
+ case AST_NODE_TYPES.TSQualifiedName: return referenceContainsTypeQuery(node.parent);
713
+ case AST_NODE_TYPES.TSTypeQuery: return true;
714
+ default: return false;
715
+ }
716
+ }
717
+ //#endregion
718
+ //#region src/utils/collect-variables.ts
719
+ /**
720
+ * This class leverages an AST visitor to mark variables as used via the
721
+ * `eslintUsed` property.
722
+ */
723
+ var UnusedVariablesVisitor = class extends Visitor {
724
+ /**
725
+ * We keep a weak cache so that multiple rules can share the calculation.
726
+ */
727
+ static RESULTS_CACHE = /* @__PURE__ */ new WeakMap();
728
+ #scopeManager;
729
+ ClassDeclaration = this.visitClass;
730
+ ClassExpression = this.visitClass;
731
+ ForInStatement = this.visitForInForOf;
732
+ /**
733
+ * #region HELPERS
734
+ */
735
+ ForOfStatement = this.visitForInForOf;
736
+ FunctionDeclaration = this.visitFunction;
737
+ FunctionExpression = this.visitFunction;
738
+ MethodDefinition = this.visitSetter;
739
+ Property = this.visitSetter;
740
+ TSCallSignatureDeclaration = this.visitFunctionTypeSignature;
741
+ TSConstructorType = this.visitFunctionTypeSignature;
742
+ TSConstructSignatureDeclaration = this.visitFunctionTypeSignature;
743
+ TSDeclareFunction = this.visitFunctionTypeSignature;
744
+ /**
745
+ * NOTE - This is a simple visitor - meaning it does not support selectors.
746
+ */
747
+ TSEmptyBodyFunctionExpression = this.visitFunctionTypeSignature;
748
+ TSFunctionType = this.visitFunctionTypeSignature;
749
+ TSMethodSignature = this.visitFunctionTypeSignature;
750
+ constructor(scopeManager) {
751
+ super({ visitChildrenEvenIfSelectorExists: true });
752
+ this.#scopeManager = scopeManager;
753
+ }
754
+ static collectUnusedVariables(program, scopeManager) {
755
+ const cached = this.RESULTS_CACHE.get(program);
756
+ if (cached) return cached;
757
+ const visitor = new this(scopeManager);
758
+ visitor.visit(program);
759
+ const unusedVariables = visitor.collectUnusedVariables({ scope: visitor.getScope(program) });
760
+ this.RESULTS_CACHE.set(program, unusedVariables);
761
+ return unusedVariables;
762
+ }
763
+ Identifier(node) {
764
+ const scope = this.getScope(node);
765
+ if (scope.type === TSESLint.Scope.ScopeType.function && node.name === "this" && "params" in scope.block && scope.block.params.includes(node)) this.markVariableAsUsed(node);
766
+ }
767
+ TSEnumDeclaration(node) {
768
+ const scope = this.getScope(node);
769
+ for (const variable of scope.variables) this.markVariableAsUsed(variable);
770
+ }
771
+ TSMappedType(node) {
772
+ this.markVariableAsUsed(node.key);
773
+ }
774
+ TSModuleDeclaration(node) {
775
+ if (node.kind === "global") this.markVariableAsUsed("global", node.parent);
776
+ }
777
+ TSParameterProperty(node) {
778
+ let identifier;
779
+ switch (node.parameter.type) {
780
+ case AST_NODE_TYPES.AssignmentPattern:
781
+ identifier = node.parameter.left;
782
+ break;
783
+ case AST_NODE_TYPES.Identifier:
784
+ identifier = node.parameter;
785
+ break;
786
+ }
787
+ this.markVariableAsUsed(identifier);
788
+ }
789
+ collectUnusedVariables({ scope, variables = {
790
+ unusedVariables: /* @__PURE__ */ new Set(),
791
+ usedVariables: /* @__PURE__ */ new Set()
792
+ } }) {
793
+ if (!scope.functionExpressionScope) for (const variable of scope.variables) {
794
+ if (variable instanceof ImplicitLibVariable) continue;
795
+ if (variable.eslintUsed || isExported$1(variable) || isMergeableExported(variable) || isUsedVariable(variable)) variables.usedVariables.add(variable);
796
+ else variables.unusedVariables.add(variable);
797
+ }
798
+ for (const childScope of scope.childScopes) this.collectUnusedVariables({
799
+ scope: childScope,
800
+ variables
801
+ });
802
+ return variables;
803
+ }
804
+ getScope(currentNode) {
805
+ const inner = currentNode.type !== AST_NODE_TYPES.Program;
806
+ let node = currentNode;
807
+ while (node) {
808
+ const scope = this.#scopeManager.acquire(node, inner);
809
+ if (scope) {
810
+ if (scope.type === ScopeType.functionExpressionName) {
811
+ const returnValue = scope.childScopes[0];
812
+ assert(returnValue, "Function expression name scope should have a child scope");
813
+ return returnValue;
814
+ }
815
+ return scope;
816
+ }
817
+ node = node.parent;
818
+ }
819
+ const returnValue = this.#scopeManager.scopes[0];
820
+ assert(returnValue, "There should be at least one scope");
821
+ return returnValue;
822
+ }
823
+ markVariableAsUsed(variableOrIdentifierOrName, parent) {
824
+ if (typeof variableOrIdentifierOrName !== "string" && !("type" in variableOrIdentifierOrName)) {
825
+ variableOrIdentifierOrName.eslintUsed = true;
826
+ return;
827
+ }
828
+ let name;
829
+ let node;
830
+ if (typeof variableOrIdentifierOrName === "string") {
831
+ name = variableOrIdentifierOrName;
832
+ assert(parent, "Parent node is required when marking by name");
833
+ node = parent;
834
+ } else {
835
+ ({name} = variableOrIdentifierOrName);
836
+ node = variableOrIdentifierOrName;
837
+ }
838
+ let currentScope = this.getScope(node);
839
+ while (currentScope) {
840
+ const variable = currentScope.variables.find((scopeVariable) => scopeVariable.name === name);
841
+ if (variable) {
842
+ variable.eslintUsed = true;
843
+ return;
844
+ }
845
+ currentScope = currentScope.upper ?? void 0;
846
+ }
847
+ }
848
+ visitClass(node) {
849
+ const scope = this.getScope(node);
850
+ for (const variable of scope.variables) if (variable.identifiers[0] === scope.block.id) {
851
+ this.markVariableAsUsed(variable);
852
+ return;
853
+ }
854
+ }
855
+ visitForInForOf(node) {
856
+ /**
857
+ * // cspell:ignore Zacher
858
+ * (Brad Zacher): I hate that this has to exist.
859
+ * But it is required for compat with the base ESLint rule.
860
+ *
861
+ * In 2015, ESLint decided to add an exception for these two specific cases.
862
+ * ```
863
+ * for (var key in object) return;
864
+ *
865
+ * var key;
866
+ * for (key in object) return;
867
+ * ```
868
+ *
869
+ * I disagree with it, but what are you going to do...
870
+ *
871
+ * Https://github.com/eslint/eslint/issues/2342.
872
+ */
873
+ let idOrVariable;
874
+ if (node.left.type === AST_NODE_TYPES.VariableDeclaration) {
875
+ const variable = this.#scopeManager.getDeclaredVariables(node.left).at(0);
876
+ if (!variable) return;
877
+ idOrVariable = variable;
878
+ }
879
+ if (node.left.type === AST_NODE_TYPES.Identifier) idOrVariable = node.left;
880
+ if (idOrVariable === void 0) return;
881
+ let { body } = node;
882
+ if (body.type === AST_NODE_TYPES.BlockStatement) {
883
+ if (body.body.length !== 1) return;
884
+ body = body.body[0];
885
+ }
886
+ if (body.type !== AST_NODE_TYPES.ReturnStatement) return;
887
+ this.markVariableAsUsed(idOrVariable);
888
+ }
889
+ visitFunction(node) {
890
+ const variable = this.getScope(node).set.get("arguments");
891
+ if (variable?.defs.length === 0) this.markVariableAsUsed(variable);
892
+ }
893
+ visitFunctionTypeSignature(node) {
894
+ for (const parameter of node.params) this.visitPattern(parameter, (name) => {
895
+ this.markVariableAsUsed(name);
896
+ });
897
+ }
898
+ visitSetter(node) {
899
+ if (node.kind === "set") for (const parameter of node.value.params) this.visitPattern(parameter, (id) => {
900
+ this.markVariableAsUsed(id);
901
+ });
902
+ }
903
+ };
904
+ /**
905
+ * Checks the position of given nodes.
906
+ * @param inner - A node which is expected as inside.
907
+ * @param outer - A node which is expected as outside.
908
+ * @returns `true` if the `inner` node exists in the `outer` node.
909
+ */
910
+ function isInside(inner, outer) {
911
+ return inner.range[0] >= outer.range[0] && inner.range[1] <= outer.range[1];
912
+ }
913
+ /**
914
+ * Determine if an identifier is referencing an enclosing name.
915
+ * This only applies to declarations that create their own scope (modules, functions, classes).
916
+ * @param ref - The reference to check.
917
+ * @param nodes - The candidate function nodes.
918
+ * @returns True if it's a self-reference, false if not.
919
+ */
920
+ function isSelfReference(ref, nodes) {
921
+ let scope = ref.from;
922
+ while (scope) {
923
+ if (nodes.has(scope.block)) return true;
924
+ scope = scope.upper ?? void 0;
925
+ }
926
+ return false;
927
+ }
928
+ const MERGEABLE_TYPES = /* @__PURE__ */ new Set([
929
+ AST_NODE_TYPES.ClassDeclaration,
930
+ AST_NODE_TYPES.FunctionDeclaration,
931
+ AST_NODE_TYPES.TSInterfaceDeclaration,
932
+ AST_NODE_TYPES.TSModuleDeclaration,
933
+ AST_NODE_TYPES.TSTypeAliasDeclaration
934
+ ]);
935
+ /**
936
+ * Determines if a given variable is being exported from a module.
937
+ * @param variable - Eslint-scope variable object.
938
+ * @returns True if the variable is exported, false if not.
939
+ */
940
+ function isExported$1(variable) {
941
+ return variable.defs.some((definition) => {
942
+ let { node } = definition;
943
+ if (node.type === AST_NODE_TYPES.VariableDeclarator) node = node.parent;
944
+ else if (definition.type === TSESLint.Scope.DefinitionType.Parameter) return false;
945
+ return node.parent.type.startsWith("Export");
946
+ });
947
+ }
948
+ /**
949
+ * Determine if the variable is directly exported.
950
+ * @param variable - The variable to check.
951
+ * @returns True if the variable is exported via a merged declaration.
952
+ */
953
+ function isMergeableExported(variable) {
954
+ for (const definition of variable.defs) {
955
+ if (definition.type === TSESLint.Scope.DefinitionType.Parameter) continue;
956
+ if (MERGEABLE_TYPES.has(definition.node.type) && definition.node.parent.type === AST_NODE_TYPES.ExportNamedDeclaration || definition.node.parent.type === AST_NODE_TYPES.ExportDefaultDeclaration) return true;
957
+ }
958
+ return false;
959
+ }
960
+ const LOGICAL_ASSIGNMENT_OPERATORS = /* @__PURE__ */ new Set([
961
+ "&&=",
962
+ "??=",
963
+ "||="
964
+ ]);
965
+ /**
966
+ * Collects the set of unused variables for a given context.
967
+ *
968
+ * Due to complexity, this does not take into consideration:
969
+ * - variables within declaration files
970
+ * - variables within ambient module declarations.
971
+ * @param context - The rule context.
972
+ * @returns The collected variables.
973
+ * @template MessageIds
974
+ * @template Options
975
+ */
976
+ function collectVariables(context) {
977
+ return UnusedVariablesVisitor.collectUnusedVariables(context.sourceCode.ast, ESLintUtils.nullThrows(context.sourceCode.scopeManager, "Missing required scope manager"));
978
+ }
979
+ /**
980
+ * Determines if the variable is used.
981
+ * @param variable - The variable to check.
982
+ * @returns True if the variable is used.
983
+ */
984
+ function isUsedVariable(variable) {
985
+ /**
986
+ * Gets a list of function definitions for a specified variable.
987
+ * @param scopeVariable - Eslint-scope variable object.
988
+ * @returns Function nodes.
989
+ */
990
+ function getFunctionDefinitions(scopeVariable) {
991
+ const functionDefinitions = /* @__PURE__ */ new Set();
992
+ for (const definition of scopeVariable.defs) {
993
+ if (definition.type === TSESLint.Scope.DefinitionType.FunctionName) functionDefinitions.add(definition.node);
994
+ if (definition.type === TSESLint.Scope.DefinitionType.Variable && (definition.node.init?.type === AST_NODE_TYPES.FunctionExpression || definition.node.init?.type === AST_NODE_TYPES.ArrowFunctionExpression)) functionDefinitions.add(definition.node.init);
995
+ }
996
+ return functionDefinitions;
997
+ }
998
+ function getTypeDeclarations(scopeVariable) {
999
+ const nodes = /* @__PURE__ */ new Set();
1000
+ for (const definition of scopeVariable.defs) if (definition.node.type === AST_NODE_TYPES.TSInterfaceDeclaration || definition.node.type === AST_NODE_TYPES.TSTypeAliasDeclaration) nodes.add(definition.node);
1001
+ return nodes;
1002
+ }
1003
+ function getModuleDeclarations(scopeVariable) {
1004
+ const nodes = /* @__PURE__ */ new Set();
1005
+ for (const definition of scopeVariable.defs) if (definition.node.type === AST_NODE_TYPES.TSModuleDeclaration) nodes.add(definition.node);
1006
+ return nodes;
1007
+ }
1008
+ function getEnumDeclarations(scopeVariable) {
1009
+ const nodes = /* @__PURE__ */ new Set();
1010
+ for (const definition of scopeVariable.defs) if (definition.node.type === AST_NODE_TYPES.TSEnumDeclaration) nodes.add(definition.node);
1011
+ return nodes;
1012
+ }
1013
+ /**
1014
+ * Checks if the ref is contained within one of the given nodes.
1015
+ * @param ref - A reference to check.
1016
+ * @param nodes - A set of nodes.
1017
+ * @returns `true` if the ref is inside one of the nodes.
1018
+ */
1019
+ function isInsideOneOf(ref, nodes) {
1020
+ for (const node of nodes) if (isInside(ref.identifier, node)) return true;
1021
+ return false;
1022
+ }
1023
+ /**
1024
+ * Checks whether a given node is unused expression or not.
1025
+ * @param node - The node itself.
1026
+ * @returns The node is an unused expression.
1027
+ */
1028
+ function isUnusedExpression(node) {
1029
+ const { parent } = node;
1030
+ if (parent.type === AST_NODE_TYPES.ExpressionStatement) return true;
1031
+ if (parent.type === AST_NODE_TYPES.SequenceExpression) {
1032
+ if (!(parent.expressions[parent.expressions.length - 1] === node)) return true;
1033
+ return isUnusedExpression(parent);
1034
+ }
1035
+ return false;
1036
+ }
1037
+ /**
1038
+ * If a given reference is left-hand side of an assignment, this gets
1039
+ * the right-hand side node of the assignment.
1040
+ *
1041
+ * In the following cases, this returns undefined.
1042
+ *
1043
+ * - The reference is not the LHS of an assignment expression.
1044
+ * - The reference is inside of a loop.
1045
+ * - The reference is inside of a function scope which is different from
1046
+ * the declaration.
1047
+ * @param ref - A reference to check.
1048
+ * @param previousRhsNode - The previous RHS node. This is for `a = a + a`-like code.
1049
+ * @returns The RHS node or undefined.
1050
+ */
1051
+ function getRhsNode(ref, previousRhsNode) {
1052
+ /**
1053
+ * Checks whether the given node is in a loop or not.
1054
+ * @param node - The node to check.
1055
+ * @returns `true` if the node is in a loop.
1056
+ */
1057
+ function isInLoop(node) {
1058
+ let currentNode = node;
1059
+ while (currentNode) {
1060
+ if (ASTUtils.isFunction(currentNode)) break;
1061
+ if (ASTUtils.isLoop(currentNode)) return true;
1062
+ currentNode = currentNode.parent;
1063
+ }
1064
+ return false;
1065
+ }
1066
+ const id = ref.identifier;
1067
+ const { parent } = id;
1068
+ const refScope = ref.from.variableScope;
1069
+ const { variableScope } = ref.resolved.scope;
1070
+ const canBeUsedLater = refScope !== variableScope || isInLoop(id);
1071
+ if (previousRhsNode && isInside(id, previousRhsNode)) return previousRhsNode;
1072
+ if (parent.type === AST_NODE_TYPES.AssignmentExpression && isUnusedExpression(parent) && id === parent.left && !canBeUsedLater) return parent.right;
1073
+ }
1074
+ /**
1075
+ * Checks whether a given reference is a read to update itself or not.
1076
+ * @param ref - A reference to check.
1077
+ * @param rhsNode - The RHS node of the previous assignment.
1078
+ * @returns The reference is a read to update itself.
1079
+ */
1080
+ function isReadForItself(ref, rhsNode) {
1081
+ /**
1082
+ * Checks whether a given Identifier node exists inside of a function node which can be used later.
1083
+ *
1084
+ * "can be used later" means:
1085
+ * - the function is assigned to a variable.
1086
+ * - the function is bound to a property and the object can be used later.
1087
+ * - the function is bound as an argument of a function call.
1088
+ *
1089
+ * If a reference exists in a function which can be used later, the reference is read when the function is called.
1090
+ * @param id - An Identifier node to check.
1091
+ * @param rightHandSideNode - The RHS node of the previous assignment.
1092
+ * @returns `true` if the `id` node exists inside of a function node which can be used later.
1093
+ */
1094
+ function isInsideOfStorableFunction(id, rightHandSideNode) {
1095
+ /**
1096
+ * Finds a function node from ancestors of a node.
1097
+ * @param node - A start node to find.
1098
+ * @returns A found function node.
1099
+ */
1100
+ function getUpperFunction(node) {
1101
+ let currentNode = node;
1102
+ while (currentNode) {
1103
+ if (ASTUtils.isFunction(currentNode)) return currentNode;
1104
+ currentNode = currentNode.parent;
1105
+ }
1106
+ }
1107
+ /**
1108
+ * Checks whether a given function node is stored to somewhere or not.
1109
+ * If the function node is stored, the function can be used later.
1110
+ * @param funcNode - A function node to check.
1111
+ * @param storableRhsNode - The RHS node of the previous assignment.
1112
+ * @returns `true` if under the following conditions:
1113
+ * - the funcNode is assigned to a variable.
1114
+ * - the funcNode is bound as an argument of a function call.
1115
+ * - the function is bound to a property and the object satisfies above conditions.
1116
+ */
1117
+ function isStorableFunction(funcNode, storableRhsNode) {
1118
+ let node = funcNode;
1119
+ let { parent } = funcNode;
1120
+ while (parent && isInside(parent, storableRhsNode)) {
1121
+ switch (parent.type) {
1122
+ case AST_NODE_TYPES.AssignmentExpression:
1123
+ case AST_NODE_TYPES.TaggedTemplateExpression:
1124
+ case AST_NODE_TYPES.YieldExpression: return true;
1125
+ case AST_NODE_TYPES.CallExpression:
1126
+ case AST_NODE_TYPES.NewExpression: return parent.callee !== node;
1127
+ case AST_NODE_TYPES.SequenceExpression:
1128
+ if (parent.expressions[parent.expressions.length - 1] !== node) return false;
1129
+ break;
1130
+ default: if (parent.type.endsWith("Statement") || parent.type.endsWith("Declaration")) return true;
1131
+ }
1132
+ node = parent;
1133
+ ({parent} = parent);
1134
+ }
1135
+ return false;
1136
+ }
1137
+ const funcNode = getUpperFunction(id);
1138
+ return !!funcNode && isInside(funcNode, rightHandSideNode) && isStorableFunction(funcNode, rightHandSideNode);
1139
+ }
1140
+ const id = ref.identifier;
1141
+ const { parent } = id;
1142
+ return ref.isRead() && (parent.type === AST_NODE_TYPES.AssignmentExpression && !LOGICAL_ASSIGNMENT_OPERATORS.has(parent.operator) && isUnusedExpression(parent) && parent.left === id || parent.type === AST_NODE_TYPES.UpdateExpression && isUnusedExpression(parent) || !!rhsNode && isInside(id, rhsNode) && !isInsideOfStorableFunction(id, rhsNode));
1143
+ }
1144
+ const functionNodes = getFunctionDefinitions(variable);
1145
+ const isFunctionDefinition = functionNodes.size > 0;
1146
+ const typeDeclNodes = getTypeDeclarations(variable);
1147
+ const isTypeDecl = typeDeclNodes.size > 0;
1148
+ const moduleDeclNodes = getModuleDeclarations(variable);
1149
+ const isModuleDecl = moduleDeclNodes.size > 0;
1150
+ const enumDeclNodes = getEnumDeclarations(variable);
1151
+ const isEnumDecl = enumDeclNodes.size > 0;
1152
+ const isImportedAsType = variable.defs.every(isTypeImport);
1153
+ let rhsNode;
1154
+ return variable.references.some((ref) => {
1155
+ const forItself = isReadForItself(ref, rhsNode);
1156
+ rhsNode = getRhsNode(ref, rhsNode);
1157
+ return ref.isRead() && !forItself && (isImportedAsType || !referenceContainsTypeQuery(ref.identifier)) && (!isFunctionDefinition || !isSelfReference(ref, functionNodes)) && (!isTypeDecl || !isInsideOneOf(ref, typeDeclNodes)) && (!isModuleDecl || !isSelfReference(ref, moduleDeclNodes)) && (!isEnumDecl || !isSelfReference(ref, enumDeclNodes));
1158
+ });
1159
+ }
1160
+ //#endregion
1161
+ //#region src/rules/naming-convention/utils/enums.ts
1162
+ const Selector = {
1163
+ variable: 1,
1164
+ function: 2,
1165
+ parameter: 4,
1166
+ objectStyleEnum: 8,
1167
+ parameterProperty: 16,
1168
+ classicAccessor: 32,
1169
+ enumMember: 64,
1170
+ classMethod: 128,
1171
+ objectLiteralMethod: 256,
1172
+ typeMethod: 512,
1173
+ classProperty: 1024,
1174
+ objectLiteralProperty: 2048,
1175
+ typeProperty: 4096,
1176
+ autoAccessor: 8192,
1177
+ class: 16384,
1178
+ interface: 32768,
1179
+ typeAlias: 65536,
1180
+ enum: 131072,
1181
+ typeParameter: 262144,
1182
+ import: 524288
1183
+ };
1184
+ const MetaSelector = {
1185
+ default: -1,
1186
+ variableLike: 15,
1187
+ memberLike: 16368,
1188
+ typeLike: 507904,
1189
+ method: 896,
1190
+ property: 7168,
1191
+ accessor: 8224
1192
+ };
1193
+ const Modifier = {
1194
+ "const": 1,
1195
+ "readonly": 2,
1196
+ "static": 4,
1197
+ "public": 8,
1198
+ "protected": 16,
1199
+ "private": 32,
1200
+ "#private": 64,
1201
+ "abstract": 128,
1202
+ "destructured": 256,
1203
+ "global": 512,
1204
+ "exported": 1024,
1205
+ "unused": 2048,
1206
+ "requiresQuotes": 4096,
1207
+ "override": 8192,
1208
+ "async": 16384,
1209
+ "default": 32768,
1210
+ "namespace": 65536
1211
+ };
1212
+ const PredefinedFormat = {
1213
+ camelCase: 1,
1214
+ strictCamelCase: 2,
1215
+ PascalCase: 3,
1216
+ StrictPascalCase: 4,
1217
+ snake_case: 5,
1218
+ UPPER_CASE: 6
1219
+ };
1220
+ const PredefinedFormatValueToKey = Object.fromEntries(Object.entries(PredefinedFormat).map(([key, value]) => [value, key]));
1221
+ const TypeModifier = {
1222
+ boolean: 131072,
1223
+ string: 262144,
1224
+ number: 524288,
1225
+ function: 1048576,
1226
+ array: 2097152
1227
+ };
1228
+ const TypeModifierValueToKey = Object.fromEntries(Object.entries(TypeModifier).map(([key, value]) => [value, key]));
1229
+ const UnderscoreOption = {
1230
+ forbid: 1,
1231
+ allow: 2,
1232
+ require: 3,
1233
+ requireDouble: 4,
1234
+ allowDouble: 5,
1235
+ allowSingleOrDouble: 6
1236
+ };
1237
+ //#endregion
1238
+ //#region src/rules/naming-convention/utils/shared.ts
1239
+ function isMetaSelector(selector) {
1240
+ return selector in MetaSelector;
1241
+ }
1242
+ function isMethodOrPropertySelector(selector) {
1243
+ return selector === MetaSelector.method || selector === MetaSelector.property;
1244
+ }
1245
+ function selectorTypeToMessageString(selectorType) {
1246
+ const notCamelCase = selectorType.replaceAll(/([A-Z])/g, " $1");
1247
+ return notCamelCase.charAt(0).toUpperCase() + notCamelCase.slice(1);
1248
+ }
1249
+ //#endregion
1250
+ //#region src/rules/naming-convention/utils/format.ts
1251
+ function isUppercaseChar(char) {
1252
+ return char === char.toUpperCase() && char !== char.toLowerCase();
1253
+ }
1254
+ function hasStrictCamelHumps(name, isUpper) {
1255
+ if (name.startsWith("_")) return false;
1256
+ for (let index = 1; index < name.length; ++index) {
1257
+ const char = name[index];
1258
+ if (char === "_") return false;
1259
+ if (isUpper === isUppercaseChar(char)) {
1260
+ if (isUpper) return false;
1261
+ } else isUpper = !isUpper;
1262
+ }
1263
+ return true;
1264
+ }
1265
+ function isCamelCase(name) {
1266
+ return name.length === 0 || name[0] === name[0]?.toLowerCase() && !name.includes("_");
1267
+ }
1268
+ function isPascalCase(name) {
1269
+ return name.length === 0 || name[0] === name[0]?.toUpperCase() && !name.includes("_");
1270
+ }
1271
+ /**
1272
+ * Check for leading trailing and adjacent underscores.
1273
+ * @param name - The name to check.
1274
+ * @returns True if the underscores are valid.
1275
+ */
1276
+ function validateUnderscores(name) {
1277
+ if (name.startsWith("_")) return false;
1278
+ let wasUnderscore = false;
1279
+ for (let index = 1; index < name.length; ++index) if (name[index] === "_") {
1280
+ if (wasUnderscore) return false;
1281
+ wasUnderscore = true;
1282
+ } else wasUnderscore = false;
1283
+ return !wasUnderscore;
1284
+ }
1285
+ function isSnakeCase(name) {
1286
+ return name.length === 0 || name === name.toLowerCase() && validateUnderscores(name);
1287
+ }
1288
+ function isStrictCamelCase(name) {
1289
+ return name.length === 0 || name[0] === name[0]?.toLowerCase() && hasStrictCamelHumps(name, false);
1290
+ }
1291
+ function isStrictPascalCase(name) {
1292
+ return name.length === 0 || name[0] === name[0]?.toUpperCase() && hasStrictCamelHumps(name, true);
1293
+ }
1294
+ function isUpperCase(name) {
1295
+ return name.length === 0 || name === name.toUpperCase() && validateUnderscores(name);
1296
+ }
1297
+ const FormatCheckersMap = {
1298
+ [PredefinedFormat.camelCase]: isCamelCase,
1299
+ [PredefinedFormat.PascalCase]: isPascalCase,
1300
+ [PredefinedFormat.snake_case]: isSnakeCase,
1301
+ [PredefinedFormat.strictCamelCase]: isStrictCamelCase,
1302
+ [PredefinedFormat.StrictPascalCase]: isStrictPascalCase,
1303
+ [PredefinedFormat.UPPER_CASE]: isUpperCase
1304
+ };
1305
+ //#endregion
1306
+ //#region src/rules/naming-convention/utils/validator.ts
1307
+ function createValidator(type, context, allConfigs) {
1308
+ const selectorType = Selector[type];
1309
+ const configs = allConfigs.filter((configItem) => {
1310
+ return (configItem.selector & selectorType) !== 0 || configItem.selector === MetaSelector.default;
1311
+ }).sort((a, b) => {
1312
+ if (a.selector === b.selector) return b.modifierWeight - a.modifierWeight;
1313
+ const aIsMeta = isMetaSelector(a.selector);
1314
+ const bIsMeta = isMetaSelector(b.selector);
1315
+ if (aIsMeta && !bIsMeta) return 1;
1316
+ if (!aIsMeta && bIsMeta) return -1;
1317
+ const aIsMethodOrProperty = isMethodOrPropertySelector(a.selector);
1318
+ const bIsMethodOrProperty = isMethodOrPropertySelector(b.selector);
1319
+ if (aIsMethodOrProperty && !bIsMethodOrProperty) return -1;
1320
+ if (!aIsMethodOrProperty && bIsMethodOrProperty) return 1;
1321
+ return b.selector - a.selector;
1322
+ });
1323
+ return (node, modifiers = /* @__PURE__ */ new Set()) => {
1324
+ const originalName = node.type === AST_NODE_TYPES.Identifier || node.type === AST_NODE_TYPES.PrivateIdentifier ? node.name : `${node.value}`;
1325
+ for (const config of configs) {
1326
+ if (config.filter?.regex.test(originalName) !== config.filter?.match) continue;
1327
+ if (config.modifiers?.some((modifier) => !modifiers.has(modifier)) === true) continue;
1328
+ if (!isCorrectType(node, config, context, selectorType)) continue;
1329
+ let name = originalName;
1330
+ name = validateUnderscore({
1331
+ name,
1332
+ config,
1333
+ node,
1334
+ originalName,
1335
+ position: "leading"
1336
+ });
1337
+ if (name === void 0) return;
1338
+ name = validateUnderscore({
1339
+ name,
1340
+ config,
1341
+ node,
1342
+ originalName,
1343
+ position: "trailing"
1344
+ });
1345
+ if (name === void 0) return;
1346
+ name = validateAffix({
1347
+ name,
1348
+ config,
1349
+ node,
1350
+ originalName,
1351
+ position: "prefix"
1352
+ });
1353
+ if (name === void 0) return;
1354
+ name = validateAffix({
1355
+ name,
1356
+ config,
1357
+ node,
1358
+ originalName,
1359
+ position: "suffix"
1360
+ });
1361
+ if (name === void 0) return;
1362
+ if (!validateCustom({
1363
+ name,
1364
+ config,
1365
+ node,
1366
+ originalName
1367
+ })) return;
1368
+ if (!validatePredefinedFormat({
1369
+ name,
1370
+ config,
1371
+ modifiers,
1372
+ node,
1373
+ originalName
1374
+ })) return;
1375
+ return;
1376
+ }
1377
+ };
1378
+ function formatReportData({ affixes, count, custom, formats, originalName, position, processedName }) {
1379
+ let regexMatch = null;
1380
+ if (custom?.match === true) regexMatch = "match";
1381
+ else if (custom?.match === false) regexMatch = "not match";
1382
+ return {
1383
+ name: originalName,
1384
+ affixes: affixes?.join(", "),
1385
+ count,
1386
+ formats: formats?.map((formatItem) => PredefinedFormatValueToKey[formatItem]).join(", "),
1387
+ position,
1388
+ processedName,
1389
+ regex: custom?.regex.toString(),
1390
+ regexMatch,
1391
+ type: selectorTypeToMessageString(type)
1392
+ };
1393
+ }
1394
+ function validateUnderscore({ name, config, node, originalName, position }) {
1395
+ const option = position === "leading" ? config.leadingUnderscore : config.trailingUnderscore;
1396
+ if (!option) return name;
1397
+ const hasSingleUnderscore = position === "leading" ? () => name.startsWith("_") : () => name.endsWith("_");
1398
+ const trimSingleUnderscore = position === "leading" ? () => name.slice(1) : () => name.slice(0, -1);
1399
+ const hasDoubleUnderscore = position === "leading" ? () => name.startsWith("__") : () => name.endsWith("__");
1400
+ const trimDoubleUnderscore = position === "leading" ? () => name.slice(2) : () => name.slice(0, -2);
1401
+ switch (option) {
1402
+ case UnderscoreOption.allow:
1403
+ if (hasSingleUnderscore()) return trimSingleUnderscore();
1404
+ return name;
1405
+ case UnderscoreOption.allowDouble:
1406
+ if (hasDoubleUnderscore()) return trimDoubleUnderscore();
1407
+ return name;
1408
+ case UnderscoreOption.allowSingleOrDouble:
1409
+ if (hasDoubleUnderscore()) return trimDoubleUnderscore();
1410
+ if (hasSingleUnderscore()) return trimSingleUnderscore();
1411
+ return name;
1412
+ case UnderscoreOption.forbid:
1413
+ if (hasSingleUnderscore()) {
1414
+ context.report({
1415
+ data: formatReportData({
1416
+ count: "one",
1417
+ originalName,
1418
+ position
1419
+ }),
1420
+ messageId: "unexpectedUnderscore",
1421
+ node
1422
+ });
1423
+ return;
1424
+ }
1425
+ return name;
1426
+ case UnderscoreOption.require:
1427
+ if (!hasSingleUnderscore()) {
1428
+ context.report({
1429
+ data: formatReportData({
1430
+ count: "one",
1431
+ originalName,
1432
+ position
1433
+ }),
1434
+ messageId: "missingUnderscore",
1435
+ node
1436
+ });
1437
+ return;
1438
+ }
1439
+ return trimSingleUnderscore();
1440
+ case UnderscoreOption.requireDouble:
1441
+ if (!hasDoubleUnderscore()) {
1442
+ context.report({
1443
+ data: formatReportData({
1444
+ count: "two",
1445
+ originalName,
1446
+ position
1447
+ }),
1448
+ messageId: "missingUnderscore",
1449
+ node
1450
+ });
1451
+ return;
1452
+ }
1453
+ return trimDoubleUnderscore();
1454
+ }
1455
+ }
1456
+ function validateAffix({ name, config, node, originalName, position }) {
1457
+ const affixes = config[position];
1458
+ if (!affixes || affixes.length === 0) return name;
1459
+ for (const affix of affixes) {
1460
+ const hasAffix = position === "prefix" ? name.startsWith(affix) : name.endsWith(affix);
1461
+ const trimAffix = position === "prefix" ? () => name.slice(affix.length) : () => name.slice(0, -affix.length);
1462
+ if (hasAffix) return trimAffix();
1463
+ }
1464
+ context.report({
1465
+ data: formatReportData({
1466
+ affixes,
1467
+ originalName,
1468
+ position
1469
+ }),
1470
+ messageId: "missingAffix",
1471
+ node
1472
+ });
1473
+ }
1474
+ function validateCustom({ name, config, node, originalName }) {
1475
+ const { custom } = config;
1476
+ if (!custom) return true;
1477
+ const result = custom.regex.test(name);
1478
+ if (custom.match && result) return true;
1479
+ if (!custom.match && !result) return true;
1480
+ context.report({
1481
+ data: formatReportData({
1482
+ custom,
1483
+ originalName
1484
+ }),
1485
+ messageId: "satisfyCustom",
1486
+ node
1487
+ });
1488
+ return false;
1489
+ }
1490
+ function validatePredefinedFormat({ name, config, modifiers, node, originalName }) {
1491
+ const formats = config.format;
1492
+ if (!formats || formats.length === 0) return true;
1493
+ if (!modifiers.has(Modifier.requiresQuotes)) for (const format of formats) {
1494
+ const checker = FormatCheckersMap[format];
1495
+ if (checker(name)) return true;
1496
+ }
1497
+ context.report({
1498
+ data: formatReportData({
1499
+ formats,
1500
+ originalName,
1501
+ processedName: name
1502
+ }),
1503
+ messageId: originalName === name ? "doesNotMatchFormat" : "doesNotMatchFormatTrimmed",
1504
+ node
1505
+ });
1506
+ return false;
1507
+ }
1508
+ }
1509
+ const SelectorsAllowedToHaveTypes = Selector.variable | Selector.parameter | Selector.classProperty | Selector.objectLiteralProperty | Selector.typeProperty | Selector.parameterProperty | Selector.classicAccessor;
1510
+ function isAllTypesMatch(type, callback) {
1511
+ if (type.isUnion()) return type.types.every((inner) => callback(inner));
1512
+ return callback(type);
1513
+ }
1514
+ function isCorrectType(node, config, context, selector) {
1515
+ if (config.types === void 0) return true;
1516
+ if ((SelectorsAllowedToHaveTypes & selector) === 0) return true;
1517
+ const services = getParserServices(context);
1518
+ const checker = services.program.getTypeChecker();
1519
+ const type = services.getTypeAtLocation(node).getNonNullableType();
1520
+ for (const allowedType of config.types) switch (allowedType) {
1521
+ case TypeModifier.array:
1522
+ if (isAllTypesMatch(type, (inner) => checker.isArrayType(inner) || checker.isTupleType(inner))) return true;
1523
+ break;
1524
+ case TypeModifier.boolean:
1525
+ case TypeModifier.number:
1526
+ case TypeModifier.string:
1527
+ if (checker.typeToString(checker.getWidenedType(checker.getBaseTypeOfLiteralType(type))) === TypeModifierValueToKey[allowedType]) return true;
1528
+ break;
1529
+ case TypeModifier.function:
1530
+ if (isAllTypesMatch(type, (inner) => inner.getCallSignatures().length > 0)) return true;
1531
+ break;
1532
+ }
1533
+ return false;
1534
+ }
1535
+ //#endregion
1536
+ //#region src/rules/naming-convention/utils/parse-options.ts
1537
+ function parseOptions(context) {
1538
+ const normalizedOptions = context.options.flatMap(normalizeOption);
1539
+ return Object.fromEntries(Object.keys(Selector).map((key) => [key, createValidator(key, context, normalizedOptions)]));
1540
+ }
1541
+ function normalizeOption(option) {
1542
+ let weight = 0;
1543
+ if (option.modifiers) for (const modifier of option.modifiers) weight |= Modifier[modifier];
1544
+ if (option.types) for (const type of option.types) weight |= TypeModifier[type];
1545
+ if (option.filter !== void 0) weight |= 1 << 30;
1546
+ const normalizedOption = {
1547
+ custom: option.custom ? {
1548
+ match: option.custom.match,
1549
+ regex: new RegExp(option.custom.regex, "u")
1550
+ } : void 0,
1551
+ filter: option.filter !== void 0 ? typeof option.filter === "string" ? {
1552
+ match: true,
1553
+ regex: new RegExp(option.filter, "u")
1554
+ } : {
1555
+ match: option.filter.match,
1556
+ regex: new RegExp(option.filter.regex, "u")
1557
+ } : void 0,
1558
+ format: option.format ? option.format.map((format) => PredefinedFormat[format]) : void 0,
1559
+ leadingUnderscore: option.leadingUnderscore !== void 0 ? UnderscoreOption[option.leadingUnderscore] : void 0,
1560
+ modifiers: option.modifiers?.map((modifier) => Modifier[modifier]) ?? void 0,
1561
+ modifierWeight: weight,
1562
+ prefix: option.prefix && option.prefix.length > 0 ? option.prefix : void 0,
1563
+ suffix: option.suffix && option.suffix.length > 0 ? option.suffix : void 0,
1564
+ trailingUnderscore: option.trailingUnderscore !== void 0 ? UnderscoreOption[option.trailingUnderscore] : void 0,
1565
+ types: option.types?.map((type) => TypeModifier[type]) ?? void 0
1566
+ };
1567
+ return (Array.isArray(option.selector) ? option.selector : [option.selector]).map((selector) => {
1568
+ return {
1569
+ selector: isMetaSelector(selector) ? MetaSelector[selector] : Selector[selector],
1570
+ ...normalizedOption
1571
+ };
1572
+ });
1573
+ }
1574
+ //#endregion
1575
+ //#region src/rules/naming-convention/utils/schema.ts
1576
+ const $DEFS = {
1577
+ formatOptionsConfig: { oneOf: [{
1578
+ additionalItems: false,
1579
+ items: { $ref: "#/$defs/predefinedFormats" },
1580
+ type: "array"
1581
+ }, { type: "null" }] },
1582
+ matchRegexConfig: {
1583
+ additionalProperties: false,
1584
+ properties: {
1585
+ match: { type: "boolean" },
1586
+ regex: { type: "string" }
1587
+ },
1588
+ required: ["match", "regex"],
1589
+ type: "object"
1590
+ },
1591
+ predefinedFormats: {
1592
+ enum: Object.keys(PredefinedFormat),
1593
+ type: "string"
1594
+ },
1595
+ prefixSuffixConfig: {
1596
+ additionalItems: false,
1597
+ items: {
1598
+ minLength: 1,
1599
+ type: "string"
1600
+ },
1601
+ type: "array"
1602
+ },
1603
+ typeModifiers: {
1604
+ enum: Object.keys(TypeModifier),
1605
+ type: "string"
1606
+ },
1607
+ underscoreOptions: {
1608
+ enum: Object.keys(UnderscoreOption),
1609
+ type: "string"
1610
+ }
1611
+ };
1612
+ const UNDERSCORE_SCHEMA = { $ref: "#/$defs/underscoreOptions" };
1613
+ const PREFIX_SUFFIX_SCHEMA = { $ref: "#/$defs/prefixSuffixConfig" };
1614
+ const MATCH_REGEX_SCHEMA = { $ref: "#/$defs/matchRegexConfig" };
1615
+ const FORMAT_OPTIONS_PROPERTIES = {
1616
+ custom: MATCH_REGEX_SCHEMA,
1617
+ failureMessage: { type: "string" },
1618
+ format: { $ref: "#/$defs/formatOptionsConfig" },
1619
+ leadingUnderscore: UNDERSCORE_SCHEMA,
1620
+ prefix: PREFIX_SUFFIX_SCHEMA,
1621
+ suffix: PREFIX_SUFFIX_SCHEMA,
1622
+ trailingUnderscore: UNDERSCORE_SCHEMA
1623
+ };
1624
+ function selectorSchema(selectorString, allowType, modifiers) {
1625
+ const selector = {
1626
+ filter: { oneOf: [{
1627
+ minLength: 1,
1628
+ type: "string"
1629
+ }, MATCH_REGEX_SCHEMA] },
1630
+ selector: {
1631
+ enum: [selectorString],
1632
+ type: "string"
1633
+ }
1634
+ };
1635
+ if (modifiers && modifiers.length > 0) selector["modifiers"] = {
1636
+ additionalItems: false,
1637
+ items: {
1638
+ enum: modifiers,
1639
+ type: "string"
1640
+ },
1641
+ type: "array"
1642
+ };
1643
+ if (allowType) selector["types"] = {
1644
+ additionalItems: false,
1645
+ items: { $ref: "#/$defs/typeModifiers" },
1646
+ type: "array"
1647
+ };
1648
+ return [{
1649
+ additionalProperties: false,
1650
+ description: `Selector '${selectorString}'`,
1651
+ properties: {
1652
+ ...FORMAT_OPTIONS_PROPERTIES,
1653
+ ...selector
1654
+ },
1655
+ required: ["selector", "format"],
1656
+ type: "object"
1657
+ }];
1658
+ }
1659
+ function selectorsSchema() {
1660
+ return {
1661
+ additionalProperties: false,
1662
+ description: "Multiple selectors in one config",
1663
+ properties: {
1664
+ ...FORMAT_OPTIONS_PROPERTIES,
1665
+ filter: { oneOf: [{
1666
+ minLength: 1,
1667
+ type: "string"
1668
+ }, MATCH_REGEX_SCHEMA] },
1669
+ modifiers: {
1670
+ additionalItems: false,
1671
+ items: {
1672
+ enum: Object.keys(Modifier),
1673
+ type: "string"
1674
+ },
1675
+ type: "array"
1676
+ },
1677
+ selector: {
1678
+ additionalItems: false,
1679
+ items: {
1680
+ enum: [...Object.keys(MetaSelector), ...Object.keys(Selector)],
1681
+ type: "string"
1682
+ },
1683
+ type: "array"
1684
+ },
1685
+ types: {
1686
+ additionalItems: false,
1687
+ items: { $ref: "#/$defs/typeModifiers" },
1688
+ type: "array"
1689
+ }
1690
+ },
1691
+ required: ["selector", "format"],
1692
+ type: "object"
1693
+ };
1694
+ }
1695
+ const SCHEMA = {
1696
+ $defs: $DEFS,
1697
+ additionalItems: false,
1698
+ items: { oneOf: [
1699
+ selectorsSchema(),
1700
+ ...selectorSchema("default", false, Object.keys(Modifier)),
1701
+ ...selectorSchema("variableLike", false, ["unused", "async"]),
1702
+ ...selectorSchema("variable", true, [
1703
+ "const",
1704
+ "destructured",
1705
+ "exported",
1706
+ "global",
1707
+ "unused",
1708
+ "async"
1709
+ ]),
1710
+ ...selectorSchema("function", false, [
1711
+ "exported",
1712
+ "global",
1713
+ "unused",
1714
+ "async"
1715
+ ]),
1716
+ ...selectorSchema("parameter", true, ["destructured", "unused"]),
1717
+ ...selectorSchema("objectStyleEnum", false, [
1718
+ "const",
1719
+ "exported",
1720
+ "global",
1721
+ "unused"
1722
+ ]),
1723
+ ...selectorSchema("memberLike", false, [
1724
+ "abstract",
1725
+ "private",
1726
+ "#private",
1727
+ "protected",
1728
+ "public",
1729
+ "readonly",
1730
+ "requiresQuotes",
1731
+ "static",
1732
+ "override",
1733
+ "async"
1734
+ ]),
1735
+ ...selectorSchema("classProperty", true, [
1736
+ "abstract",
1737
+ "private",
1738
+ "#private",
1739
+ "protected",
1740
+ "public",
1741
+ "readonly",
1742
+ "requiresQuotes",
1743
+ "static",
1744
+ "override"
1745
+ ]),
1746
+ ...selectorSchema("objectLiteralProperty", true, ["public", "requiresQuotes"]),
1747
+ ...selectorSchema("typeProperty", true, [
1748
+ "public",
1749
+ "readonly",
1750
+ "requiresQuotes"
1751
+ ]),
1752
+ ...selectorSchema("parameterProperty", true, [
1753
+ "private",
1754
+ "protected",
1755
+ "public",
1756
+ "readonly"
1757
+ ]),
1758
+ ...selectorSchema("property", true, [
1759
+ "abstract",
1760
+ "private",
1761
+ "#private",
1762
+ "protected",
1763
+ "public",
1764
+ "readonly",
1765
+ "requiresQuotes",
1766
+ "static",
1767
+ "override",
1768
+ "async"
1769
+ ]),
1770
+ ...selectorSchema("classMethod", false, [
1771
+ "abstract",
1772
+ "private",
1773
+ "#private",
1774
+ "protected",
1775
+ "public",
1776
+ "requiresQuotes",
1777
+ "static",
1778
+ "override",
1779
+ "async"
1780
+ ]),
1781
+ ...selectorSchema("objectLiteralMethod", false, [
1782
+ "public",
1783
+ "requiresQuotes",
1784
+ "async"
1785
+ ]),
1786
+ ...selectorSchema("typeMethod", false, ["public", "requiresQuotes"]),
1787
+ ...selectorSchema("method", false, [
1788
+ "abstract",
1789
+ "private",
1790
+ "#private",
1791
+ "protected",
1792
+ "public",
1793
+ "requiresQuotes",
1794
+ "static",
1795
+ "override",
1796
+ "async"
1797
+ ]),
1798
+ ...selectorSchema("classicAccessor", true, [
1799
+ "abstract",
1800
+ "private",
1801
+ "protected",
1802
+ "public",
1803
+ "requiresQuotes",
1804
+ "static",
1805
+ "override"
1806
+ ]),
1807
+ ...selectorSchema("autoAccessor", true, [
1808
+ "abstract",
1809
+ "private",
1810
+ "protected",
1811
+ "public",
1812
+ "requiresQuotes",
1813
+ "static",
1814
+ "override"
1815
+ ]),
1816
+ ...selectorSchema("accessor", true, [
1817
+ "abstract",
1818
+ "private",
1819
+ "protected",
1820
+ "public",
1821
+ "requiresQuotes",
1822
+ "static",
1823
+ "override"
1824
+ ]),
1825
+ ...selectorSchema("enumMember", false, ["requiresQuotes"]),
1826
+ ...selectorSchema("typeLike", false, [
1827
+ "abstract",
1828
+ "exported",
1829
+ "unused"
1830
+ ]),
1831
+ ...selectorSchema("class", false, [
1832
+ "abstract",
1833
+ "exported",
1834
+ "unused"
1835
+ ]),
1836
+ ...selectorSchema("interface", false, ["exported", "unused"]),
1837
+ ...selectorSchema("typeAlias", false, ["exported", "unused"]),
1838
+ ...selectorSchema("enum", false, ["exported", "unused"]),
1839
+ ...selectorSchema("typeParameter", false, ["unused"]),
1840
+ ...selectorSchema("import", false, ["default", "namespace"])
1841
+ ] },
1842
+ type: "array"
1843
+ };
1844
+ //#endregion
1845
+ //#region src/rules/naming-convention/rule.ts
1846
+ const RULE_NAME$9 = "naming-convention";
1847
+ const messages$9 = {
1848
+ doesNotMatchFormat: "{{type}} name `{{name}}` must match one of the following formats: {{formats}}",
1849
+ doesNotMatchFormatTrimmed: "{{type}} name `{{name}}` trimmed as `{{processedName}}` must match one of the following formats: {{formats}}",
1850
+ missingAffix: "{{type}} name `{{name}}` must have one of the following {{position}}es: {{affixes}}",
1851
+ missingUnderscore: "{{type}} name `{{name}}` must have {{count}} {{position}} underscore(s).",
1852
+ satisfyCustom: "{{type}} name `{{name}}` must {{regexMatch}} the RegExp: {{regex}}",
1853
+ unexpectedUnderscore: "{{type}} name `{{name}}` must not have a {{position}} underscore."
1854
+ };
1855
+ const camelCaseNamingConfig = [
1856
+ {
1857
+ format: ["camelCase"],
1858
+ leadingUnderscore: "allow",
1859
+ selector: "default",
1860
+ trailingUnderscore: "allow"
1861
+ },
1862
+ {
1863
+ format: ["camelCase", "PascalCase"],
1864
+ selector: "import"
1865
+ },
1866
+ {
1867
+ format: ["camelCase", "UPPER_CASE"],
1868
+ leadingUnderscore: "allow",
1869
+ selector: "variable",
1870
+ trailingUnderscore: "allow"
1871
+ },
1872
+ {
1873
+ format: ["PascalCase"],
1874
+ selector: "typeLike"
1875
+ }
1876
+ ];
1877
+ function create$3(contextWithoutDefaults) {
1878
+ const context = contextWithoutDefaults.options.length > 0 ? contextWithoutDefaults : Object.setPrototypeOf({ options: camelCaseNamingConfig }, contextWithoutDefaults);
1879
+ const validators = parseOptions(context);
1880
+ const compilerOptions = getParserServices(context, true).program?.getCompilerOptions() ?? {};
1881
+ function handleMember(validator, { key }, modifiers) {
1882
+ if (requiresQuoting$1(key, compilerOptions.target)) modifiers.add(Modifier.requiresQuotes);
1883
+ validator(key, modifiers);
1884
+ }
1885
+ function getMemberModifiers(node) {
1886
+ const modifiers = /* @__PURE__ */ new Set();
1887
+ if ("key" in node && node.key.type === AST_NODE_TYPES.PrivateIdentifier) modifiers.add(Modifier["#private"]);
1888
+ else if (node.accessibility) modifiers.add(Modifier[node.accessibility]);
1889
+ else modifiers.add(Modifier.public);
1890
+ if (node.static) modifiers.add(Modifier.static);
1891
+ if ("readonly" in node && node.readonly) modifiers.add(Modifier.readonly);
1892
+ if ("override" in node && node.override) modifiers.add(Modifier.override);
1893
+ if (node.type === AST_NODE_TYPES.TSAbstractPropertyDefinition || node.type === AST_NODE_TYPES.TSAbstractMethodDefinition || node.type === AST_NODE_TYPES.TSAbstractAccessorProperty) modifiers.add(Modifier.abstract);
1894
+ return modifiers;
1895
+ }
1896
+ const { unusedVariables } = collectVariables(context);
1897
+ function isUnused(name, initialScope) {
1898
+ let variable = null;
1899
+ let scope = initialScope;
1900
+ while (scope) {
1901
+ variable = scope.set.get(name) ?? null;
1902
+ if (variable) break;
1903
+ scope = scope.upper;
1904
+ }
1905
+ if (!variable) return false;
1906
+ return unusedVariables.has(variable);
1907
+ }
1908
+ function isDestructured(id) {
1909
+ return id.parent.type === AST_NODE_TYPES.Property && id.parent.shorthand || id.parent.type === AST_NODE_TYPES.AssignmentPattern && id.parent.parent.type === AST_NODE_TYPES.Property && id.parent.parent.shorthand;
1910
+ }
1911
+ function isAsyncMemberOrProperty(propertyOrMemberNode) {
1912
+ return Boolean("value" in propertyOrMemberNode && propertyOrMemberNode.value && "async" in propertyOrMemberNode.value && propertyOrMemberNode.value.async);
1913
+ }
1914
+ function isAsyncVariableIdentifier(id) {
1915
+ return Boolean("async" in id.parent && id.parent.async || "init" in id.parent && id.parent.init && "async" in id.parent.init && id.parent.init.async);
1916
+ }
1917
+ /**
1918
+ * Determines if a VariableDeclarator represents an object-style enum declaration.
1919
+ *
1920
+ * Object-style enums are const assertions applied to object expressions, commonly
1921
+ * used as an alternative to TypeScript enums. Examples:
1922
+ * - `const Colors = { RED: 'red', BLUE: 'blue' } as const`
1923
+ * - `const Status = <const>{ OK: 200, ERROR: 500 }`.
1924
+ *
1925
+ * @param node - The VariableDeclarator AST node to check.
1926
+ * @param parent - The parent VariableDeclaration node.
1927
+ * @returns True if this represents an object-style enum declaration.
1928
+ */
1929
+ function isObjectStyleEnumDeclaration(node, parent) {
1930
+ if (parent.kind !== "const") return false;
1931
+ if (!node.init) return false;
1932
+ if (node.init.type === AST_NODE_TYPES.TSAsExpression && node.init.typeAnnotation.type === AST_NODE_TYPES.TSTypeReference && node.init.typeAnnotation.typeName.type === AST_NODE_TYPES.Identifier && node.init.typeAnnotation.typeName.name === "const" && node.init.expression.type === AST_NODE_TYPES.ObjectExpression) return true;
1933
+ if (node.init.type === AST_NODE_TYPES.TSTypeAssertion && node.init.typeAnnotation.type === AST_NODE_TYPES.TSTypeReference && node.init.typeAnnotation.typeName.type === AST_NODE_TYPES.Identifier && node.init.typeAnnotation.typeName.name === "const" && node.init.expression.type === AST_NODE_TYPES.ObjectExpression) return true;
1934
+ return false;
1935
+ }
1936
+ /**
1937
+ * Checks if a method name exists in any of the implemented interfaces.
1938
+ *
1939
+ * @param implementsList - List of implemented interfaces.
1940
+ * @param methodName - Name of the method to check.
1941
+ * @param checker - TypeScript type checker.
1942
+ * @param services - Parser services.
1943
+ * @returns True if the method name is found in any interface.
1944
+ */
1945
+ function checkInterfacesForMethod(implementsList, methodName, checker, services) {
1946
+ for (const implementsClause of implementsList) {
1947
+ const interfaceType = checker.getTypeAtLocation(services.esTreeNodeToTSNodeMap.get(implementsClause));
1948
+ if (!interfaceType.getSymbol()) continue;
1949
+ const interfaceMembers = checker.getPropertiesOfType(interfaceType);
1950
+ for (const member of interfaceMembers) if (member.name === methodName) return true;
1951
+ }
1952
+ return false;
1953
+ }
1954
+ /**
1955
+ * Determines if a class method is implementing an interface method.
1956
+ *
1957
+ * @param node - The class method node to check.
1958
+ * @returns True if the method is implementing an interface method.
1959
+ */
1960
+ function isImplementingInterfaceMethod(node) {
1961
+ const services = getParserServices(context, true);
1962
+ if (!services.program) return false;
1963
+ const checker = services.program.getTypeChecker();
1964
+ let parentClass = null;
1965
+ let current = node.parent;
1966
+ while (current) {
1967
+ if (current.type === AST_NODE_TYPES.ClassDeclaration || current.type === AST_NODE_TYPES.ClassExpression) {
1968
+ parentClass = current;
1969
+ break;
1970
+ }
1971
+ current = current.parent;
1972
+ }
1973
+ if (!parentClass?.implements || parentClass.implements.length === 0) return false;
1974
+ let methodName = null;
1975
+ if (node.key.type === AST_NODE_TYPES.Identifier) methodName = node.key.name;
1976
+ else if (node.key.type === AST_NODE_TYPES.Literal) methodName = String(node.key.value);
1977
+ if (methodName === null) return false;
1978
+ return checkInterfacesForMethod(parentClass.implements, methodName, checker, services);
1979
+ }
1980
+ const selectors = {
1981
+ ":matches(PropertyDefinition, TSAbstractPropertyDefinition)[computed = false][value.type != \"ArrowFunctionExpression\"][value.type != \"FunctionExpression\"][value.type != \"TSEmptyBodyFunctionExpression\"]": {
1982
+ handler: (node, validator) => {
1983
+ handleMember(validator, node, getMemberModifiers(node));
1984
+ },
1985
+ validator: validators.classProperty
1986
+ },
1987
+ ":not(ObjectPattern) > Property[computed = false][kind = \"init\"][value.type != \"ArrowFunctionExpression\"][value.type != \"FunctionExpression\"][value.type != \"TSEmptyBodyFunctionExpression\"]": {
1988
+ handler: (node, validator) => {
1989
+ handleMember(validator, node, /* @__PURE__ */ new Set([Modifier.public]));
1990
+ },
1991
+ validator: validators.objectLiteralProperty
1992
+ },
1993
+ [["TSMethodSignature[computed = false]", "TSPropertySignature[computed = false][typeAnnotation.typeAnnotation.type = \"TSFunctionType\"]"].join(", ")]: {
1994
+ handler: (node, validator) => {
1995
+ handleMember(validator, node, /* @__PURE__ */ new Set([Modifier.public]));
1996
+ },
1997
+ validator: validators.typeMethod
1998
+ },
1999
+ [[
2000
+ ":matches(PropertyDefinition, TSAbstractPropertyDefinition)[computed = false][value.type = \"ArrowFunctionExpression\"]",
2001
+ ":matches(PropertyDefinition, TSAbstractPropertyDefinition)[computed = false][value.type = \"FunctionExpression\"]",
2002
+ ":matches(PropertyDefinition, TSAbstractPropertyDefinition)[computed = false][value.type = \"TSEmptyBodyFunctionExpression\"]",
2003
+ ":matches(MethodDefinition, TSAbstractMethodDefinition)[computed = false][kind = \"method\"]"
2004
+ ].join(", ")]: {
2005
+ handler: (node, validator) => {
2006
+ if (isImplementingInterfaceMethod(node)) return;
2007
+ const modifiers = getMemberModifiers(node);
2008
+ if (isAsyncMemberOrProperty(node)) modifiers.add(Modifier.async);
2009
+ handleMember(validator, node, modifiers);
2010
+ },
2011
+ validator: validators.classMethod
2012
+ },
2013
+ [["MethodDefinition[computed = false]:matches([kind = \"get\"], [kind = \"set\"])", "TSAbstractMethodDefinition[computed = false]:matches([kind=\"get\"], [kind=\"set\"])"].join(", ")]: {
2014
+ handler: (node, validator) => {
2015
+ handleMember(validator, node, getMemberModifiers(node));
2016
+ },
2017
+ validator: validators.classicAccessor
2018
+ },
2019
+ [[
2020
+ "Property[computed = false][kind = \"init\"][value.type = \"ArrowFunctionExpression\"]",
2021
+ "Property[computed = false][kind = \"init\"][value.type = \"FunctionExpression\"]",
2022
+ "Property[computed = false][kind = \"init\"][value.type = \"TSEmptyBodyFunctionExpression\"]"
2023
+ ].join(", ")]: {
2024
+ handler: (node, validator) => {
2025
+ const modifiers = /* @__PURE__ */ new Set([Modifier.public]);
2026
+ if (isAsyncMemberOrProperty(node)) modifiers.add(Modifier.async);
2027
+ handleMember(validator, node, modifiers);
2028
+ },
2029
+ validator: validators.objectLiteralMethod
2030
+ },
2031
+ [[AST_NODE_TYPES.AccessorProperty, AST_NODE_TYPES.TSAbstractAccessorProperty].join(", ")]: {
2032
+ handler: (node, validator) => {
2033
+ handleMember(validator, node, getMemberModifiers(node));
2034
+ },
2035
+ validator: validators.autoAccessor
2036
+ },
2037
+ "ClassDeclaration, ClassExpression": {
2038
+ handler: (node, validator) => {
2039
+ const { id, abstract } = node;
2040
+ if (id === null) return;
2041
+ const modifiers = /* @__PURE__ */ new Set();
2042
+ const scope = context.sourceCode.getScope(node).upper;
2043
+ if (abstract) modifiers.add(Modifier.abstract);
2044
+ if (isExported(node, id.name, scope)) modifiers.add(Modifier.exported);
2045
+ if (isUnused(id.name, scope)) modifiers.add(Modifier.unused);
2046
+ validator(id, modifiers);
2047
+ },
2048
+ validator: validators.class
2049
+ },
2050
+ "FunctionDeclaration, TSDeclareFunction, FunctionExpression": {
2051
+ handler: (node, validator) => {
2052
+ if (node.id === null) return;
2053
+ const modifiers = /* @__PURE__ */ new Set();
2054
+ const scope = context.sourceCode.getScope(node).upper;
2055
+ if (isGlobal(scope)) modifiers.add(Modifier.global);
2056
+ if (isExported(node, node.id.name, scope)) modifiers.add(Modifier.exported);
2057
+ if (isUnused(node.id.name, scope)) modifiers.add(Modifier.unused);
2058
+ if (node.async) modifiers.add(Modifier.async);
2059
+ validator(node.id, modifiers);
2060
+ },
2061
+ validator: validators.function
2062
+ },
2063
+ "FunctionDeclaration, TSDeclareFunction, TSEmptyBodyFunctionExpression, FunctionExpression, ArrowFunctionExpression": {
2064
+ handler: (node, validator) => {
2065
+ for (const parameter of node.params) {
2066
+ if (parameter.type === AST_NODE_TYPES.TSParameterProperty) continue;
2067
+ const identifiers = getIdentifiersFromPattern(parameter);
2068
+ for (const index of identifiers) {
2069
+ const modifiers = /* @__PURE__ */ new Set();
2070
+ if (isDestructured(index)) modifiers.add(Modifier.destructured);
2071
+ if (isUnused(index.name, context.sourceCode.getScope(index))) modifiers.add(Modifier.unused);
2072
+ validator(index, modifiers);
2073
+ }
2074
+ }
2075
+ },
2076
+ validator: validators.parameter
2077
+ },
2078
+ "ImportDefaultSpecifier, ImportNamespaceSpecifier, ImportSpecifier": {
2079
+ handler: (node, validator) => {
2080
+ const modifiers = /* @__PURE__ */ new Set();
2081
+ switch (node.type) {
2082
+ case AST_NODE_TYPES.ImportDefaultSpecifier:
2083
+ modifiers.add(Modifier.default);
2084
+ break;
2085
+ case AST_NODE_TYPES.ImportNamespaceSpecifier:
2086
+ modifiers.add(Modifier.namespace);
2087
+ break;
2088
+ case AST_NODE_TYPES.ImportSpecifier:
2089
+ if (node.imported.type === AST_NODE_TYPES.Identifier && node.imported.name !== "default") return;
2090
+ modifiers.add(Modifier.default);
2091
+ break;
2092
+ }
2093
+ validator(node.local, modifiers);
2094
+ },
2095
+ validator: validators.import
2096
+ },
2097
+ "Property[computed = false]:matches([kind = \"get\"], [kind = \"set\"])": {
2098
+ handler: (node, validator) => {
2099
+ handleMember(validator, node, /* @__PURE__ */ new Set([Modifier.public]));
2100
+ },
2101
+ validator: validators.classicAccessor
2102
+ },
2103
+ "TSEnumDeclaration": {
2104
+ handler: (node, validator) => {
2105
+ const modifiers = /* @__PURE__ */ new Set();
2106
+ const scope = context.sourceCode.getScope(node).upper;
2107
+ if (isExported(node, node.id.name, scope)) modifiers.add(Modifier.exported);
2108
+ if (isUnused(node.id.name, scope)) modifiers.add(Modifier.unused);
2109
+ validator(node.id, modifiers);
2110
+ },
2111
+ validator: validators.enum
2112
+ },
2113
+ "TSEnumMember": {
2114
+ handler: ({ id }, validator) => {
2115
+ const modifiers = /* @__PURE__ */ new Set();
2116
+ if (requiresQuoting$1(id, compilerOptions.target)) modifiers.add(Modifier.requiresQuotes);
2117
+ validator(id, modifiers);
2118
+ },
2119
+ validator: validators.enumMember
2120
+ },
2121
+ "TSInterfaceDeclaration": {
2122
+ handler: (node, validator) => {
2123
+ const modifiers = /* @__PURE__ */ new Set();
2124
+ const scope = context.sourceCode.getScope(node);
2125
+ if (isExported(node, node.id.name, scope)) modifiers.add(Modifier.exported);
2126
+ if (isUnused(node.id.name, scope)) modifiers.add(Modifier.unused);
2127
+ validator(node.id, modifiers);
2128
+ },
2129
+ validator: validators.interface
2130
+ },
2131
+ "TSParameterProperty": {
2132
+ handler: (node, validator) => {
2133
+ const modifiers = getMemberModifiers(node);
2134
+ const identifiers = getIdentifiersFromPattern(node.parameter);
2135
+ for (const index of identifiers) validator(index, modifiers);
2136
+ },
2137
+ validator: validators.parameterProperty
2138
+ },
2139
+ "TSPropertySignature[computed = false][typeAnnotation.typeAnnotation.type != \"TSFunctionType\"]": {
2140
+ handler: (node, validator) => {
2141
+ const modifiers = /* @__PURE__ */ new Set([Modifier.public]);
2142
+ if (node.readonly) modifiers.add(Modifier.readonly);
2143
+ handleMember(validator, node, modifiers);
2144
+ },
2145
+ validator: validators.typeProperty
2146
+ },
2147
+ "TSTypeAliasDeclaration": {
2148
+ handler: (node, validator) => {
2149
+ const modifiers = /* @__PURE__ */ new Set();
2150
+ const scope = context.sourceCode.getScope(node);
2151
+ if (isExported(node, node.id.name, scope)) modifiers.add(Modifier.exported);
2152
+ if (isUnused(node.id.name, scope)) modifiers.add(Modifier.unused);
2153
+ validator(node.id, modifiers);
2154
+ },
2155
+ validator: validators.typeAlias
2156
+ },
2157
+ "TSTypeParameterDeclaration > TSTypeParameter": {
2158
+ handler: (node, validator) => {
2159
+ const modifiers = /* @__PURE__ */ new Set();
2160
+ const scope = context.sourceCode.getScope(node);
2161
+ if (isUnused(node.name.name, scope)) modifiers.add(Modifier.unused);
2162
+ validator(node.name, modifiers);
2163
+ },
2164
+ validator: validators.typeParameter
2165
+ },
2166
+ "VariableDeclarator": {
2167
+ handler: (node, validator) => {
2168
+ const identifiers = getIdentifiersFromPattern(node.id);
2169
+ const baseModifiers = /* @__PURE__ */ new Set();
2170
+ const { parent } = node;
2171
+ if (parent.kind === "const") baseModifiers.add(Modifier.const);
2172
+ if (isGlobal(context.sourceCode.getScope(node))) baseModifiers.add(Modifier.global);
2173
+ const isObjectStyleEnum = isObjectStyleEnumDeclaration(node, parent);
2174
+ for (const id of identifiers) {
2175
+ const modifiers = new Set(baseModifiers);
2176
+ if (isDestructured(id)) modifiers.add(Modifier.destructured);
2177
+ const scope = context.sourceCode.getScope(id);
2178
+ if (isExported(parent, id.name, scope)) modifiers.add(Modifier.exported);
2179
+ if (isUnused(id.name, scope)) modifiers.add(Modifier.unused);
2180
+ if (isAsyncVariableIdentifier(id)) modifiers.add(Modifier.async);
2181
+ if (isObjectStyleEnum) validators.objectStyleEnum(id, modifiers);
2182
+ else validator(id, modifiers);
2183
+ }
2184
+ },
2185
+ validator: validators.variable
2186
+ }
2187
+ };
2188
+ return Object.fromEntries(Object.entries(selectors).map(([selector, { handler, validator }]) => {
2189
+ return [selector, (node) => {
2190
+ handler(node, validator);
2191
+ }];
2192
+ }));
2193
+ }
2194
+ const namingConvention = createEslintRule({
2195
+ name: RULE_NAME$9,
2196
+ create: create$3,
2197
+ defaultOptions: camelCaseNamingConfig,
2198
+ meta: {
2199
+ docs: {
2200
+ description: "Enforce naming conventions for everything across a codebase",
2201
+ recommended: true,
2202
+ requiresTypeChecking: true
2203
+ },
2204
+ fixable: void 0,
2205
+ hasSuggestions: false,
2206
+ messages: messages$9,
2207
+ schema: SCHEMA,
2208
+ type: "suggestion"
2209
+ }
2210
+ });
2211
+ function getIdentifiersFromPattern(pattern) {
2212
+ const identifiers = [];
2213
+ new PatternVisitor({}, pattern, (id) => {
2214
+ identifiers.push(id);
2215
+ }).visit(pattern);
2216
+ return identifiers;
2217
+ }
2218
+ function isExported(node, name, scope) {
2219
+ if (node?.parent?.type === AST_NODE_TYPES.ExportDefaultDeclaration || node?.parent?.type === AST_NODE_TYPES.ExportNamedDeclaration) return true;
2220
+ if (scope === null) return false;
2221
+ const variable = scope.set.get(name);
2222
+ if (variable) for (const ref of variable.references) {
2223
+ const refParent = ref.identifier.parent;
2224
+ if (refParent.type === AST_NODE_TYPES.ExportDefaultDeclaration || refParent.type === AST_NODE_TYPES.ExportSpecifier) return true;
2225
+ }
2226
+ return false;
2227
+ }
2228
+ function isGlobal(scope) {
2229
+ if (scope === null) return false;
2230
+ return scope.type === TSESLint.Scope.ScopeType.global || scope.type === TSESLint.Scope.ScopeType.module;
2231
+ }
2232
+ function requiresQuoting$1(node, target) {
2233
+ return requiresQuoting(node.type === AST_NODE_TYPES.Identifier || node.type === AST_NODE_TYPES.PrivateIdentifier ? node.name : `${node.value}`, target);
2234
+ }
2235
+ //#endregion
2236
+ //#region src/rules/no-export-default-arrow/rule.ts
2237
+ const RULE_NAME$8 = "no-export-default-arrow";
2238
+ const MESSAGE_ID$4 = "disallowExportDefaultArrow";
2239
+ const messages$8 = { [MESSAGE_ID$4]: "Assign the arrow function to a named constant instead of exporting it anonymously." };
2240
+ /**
2241
+ * Converts a filename stem to camelCase, treating `-`, `_`, and whitespace as
2242
+ * word separators (`use-mouse` -> `useMouse`).
2243
+ *
2244
+ * @param value - The filename stem.
2245
+ * @returns The camelCase name.
2246
+ */
2247
+ function toCamelCase(value) {
2248
+ return value.replace(/[-_\s]+(.)?/g, (_, char) => char?.toUpperCase() ?? "").replace(/^[A-Z]/, (char) => char.toLowerCase());
2249
+ }
2250
+ /**
2251
+ * Converts a filename stem to PascalCase, treating `-`, `_`, and whitespace as
2252
+ * word separators (`use-mouse` -> `UseMouse`).
2253
+ *
2254
+ * @param value - The filename stem.
2255
+ * @returns The PascalCase name.
2256
+ */
2257
+ function toPascalCase(value) {
2258
+ return value.replace(/[-_\s]+(.)?/g, (_, char) => char?.toUpperCase() ?? "").replace(/^[a-z]/, (char) => char.toUpperCase());
2259
+ }
2260
+ /**
2261
+ * Checks whether a node is a JSX element or fragment.
2262
+ *
2263
+ * @param node - The expression to test.
2264
+ * @returns `true` when the node renders JSX.
2265
+ */
2266
+ function isJsxElement(node) {
2267
+ return node.type === AST_NODE_TYPES.JSXElement || node.type === AST_NODE_TYPES.JSXFragment;
2268
+ }
2269
+ /**
2270
+ * Collects the expressions an arrow function can return: the body itself for a
2271
+ * concise arrow, or every `return` argument for a block body.
2272
+ *
2273
+ * @param body - The arrow function's body.
2274
+ * @returns The returned expressions, in source order.
2275
+ */
2276
+ function getArrowReturnValues(body) {
2277
+ if (body.type !== AST_NODE_TYPES.BlockStatement) return [body];
2278
+ return body.body.filter((node) => node.type === AST_NODE_TYPES.ReturnStatement).map((node) => node.argument).filter((argument) => argument !== null);
2279
+ }
2280
+ /**
2281
+ * Determines whether an arrow function is a component — that is, whether any of
2282
+ * its return values is JSX — which selects PascalCase for the generated name.
2283
+ *
2284
+ * @param body - The arrow function's body.
2285
+ * @returns `true` when the arrow can return JSX.
2286
+ */
2287
+ function arrowReturnIsJsxElement(body) {
2288
+ return getArrowReturnValues(body).some((node) => isJsxElement(node));
2289
+ }
2290
+ /**
2291
+ * Builds the autofix: replaces the `export default` declaration with a named
2292
+ * `const` derived from the filename, and appends `export default <name>` after
2293
+ * the file's last token (comments included, so a trailing comment keeps its
2294
+ * position).
2295
+ *
2296
+ * @param options - The reported arrow, its export declaration, and the context
2297
+ * and source code needed to derive the name and locate the file's end.
2298
+ * @returns A fixer callback producing both edits.
2299
+ */
2300
+ function createFixFunction({ arrowFunction, context, exportDeclaration, sourceCode }) {
2301
+ return (fixer) => {
2302
+ const program = sourceCode.ast;
2303
+ const lastToken = sourceCode.getLastToken(program, { includeComments: true });
2304
+ const fileName = context.physicalFilename || context.filename || "namedFunction";
2305
+ const { name: stem } = path.parse(fileName);
2306
+ const functionName = arrowReturnIsJsxElement(arrowFunction.body) ? toPascalCase(stem) : toCamelCase(stem);
2307
+ return [fixer.replaceText(exportDeclaration, `const ${functionName} = ${sourceCode.getText(arrowFunction)}`), fixer.insertTextAfter(lastToken ?? exportDeclaration, `\n\nexport default ${functionName}`)];
2308
+ };
2309
+ }
2310
+ /**
2311
+ * Reports anonymous arrow functions used as `export default`, which surface as
2312
+ * unnamed functions in stack traces and devtools.
2313
+ *
2314
+ * @param context - The rule context.
2315
+ * @returns The rule listener.
2316
+ */
2317
+ function createOnce$5(context) {
2318
+ return { ArrowFunctionExpression(node) {
2319
+ const { parent } = node;
2320
+ if (parent.type !== AST_NODE_TYPES.ExportDefaultDeclaration) return;
2321
+ context.report({
2322
+ fix: createFixFunction({
2323
+ arrowFunction: node,
2324
+ context,
2325
+ exportDeclaration: parent,
2326
+ sourceCode: context.sourceCode
2327
+ }),
2328
+ messageId: MESSAGE_ID$4,
2329
+ node
2330
+ });
2331
+ } };
2332
+ }
2333
+ const noExportDefaultArrow = createFlawlessRule({
2334
+ name: RULE_NAME$8,
2335
+ createOnce: createOnce$5,
2336
+ defaultOptions: [],
2337
+ meta: {
2338
+ docs: {
2339
+ description: "Disallow anonymous arrow functions as export default declarations",
2340
+ recommended: false,
2341
+ requiresTypeChecking: false
2342
+ },
2343
+ fixable: "code",
2344
+ hasSuggestions: false,
2345
+ messages: messages$8,
2346
+ schema: [],
2347
+ type: "suggestion"
2348
+ }
2349
+ });
2350
+ //#endregion
2351
+ //#region src/utils/nested-expressions.ts
2352
+ /**
2353
+ * Determines whether the given AST subtree contains a call or `new`
2354
+ * expression.
2355
+ *
2356
+ * Replaces `@eslint-react/ast`'s `getNestedCallExpressions` /
2357
+ * `getNestedNewExpressions` (not exposed by `@eslint-react/kit`). The `useMemo`
2358
+ * rule uses this to avoid flagging a factory that performs real computation.
2359
+ * The walk descends into every child node (skipping the `parent` back-link to
2360
+ * avoid cycles), so calls nested in awaits, tagged templates, computed member
2361
+ * expressions, call targets and the like are all detected.
2362
+ *
2363
+ * @param root - The subtree root to inspect (typically a function body).
2364
+ * @returns `true` if a `CallExpression` or `NewExpression` is present.
2365
+ */
2366
+ function hasNestedCallOrNew(root) {
2367
+ const stack = [root];
2368
+ while (stack.length > 0) {
2369
+ const current = stack.pop();
2370
+ if (current === void 0) break;
2371
+ if (current.type === AST_NODE_TYPES.CallExpression || current.type === AST_NODE_TYPES.NewExpression) return true;
2372
+ pushChildNodes(current, stack);
2373
+ }
2374
+ return false;
2375
+ }
2376
+ /**
2377
+ * Type guard for an AST node value encountered while walking arbitrary
2378
+ * properties of a parent node.
2379
+ *
2380
+ * @param value - The candidate value.
2381
+ * @returns `true` if the value looks like a `TSESTree.Node`.
2382
+ */
2383
+ function isNode(value) {
2384
+ return typeof value === "object" && value !== null && typeof value.type === "string";
2385
+ }
2386
+ /**
2387
+ * Pushes every child node of `node` onto the traversal stack, skipping the
2388
+ * `parent` back-link.
2389
+ *
2390
+ * @param node - The node whose children to enqueue.
2391
+ * @param stack - The traversal stack to push onto.
2392
+ */
2393
+ function pushChildNodes(node, stack) {
2394
+ for (const key of Object.keys(node)) {
2395
+ if (key === "parent") continue;
2396
+ const value = node[key];
2397
+ if (Array.isArray(value)) {
2398
+ for (const item of value) if (isNode(item)) stack.push(item);
2399
+ } else if (isNode(value)) stack.push(value);
2400
+ }
2401
+ }
2402
+ //#endregion
2403
+ //#region src/utils/resolve.ts
2404
+ /**
2405
+ * Resolves an identifier to the AST node that represents its value.
2406
+ *
2407
+ * This is a focused re-implementation of `@eslint-react/var`'s `resolve`
2408
+ * covering only the cases the unnecessary-hook rules need: variable
2409
+ * initializers and function/class declarations. Every other definition kind
2410
+ * (imports, parameters, catch bindings, ...) resolves to `null`.
2411
+ *
2412
+ * @param sourceCode - Provides the scope used to look up the binding.
2413
+ * @param node - The identifier to resolve.
2414
+ * @returns The resolved value node, or `null` when it cannot be determined.
2415
+ */
2416
+ function resolve(sourceCode, node) {
2417
+ const variable = findVariable(sourceCode.getScope(node), node);
2418
+ if (variable === null) return null;
2419
+ const definition = variable.defs.at(0);
2420
+ if (definition === void 0) return null;
2421
+ if (definition.type === DefinitionType.ClassName || definition.type === DefinitionType.FunctionName) return definition.node;
2422
+ if (definition.type === DefinitionType.Variable) return definition.node.init;
2423
+ return null;
2424
+ }
2425
+ //#endregion
2426
+ //#region src/utils/unnecessary-hook.ts
2427
+ /**
2428
+ * Builds the `create` for the `no-unnecessary-use-memo` /
2429
+ * `no-unnecessary-use-callback` rules.
2430
+ *
2431
+ * Faithfully ports the rules removed from `eslint-plugin-react-x` (they were
2432
+ * dropped because the React Compiler makes them redundant; the Roblox /
2433
+ * `@rbxts/react` ecosystem has no Compiler). Hook detection is delegated to
2434
+ * `@eslint-react/core` so it matches the upstream semantics (fully-qualified
2435
+ * name resolution across imports, namespaces and `require`).
2436
+ *
2437
+ * @param config - The hook-specific configuration.
2438
+ * @returns A rule `createOnce` function.
2439
+ * @template MessageIds - The rule's message identifiers.
2440
+ */
2441
+ function createUnnecessaryHookRule({ hook, messageIds }) {
2442
+ const skipComputation = hook === "useMemo";
2443
+ return (context) => {
2444
+ const reactContext = context;
2445
+ const detectHookCall = hook === "useMemo" ? core.isUseMemoCall : core.isUseCallbackCall;
2446
+ return { VariableDeclarator(node) {
2447
+ const { sourceCode } = context;
2448
+ const { id, init } = node;
2449
+ if (id.type !== AST_NODE_TYPES.Identifier || init?.type !== AST_NODE_TYPES.CallExpression || !detectHookCall(reactContext, init)) return;
2450
+ const [variable, ...rest] = sourceCode.getDeclaredVariables(node);
2451
+ if (variable === void 0 || rest.length > 0) return;
2452
+ const insideEffectReport = checkForUsageInsideUseEffect(sourceCode, init, messageIds.insideUseEffect);
2453
+ const component = sourceCode.getScope(init).block;
2454
+ if (!ASTUtils.isFunction(component)) return;
2455
+ const [argument0, argument1] = init.arguments;
2456
+ if (argument0 === void 0 || argument1 === void 0) return;
2457
+ if (skipComputation && ASTUtils.isFunction(argument0) && hasNestedCallOrNew(argument0.body)) {
2458
+ reportIf(context, insideEffectReport);
2459
+ return;
2460
+ }
2461
+ if (!hasEmptyDeps(sourceCode, argument1)) {
2462
+ reportIf(context, insideEffectReport);
2463
+ return;
2464
+ }
2465
+ const factory = resolveFactory(sourceCode, argument0);
2466
+ if (factory === null) return;
2467
+ if (!referencesComponentScope(sourceCode, factory, component)) {
2468
+ context.report({
2469
+ messageId: messageIds.default,
2470
+ node
2471
+ });
2472
+ return;
2473
+ }
2474
+ reportIf(context, insideEffectReport);
2475
+ } };
2476
+ };
2477
+ }
2478
+ /**
2479
+ * Finds the nearest ancestor that is a `useEffect`-like call.
2480
+ *
2481
+ * @param node - The node to search upward from.
2482
+ * @returns The enclosing effect call, or `null` when there is none.
2483
+ */
2484
+ function findEnclosingEffect(node) {
2485
+ let current = node.parent;
2486
+ while (current !== void 0) {
2487
+ if (core.isUseEffectLikeCall(current)) return current;
2488
+ if (current.type === AST_NODE_TYPES.Program) return null;
2489
+ current = current.parent;
2490
+ }
2491
+ return null;
2492
+ }
2493
+ /**
2494
+ * Reports the "used inside a single useEffect" case when applicable.
2495
+ *
2496
+ * @param sourceCode - Provides declared-variable and text lookups.
2497
+ * @param node - The hook call expression.
2498
+ * @param messageId - The message id to report.
2499
+ * @returns A report descriptor, or `null` when the case does not apply.
2500
+ * @template MessageIds - The rule's message identifiers.
2501
+ */
2502
+ function checkForUsageInsideUseEffect(sourceCode, node, messageId) {
2503
+ if (!/use\w*Effect/u.test(sourceCode.text)) return null;
2504
+ const { parent } = node;
2505
+ if (parent.type !== AST_NODE_TYPES.VariableDeclarator || parent.id.type !== AST_NODE_TYPES.Identifier) return null;
2506
+ const usages = (sourceCode.getDeclaredVariables(parent).at(0)?.references ?? []).filter((reference) => reference.init !== true);
2507
+ if (usages.length === 0) return null;
2508
+ const effects = /* @__PURE__ */ new Set();
2509
+ for (const usage of usages) {
2510
+ const effect = findEnclosingEffect(usage.identifier);
2511
+ if (effect === null) return null;
2512
+ effects.add(effect);
2513
+ if (effects.size > 1) return null;
2514
+ }
2515
+ return {
2516
+ data: { name: parent.id.name },
2517
+ messageId,
2518
+ node
2519
+ };
2520
+ }
2521
+ /**
2522
+ * Determines whether a dependency argument is an empty array (directly or via a
2523
+ * resolvable identifier).
2524
+ *
2525
+ * @param sourceCode - Provides scope lookup to resolve identifiers.
2526
+ * @param node - The dependency argument node.
2527
+ * @returns `true` if the dependencies are empty.
2528
+ */
2529
+ function hasEmptyDeps(sourceCode, node) {
2530
+ if (node.type === AST_NODE_TYPES.ArrayExpression) return node.elements.length === 0;
2531
+ if (node.type === AST_NODE_TYPES.Identifier) {
2532
+ const resolved = resolve(sourceCode, node);
2533
+ return resolved?.type === AST_NODE_TYPES.ArrayExpression && resolved.elements.length === 0;
2534
+ }
2535
+ return false;
2536
+ }
2537
+ /**
2538
+ * Collects a scope together with all of its descendant scopes.
2539
+ *
2540
+ * @param scope - The root scope.
2541
+ * @returns The scope and every nested child scope.
2542
+ */
2543
+ function flattenScopes(scope) {
2544
+ return scope.childScopes.reduce((accumulator, child) => [...accumulator, ...flattenScopes(child)], [scope]);
2545
+ }
2546
+ /**
2547
+ * Determines whether any reference inside the factory resolves to a binding
2548
+ * declared in the component's scope.
2549
+ *
2550
+ * @param sourceCode - Provides scope lookup for the factory.
2551
+ * @param factory - The memoized factory function node.
2552
+ * @param component - The enclosing component function node.
2553
+ * @returns `true` if the factory reads from the component scope.
2554
+ */
2555
+ function referencesComponentScope(sourceCode, factory, component) {
2556
+ return flattenScopes(sourceCode.getScope(factory)).flatMap((scope) => scope.references).some((reference) => reference.resolved?.scope.block === component);
2557
+ }
2558
+ /**
2559
+ * Reports the descriptor when it is present.
2560
+ *
2561
+ * @param context - The rule context.
2562
+ * @param descriptor - The descriptor to report, or `null` to skip.
2563
+ * @template MessageIds - The rule's message identifiers.
2564
+ */
2565
+ function reportIf(context, descriptor) {
2566
+ if (descriptor !== null) context.report(descriptor);
2567
+ }
2568
+ /**
2569
+ * Resolves the first argument of the hook call to the underlying factory
2570
+ * function node, unwrapping a curried arrow (`() => () => ...`) and resolving
2571
+ * identifiers to their initializer.
2572
+ *
2573
+ * @param sourceCode - Provides scope lookup to resolve identifiers.
2574
+ * @param node - The first hook argument.
2575
+ * @returns The factory function node, or `null` when it is not a function.
2576
+ */
2577
+ function resolveFactory(sourceCode, node) {
2578
+ if (node.type === AST_NODE_TYPES.ArrowFunctionExpression) return node.body.type === AST_NODE_TYPES.ArrowFunctionExpression ? node.body : node;
2579
+ if (node.type === AST_NODE_TYPES.FunctionExpression) return node;
2580
+ if (node.type === AST_NODE_TYPES.Identifier) {
2581
+ const resolved = resolve(sourceCode, node);
2582
+ if (resolved?.type === AST_NODE_TYPES.ArrowFunctionExpression || resolved?.type === AST_NODE_TYPES.FunctionExpression) return resolved;
2583
+ }
2584
+ return null;
2585
+ }
2586
+ //#endregion
2587
+ //#region src/rules/no-unnecessary-use-callback/rule.ts
2588
+ const RULE_NAME$7 = "no-unnecessary-use-callback";
2589
+ const MESSAGE_ID_DEFAULT$1 = "default";
2590
+ const MESSAGE_ID_INSIDE_USE_EFFECT$1 = "noUnnecessaryUseCallbackInsideUseEffect";
2591
+ const messages$7 = {
2592
+ [MESSAGE_ID_DEFAULT$1]: "An 'useCallback' with empty deps and no references to the component scope may be unnecessary.",
2593
+ [MESSAGE_ID_INSIDE_USE_EFFECT$1]: "'{{name}}' is only used inside 1 useEffect, which may be unnecessary. You can move the computation into useEffect directly and merge the dependency arrays."
2594
+ };
2595
+ const noUnnecessaryUseCallback = createFlawlessRule({
2596
+ name: RULE_NAME$7,
2597
+ createOnce: createUnnecessaryHookRule({
2598
+ hook: "useCallback",
2599
+ messageIds: {
2600
+ default: MESSAGE_ID_DEFAULT$1,
2601
+ insideUseEffect: MESSAGE_ID_INSIDE_USE_EFFECT$1
2602
+ }
2603
+ }),
2604
+ defaultOptions: [],
2605
+ meta: {
2606
+ docs: {
2607
+ description: "Disallow unnecessary usage of 'useCallback'",
2608
+ recommended: false,
2609
+ requiresTypeChecking: false
2610
+ },
2611
+ hasSuggestions: false,
2612
+ messages: messages$7,
2613
+ schema: [],
2614
+ type: "suggestion"
2615
+ }
2616
+ });
2617
+ //#endregion
2618
+ //#region src/rules/no-unnecessary-use-memo/rule.ts
2619
+ const RULE_NAME$6 = "no-unnecessary-use-memo";
2620
+ const MESSAGE_ID_DEFAULT = "default";
2621
+ const MESSAGE_ID_INSIDE_USE_EFFECT = "noUnnecessaryUseMemoInsideUseEffect";
2622
+ const messages$6 = {
2623
+ [MESSAGE_ID_DEFAULT]: "An 'useMemo' with empty deps and no references to the component scope may be unnecessary.",
2624
+ [MESSAGE_ID_INSIDE_USE_EFFECT]: "'{{name}}' is only used inside 1 useEffect, which may be unnecessary. You can move the computation into useEffect directly and merge the dependency arrays."
2625
+ };
2626
+ const noUnnecessaryUseMemo = createFlawlessRule({
2627
+ name: RULE_NAME$6,
2628
+ createOnce: createUnnecessaryHookRule({
2629
+ hook: "useMemo",
2630
+ messageIds: {
2631
+ default: MESSAGE_ID_DEFAULT,
2632
+ insideUseEffect: MESSAGE_ID_INSIDE_USE_EFFECT
2633
+ }
2634
+ }),
2635
+ defaultOptions: [],
2636
+ meta: {
2637
+ docs: {
2638
+ description: "Disallow unnecessary usage of 'useMemo'",
2639
+ recommended: false,
2640
+ requiresTypeChecking: false
2641
+ },
2642
+ hasSuggestions: false,
2643
+ messages: messages$6,
2644
+ schema: [],
2645
+ type: "suggestion"
2646
+ }
2647
+ });
2648
+ //#endregion
2649
+ //#region src/rules/prefer-destructuring-assignment/rule.ts
2650
+ const RULE_NAME$5 = "prefer-destructuring-assignment";
2651
+ const MESSAGE_ID$3 = "default";
2652
+ const messages$5 = { [MESSAGE_ID$3]: "Use destructuring assignment for component props." };
2653
+ /**
2654
+ * Identifiers that cannot be used as a binding name in strict-mode module code.
2655
+ * A property may be accessed with such a name (`props.default`), but a shorthand
2656
+ * destructuring pattern built from it (`{ default }`) would be a syntax error,
2657
+ * so the autofix bails when one is encountered.
2658
+ */
2659
+ const RESERVED_WORDS = /* @__PURE__ */ new Set([
2660
+ "arguments",
2661
+ "await",
2662
+ "break",
2663
+ "case",
2664
+ "catch",
2665
+ "class",
2666
+ "const",
2667
+ "continue",
2668
+ "debugger",
2669
+ "default",
2670
+ "delete",
2671
+ "do",
2672
+ "else",
2673
+ "enum",
2674
+ "eval",
2675
+ "export",
2676
+ "extends",
2677
+ "false",
2678
+ "finally",
2679
+ "for",
2680
+ "function",
2681
+ "if",
2682
+ "implements",
2683
+ "import",
2684
+ "in",
2685
+ "instanceof",
2686
+ "interface",
2687
+ "let",
2688
+ "new",
2689
+ "null",
2690
+ "package",
2691
+ "private",
2692
+ "protected",
2693
+ "public",
2694
+ "return",
2695
+ "static",
2696
+ "super",
2697
+ "switch",
2698
+ "this",
2699
+ "throw",
2700
+ "true",
2701
+ "try",
2702
+ "typeof",
2703
+ "var",
2704
+ "void",
2705
+ "while",
2706
+ "with",
2707
+ "yield"
2708
+ ]);
2709
+ /**
2710
+ * Collects the unique accessed property names, in first-seen order, when every
2711
+ * member reference is a simple non-computed `props.<identifier>` access.
2712
+ *
2713
+ * @param memberReferences - The member accesses to inspect.
2714
+ * @returns The property names, or `null` when any access is not destructurable
2715
+ * (computed access such as `props[key]`, `props` used as a computed key, or a
2716
+ * property whose name is a reserved word that cannot be a binding).
2717
+ */
2718
+ function collectPropertyNames(memberReferences) {
2719
+ const names = [];
2720
+ for (const { member, reference } of memberReferences) {
2721
+ if (member.object !== reference.identifier || member.computed || member.property.type !== AST_NODE_TYPES.Identifier || RESERVED_WORDS.has(member.property.name)) return null;
2722
+ if (!names.includes(member.property.name)) names.push(member.property.name);
2723
+ }
2724
+ return names;
2725
+ }
2726
+ /**
2727
+ * Determines whether rewriting `props.foo` to `foo` would resolve to a different
2728
+ * binding than the new destructured parameter at any access site.
2729
+ *
2730
+ * A name is unsafe when it is already bound in the component scope (other than by
2731
+ * the props parameter itself) or in any scope nested between an access and the
2732
+ * component, since the rewritten `foo` reference would then resolve to that
2733
+ * binding instead of the destructured prop.
2734
+ *
2735
+ * @param sourceCode - Provides scope lookup for each reference.
2736
+ * @param componentScope - The component function's scope.
2737
+ * @param propsVariable - The props parameter variable (excluded from the check).
2738
+ * @param memberReferences - The member accesses that would be rewritten.
2739
+ * @returns `true` if any rewritten name would collide with an existing binding.
2740
+ */
2741
+ function wouldShadowExistingBinding(sourceCode, componentScope, propsVariable, memberReferences) {
2742
+ for (const { member, reference } of memberReferences) {
2743
+ const { name } = member.property;
2744
+ let scope = sourceCode.getScope(reference.identifier);
2745
+ while (scope !== null) {
2746
+ if (scope.variables.some((variable) => variable.name === name && variable !== propsVariable)) return true;
2747
+ if (scope === componentScope) break;
2748
+ scope = scope.upper;
2749
+ }
2750
+ }
2751
+ return false;
2752
+ }
2753
+ /**
2754
+ * Reports every `props.<member>` access on a component's props parameter,
2755
+ * attaching an autofix when the parameter can be safely destructured.
2756
+ *
2757
+ * @param context - The rule context.
2758
+ * @param scope - The component function's scope.
2759
+ * @param propsParameter - The props parameter identifier.
2760
+ * @param propertyVariable - The resolved variable for the props parameter.
2761
+ */
2762
+ function reportComponent(context, scope, propsParameter, propertyVariable) {
2763
+ const memberReferences = [];
2764
+ let hasNonMemberReference = false;
2765
+ for (const reference of propertyVariable.references) {
2766
+ const { parent } = reference.identifier;
2767
+ if (parent.type === AST_NODE_TYPES.MemberExpression) memberReferences.push({
2768
+ member: parent,
2769
+ reference
2770
+ });
2771
+ else hasNonMemberReference = true;
2772
+ }
2773
+ if (memberReferences.length === 0) return;
2774
+ memberReferences.sort((left, right) => left.member.range[0] - right.member.range[0]);
2775
+ const propertyNames = collectPropertyNames(memberReferences);
2776
+ if (!(!hasNonMemberReference && propertyNames !== null && !wouldShadowExistingBinding(context.sourceCode, scope, propertyVariable, memberReferences))) {
2777
+ for (const { member } of memberReferences) context.report({
2778
+ messageId: MESSAGE_ID$3,
2779
+ node: member
2780
+ });
2781
+ return;
2782
+ }
2783
+ const needsParentheses = propsParameter.parent.type === AST_NODE_TYPES.ArrowFunctionExpression && context.sourceCode.getTokenAfter(propsParameter)?.value === "=>";
2784
+ const destructured = `{ ${propertyNames.join(", ")} }`;
2785
+ const pattern = needsParentheses ? `(${destructured})` : destructured;
2786
+ const nameEnd = propsParameter.typeAnnotation?.range[0] ?? propsParameter.range[1];
2787
+ for (const [index, { member }] of memberReferences.entries()) {
2788
+ const propertyName = member.property.name;
2789
+ context.report({
2790
+ fix(fixer) {
2791
+ const replaceAccess = fixer.replaceText(member, propertyName);
2792
+ if (index === 0) return [fixer.replaceTextRange([propsParameter.range[0], nameEnd], pattern), replaceAccess];
2793
+ return replaceAccess;
2794
+ },
2795
+ messageId: MESSAGE_ID$3,
2796
+ node: member
2797
+ });
2798
+ }
2799
+ }
2800
+ /**
2801
+ * Faithfully ports `react-x/prefer-destructuring-assignment`, removed from
2802
+ * `eslint-plugin-react-x` in v5.0.0 (deprecated due to low usage). Component
2803
+ * detection is delegated to `@eslint-react/core` so it matches the upstream
2804
+ * semantics.
2805
+ *
2806
+ * Unlike upstream, this port adds an autofix that rewrites the props parameter
2807
+ * into a destructuring pattern (`(props) => props.id` becomes `({ id }) => id`).
2808
+ * The fix is only offered when it is unambiguously safe; see
2809
+ * {@link collectPropertyNames} and {@link wouldShadowExistingBinding}.
2810
+ *
2811
+ * @param context - The rule context.
2812
+ * @returns The rule listener.
2813
+ */
2814
+ function createOnce$2(context) {
2815
+ let collector = core.getFunctionComponentCollector(context);
2816
+ const selectorKeys = Object.keys(collector.visitor);
2817
+ const listener = {
2818
+ "before": function() {
2819
+ collector = core.getFunctionComponentCollector(context);
2820
+ },
2821
+ "Program:exit": function(program) {
2822
+ for (const component of collector.api.getAllComponents(program)) {
2823
+ if (component.name === null || component.isExportDefaultDeclaration) continue;
2824
+ const [propsParameter] = component.node.params;
2825
+ if (propsParameter?.type !== AST_NODE_TYPES.Identifier) continue;
2826
+ const scope = context.sourceCode.getScope(component.node);
2827
+ const propertyVariable = scope.variables.find((variable) => variable.name === propsParameter.name);
2828
+ if (propertyVariable === void 0) continue;
2829
+ reportComponent(context, scope, propsParameter, propertyVariable);
2830
+ }
2831
+ }
2832
+ };
2833
+ const handlers = listener;
2834
+ for (const key of selectorKeys) handlers[key] = (node) => {
2835
+ collector.visitor[key]?.(node);
2836
+ };
2837
+ return listener;
2838
+ }
2839
+ const preferDestructuringAssignment = createFlawlessRule({
2840
+ name: RULE_NAME$5,
2841
+ createOnce: createOnce$2,
2842
+ defaultOptions: [],
2843
+ meta: {
2844
+ docs: {
2845
+ description: "Enforce destructuring assignment for component props",
2846
+ recommended: false,
2847
+ requiresTypeChecking: false
2848
+ },
2849
+ fixable: "code",
2850
+ hasSuggestions: false,
2851
+ messages: messages$5,
2852
+ schema: [],
2853
+ type: "problem"
2854
+ }
2855
+ });
2856
+ //#endregion
2857
+ //#region src/rules/prefer-parameter-destructuring/rule.ts
2858
+ const RULE_NAME$4 = "prefer-parameter-destructuring";
2859
+ const MESSAGE_ID$2 = "default";
2860
+ const messages$4 = { [MESSAGE_ID$2]: "Destructure parameter '{{name}}' in the function signature instead of the body." };
2861
+ /**
2862
+ * Collects the binding names introduced by a pattern (or parameter), including
2863
+ * names nested in object/array patterns, defaults, and rest elements.
2864
+ *
2865
+ * @param node - The pattern node to walk.
2866
+ * @param out - Receives every bound name, in source order.
2867
+ */
2868
+ function collectBoundNames(node, out) {
2869
+ switch (node.type) {
2870
+ case AST_NODE_TYPES.ArrayPattern:
2871
+ for (const element of node.elements) if (element !== null) collectBoundNames(element, out);
2872
+ break;
2873
+ case AST_NODE_TYPES.AssignmentPattern:
2874
+ collectBoundNames(node.left, out);
2875
+ break;
2876
+ case AST_NODE_TYPES.Identifier:
2877
+ out.push(node.name);
2878
+ break;
2879
+ case AST_NODE_TYPES.ObjectPattern:
2880
+ for (const property of node.properties) collectBoundNames(property, out);
2881
+ break;
2882
+ case AST_NODE_TYPES.Property:
2883
+ collectBoundNames(node.value, out);
2884
+ break;
2885
+ case AST_NODE_TYPES.RestElement:
2886
+ collectBoundNames(node.argument, out);
2887
+ break;
2888
+ case AST_NODE_TYPES.TSParameterProperty:
2889
+ collectBoundNames(node.parameter, out);
2890
+ break;
2891
+ default: break;
2892
+ }
2893
+ }
2894
+ /**
2895
+ * Collects every top-level-body object destructuring of the parameter, or
2896
+ * bails when the parameter has any other reference (member access, call
2897
+ * argument, reassignment, a destructure inside a nested block or closure, …) —
2898
+ * those mean the parameter is genuinely used and must stay.
2899
+ *
2900
+ * @param variable - The parameter's resolved scope variable.
2901
+ * @param identifier - The parameter identifier (its default-value write
2902
+ * reference is not a use).
2903
+ * @param body - The function body; only its direct child declarations qualify.
2904
+ * @returns The qualifying destructuring statements in source order, or `null`
2905
+ * when the parameter has a non-destructuring reference.
2906
+ */
2907
+ function collectDestructureStatements(variable, identifier, body) {
2908
+ const statements = [];
2909
+ for (const reference of variable.references) {
2910
+ const referenceIdentifier = reference.identifier;
2911
+ if (referenceIdentifier === identifier) continue;
2912
+ const { parent } = referenceIdentifier;
2913
+ if (parent.type === AST_NODE_TYPES.VariableDeclarator && parent.init === referenceIdentifier && parent.id.type === AST_NODE_TYPES.ObjectPattern && parent.parent.parent === body) statements.push({
2914
+ declaration: parent.parent,
2915
+ declarator: parent,
2916
+ pattern: parent.id
2917
+ });
2918
+ else return null;
2919
+ }
2920
+ statements.sort((left, right) => left.declarator.range[0] - right.declarator.range[0]);
2921
+ return statements;
2922
+ }
2923
+ /**
2924
+ * Collects the binding names of every parameter other than the target one, so
2925
+ * the fix can avoid creating a duplicate parameter name (a SyntaxError in a
2926
+ * non-simple parameter list).
2927
+ *
2928
+ * @param node - The function whose parameters are inspected.
2929
+ * @param parameter - The parameter being rewritten (excluded).
2930
+ * @returns The other parameters' bound names.
2931
+ */
2932
+ function collectOtherParameterNames(node, parameter) {
2933
+ const names = [];
2934
+ for (const other of node.params) if (other !== parameter) collectBoundNames(other, names);
2935
+ return new Set(names);
2936
+ }
2937
+ /**
2938
+ * A structural check for AST nodes reached through visitor keys.
2939
+ *
2940
+ * @param value - The child value to test.
2941
+ * @returns `true` when the value is an AST node.
2942
+ */
2943
+ function isNodeLike(value) {
2944
+ return typeof value === "object" && value !== null && typeof value.type === "string";
2945
+ }
2946
+ /**
2947
+ * Checks whether a pattern subtree contains `await` or `yield` (in a computed
2948
+ * key or default value). Parameter initializers may not contain either, so no
2949
+ * signature form exists and the destructuring is not reported.
2950
+ *
2951
+ * @param root - The pattern to walk.
2952
+ * @param visitorKeys - The parser's visitor keys, used to walk without
2953
+ * following `parent` links.
2954
+ * @returns `true` if `await`/`yield` appears anywhere in the pattern.
2955
+ */
2956
+ function containsAwaitOrYield(root, visitorKeys) {
2957
+ const stack = [root];
2958
+ for (let current = stack.pop(); current !== void 0; current = stack.pop()) {
2959
+ if (current.type === AST_NODE_TYPES.AwaitExpression || current.type === AST_NODE_TYPES.YieldExpression) return true;
2960
+ for (const key of visitorKeys[current.type] ?? []) {
2961
+ const child = current[key];
2962
+ const children = Array.isArray(child) ? child : [child];
2963
+ for (const item of children) if (isNodeLike(item)) stack.push(item);
2964
+ }
2965
+ }
2966
+ return false;
2967
+ }
2968
+ /**
2969
+ * Extracts the identifier a parameter binds, unwrapping a default value
2970
+ * (`obj = {}`); patterns, rest parameters, and TS parameter properties yield
2971
+ * `null`.
2972
+ *
2973
+ * @param parameter - The parameter node.
2974
+ * @returns The parameter's identifier, or `null` when it is not a plain one.
2975
+ */
2976
+ function getParameterIdentifier(parameter) {
2977
+ if (parameter.type === AST_NODE_TYPES.Identifier) return parameter;
2978
+ if (parameter.type === AST_NODE_TYPES.AssignmentPattern && parameter.left.type === AST_NODE_TYPES.Identifier) return parameter.left;
2979
+ return null;
2980
+ }
2981
+ /**
2982
+ * Determines whether any expression inside the patterns (computed keys,
2983
+ * default values, moved type annotations) references a binding that would be
2984
+ * out of scope — or in its temporal dead zone — at the parameter position:
2985
+ * anything declared in the function body, a parameter at or after the target,
2986
+ * or this function's own `arguments`.
2987
+ *
2988
+ * @param query - The rewrite being planned.
2989
+ * @param patterns - The object patterns being moved.
2990
+ * @returns `true` when moving the patterns would break a reference.
2991
+ */
2992
+ function hasUnsafePatternReferences({ body, identifier, node, scope }, patterns) {
2993
+ function inPattern(range) {
2994
+ return patterns.some((pattern) => range[0] >= pattern.range[0] && range[1] <= pattern.range[1]);
2995
+ }
2996
+ const stack = [scope];
2997
+ for (let current = stack.pop(); current !== void 0; current = stack.pop()) {
2998
+ stack.push(...current.childScopes);
2999
+ for (const reference of current.references) {
3000
+ if (!inPattern(reference.identifier.range)) continue;
3001
+ const { resolved } = reference;
3002
+ if (resolved === null) continue;
3003
+ if (resolved.defs.length === 0) {
3004
+ const blockRange = resolved.scope.block.range;
3005
+ if (blockRange[0] >= node.range[0] && blockRange[1] <= node.range[1]) return true;
3006
+ continue;
3007
+ }
3008
+ if (resolved.defs.some((definition) => {
3009
+ const { range } = definition.name;
3010
+ if (inPattern(range)) return false;
3011
+ if (range[1] <= node.range[0] || range[0] >= node.range[1]) return false;
3012
+ if (range[0] >= body.range[0]) return true;
3013
+ return range[0] >= identifier.range[0];
3014
+ })) return true;
3015
+ }
3016
+ }
3017
+ return false;
3018
+ }
3019
+ /**
3020
+ * Checks whether a function body opens with a `"use strict"` directive. A
3021
+ * destructured parameter makes the parameter list non-simple, and a function
3022
+ * with a non-simple parameter list may not contain a `"use strict"` directive,
3023
+ * so such functions are skipped entirely.
3024
+ *
3025
+ * @param body - The function body.
3026
+ * @returns `true` if the body has a `"use strict"` directive.
3027
+ */
3028
+ function hasUseStrictDirective(body) {
3029
+ for (const statement of body.body) {
3030
+ if (statement.type !== AST_NODE_TYPES.ExpressionStatement || typeof statement.directive !== "string") break;
3031
+ if (statement.directive === "use strict") return true;
3032
+ }
3033
+ return false;
3034
+ }
3035
+ /**
3036
+ * Checks whether evaluating the pattern can execute arbitrary code — a default
3037
+ * value or a computed key — as opposed to only performing property reads.
3038
+ *
3039
+ * @param node - The pattern node to walk.
3040
+ * @returns `true` when the pattern contains a default value or computed key.
3041
+ */
3042
+ function patternExecutesCode(node) {
3043
+ if (node.type === AST_NODE_TYPES.AssignmentPattern) return true;
3044
+ if (node.type === AST_NODE_TYPES.Property) return node.computed || patternExecutesCode(node.value);
3045
+ if (node.type === AST_NODE_TYPES.ObjectPattern) return node.properties.some((property) => patternExecutesCode(property));
3046
+ if (node.type === AST_NODE_TYPES.ArrayPattern) return node.elements.some((element) => element !== null && patternExecutesCode(element));
3047
+ if (node.type === AST_NODE_TYPES.RestElement) return patternExecutesCode(node.argument);
3048
+ return false;
3049
+ }
3050
+ /**
3051
+ * Computes the removal range for a declaration, swallowing the whole line
3052
+ * (indentation and trailing newline) when the declaration is alone on it.
3053
+ *
3054
+ * @param sourceCode - Provides the raw text around the declaration.
3055
+ * @param statement - The declaration to remove.
3056
+ * @returns The range to delete.
3057
+ */
3058
+ function statementRemovalRange({ text }, statement) {
3059
+ const [start, end] = statement.range;
3060
+ const lineStart = text.lastIndexOf("\n", start - 1) + 1;
3061
+ const newlineIndex = text.indexOf("\n", end);
3062
+ const lineEnd = newlineIndex === -1 ? text.length : newlineIndex + 1;
3063
+ const leadingIsBlank = text.slice(lineStart, start).trim().length === 0;
3064
+ const trailingIsBlank = text.slice(end, newlineIndex === -1 ? text.length : newlineIndex).trim().length === 0;
3065
+ if (leadingIsBlank && trailingIsBlank) return [lineStart, lineEnd];
3066
+ return [start, end];
3067
+ }
3068
+ /**
3069
+ * Builds the autofix, or returns `null` when the rewrite is not unambiguously
3070
+ * safe: unrelated sibling declarators, unmergeable or annotated patterns,
3071
+ * duplicate or colliding binding names, expressions that reference bindings
3072
+ * unavailable at the parameter position, a defused temporal dead zone, or —
3073
+ * with `allowSideEffectReordering: false` — side effects that would be
3074
+ * reordered.
3075
+ *
3076
+ * @param query - The rewrite being planned.
3077
+ * @returns The fix plan, or `null` when only a report should be emitted.
3078
+ */
3079
+ function planFix(query) {
3080
+ const { allowSideEffectReordering, body, identifier, node, otherParameterNames, sourceCode, statements } = query;
3081
+ const declaratorSet = new Set(statements.map((statement) => statement.declarator));
3082
+ const declarations = [...new Set(statements.map((statement) => statement.declaration))];
3083
+ if (!declarations.every((declaration) => {
3084
+ return declaration.declarations.every((declarator) => declaratorSet.has(declarator));
3085
+ })) return null;
3086
+ const patterns = statements.map((statement) => statement.pattern);
3087
+ if (!allowSideEffectReordering && patterns.some((pattern) => patternExecutesCode(pattern))) {
3088
+ const leadingStatements = new Set(body.body.slice(0, declarations.length));
3089
+ if (!declarations.every((declaration) => leadingStatements.has(declaration))) return null;
3090
+ }
3091
+ if (declarations.some((declaration) => {
3092
+ return sourceCode.getDeclaredVariables(declaration).some((declared) => {
3093
+ return declared.references.some((reference) => reference.identifier.range[0] < declaration.range[0]);
3094
+ });
3095
+ })) return null;
3096
+ if (patterns.some((pattern) => pattern.typeAnnotation !== void 0) && (patterns.length > 1 || identifier.typeAnnotation !== void 0)) return null;
3097
+ if (patterns.length > 1) {
3098
+ if (patterns.some((pattern) => {
3099
+ return pattern.properties.some((property) => property.type === AST_NODE_TYPES.RestElement);
3100
+ })) return null;
3101
+ }
3102
+ const boundNames = [];
3103
+ for (const pattern of patterns) collectBoundNames(pattern, boundNames);
3104
+ if (new Set(boundNames).size !== boundNames.length) return null;
3105
+ if (boundNames.some((name) => otherParameterNames.has(name))) return null;
3106
+ if (hasUnsafePatternReferences(query, patterns)) return null;
3107
+ const [firstPattern] = patterns;
3108
+ let parameterText;
3109
+ if (patterns.length === 1 && firstPattern !== void 0) parameterText = sourceCode.getText(firstPattern);
3110
+ else {
3111
+ const innerTexts = patterns.map((pattern) => sourceCode.getText(pattern).slice(1, -1).trim().replace(/,$/u, "").trim()).filter((text) => text.length > 0);
3112
+ parameterText = innerTexts.length === 0 ? "{}" : `{ ${innerTexts.join(", ")} }`;
3113
+ }
3114
+ if (node.type === AST_NODE_TYPES.ArrowFunctionExpression && sourceCode.getTokenAfter(identifier)?.value === "=>") parameterText = `(${parameterText})`;
3115
+ const parameterRange = [identifier.range[0], identifier.typeAnnotation?.range[0] ?? identifier.range[1]];
3116
+ const removalRanges = declarations.map((declaration) => {
3117
+ return statementRemovalRange(sourceCode, declaration);
3118
+ });
3119
+ return {
3120
+ parameterRange,
3121
+ parameterText,
3122
+ removalRanges
3123
+ };
3124
+ }
3125
+ /**
3126
+ * Reports body destructuring statements of parameters that have no other use,
3127
+ * preferring the pattern in the function signature. The autofix rewrites the
3128
+ * parameter (merging multiple statements into one pattern) and removes the
3129
+ * statements; it is withheld when the rewrite is not unambiguously safe, see
3130
+ * {@link planFix}.
3131
+ *
3132
+ * @param context - The rule context.
3133
+ * @returns The rule listener.
3134
+ */
3135
+ function createOnce$1(context) {
3136
+ let allowSideEffectReordering = true;
3137
+ function checkFunction(node) {
3138
+ const { body, params } = node;
3139
+ if (body.type !== AST_NODE_TYPES.BlockStatement || hasUseStrictDirective(body)) return;
3140
+ const scope = context.sourceCode.getScope(node);
3141
+ for (const parameter of params) {
3142
+ const identifier = getParameterIdentifier(parameter);
3143
+ if (identifier === null || identifier.name === "this") continue;
3144
+ const variable = scope.variables.find((candidate) => {
3145
+ return candidate.defs.some((definition) => definition.name === identifier);
3146
+ });
3147
+ if (variable?.defs.length !== 1) continue;
3148
+ const statements = collectDestructureStatements(variable, identifier, body);
3149
+ if (statements === null || statements.length === 0) continue;
3150
+ if (statements.some(({ pattern }) => {
3151
+ return containsAwaitOrYield(pattern, context.sourceCode.visitorKeys);
3152
+ })) continue;
3153
+ const otherParameterNames = collectOtherParameterNames(node, parameter);
3154
+ if (otherParameterNames.has(identifier.name)) continue;
3155
+ const plan = planFix({
3156
+ allowSideEffectReordering,
3157
+ body,
3158
+ identifier,
3159
+ node,
3160
+ otherParameterNames,
3161
+ scope,
3162
+ sourceCode: context.sourceCode,
3163
+ statements
3164
+ });
3165
+ for (const [index, statement] of statements.entries()) context.report({
3166
+ data: { name: identifier.name },
3167
+ fix(fixer) {
3168
+ if (index !== 0 || plan === null) return null;
3169
+ return [fixer.replaceTextRange(plan.parameterRange, plan.parameterText), ...plan.removalRanges.map((range) => fixer.removeRange(range))];
3170
+ },
3171
+ messageId: MESSAGE_ID$2,
3172
+ node: statement.declarator
3173
+ });
3174
+ }
3175
+ }
3176
+ return {
3177
+ ArrowFunctionExpression: checkFunction,
3178
+ before() {
3179
+ allowSideEffectReordering = context.options.at(0)?.allowSideEffectReordering ?? true;
3180
+ },
3181
+ FunctionDeclaration: checkFunction,
3182
+ FunctionExpression: checkFunction
3183
+ };
3184
+ }
3185
+ const preferParameterDestructuring = createFlawlessRule({
3186
+ name: RULE_NAME$4,
3187
+ createOnce: createOnce$1,
3188
+ defaultOptions: [{ allowSideEffectReordering: true }],
3189
+ meta: {
3190
+ defaultOptions: [{ allowSideEffectReordering: true }],
3191
+ docs: {
3192
+ description: "Enforce destructuring parameters in the function signature",
3193
+ recommended: false,
3194
+ requiresTypeChecking: false
3195
+ },
3196
+ fixable: "code",
3197
+ hasSuggestions: false,
3198
+ messages: messages$4,
3199
+ schema: [{
3200
+ additionalProperties: false,
3201
+ properties: { allowSideEffectReordering: {
3202
+ description: "Whether the autofix may hoist pattern defaults and computed keys past earlier statements, reordering their side effects. Set to false to withhold the fix in those cases.",
3203
+ type: "boolean"
3204
+ } },
3205
+ type: "object"
3206
+ }],
3207
+ type: "suggestion"
3208
+ }
3209
+ });
3210
+ //#endregion
3211
+ //#region node_modules/.pnpm/ts-api-utils@2.5.0_typescript@6.0.3/node_modules/ts-api-utils/lib/index.js
3212
+ var [tsMajor, tsMinor] = ts9.versionMajorMinor.split(".").map((raw) => Number.parseInt(raw, 10));
3213
+ function isModifierFlagSet(node, flag) {
3214
+ return isFlagSet(ts9.getCombinedModifierFlags(node), flag);
3215
+ }
3216
+ function isFlagSet(allFlags, flag) {
3217
+ return (allFlags & flag) !== 0;
3218
+ }
3219
+ function isFlagSetOnObject(obj, flag) {
3220
+ return isFlagSet(obj.flags, flag);
3221
+ }
3222
+ var isNodeFlagSet = isFlagSetOnObject;
3223
+ function isObjectFlagSet(objectType, flag) {
3224
+ return isFlagSet(objectType.objectFlags, flag);
3225
+ }
3226
+ var isSymbolFlagSet = isFlagSetOnObject;
3227
+ function isTransientSymbolLinksFlagSet(links, flag) {
3228
+ return isFlagSet(links.checkFlags, flag);
3229
+ }
3230
+ var isTypeFlagSet = isFlagSetOnObject;
3231
+ function isNumericPropertyName(name) {
3232
+ return String(+name) === name;
3233
+ }
3234
+ function isEntityNameExpression(node) {
3235
+ return ts9.isIdentifier(node) || isPropertyAccessEntityNameExpression(node);
3236
+ }
3237
+ function isConstAssertionExpression(node) {
3238
+ return ts9.isTypeReferenceNode(node.type) && ts9.isIdentifier(node.type.typeName) && node.type.typeName.escapedText === "const";
3239
+ }
3240
+ function isNumericOrStringLikeLiteral(node) {
3241
+ switch (node.kind) {
3242
+ case ts9.SyntaxKind.NoSubstitutionTemplateLiteral:
3243
+ case ts9.SyntaxKind.NumericLiteral:
3244
+ case ts9.SyntaxKind.StringLiteral: return true;
3245
+ default: return false;
3246
+ }
3247
+ }
3248
+ function isPropertyAccessEntityNameExpression(node) {
3249
+ return ts9.isPropertyAccessExpression(node) && ts9.isIdentifier(node.name) && isEntityNameExpression(node.expression);
3250
+ }
3251
+ ts9.TypeFlags.Intrinsic ?? ts9.TypeFlags.Any | ts9.TypeFlags.Unknown | ts9.TypeFlags.String | ts9.TypeFlags.Number | ts9.TypeFlags.BigInt | ts9.TypeFlags.Boolean | ts9.TypeFlags.BooleanLiteral | ts9.TypeFlags.ESSymbol | ts9.TypeFlags.Void | ts9.TypeFlags.Undefined | ts9.TypeFlags.Null | ts9.TypeFlags.Never | ts9.TypeFlags.NonPrimitive;
3252
+ function isIntersectionType(type) {
3253
+ return isTypeFlagSet(type, ts9.TypeFlags.Intersection);
3254
+ }
3255
+ function isObjectType(type) {
3256
+ return isTypeFlagSet(type, ts9.TypeFlags.Object);
3257
+ }
3258
+ function isUnionType(type) {
3259
+ return isTypeFlagSet(type, ts9.TypeFlags.Union);
3260
+ }
3261
+ function isTupleType(type) {
3262
+ return isObjectType(type) && isObjectFlagSet(type, ts9.ObjectFlags.Tuple);
3263
+ }
3264
+ function isTypeReference(type) {
3265
+ return isObjectType(type) && isObjectFlagSet(type, ts9.ObjectFlags.Reference);
3266
+ }
3267
+ function isTupleTypeReference(type) {
3268
+ return isTypeReference(type) && isTupleType(type.target);
3269
+ }
3270
+ function isBooleanLiteralType(type) {
3271
+ return isTypeFlagSet(type, ts9.TypeFlags.BooleanLiteral);
3272
+ }
3273
+ function isFalseLiteralType(type) {
3274
+ return isBooleanLiteralType(type) && type.intrinsicName === "false";
3275
+ }
3276
+ function getPropertyOfType(type, name) {
3277
+ if (!name.startsWith("__")) return type.getProperty(name);
3278
+ return type.getProperties().find((s) => s.escapedName === name);
3279
+ }
3280
+ function isBindableObjectDefinePropertyCall(node) {
3281
+ return node.arguments.length === 3 && isEntityNameExpression(node.arguments[0]) && isNumericOrStringLikeLiteral(node.arguments[1]) && ts9.isPropertyAccessExpression(node.expression) && node.expression.name.escapedText === "defineProperty" && ts9.isIdentifier(node.expression.expression) && node.expression.expression.escapedText === "Object";
3282
+ }
3283
+ function isInConstContext(node, typeChecker) {
3284
+ let current = node;
3285
+ while (true) {
3286
+ const parent = current.parent;
3287
+ outer: switch (parent.kind) {
3288
+ case ts9.SyntaxKind.ArrayLiteralExpression:
3289
+ case ts9.SyntaxKind.ObjectLiteralExpression:
3290
+ case ts9.SyntaxKind.ParenthesizedExpression:
3291
+ case ts9.SyntaxKind.TemplateExpression:
3292
+ current = parent;
3293
+ break;
3294
+ case ts9.SyntaxKind.AsExpression:
3295
+ case ts9.SyntaxKind.TypeAssertionExpression: return isConstAssertionExpression(parent);
3296
+ case ts9.SyntaxKind.CallExpression: {
3297
+ if (!ts9.isExpression(current)) return false;
3298
+ const functionSignature = typeChecker.getResolvedSignature(parent);
3299
+ if (functionSignature === void 0) return false;
3300
+ const argumentIndex = parent.arguments.indexOf(current);
3301
+ if (argumentIndex < 0) return false;
3302
+ const parameterSymbol = functionSignature.getParameters()[argumentIndex];
3303
+ if (parameterSymbol === void 0 || !("links" in parameterSymbol)) return false;
3304
+ const propertySymbol = parameterSymbol.links.type?.getProperties()?.[argumentIndex];
3305
+ if (propertySymbol === void 0 || !("links" in propertySymbol)) return false;
3306
+ return !!propertySymbol.links && isTransientSymbolLinksFlagSet(propertySymbol.links, ts9.CheckFlags.Readonly);
3307
+ }
3308
+ case ts9.SyntaxKind.PrefixUnaryExpression:
3309
+ if (current.kind !== ts9.SyntaxKind.NumericLiteral) return false;
3310
+ switch (parent.operator) {
3311
+ case ts9.SyntaxKind.MinusToken:
3312
+ case ts9.SyntaxKind.PlusToken:
3313
+ current = parent;
3314
+ break outer;
3315
+ default: return false;
3316
+ }
3317
+ case ts9.SyntaxKind.PropertyAssignment:
3318
+ if (parent.initializer !== current) return false;
3319
+ current = parent.parent;
3320
+ break;
3321
+ case ts9.SyntaxKind.ShorthandPropertyAssignment:
3322
+ current = parent.parent;
3323
+ break;
3324
+ default: return false;
3325
+ }
3326
+ }
3327
+ }
3328
+ function intersectionConstituents(type) {
3329
+ return isIntersectionType(type) ? type.types : [type];
3330
+ }
3331
+ function isPropertyReadonlyInType(type, name, typeChecker) {
3332
+ let seenProperty = false;
3333
+ let seenReadonlySignature = false;
3334
+ for (const subType of unionConstituents(type)) if (getPropertyOfType(subType, name) === void 0) {
3335
+ if (((isNumericPropertyName(name) ? typeChecker.getIndexInfoOfType(subType, ts9.IndexKind.Number) : void 0) ?? typeChecker.getIndexInfoOfType(subType, ts9.IndexKind.String))?.isReadonly) {
3336
+ if (seenProperty) return true;
3337
+ seenReadonlySignature = true;
3338
+ }
3339
+ } else if (seenReadonlySignature || isReadonlyPropertyIntersection(subType, name, typeChecker)) return true;
3340
+ else seenProperty = true;
3341
+ return false;
3342
+ }
3343
+ function symbolHasReadonlyDeclaration(symbol, typeChecker) {
3344
+ return !!((symbol.flags & ts9.SymbolFlags.Accessor) === ts9.SymbolFlags.GetAccessor || symbol.declarations?.some((node) => isModifierFlagSet(node, ts9.ModifierFlags.Readonly) || ts9.isVariableDeclaration(node) && isNodeFlagSet(node.parent, ts9.NodeFlags.Const) || ts9.isCallExpression(node) && isReadonlyAssignmentDeclaration(node, typeChecker) || ts9.isEnumMember(node) || (ts9.isPropertyAssignment(node) || ts9.isShorthandPropertyAssignment(node)) && isInConstContext(node, typeChecker)));
3345
+ }
3346
+ function unionConstituents(type) {
3347
+ return isUnionType(type) ? type.types : [type];
3348
+ }
3349
+ function isReadonlyAssignmentDeclaration(node, typeChecker) {
3350
+ if (!isBindableObjectDefinePropertyCall(node)) return false;
3351
+ const descriptorType = typeChecker.getTypeAtLocation(node.arguments[2]);
3352
+ if (descriptorType.getProperty("value") === void 0) return descriptorType.getProperty("set") === void 0;
3353
+ const writableProp = descriptorType.getProperty("writable");
3354
+ if (writableProp === void 0) return false;
3355
+ return isFalseLiteralType(writableProp.valueDeclaration !== void 0 && ts9.isPropertyAssignment(writableProp.valueDeclaration) ? typeChecker.getTypeAtLocation(writableProp.valueDeclaration.initializer) : typeChecker.getTypeOfSymbolAtLocation(writableProp, node.arguments[2]));
3356
+ }
3357
+ function isReadonlyPropertyFromMappedType(type, name, typeChecker) {
3358
+ if (!isObjectType(type) || !isObjectFlagSet(type, ts9.ObjectFlags.Mapped)) return;
3359
+ const declaration = type.symbol.declarations[0];
3360
+ if (declaration.readonlyToken !== void 0 && !/^__@[^@]+$/.test(name)) return declaration.readonlyToken.kind !== ts9.SyntaxKind.MinusToken;
3361
+ const { modifiersType } = type;
3362
+ return modifiersType && isPropertyReadonlyInType(modifiersType, name, typeChecker);
3363
+ }
3364
+ function isReadonlyPropertyIntersection(type, name, typeChecker) {
3365
+ return intersectionConstituents(type).some((constituent) => {
3366
+ const prop = getPropertyOfType(constituent, name);
3367
+ if (prop === void 0) return false;
3368
+ if (prop.flags & ts9.SymbolFlags.Transient) {
3369
+ if (/^(?:[1-9]\d*|0)$/.test(name) && isTupleTypeReference(constituent)) return constituent.target.readonly;
3370
+ switch (isReadonlyPropertyFromMappedType(constituent, name, typeChecker)) {
3371
+ case false: return false;
3372
+ case true: return true;
3373
+ }
3374
+ }
3375
+ return isSymbolFlagSet(prop, ts9.SymbolFlags.ValueModule) || symbolHasReadonlyDeclaration(prop, typeChecker);
3376
+ });
3377
+ }
3378
+ //#endregion
3379
+ //#region src/rules/prefer-read-only-props/readonly-type.ts
3380
+ /**
3381
+ * Type aliases whose presence guarantees every property is read-only, so the
3382
+ * type is treated as fully read-only without inspecting its members.
3383
+ */
3384
+ const READONLY_WRAPPER_NAMES = /* @__PURE__ */ new Set([
3385
+ "DeepReadOnly",
3386
+ "DeepReadonly",
3387
+ "Readonly",
3388
+ "ReadonlyArray",
3389
+ "ReadonlyDeep"
3390
+ ]);
3391
+ /**
3392
+ * Props React manages itself; they are always effectively read-only from a
3393
+ * component's perspective and never need a `readonly` modifier.
3394
+ */
3395
+ const REACT_BUILTIN_PROPS = /* @__PURE__ */ new Set([
3396
+ "children",
3397
+ "key",
3398
+ "ref"
3399
+ ]);
3400
+ /**
3401
+ * Determines whether every property of a props type is read-only.
3402
+ *
3403
+ * Ported from `eslint-cease-nonsense-rules`. Union and intersection members are
3404
+ * checked recursively, index signatures must be read-only, and base (extended)
3405
+ * types are traversed so an inherited `readonly` modifier still counts. A type
3406
+ * aliased to a known read-only wrapper (such as `Readonly<T>`) short-circuits to
3407
+ * read-only.
3408
+ *
3409
+ * @param checker - The TypeScript type checker.
3410
+ * @param type - The props type to inspect.
3411
+ * @param extraWrapperName - An additional alias name (the configured autofix
3412
+ * wrapper, such as `Immutable`) whose presence short-circuits to read-only,
3413
+ * so already-wrapped props are recognized in O(1) without walking members.
3414
+ * @returns `true` when the type exposes no mutable properties.
3415
+ */
3416
+ function isTypeFullyReadonly(checker, type, extraWrapperName) {
3417
+ const aliasSymbol = type.aliasSymbol ?? type.getSymbol();
3418
+ if (aliasSymbol) {
3419
+ const name = aliasSymbol.getName();
3420
+ if (READONLY_WRAPPER_NAMES.has(name) || name === extraWrapperName) return true;
3421
+ }
3422
+ if (type.isUnion()) return type.types.every((unionType) => {
3423
+ return isTypeFullyReadonly(checker, unionType, extraWrapperName);
3424
+ });
3425
+ if (type.isIntersection()) return type.types.every((intersectionType) => {
3426
+ return isTypeFullyReadonly(checker, intersectionType, extraWrapperName);
3427
+ });
3428
+ const indexInfos = checker.getIndexInfosOfType(type);
3429
+ for (const indexInfo of indexInfos) if (!indexInfo.isReadonly) return false;
3430
+ const properties = checker.getPropertiesOfType(type);
3431
+ if (properties.length === 0) return true;
3432
+ for (const property of properties) if (!isReadonlyPropertiesProperty(checker, type, property)) return false;
3433
+ return true;
3434
+ }
3435
+ /**
3436
+ * Collects the mutable members of a props type so the autofix can add a
3437
+ * `readonly` modifier to each. Mirrors {@link isTypeFullyReadonly}'s notion of
3438
+ * read-only (React built-ins and inherited `readonly` count as read-only), but
3439
+ * gathers the offenders instead of returning a boolean.
3440
+ *
3441
+ * Union and intersection types are not attributable to a single declaration, so
3442
+ * they return `null` — the caller withholds the `readonly`-modifier fix.
3443
+ *
3444
+ * @param checker - The TypeScript type checker.
3445
+ * @param type - The props type to inspect.
3446
+ * @returns The mutable members, or `null` when the type is a union/intersection.
3447
+ */
3448
+ function collectMutableProperties(checker, type) {
3449
+ if (type.isUnion() || type.isIntersection()) return null;
3450
+ return {
3451
+ indexInfos: checker.getIndexInfosOfType(type).filter((indexInfo) => !indexInfo.isReadonly),
3452
+ properties: checker.getPropertiesOfType(type).filter((property) => !isReadonlyPropertiesProperty(checker, type, property))
3453
+ };
3454
+ }
3455
+ function isTypePropertyReadonly(checker, type, property) {
3456
+ return isPropertyReadonlyInType(type, property.getEscapedName(), checker);
3457
+ }
3458
+ function getBaseTypes(type) {
3459
+ return type.getBaseTypes() ?? [];
3460
+ }
3461
+ function isPropertyReadonlyInBaseType(checker, type, property) {
3462
+ const propertyName = property.getName();
3463
+ const baseTypes = [...getBaseTypes(type)];
3464
+ for (const baseType of baseTypes) {
3465
+ const baseProperty = baseType.getProperty(propertyName);
3466
+ if (baseProperty === void 0) continue;
3467
+ if (isTypePropertyReadonly(checker, baseType, baseProperty)) return true;
3468
+ baseTypes.push(...getBaseTypes(baseType));
3469
+ }
3470
+ return false;
3471
+ }
3472
+ function isReadonlyPropertiesProperty(checker, type, property) {
3473
+ const propertyName = property.getName();
3474
+ if (REACT_BUILTIN_PROPS.has(propertyName)) return true;
3475
+ if (isTypePropertyReadonly(checker, type, property)) return true;
3476
+ return isPropertyReadonlyInBaseType(checker, type, property);
3477
+ }
3478
+ //#endregion
3479
+ //#region src/rules/prefer-read-only-props/rule.ts
3480
+ const RULE_NAME$3 = "prefer-read-only-props";
3481
+ const DEFAULT_WRAPPER_TYPE = "Readonly";
3482
+ const messages$3 = { preferReadOnlyProps: "A function component's props should be read-only." };
3483
+ /** Names of the `React.forwardRef` wrapper whose props are its second type argument. */
3484
+ const FORWARD_REF_NAMES = /* @__PURE__ */ new Set(["forwardRef"]);
3485
+ /** Names of the `React.memo` wrapper whose props are its first type argument. */
3486
+ const MEMO_NAMES = /* @__PURE__ */ new Set(["memo"]);
3487
+ /** Named function-component type aliases whose props are their first type argument. */
3488
+ const FC_TYPE_NAMES = /* @__PURE__ */ new Set([
3489
+ "FC",
3490
+ "FunctionComponent",
3491
+ "VFC",
3492
+ "VoidFunctionComponent"
3493
+ ]);
3494
+ /**
3495
+ * Strips assignment defaults and rest wrappers to reach the binding pattern
3496
+ * that may carry a type annotation.
3497
+ *
3498
+ * @param node - The component's first parameter.
3499
+ * @returns The underlying binding pattern.
3500
+ */
3501
+ function unwrapParameter(node) {
3502
+ if (node.type === AST_NODE_TYPES.AssignmentPattern) return unwrapParameter(node.left);
3503
+ if (node.type === AST_NODE_TYPES.RestElement) return unwrapParameter(node.argument);
3504
+ return node;
3505
+ }
3506
+ function getEntityName(typeName) {
3507
+ if (typeName.type === AST_NODE_TYPES.Identifier) return typeName.name;
3508
+ if (typeName.type === AST_NODE_TYPES.TSQualifiedName) return typeName.right.name;
3509
+ }
3510
+ function getCalleeName(callee) {
3511
+ if (callee.type === AST_NODE_TYPES.MemberExpression && callee.property.type === AST_NODE_TYPES.Identifier) return callee.property.name;
3512
+ if (callee.type === AST_NODE_TYPES.Identifier) return callee.name;
3513
+ }
3514
+ /**
3515
+ * Extracts the props type argument from a `FC<Props>`-style type reference.
3516
+ *
3517
+ * @param typeNode - The variable's type annotation.
3518
+ * @returns The props type argument node, if the reference is a known FC alias.
3519
+ */
3520
+ function getFcTypeArgument(typeNode) {
3521
+ if (typeNode.type !== AST_NODE_TYPES.TSTypeReference) return;
3522
+ const { typeArguments, typeName } = typeNode;
3523
+ if (typeArguments === void 0 || typeArguments.params.length === 0) return;
3524
+ const name = getEntityName(typeName);
3525
+ if (name === void 0 || !FC_TYPE_NAMES.has(name)) return;
3526
+ return typeArguments.params[0];
3527
+ }
3528
+ /**
3529
+ * Locates the props type argument of a `forwardRef`/`memo` wrapper call whose
3530
+ * callback is the component function.
3531
+ *
3532
+ * @param functionNode - The component function node.
3533
+ * @returns The props type argument node, if present.
3534
+ */
3535
+ function getWrapperCallTypeArgument({ parent }) {
3536
+ if (parent?.type !== AST_NODE_TYPES.CallExpression || parent.typeArguments === void 0) return;
3537
+ const name = getCalleeName(parent.callee);
3538
+ if (name === void 0) return;
3539
+ if (FORWARD_REF_NAMES.has(name)) return parent.typeArguments.params[1];
3540
+ if (MEMO_NAMES.has(name)) return parent.typeArguments.params[0];
3541
+ }
3542
+ /**
3543
+ * Finds the source type node expressing a component's props, so it can be
3544
+ * wrapped in `Readonly<>`. Prefers an explicit parameter annotation and falls
3545
+ * back to an `FC`/`forwardRef`/`memo` type argument.
3546
+ *
3547
+ * @param functionNode - The component function node.
3548
+ * @returns The props type node to wrap, or `undefined` when none is locatable.
3549
+ */
3550
+ function findPropsTypeNode(functionNode) {
3551
+ if (functionNode.type !== AST_NODE_TYPES.ArrowFunctionExpression && functionNode.type !== AST_NODE_TYPES.FunctionDeclaration && functionNode.type !== AST_NODE_TYPES.FunctionExpression) return;
3552
+ const [firstParameter] = functionNode.params;
3553
+ if (firstParameter !== void 0) {
3554
+ const binding = unwrapParameter(firstParameter);
3555
+ if ((binding.type === AST_NODE_TYPES.Identifier || binding.type === AST_NODE_TYPES.ObjectPattern || binding.type === AST_NODE_TYPES.ArrayPattern) && binding.typeAnnotation) return binding.typeAnnotation.typeAnnotation;
3556
+ }
3557
+ const { parent } = functionNode;
3558
+ if (parent.type === AST_NODE_TYPES.VariableDeclarator && parent.id.type === AST_NODE_TYPES.Identifier && parent.id.typeAnnotation) {
3559
+ const fromAnnotation = getFcTypeArgument(parent.id.typeAnnotation.typeAnnotation);
3560
+ if (fromAnnotation !== void 0) return fromAnnotation;
3561
+ }
3562
+ return getWrapperCallTypeArgument(functionNode);
3563
+ }
3564
+ /**
3565
+ * Builds a fix that makes `name` importable from `source`, or `undefined` when
3566
+ * it is already imported. Merges into an existing named import from the same
3567
+ * module when possible, otherwise prepends a fresh `import type` statement.
3568
+ *
3569
+ * @param fixer - The rule fixer.
3570
+ * @param sourceCode - The source code, for locating existing imports.
3571
+ * @param name - The type name to import.
3572
+ * @param source - The module specifier to import it from.
3573
+ * @returns An import fix, or `undefined` when no import is needed.
3574
+ */
3575
+ function buildImportFix(fixer, sourceCode, name, source) {
3576
+ const { body } = sourceCode.ast;
3577
+ let matchingImport;
3578
+ for (const statement of body) {
3579
+ if (statement.type !== AST_NODE_TYPES.ImportDeclaration || statement.source.value !== source) continue;
3580
+ matchingImport = statement;
3581
+ for (const specifier of statement.specifiers) if (specifier.type === AST_NODE_TYPES.ImportSpecifier && specifier.imported.type === AST_NODE_TYPES.Identifier && specifier.imported.name === name) return;
3582
+ }
3583
+ if (matchingImport !== void 0) {
3584
+ const lastNamed = matchingImport.specifiers.filter((specifier) => specifier.type === AST_NODE_TYPES.ImportSpecifier).at(-1);
3585
+ if (lastNamed !== void 0) {
3586
+ const prefix = matchingImport.importKind === "type" ? "" : "type ";
3587
+ return fixer.insertTextAfter(lastNamed, `, ${prefix}${name}`);
3588
+ }
3589
+ }
3590
+ const insertText = `import type { ${name} } from "${source}";\n`;
3591
+ const firstStatement = body.find((statement) => statement.type === AST_NODE_TYPES.ImportDeclaration);
3592
+ if (firstStatement !== void 0) return fixer.insertTextBefore(firstStatement, insertText);
3593
+ if (body[0] !== void 0) return fixer.insertTextBefore(body[0], insertText);
3594
+ return fixer.insertTextBeforeRange([0, 0], insertText);
3595
+ }
3596
+ function create$2(context) {
3597
+ const services = getParserServices(context, true);
3598
+ if (!services.program) return {};
3599
+ const checker = services.program.getTypeChecker();
3600
+ const { sourceCode } = context;
3601
+ const options = context.options.at(0) ?? {};
3602
+ const wrapperType = options.wrapperType ?? DEFAULT_WRAPPER_TYPE;
3603
+ const fixStyle = options.fixStyle ?? "wrap";
3604
+ const { importSource } = options;
3605
+ const collector = core.getFunctionComponentCollector(context);
3606
+ function getParameterType(parameter) {
3607
+ return checker.getTypeAtLocation(services.esTreeNodeToTSNodeMap.get(parameter));
3608
+ }
3609
+ /**
3610
+ * Maps a member's TypeScript declaration back to its editable AST node.
3611
+ *
3612
+ * @param declaration - The member's declaration, or `undefined` when the
3613
+ * symbol has none (a synthesized member).
3614
+ * @param expected - The AST node type the declaration must map to.
3615
+ * @returns The AST node, or `undefined` when the declaration is missing, lives
3616
+ * in another file (absent from this file's node map), or is not the expected
3617
+ * kind (such as a method signature, which cannot take a `readonly`).
3618
+ */
3619
+ function resolveMemberNode(declaration, expected) {
3620
+ if (declaration === void 0) return;
3621
+ const node = services.tsNodeToESTreeNodeMap.get(declaration);
3622
+ if (node?.type !== expected) return;
3623
+ return node;
3624
+ }
3625
+ /**
3626
+ * Resolves every mutable member to its editable AST node, so a `readonly`
3627
+ * modifier can be inserted before each.
3628
+ *
3629
+ * @param members - The mutable properties and index signatures of the props.
3630
+ * @returns The member nodes, or `undefined` when any member cannot be edited
3631
+ * here (cross-file, synthesized, or a method signature) — in which case the
3632
+ * `"modifier"` fix is withheld.
3633
+ */
3634
+ function resolveModifierTargets(members) {
3635
+ const nodes = /* @__PURE__ */ new Set();
3636
+ for (const property of members.properties) {
3637
+ const node = resolveMemberNode(property.valueDeclaration, AST_NODE_TYPES.TSPropertySignature);
3638
+ if (node === void 0) return;
3639
+ nodes.add(node);
3640
+ }
3641
+ for (const indexInfo of members.indexInfos) {
3642
+ const node = resolveMemberNode(indexInfo.declaration, AST_NODE_TYPES.TSIndexSignature);
3643
+ if (node === void 0) return;
3644
+ nodes.add(node);
3645
+ }
3646
+ return nodes.size === 0 ? void 0 : [...nodes];
3647
+ }
3648
+ function checkComponent(functionNode) {
3649
+ if (functionNode.type !== AST_NODE_TYPES.ArrowFunctionExpression && functionNode.type !== AST_NODE_TYPES.FunctionDeclaration && functionNode.type !== AST_NODE_TYPES.FunctionExpression) return;
3650
+ const [firstParameter] = functionNode.params;
3651
+ if (firstParameter === void 0) return;
3652
+ const propsType = getParameterType(firstParameter);
3653
+ if (isTypeFullyReadonly(checker, propsType, wrapperType)) return;
3654
+ if (fixStyle === "modifier") {
3655
+ const members = collectMutableProperties(checker, propsType);
3656
+ const targets = members === null ? void 0 : resolveModifierTargets(members);
3657
+ context.report({
3658
+ fix: targets === void 0 ? void 0 : (fixer) => {
3659
+ return targets.map((node) => fixer.insertTextBefore(node, "readonly "));
3660
+ },
3661
+ messageId: "preferReadOnlyProps",
3662
+ node: functionNode
3663
+ });
3664
+ return;
3665
+ }
3666
+ const typeNode = findPropsTypeNode(functionNode);
3667
+ context.report({
3668
+ fix: typeNode === void 0 ? void 0 : (fixer) => {
3669
+ const fixes = [fixer.replaceText(typeNode, `${wrapperType}<${sourceCode.getText(typeNode)}>`)];
3670
+ if (importSource !== void 0) {
3671
+ const importFix = buildImportFix(fixer, sourceCode, wrapperType, importSource);
3672
+ if (importFix !== void 0) fixes.push(importFix);
3673
+ }
3674
+ return fixes;
3675
+ },
3676
+ messageId: "preferReadOnlyProps",
3677
+ node: functionNode
3678
+ });
3679
+ }
3680
+ const { api, visitor } = collector;
3681
+ const collectorExit = visitor["Program:exit"];
3682
+ return {
3683
+ ...visitor,
3684
+ "Program:exit": (node) => {
3685
+ collectorExit?.(node);
3686
+ for (const component of api.getAllComponents(node)) checkComponent(component.node);
3687
+ }
3688
+ };
3689
+ }
3690
+ const preferReadOnlyProps = createEslintRule({
3691
+ name: RULE_NAME$3,
3692
+ create: create$2,
3693
+ defaultOptions: [{}],
3694
+ meta: {
3695
+ defaultOptions: [{}],
3696
+ docs: {
3697
+ description: "Enforce that function component props are read-only",
3698
+ recommended: false,
3699
+ requiresTypeChecking: true
3700
+ },
3701
+ fixable: "code",
3702
+ hasSuggestions: false,
3703
+ messages: messages$3,
3704
+ schema: [{
3705
+ additionalProperties: false,
3706
+ properties: {
3707
+ fixStyle: {
3708
+ description: "How the autofix makes props read-only: \"wrap\" wraps the type in the wrapper (Readonly<> by default); \"modifier\" adds a readonly modifier to each property, but only when every property is inline or declared in the same file, otherwise no fix is offered. \"modifier\" ignores wrapperType/importSource.",
3709
+ enum: ["wrap", "modifier"],
3710
+ type: "string"
3711
+ },
3712
+ importSource: {
3713
+ description: "Module to import `wrapperType` from when the autofix inserts it. Omit when the wrapper is globally available (the default `Readonly` needs no import).",
3714
+ type: "string"
3715
+ },
3716
+ wrapperType: {
3717
+ description: "Utility type the autofix wraps props in. Defaults to `Readonly`; set to a deep-readonly type such as `Immutable` to enforce nested immutability.",
3718
+ type: "string"
3719
+ }
3720
+ },
3721
+ type: "object"
3722
+ }],
3723
+ type: "suggestion"
3724
+ }
3725
+ });
3726
+ //#endregion
3727
+ //#region src/rules/purity/rule.ts
3728
+ const RULE_NAME$2 = "purity";
3729
+ const MESSAGE_ID$1 = "impureCall";
3730
+ /**
3731
+ * Non-deterministic Luau / Roblox calls, written as dotted paths matching how
3732
+ * they appear in roblox-ts source. `new Random()` is normalized to
3733
+ * `"Random.new"` (see the `NewExpression` visitor).
3734
+ */
3735
+ const DEFAULT_SIGNATURES = [
3736
+ "DateTime.now",
3737
+ "HttpService.GenerateGUID",
3738
+ "Random.new",
3739
+ "Workspace.GetServerTimeNow",
3740
+ "elapsedTime",
3741
+ "math.random",
3742
+ "math.randomseed",
3743
+ "os.clock",
3744
+ "os.date",
3745
+ "os.time",
3746
+ "tick",
3747
+ "time"
3748
+ ];
3749
+ /**
3750
+ * Bare Luau globals for which a matching local binding means the reference is
3751
+ * shadowed rather than the ambient global. Service objects are intentionally
3752
+ * excluded because they are impure even when imported from `@rbxts/services`.
3753
+ */
3754
+ const GUARDED_GLOBALS = /* @__PURE__ */ new Set([
3755
+ "elapsedTime",
3756
+ "math",
3757
+ "os",
3758
+ "tick",
3759
+ "time"
3760
+ ]);
3761
+ const messages$2 = { [MESSAGE_ID$1]: "Do not call '{{name}}' during render. Components and hooks must be pure. Move this call into an event handler, effect, or state initializer." };
3762
+ const schema$1 = [{
3763
+ additionalProperties: false,
3764
+ properties: {
3765
+ additionalFunctions: {
3766
+ description: "Extra dotted call signatures to treat as impure (e.g. \"Math.random\").",
3767
+ items: { type: "string" },
3768
+ type: "array",
3769
+ uniqueItems: true
3770
+ },
3771
+ ignore: {
3772
+ description: "Default signatures to exclude (e.g. \"os.date\").",
3773
+ items: { type: "string" },
3774
+ type: "array",
3775
+ uniqueItems: true
3776
+ }
3777
+ },
3778
+ type: "object"
3779
+ }];
3780
+ /**
3781
+ * Strips wrappers that are irrelevant to the callee identity so that
3782
+ * `a.b?.()` and `a.b!()` still match.
3783
+ *
3784
+ * @param node - The node to unwrap.
3785
+ * @returns The unwrapped node.
3786
+ */
3787
+ function unwrap(node) {
3788
+ let current = node;
3789
+ while (current.type === AST_NODE_TYPES.ChainExpression || current.type === AST_NODE_TYPES.TSNonNullExpression) current = current.expression;
3790
+ return current;
3791
+ }
3792
+ /**
3793
+ * Builds the dotted path for a callee, e.g. `math.random` or `tick`.
3794
+ *
3795
+ * @param callee - The callee expression.
3796
+ * @returns The match, or `null` when the callee cannot be represented as a
3797
+ * simple dotted path (computed access, or an object that is an expression).
3798
+ */
3799
+ function dottedCalleePath(callee) {
3800
+ let current = unwrap(callee);
3801
+ const parts = [];
3802
+ while (current.type === AST_NODE_TYPES.MemberExpression) {
3803
+ if (current.computed || current.property.type !== AST_NODE_TYPES.Identifier) return null;
3804
+ parts.unshift(current.property.name);
3805
+ current = unwrap(current.object);
3806
+ }
3807
+ if (current.type !== AST_NODE_TYPES.Identifier) return null;
3808
+ parts.unshift(current.name);
3809
+ return {
3810
+ path: parts.join("."),
3811
+ root: current
3812
+ };
3813
+ }
3814
+ /**
3815
+ * Determines whether an identifier resolves to a real user binding (import,
3816
+ * parameter, or local declaration) rather than the ambient Luau global.
3817
+ *
3818
+ * @param sourceCode - Provides scope lookup.
3819
+ * @param node - The identifier node.
3820
+ * @returns `true` when the identifier is a user binding rather than the ambient
3821
+ * global.
3822
+ */
3823
+ function isUserBinding(sourceCode, node) {
3824
+ let scope = sourceCode.getScope(node);
3825
+ while (scope !== null) {
3826
+ const variable = scope.variables.find((candidate) => candidate.name === node.name);
3827
+ if (variable !== void 0) return variable.defs.length > 0;
3828
+ scope = scope.upper;
3829
+ }
3830
+ return false;
3831
+ }
3832
+ /**
3833
+ * Finds the immediate enclosing function of a node, stopping at the program.
3834
+ *
3835
+ * @param node - The node to search upward from.
3836
+ * @returns The enclosing function, or `null` when the node is at module level.
3837
+ */
3838
+ function enclosingFunction(node) {
3839
+ let current = node.parent;
3840
+ while (current !== void 0) {
3841
+ if (ASTUtils.isFunction(current)) return current;
3842
+ if (current.type === AST_NODE_TYPES.Program) return null;
3843
+ current = current.parent;
3844
+ }
3845
+ return null;
3846
+ }
3847
+ /**
3848
+ * Determines whether a function executes during render: a component body, a
3849
+ * custom/builtin hook body, or a `useMemo` callback.
3850
+ *
3851
+ * @param reactContext - The context `@eslint-react/core` predicates expect.
3852
+ * @param func - The enclosing function node.
3853
+ * @returns `true` when the function runs during render.
3854
+ */
3855
+ function isRenderContext(reactContext, func) {
3856
+ if (core.isFunctionComponentDefinition(reactContext, func, core.DEFAULT_COMPONENT_DETECTION_HINT)) return true;
3857
+ if (core.isHookDefinition(func)) return true;
3858
+ const { parent } = func;
3859
+ return parent.type === AST_NODE_TYPES.CallExpression && parent.arguments[0] === func && core.isUseMemoCall(reactContext, parent);
3860
+ }
3861
+ function createOnce(context) {
3862
+ const reactContext = context;
3863
+ let signatures;
3864
+ function handle(node, match, path) {
3865
+ if (!signatures.has(path)) return;
3866
+ if (path === "Random.new" && node.arguments.length > 0) return;
3867
+ if (GUARDED_GLOBALS.has(match.root.name) && isUserBinding(context.sourceCode, match.root)) return;
3868
+ const func = enclosingFunction(node);
3869
+ if (func === null || !isRenderContext(reactContext, func)) return;
3870
+ context.report({
3871
+ data: { name: path },
3872
+ messageId: MESSAGE_ID$1,
3873
+ node
3874
+ });
3875
+ }
3876
+ return {
3877
+ before() {
3878
+ const options = context.options[0] ?? {};
3879
+ signatures = new Set(DEFAULT_SIGNATURES);
3880
+ for (const signature of options.additionalFunctions ?? []) signatures.add(signature);
3881
+ for (const signature of options.ignore ?? []) signatures.delete(signature);
3882
+ },
3883
+ CallExpression(node) {
3884
+ const match = dottedCalleePath(node.callee);
3885
+ if (match !== null) handle(node, match, match.path);
3886
+ },
3887
+ NewExpression(node) {
3888
+ const match = dottedCalleePath(node.callee);
3889
+ if (match !== null) handle(node, match, `${match.path}.new`);
3890
+ }
3891
+ };
3892
+ }
3893
+ const purity = createFlawlessRule({
3894
+ name: RULE_NAME$2,
3895
+ createOnce,
3896
+ defaultOptions: [{}],
3897
+ meta: {
3898
+ defaultOptions: [{}],
3899
+ docs: {
3900
+ description: "Disallow impure calls such as `math.random` or `os.clock` during render",
3901
+ recommended: false,
3902
+ requiresTypeChecking: false
3903
+ },
3904
+ hasSuggestions: false,
3905
+ messages: messages$2,
3906
+ schema: schema$1,
3907
+ type: "problem"
3908
+ }
3909
+ });
3910
+ //#endregion
3911
+ //#region src/rules/toml-sort-keys/rule.ts
3912
+ const RULE_NAME$1 = "toml-sort-keys";
3913
+ const MESSAGE_ID = "unsorted";
3914
+ const messages$1 = { [MESSAGE_ID]: "Expected {{target}} to follow the configured order." };
3915
+ const schema = {
3916
+ items: {
3917
+ additionalProperties: false,
3918
+ properties: {
3919
+ order: { oneOf: [{
3920
+ items: { type: "string" },
3921
+ type: "array"
3922
+ }, {
3923
+ additionalProperties: false,
3924
+ properties: {
3925
+ caseSensitive: { type: "boolean" },
3926
+ natural: { type: "boolean" },
3927
+ type: {
3928
+ enum: ["asc", "desc"],
3929
+ type: "string"
3930
+ }
3931
+ },
3932
+ type: "object"
3933
+ }] },
3934
+ pathPattern: { type: "string" }
3935
+ },
3936
+ required: ["order", "pathPattern"],
3937
+ type: "object"
3938
+ },
3939
+ type: "array"
3940
+ };
3941
+ function isTable(entry) {
3942
+ return entry.type === "TOMLTable";
3943
+ }
3944
+ /**
3945
+ * The dotted name of an entry, e.g. `settings`, `settings.node`, `_.path`.
3946
+ *
3947
+ * @param entry - The table header or key-value entry.
3948
+ * @returns The dotted key path as a string.
3949
+ */
3950
+ function nameOf(entry) {
3951
+ return getStaticTOMLValue(entry.key).join(".");
3952
+ }
3953
+ /**
3954
+ * Builds a rank function from an explicit order list. An entry matches an order
3955
+ * item when it equals it or is a dotted child of it (`settings.node` matches
3956
+ * `settings`), which keeps sub-tables grouped under their parent's slot.
3957
+ *
3958
+ * @param order - The configured names in their desired order.
3959
+ * @returns A function giving a name's rank, or `Infinity` when unlisted.
3960
+ */
3961
+ function makeRank(order) {
3962
+ return (name) => {
3963
+ for (const [index, item] of order.entries()) if (name === item || name.startsWith(`${item}.`)) return index;
3964
+ return Number.POSITIVE_INFINITY;
3965
+ };
3966
+ }
3967
+ function makeFallback({ caseSensitive = true, natural = false, type = "asc" }) {
3968
+ const sensitivity = caseSensitive ? "variant" : "accent";
3969
+ return (a, b) => {
3970
+ const result = a.localeCompare(b, "en", {
3971
+ numeric: natural,
3972
+ sensitivity
3973
+ });
3974
+ return type === "desc" ? -result : result;
3975
+ };
3976
+ }
3977
+ /**
3978
+ * Builds a comparator for one table body. Explicit-order entries sort first by
3979
+ * their listed position, then unlisted entries fall back to a natural/asc sort
3980
+ * (mirroring `yaml/sort-keys`). The fallback compares keys as written in
3981
+ * source, so quotes participate in the order and quoted keys (e.g.
3982
+ * `"github:owner/repo"`) group before bare keys. At the top level, bare
3983
+ * key-values are always kept before any `[table]` header, since TOML would
3984
+ * otherwise re-scope them.
3985
+ *
3986
+ * @param order - The order configuration for this table's path.
3987
+ * @param isTopLevel - Whether the body is the top-level table.
3988
+ * @param rawName - Returns an entry's key exactly as written in source.
3989
+ * @returns A comparator over two entries.
3990
+ */
3991
+ function makeComparator(order, isTopLevel, rawName) {
3992
+ const rank = Array.isArray(order) ? makeRank(order) : void 0;
3993
+ const fallback = makeFallback(Array.isArray(order) ? {
3994
+ natural: true,
3995
+ type: "asc"
3996
+ } : order);
3997
+ return (a, b) => {
3998
+ if (isTopLevel) {
3999
+ const aRank = isTable(a) ? 1 : 0;
4000
+ const bRank = isTable(b) ? 1 : 0;
4001
+ if (aRank !== bRank) return aRank - bRank;
4002
+ }
4003
+ if (rank !== void 0) {
4004
+ const aRank = rank(nameOf(a));
4005
+ const bRank = rank(nameOf(b));
4006
+ if (aRank !== bRank) {
4007
+ if (aRank === Number.POSITIVE_INFINITY) return 1;
4008
+ if (bRank === Number.POSITIVE_INFINITY) return -1;
4009
+ return aRank - bRank;
4010
+ }
4011
+ }
4012
+ return fallback(rawName(a), rawName(b));
4013
+ };
4014
+ }
4015
+ function create$1(context) {
4016
+ const { options, sourceCode } = context;
4017
+ if (sourceCode.parserServices.isTOML !== true) return {};
4018
+ const specs = options.map(({ order, pathPattern }) => {
4019
+ return {
4020
+ order,
4021
+ pattern: new RegExp(pathPattern, "u")
4022
+ };
4023
+ });
4024
+ if (specs.length === 0) return {};
4025
+ function resolveOrder(path) {
4026
+ for (const spec of specs) if (spec.pattern.test(path)) return spec.order;
4027
+ }
4028
+ /**
4029
+ * A comment counts as attached to an entry when it sits on its own line
4030
+ * directly above it (no blank line, no trailing code). Such comments travel
4031
+ * with the entry when it moves.
4032
+ *
4033
+ * @param comment - The comment to classify.
4034
+ * @returns Whether the comment stands alone on its line.
4035
+ */
4036
+ function isOwnLineComment(comment) {
4037
+ const before = sourceCode.getTokenBefore(comment, { includeComments: true });
4038
+ return before === null || before.loc.end.line < comment.loc.start.line;
4039
+ }
4040
+ function leadingStart(entry) {
4041
+ const comments = sourceCode.getCommentsBefore(entry);
4042
+ let start = entry.range[0];
4043
+ let boundaryLine = entry.loc.start.line;
4044
+ for (let index = comments.length - 1; index >= 0; index -= 1) {
4045
+ const comment = comments[index];
4046
+ if (comment === void 0) break;
4047
+ if (comment.loc.end.line !== boundaryLine - 1 || !isOwnLineComment(comment)) break;
4048
+ start = comment.range[0];
4049
+ boundaryLine = comment.loc.start.line;
4050
+ }
4051
+ return start;
4052
+ }
4053
+ function trailingEnd(entry) {
4054
+ const [comment] = sourceCode.getCommentsAfter(entry);
4055
+ if (comment?.loc.start.line === entry.loc.end.line) return comment.range[1];
4056
+ return entry.range[1];
4057
+ }
4058
+ function buildFix(entries, target) {
4059
+ const text = sourceCode.getText();
4060
+ const blocks = entries.map((entry) => {
4061
+ return {
4062
+ end: trailingEnd(entry),
4063
+ entry,
4064
+ start: leadingStart(entry)
4065
+ };
4066
+ });
4067
+ for (let index = 1; index < blocks.length; index += 1) {
4068
+ const previous = blocks[index - 1];
4069
+ const current = blocks[index];
4070
+ if (previous === void 0 || current === void 0) return;
4071
+ if (/\S/u.test(text.slice(previous.end, current.start))) return;
4072
+ }
4073
+ const first = blocks[0];
4074
+ const last = blocks[blocks.length - 1];
4075
+ if (first === void 0 || last === void 0) return;
4076
+ const textByEntry = new Map(blocks.map((block) => [block.entry, text.slice(block.start, block.end)]));
4077
+ const parts = [];
4078
+ for (const [index, entry] of target.entries()) {
4079
+ const previous = target[index - 1];
4080
+ if (previous !== void 0) parts.push(isTable(entry) || isTable(previous) ? "\n\n" : "\n");
4081
+ parts.push(textByEntry.get(entry) ?? "");
4082
+ }
4083
+ const sortedText = parts.join("");
4084
+ return (fixer) => fixer.replaceTextRange([first.start, last.end], sortedText);
4085
+ }
4086
+ function verify(path, body, isTopLevel) {
4087
+ if (body.length < 2) return;
4088
+ const order = resolveOrder(path);
4089
+ if (order === void 0) return;
4090
+ const entries = [...body];
4091
+ const target = [...entries].sort(makeComparator(order, isTopLevel, (entry) => sourceCode.getText(entry.key)));
4092
+ let outOfPlace;
4093
+ for (const [index, entry] of entries.entries()) if (entry !== target[index]) {
4094
+ outOfPlace = entry;
4095
+ break;
4096
+ }
4097
+ if (outOfPlace === void 0) return;
4098
+ context.report({
4099
+ data: { target: path === "" ? "top-level tables" : `keys in "${path}"` },
4100
+ fix: buildFix(entries, target),
4101
+ loc: outOfPlace.key.loc,
4102
+ messageId: MESSAGE_ID
4103
+ });
4104
+ }
4105
+ return {
4106
+ TOMLTable(node) {
4107
+ verify(getStaticTOMLValue(node.key).join("."), node.body, false);
4108
+ },
4109
+ TOMLTopLevelTable(node) {
4110
+ verify("", node.body, true);
4111
+ }
4112
+ };
4113
+ }
4114
+ const tomlSortKeys = createEslintRule({
4115
+ name: RULE_NAME$1,
4116
+ create: create$1,
4117
+ defaultOptions: [],
4118
+ meta: {
4119
+ defaultOptions: [],
4120
+ docs: {
4121
+ description: "Enforce a configured sort order for TOML keys and tables",
4122
+ recommended: false,
4123
+ requiresTypeChecking: false
4124
+ },
4125
+ fixable: "code",
4126
+ hasSuggestions: false,
4127
+ messages: messages$1,
4128
+ schema,
4129
+ type: "layout"
4130
+ }
4131
+ });
4132
+ //#endregion
4133
+ //#region src/rules/yaml-block-key-blank-lines/rule.ts
4134
+ const RULE_NAME = "yaml-block-key-blank-lines";
4135
+ const messages = { blankLine: "Expected {{count}} blank line(s) around this top-level key." };
4136
+ /**
4137
+ * Determines whether a pair value is a block collection (block mapping or block
4138
+ * sequence). Flow collections (`{ ... }` / `[ ... ]`) count as scalars.
4139
+ *
4140
+ * @param value - The value node of a YAML pair.
4141
+ * @returns True if the value is a block mapping or block sequence.
4142
+ */
4143
+ function isBlock(value) {
4144
+ return value !== null && (value.type === "YAMLMapping" || value.type === "YAMLSequence") && value.style === "block";
4145
+ }
4146
+ function create(context) {
4147
+ const { sourceCode } = context;
4148
+ if (sourceCode.parserServices.isYAML !== true) return {};
4149
+ return { YAMLMapping(yamlNode) {
4150
+ if (yamlNode.parent.type !== "YAMLDocument") return;
4151
+ const { pairs } = yamlNode;
4152
+ for (let index = 1; index < pairs.length; index += 1) {
4153
+ const previous = pairs[index - 1];
4154
+ const current = pairs[index];
4155
+ if (previous === void 0 || current === void 0) continue;
4156
+ const left = sourceCode.getLastToken(previous);
4157
+ const right = sourceCode.getFirstToken(current);
4158
+ if (sourceCode.commentsExistBetween(left, right)) continue;
4159
+ const blanks = right.loc.start.line - left.loc.end.line - 1;
4160
+ const want = isBlock(previous.value) || isBlock(current.value) ? 1 : 0;
4161
+ if (blanks === want) continue;
4162
+ context.report({
4163
+ data: { count: want },
4164
+ fix(fixer) {
4165
+ return fixer.replaceTextRange([left.range[1], right.range[0]], "\n".repeat(want + 1));
4166
+ },
4167
+ loc: (current.key ?? current).loc,
4168
+ messageId: "blankLine"
4169
+ });
4170
+ }
4171
+ } };
4172
+ }
4173
+ const yamlBlockKeyBlankLines = createEslintRule({
4174
+ name: RULE_NAME,
4175
+ create,
4176
+ defaultOptions: [],
4177
+ meta: {
4178
+ docs: {
4179
+ description: "Enforce blank lines around top-level YAML block collection keys",
4180
+ recommended: false,
4181
+ requiresTypeChecking: false
4182
+ },
4183
+ fixable: "whitespace",
4184
+ hasSuggestions: false,
4185
+ messages,
4186
+ schema: [],
4187
+ type: "layout"
4188
+ }
4189
+ });
4190
+ //#endregion
4191
+ //#region src/plugin.ts
4192
+ const PLUGIN_NAME = name.replace(/^eslint-plugin-/, "");
4193
+ /**
4194
+ * Generates a rules record where all plugin rules are set to "error".
4195
+ *
4196
+ * @param pluginName - The plugin identifier used to prefix rule names.
4197
+ * @param rules - The rules record to transform.
4198
+ * @returns A Linter.RulesRecord with all rules enabled.
4199
+ */
4200
+ function getRules(pluginName, rules) {
4201
+ return Object.fromEntries(Object.keys(rules).map((ruleName) => [`${pluginName}/${ruleName}`, "error"]));
4202
+ }
4203
+ const plugin = {
4204
+ meta: {
4205
+ name: PLUGIN_NAME,
4206
+ version
4207
+ },
4208
+ rules: {
4209
+ "arrow-return-style": arrowReturnStyle,
4210
+ "jsx-shorthand-boolean": jsxShorthandBoolean,
4211
+ "jsx-shorthand-fragment": jsxShorthandFragment,
4212
+ "naming-convention": namingConvention,
4213
+ "no-export-default-arrow": noExportDefaultArrow,
4214
+ "no-unnecessary-use-callback": noUnnecessaryUseCallback,
4215
+ "no-unnecessary-use-memo": noUnnecessaryUseMemo,
4216
+ "prefer-destructuring-assignment": preferDestructuringAssignment,
4217
+ "prefer-parameter-destructuring": preferParameterDestructuring,
4218
+ "prefer-read-only-props": preferReadOnlyProps,
4219
+ "purity": purity,
4220
+ "toml-sort-keys": tomlSortKeys,
4221
+ "yaml-block-key-blank-lines": yamlBlockKeyBlankLines
4222
+ }
4223
+ };
4224
+ getRules(PLUGIN_NAME, plugin.rules);
4225
+ //#endregion
4226
+ export { plugin as n, PLUGIN_NAME as t };