eslint-plugin-flawless 0.1.9 → 0.1.11

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