eslint-plugin-flawless 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -171,12 +171,16 @@ pnpm eslint-docs
171
171
 
172
172
  <!-- begin auto-generated rules list -->
173
173
 
174
- 💭 Requires
175
- [type information](https://typescript-eslint.io/linting/typed-linting).
176
-
177
- | Name | Description | 💭 |
178
- | :---------------------------------------------------------------- | :---------------------------------------------------------- | :-- |
179
- | [naming-convention](src/rules/naming-convention/documentation.md) | Enforce naming conventions for everything across a codebase | 💭 |
174
+ 🔧 Automatically fixable by the
175
+ [`--fix` CLI option](https://eslint.org/docs/user-guide/command-line-interface#--fix).\
176
+ 💭
177
+ Requires [type information](https://typescript-eslint.io/linting/typed-linting).
178
+
179
+ | Name                       | Description | 🔧 | 💭 |
180
+ | :---------------------------------------------------------------------------------- | :-------------------------------------------------------------- | :-- | :-- |
181
+ | [naming-convention](src/rules/naming-convention/documentation.md) | Enforce naming conventions for everything across a codebase | | 💭 |
182
+ | [toml-sort-keys](src/rules/toml-sort-keys/documentation.md) | Enforce a configured sort order for TOML keys and tables | 🔧 | |
183
+ | [yaml-block-key-blank-lines](src/rules/yaml-block-key-blank-lines/documentation.md) | Enforce blank lines around top-level YAML block collection keys | 🔧 | |
180
184
 
181
185
  <!-- end auto-generated rules list -->
182
186
 
package/dist/index.d.mts CHANGED
@@ -1,6 +1,4 @@
1
1
  import { TSESLint } from "@typescript-eslint/utils";
2
- import "@typescript-eslint/utils/eslint-utils";
3
- import * as _typescript_eslint_utils_ts_eslint0 from "@typescript-eslint/utils/ts-eslint";
4
2
  import { Linter } from "eslint";
5
3
 
6
4
  //#region src/util.d.ts
@@ -117,9 +115,22 @@ interface NamingSelector {
117
115
  }
118
116
  //#endregion
119
117
  //#region src/rules/naming-convention/rule.d.ts
120
- type MessageIds$1 = "doesNotMatchFormat" | "doesNotMatchFormatTrimmed" | "missingAffix" | "missingUnderscore" | "satisfyCustom" | "unexpectedUnderscore";
118
+ type MessageIds = "doesNotMatchFormat" | "doesNotMatchFormatTrimmed" | "missingAffix" | "missingUnderscore" | "satisfyCustom" | "unexpectedUnderscore";
121
119
  type Options$1 = Array<NamingSelector>;
122
120
  //#endregion
121
+ //#region src/rules/toml-sort-keys/rule.d.ts
122
+ type Options = Array<SortSpec>;
123
+ interface SortOrderObject {
124
+ caseSensitive?: boolean;
125
+ natural?: boolean;
126
+ type?: "asc" | "desc";
127
+ }
128
+ type SortOrder = Array<string> | SortOrderObject;
129
+ interface SortSpec {
130
+ order: SortOrder;
131
+ pathPattern: string;
132
+ }
133
+ //#endregion
123
134
  //#region src/plugin.d.ts
124
135
  declare const plugin: {
125
136
  meta: {
@@ -127,7 +138,15 @@ declare const plugin: {
127
138
  version: string;
128
139
  };
129
140
  rules: {
130
- "naming-convention": TSESLint.RuleModule<MessageIds$1, Options$1, PluginDocumentation, TSESLint.RuleListener>;
141
+ "naming-convention": TSESLint.RuleModule<MessageIds, Options$1, PluginDocumentation, TSESLint.RuleListener> & {
142
+ name: string;
143
+ };
144
+ "toml-sort-keys": TSESLint.RuleModule<"unsorted", Options, PluginDocumentation, TSESLint.RuleListener> & {
145
+ name: string;
146
+ };
147
+ "yaml-block-key-blank-lines": TSESLint.RuleModule<"blankLine", [], PluginDocumentation, TSESLint.RuleListener> & {
148
+ name: string;
149
+ };
131
150
  };
132
151
  };
133
152
  //#endregion
@@ -142,7 +161,15 @@ declare const _default: {
142
161
  version: string;
143
162
  };
144
163
  rules: {
145
- "naming-convention": _typescript_eslint_utils_ts_eslint0.RuleModule<MessageIds$1, Options$1, PluginDocumentation, _typescript_eslint_utils_ts_eslint0.RuleListener>;
164
+ "naming-convention": import("${configDir}").RuleModule<MessageIds, Options$1, PluginDocumentation, import("${configDir}").RuleListener> & {
165
+ name: string;
166
+ };
167
+ "toml-sort-keys": import("${configDir}").RuleModule<"unsorted", Options, PluginDocumentation, import("${configDir}").RuleListener> & {
168
+ name: string;
169
+ };
170
+ "yaml-block-key-blank-lines": import("${configDir}").RuleModule<"blankLine", [], PluginDocumentation, import("${configDir}").RuleListener> & {
171
+ name: string;
172
+ };
146
173
  };
147
174
  };
148
175
  };
@@ -154,10 +181,18 @@ declare const _default: {
154
181
  version: string;
155
182
  };
156
183
  rules: {
157
- "naming-convention": _typescript_eslint_utils_ts_eslint0.RuleModule<MessageIds$1, Options$1, PluginDocumentation, _typescript_eslint_utils_ts_eslint0.RuleListener>;
184
+ "naming-convention": import("${configDir}").RuleModule<MessageIds, Options$1, PluginDocumentation, import("${configDir}").RuleListener> & {
185
+ name: string;
186
+ };
187
+ "toml-sort-keys": import("${configDir}").RuleModule<"unsorted", Options, PluginDocumentation, import("${configDir}").RuleListener> & {
188
+ name: string;
189
+ };
190
+ "yaml-block-key-blank-lines": import("${configDir}").RuleModule<"blankLine", [], PluginDocumentation, import("${configDir}").RuleListener> & {
191
+ name: string;
192
+ };
158
193
  };
159
194
  };
160
- type RuleOptions = { [K in keyof RuleDefinitions]: RuleDefinitions[K]["defaultOptions"] };
195
+ type RuleOptions = { [K in keyof RuleDefinitions]: NonNullable<RuleDefinitions[K]["defaultOptions"]> };
161
196
  type Rules = { [K in keyof RuleOptions]: Linter.RuleEntry<RuleOptions[K]> };
162
197
  type RuleDefinitions = typeof plugin.rules;
163
198
  //#endregion
package/dist/index.mjs CHANGED
@@ -3,21 +3,34 @@ import { requiresQuoting } from "@typescript-eslint/type-utils";
3
3
  import { ASTUtils, AST_NODE_TYPES, ESLintUtils, TSESLint } from "@typescript-eslint/utils";
4
4
  import { RuleCreator, getParserServices } from "@typescript-eslint/utils/eslint-utils";
5
5
  import assert from "node:assert";
6
-
6
+ import { getStaticTOMLValue } from "toml-eslint-parser";
7
7
  //#region package.json
8
8
  var name = "eslint-plugin-flawless";
9
- var version = "0.1.0";
9
+ var version = "0.1.2";
10
10
  var repository = {
11
11
  "url": "git+https://github.com/christopher-buss/eslint-plugin-flawless.git",
12
12
  "type": "git"
13
13
  };
14
-
15
14
  //#endregion
16
15
  //#region src/util.ts
17
- const createEslintRule = RuleCreator((name$1) => {
18
- return `${repository.url.replace(/^git\+/, "").replace(/\.git$/, "")}/blob/v${version}/src/rules/${name$1}/documentation.md`;
16
+ const createRule = RuleCreator((name) => {
17
+ return `${repository.url.replace(/^git\+/, "").replace(/\.git$/, "")}/blob/v${version}/src/rules/${name}/documentation.md`;
19
18
  });
20
-
19
+ /**
20
+ * Creates a rule with a docs URL, allowing a rule's `create` to receive a
21
+ * context with a custom `sourceCode` type (such as a YAML source code). The
22
+ * `SourceCode` type parameter defaults to ESLint's `SourceCode`, so standard
23
+ * rules are unaffected.
24
+ *
25
+ * @template Options - The rule's options tuple.
26
+ * @template MessageIds - The rule's message identifiers.
27
+ * @template SourceCode - The source code type exposed on `context.sourceCode`.
28
+ * @param rule - The rule definition (meta, name, defaultOptions, create).
29
+ * @returns The created rule module.
30
+ */
31
+ function createEslintRule(rule) {
32
+ return createRule(rule);
33
+ }
21
34
  //#endregion
22
35
  //#region src/utils/is-type-import.ts
23
36
  /**
@@ -34,7 +47,6 @@ const createEslintRule = RuleCreator((name$1) => {
34
47
  function isTypeImport(definition) {
35
48
  return definition?.type === DefinitionType.ImportBinding && (definition.parent.importKind === "type" || definition.node.type === AST_NODE_TYPES.ImportSpecifier && definition.node.importKind === "type");
36
49
  }
37
-
38
50
  //#endregion
39
51
  //#region src/utils/reference-contains-type-query.ts
40
52
  /**
@@ -50,7 +62,6 @@ function referenceContainsTypeQuery(node) {
50
62
  default: return false;
51
63
  }
52
64
  }
53
-
54
65
  //#endregion
55
66
  //#region src/utils/collect-variables.ts
56
67
  /**
@@ -113,10 +124,15 @@ var UnusedVariablesVisitor = class extends Visitor {
113
124
  }
114
125
  TSParameterProperty(node) {
115
126
  let identifier;
116
- if (node.parameter.type === AST_NODE_TYPES.AssignmentPattern) {
117
- if (node.parameter.left.type === AST_NODE_TYPES.Identifier) identifier = node.parameter.left;
118
- } else if (node.parameter.type === AST_NODE_TYPES.Identifier) identifier = node.parameter;
119
- if (identifier) this.markVariableAsUsed(identifier);
127
+ switch (node.parameter.type) {
128
+ case AST_NODE_TYPES.AssignmentPattern:
129
+ identifier = node.parameter.left;
130
+ break;
131
+ case AST_NODE_TYPES.Identifier:
132
+ identifier = node.parameter;
133
+ break;
134
+ }
135
+ this.markVariableAsUsed(identifier);
120
136
  }
121
137
  collectUnusedVariables({ scope, variables = {
122
138
  unusedVariables: /* @__PURE__ */ new Set(),
@@ -140,9 +156,9 @@ var UnusedVariablesVisitor = class extends Visitor {
140
156
  const scope = this.#scopeManager.acquire(node, inner);
141
157
  if (scope) {
142
158
  if (scope.type === ScopeType.functionExpressionName) {
143
- const returnValue$1 = scope.childScopes[0];
144
- assert(returnValue$1, "Function expression name scope should have a child scope");
145
- return returnValue$1;
159
+ const returnValue = scope.childScopes[0];
160
+ assert(returnValue, "Function expression name scope should have a child scope");
161
+ return returnValue;
146
162
  }
147
163
  return scope;
148
164
  }
@@ -157,19 +173,19 @@ var UnusedVariablesVisitor = class extends Visitor {
157
173
  variableOrIdentifierOrName.eslintUsed = true;
158
174
  return;
159
175
  }
160
- let name$1;
176
+ let name;
161
177
  let node;
162
178
  if (typeof variableOrIdentifierOrName === "string") {
163
- name$1 = variableOrIdentifierOrName;
179
+ name = variableOrIdentifierOrName;
164
180
  assert(parent, "Parent node is required when marking by name");
165
181
  node = parent;
166
182
  } else {
167
- ({name: name$1} = variableOrIdentifierOrName);
183
+ ({name} = variableOrIdentifierOrName);
168
184
  node = variableOrIdentifierOrName;
169
185
  }
170
186
  let currentScope = this.getScope(node);
171
187
  while (currentScope) {
172
- const variable = currentScope.variables.find((scopeVariable) => scopeVariable.name === name$1);
188
+ const variable = currentScope.variables.find((scopeVariable) => scopeVariable.name === name);
173
189
  if (variable) {
174
190
  variable.eslintUsed = true;
175
191
  return;
@@ -223,8 +239,8 @@ var UnusedVariablesVisitor = class extends Visitor {
223
239
  if (variable?.defs.length === 0) this.markVariableAsUsed(variable);
224
240
  }
225
241
  visitFunctionTypeSignature(node) {
226
- for (const parameter of node.params) this.visitPattern(parameter, (name$1) => {
227
- this.markVariableAsUsed(name$1);
242
+ for (const parameter of node.params) this.visitPattern(parameter, (name) => {
243
+ this.markVariableAsUsed(name);
228
244
  });
229
245
  }
230
246
  visitSetter(node) {
@@ -257,7 +273,7 @@ function isSelfReference(ref, nodes) {
257
273
  }
258
274
  return false;
259
275
  }
260
- const MERGEABLE_TYPES = new Set([
276
+ const MERGEABLE_TYPES = /* @__PURE__ */ new Set([
261
277
  AST_NODE_TYPES.ClassDeclaration,
262
278
  AST_NODE_TYPES.FunctionDeclaration,
263
279
  AST_NODE_TYPES.TSInterfaceDeclaration,
@@ -289,7 +305,7 @@ function isMergeableExported(variable) {
289
305
  }
290
306
  return false;
291
307
  }
292
- const LOGICAL_ASSIGNMENT_OPERATORS = new Set([
308
+ const LOGICAL_ASSIGNMENT_OPERATORS = /* @__PURE__ */ new Set([
293
309
  "&&=",
294
310
  "??=",
295
311
  "||="
@@ -409,7 +425,7 @@ function isUsedVariable(variable) {
409
425
  * @param rhsNode - The RHS node of the previous assignment.
410
426
  * @returns The reference is a read to update itself.
411
427
  */
412
- function isReadForItself(ref, rhsNode$1) {
428
+ function isReadForItself(ref, rhsNode) {
413
429
  /**
414
430
  * Checks whether a given Identifier node exists inside of a function node which can be used later.
415
431
  *
@@ -423,7 +439,7 @@ function isUsedVariable(variable) {
423
439
  * @param rightHandSideNode - The RHS node of the previous assignment.
424
440
  * @returns `true` if the `id` node exists inside of a function node which can be used later.
425
441
  */
426
- function isInsideOfStorableFunction(id$1, rightHandSideNode) {
442
+ function isInsideOfStorableFunction(id, rightHandSideNode) {
427
443
  /**
428
444
  * Finds a function node from ancestors of a node.
429
445
  * @param node - A start node to find.
@@ -446,32 +462,32 @@ function isUsedVariable(variable) {
446
462
  * - the funcNode is bound as an argument of a function call.
447
463
  * - the function is bound to a property and the object satisfies above conditions.
448
464
  */
449
- function isStorableFunction(funcNode$1, storableRhsNode) {
450
- let node = funcNode$1;
451
- let { parent: parent$1 } = funcNode$1;
452
- while (parent$1 && isInside(parent$1, storableRhsNode)) {
453
- switch (parent$1.type) {
465
+ function isStorableFunction(funcNode, storableRhsNode) {
466
+ let node = funcNode;
467
+ let { parent } = funcNode;
468
+ while (parent && isInside(parent, storableRhsNode)) {
469
+ switch (parent.type) {
454
470
  case AST_NODE_TYPES.AssignmentExpression:
455
471
  case AST_NODE_TYPES.TaggedTemplateExpression:
456
472
  case AST_NODE_TYPES.YieldExpression: return true;
457
473
  case AST_NODE_TYPES.CallExpression:
458
- case AST_NODE_TYPES.NewExpression: return parent$1.callee !== node;
474
+ case AST_NODE_TYPES.NewExpression: return parent.callee !== node;
459
475
  case AST_NODE_TYPES.SequenceExpression:
460
- if (parent$1.expressions[parent$1.expressions.length - 1] !== node) return false;
476
+ if (parent.expressions[parent.expressions.length - 1] !== node) return false;
461
477
  break;
462
- default: if (parent$1.type.endsWith("Statement") || parent$1.type.endsWith("Declaration")) return true;
478
+ default: if (parent.type.endsWith("Statement") || parent.type.endsWith("Declaration")) return true;
463
479
  }
464
- node = parent$1;
465
- ({parent: parent$1} = parent$1);
480
+ node = parent;
481
+ ({parent} = parent);
466
482
  }
467
483
  return false;
468
484
  }
469
- const funcNode = getUpperFunction(id$1);
485
+ const funcNode = getUpperFunction(id);
470
486
  return !!funcNode && isInside(funcNode, rightHandSideNode) && isStorableFunction(funcNode, rightHandSideNode);
471
487
  }
472
488
  const id = ref.identifier;
473
489
  const { parent } = id;
474
- 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$1 && isInside(id, rhsNode$1) && !isInsideOfStorableFunction(id, rhsNode$1));
490
+ 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));
475
491
  }
476
492
  const functionNodes = getFunctionDefinitions(variable);
477
493
  const isFunctionDefinition = functionNodes.size > 0;
@@ -489,7 +505,6 @@ function isUsedVariable(variable) {
489
505
  return ref.isRead() && !forItself && (isImportedAsType || !referenceContainsTypeQuery(ref.identifier)) && (!isFunctionDefinition || !isSelfReference(ref, functionNodes)) && (!isTypeDecl || !isInsideOneOf(ref, typeDeclNodes)) && (!isModuleDecl || !isSelfReference(ref, moduleDeclNodes)) && (!isEnumDecl || !isSelfReference(ref, enumDeclNodes));
490
506
  });
491
507
  }
492
-
493
508
  //#endregion
494
509
  //#region src/rules/naming-convention/utils/enums.ts
495
510
  const Selector = {
@@ -567,7 +582,6 @@ const UnderscoreOption = {
567
582
  allowDouble: 5,
568
583
  allowSingleOrDouble: 6
569
584
  };
570
-
571
585
  //#endregion
572
586
  //#region src/rules/naming-convention/utils/shared.ts
573
587
  function isMetaSelector(selector) {
@@ -580,13 +594,15 @@ function selectorTypeToMessageString(selectorType) {
580
594
  const notCamelCase = selectorType.replaceAll(/([A-Z])/g, " $1");
581
595
  return notCamelCase.charAt(0).toUpperCase() + notCamelCase.slice(1);
582
596
  }
583
-
584
597
  //#endregion
585
598
  //#region src/rules/naming-convention/utils/format.ts
586
- function hasStrictCamelHumps(name$1, isUpper) {
587
- if (name$1.startsWith("_")) return false;
588
- for (let index = 1; index < name$1.length; ++index) {
589
- const char = name$1[index];
599
+ function isUppercaseChar(char) {
600
+ return char === char.toUpperCase() && char !== char.toLowerCase();
601
+ }
602
+ function hasStrictCamelHumps(name, isUpper) {
603
+ if (name.startsWith("_")) return false;
604
+ for (let index = 1; index < name.length; ++index) {
605
+ const char = name[index];
590
606
  if (char === "_") return false;
591
607
  if (isUpper === isUppercaseChar(char)) {
592
608
  if (isUpper) return false;
@@ -594,41 +610,38 @@ function hasStrictCamelHumps(name$1, isUpper) {
594
610
  }
595
611
  return true;
596
612
  }
597
- function isCamelCase(name$1) {
598
- return name$1.length === 0 || name$1[0] === name$1[0]?.toLowerCase() && !name$1.includes("_");
599
- }
600
- function isPascalCase(name$1) {
601
- return name$1.length === 0 || name$1[0] === name$1[0]?.toUpperCase() && !name$1.includes("_");
602
- }
603
- function isSnakeCase(name$1) {
604
- return name$1.length === 0 || name$1 === name$1.toLowerCase() && validateUnderscores(name$1);
605
- }
606
- function isStrictCamelCase(name$1) {
607
- return name$1.length === 0 || name$1[0] === name$1[0]?.toLowerCase() && hasStrictCamelHumps(name$1, false);
608
- }
609
- function isStrictPascalCase(name$1) {
610
- return name$1.length === 0 || name$1[0] === name$1[0]?.toUpperCase() && hasStrictCamelHumps(name$1, true);
611
- }
612
- function isUpperCase(name$1) {
613
- return name$1.length === 0 || name$1 === name$1.toUpperCase() && validateUnderscores(name$1);
613
+ function isCamelCase(name) {
614
+ return name.length === 0 || name[0] === name[0]?.toLowerCase() && !name.includes("_");
614
615
  }
615
- function isUppercaseChar(char) {
616
- return char === char.toUpperCase() && char !== char.toLowerCase();
616
+ function isPascalCase(name) {
617
+ return name.length === 0 || name[0] === name[0]?.toUpperCase() && !name.includes("_");
617
618
  }
618
619
  /**
619
620
  * Check for leading trailing and adjacent underscores.
620
621
  * @param name - The name to check.
621
622
  * @returns True if the underscores are valid.
622
623
  */
623
- function validateUnderscores(name$1) {
624
- if (name$1.startsWith("_")) return false;
624
+ function validateUnderscores(name) {
625
+ if (name.startsWith("_")) return false;
625
626
  let wasUnderscore = false;
626
- for (let index = 1; index < name$1.length; ++index) if (name$1[index] === "_") {
627
+ for (let index = 1; index < name.length; ++index) if (name[index] === "_") {
627
628
  if (wasUnderscore) return false;
628
629
  wasUnderscore = true;
629
630
  } else wasUnderscore = false;
630
631
  return !wasUnderscore;
631
632
  }
633
+ function isSnakeCase(name) {
634
+ return name.length === 0 || name === name.toLowerCase() && validateUnderscores(name);
635
+ }
636
+ function isStrictCamelCase(name) {
637
+ return name.length === 0 || name[0] === name[0]?.toLowerCase() && hasStrictCamelHumps(name, false);
638
+ }
639
+ function isStrictPascalCase(name) {
640
+ return name.length === 0 || name[0] === name[0]?.toUpperCase() && hasStrictCamelHumps(name, true);
641
+ }
642
+ function isUpperCase(name) {
643
+ return name.length === 0 || name === name.toUpperCase() && validateUnderscores(name);
644
+ }
632
645
  const FormatCheckersMap = {
633
646
  [PredefinedFormat.camelCase]: isCamelCase,
634
647
  [PredefinedFormat.PascalCase]: isPascalCase,
@@ -637,12 +650,11 @@ const FormatCheckersMap = {
637
650
  [PredefinedFormat.StrictPascalCase]: isStrictPascalCase,
638
651
  [PredefinedFormat.UPPER_CASE]: isUpperCase
639
652
  };
640
-
641
653
  //#endregion
642
654
  //#region src/rules/naming-convention/utils/validator.ts
643
655
  function createValidator(type, context, allConfigs) {
644
656
  const selectorType = Selector[type];
645
- const configs$1 = allConfigs.filter((configItem) => {
657
+ const configs = allConfigs.filter((configItem) => {
646
658
  return (configItem.selector & selectorType) !== 0 || configItem.selector === MetaSelector.default;
647
659
  }).sort((a, b) => {
648
660
  if (a.selector === b.selector) return b.modifierWeight - a.modifierWeight;
@@ -658,53 +670,53 @@ function createValidator(type, context, allConfigs) {
658
670
  });
659
671
  return (node, modifiers = /* @__PURE__ */ new Set()) => {
660
672
  const originalName = node.type === AST_NODE_TYPES.Identifier || node.type === AST_NODE_TYPES.PrivateIdentifier ? node.name : `${node.value}`;
661
- for (const config of configs$1) {
673
+ for (const config of configs) {
662
674
  if (config.filter?.regex.test(originalName) !== config.filter?.match) continue;
663
675
  if (config.modifiers?.some((modifier) => !modifiers.has(modifier)) === true) continue;
664
676
  if (!isCorrectType(node, config, context, selectorType)) continue;
665
- let name$1 = originalName;
666
- name$1 = validateUnderscore({
677
+ let name = originalName;
678
+ name = validateUnderscore({
679
+ name,
667
680
  config,
668
- name: name$1,
669
681
  node,
670
682
  originalName,
671
683
  position: "leading"
672
684
  });
673
- if (name$1 === void 0) return;
674
- name$1 = validateUnderscore({
685
+ if (name === void 0) return;
686
+ name = validateUnderscore({
687
+ name,
675
688
  config,
676
- name: name$1,
677
689
  node,
678
690
  originalName,
679
691
  position: "trailing"
680
692
  });
681
- if (name$1 === void 0) return;
682
- name$1 = validateAffix({
693
+ if (name === void 0) return;
694
+ name = validateAffix({
695
+ name,
683
696
  config,
684
- name: name$1,
685
697
  node,
686
698
  originalName,
687
699
  position: "prefix"
688
700
  });
689
- if (name$1 === void 0) return;
690
- name$1 = validateAffix({
701
+ if (name === void 0) return;
702
+ name = validateAffix({
703
+ name,
691
704
  config,
692
- name: name$1,
693
705
  node,
694
706
  originalName,
695
707
  position: "suffix"
696
708
  });
697
- if (name$1 === void 0) return;
709
+ if (name === void 0) return;
698
710
  if (!validateCustom({
711
+ name,
699
712
  config,
700
- name: name$1,
701
713
  node,
702
714
  originalName
703
715
  })) return;
704
716
  if (!validatePredefinedFormat({
717
+ name,
705
718
  config,
706
719
  modifiers,
707
- name: name$1,
708
720
  node,
709
721
  originalName
710
722
  })) return;
@@ -716,10 +728,10 @@ function createValidator(type, context, allConfigs) {
716
728
  if (custom?.match === true) regexMatch = "match";
717
729
  else if (custom?.match === false) regexMatch = "not match";
718
730
  return {
731
+ name: originalName,
719
732
  affixes: affixes?.join(", "),
720
733
  count,
721
734
  formats: formats?.map((formatItem) => PredefinedFormatValueToKey[formatItem]).join(", "),
722
- name: originalName,
723
735
  position,
724
736
  processedName,
725
737
  regex: custom?.regex.toString(),
@@ -727,24 +739,24 @@ function createValidator(type, context, allConfigs) {
727
739
  type: selectorTypeToMessageString(type)
728
740
  };
729
741
  }
730
- function validateUnderscore({ config, name: name$1, node, originalName, position }) {
742
+ function validateUnderscore({ name, config, node, originalName, position }) {
731
743
  const option = position === "leading" ? config.leadingUnderscore : config.trailingUnderscore;
732
- if (!option) return name$1;
733
- const hasSingleUnderscore = position === "leading" ? () => name$1.startsWith("_") : () => name$1.endsWith("_");
734
- const trimSingleUnderscore = position === "leading" ? () => name$1.slice(1) : () => name$1.slice(0, -1);
735
- const hasDoubleUnderscore = position === "leading" ? () => name$1.startsWith("__") : () => name$1.endsWith("__");
736
- const trimDoubleUnderscore = position === "leading" ? () => name$1.slice(2) : () => name$1.slice(0, -2);
744
+ if (!option) return name;
745
+ const hasSingleUnderscore = position === "leading" ? () => name.startsWith("_") : () => name.endsWith("_");
746
+ const trimSingleUnderscore = position === "leading" ? () => name.slice(1) : () => name.slice(0, -1);
747
+ const hasDoubleUnderscore = position === "leading" ? () => name.startsWith("__") : () => name.endsWith("__");
748
+ const trimDoubleUnderscore = position === "leading" ? () => name.slice(2) : () => name.slice(0, -2);
737
749
  switch (option) {
738
750
  case UnderscoreOption.allow:
739
751
  if (hasSingleUnderscore()) return trimSingleUnderscore();
740
- return name$1;
752
+ return name;
741
753
  case UnderscoreOption.allowDouble:
742
754
  if (hasDoubleUnderscore()) return trimDoubleUnderscore();
743
- return name$1;
755
+ return name;
744
756
  case UnderscoreOption.allowSingleOrDouble:
745
757
  if (hasDoubleUnderscore()) return trimDoubleUnderscore();
746
758
  if (hasSingleUnderscore()) return trimSingleUnderscore();
747
- return name$1;
759
+ return name;
748
760
  case UnderscoreOption.forbid:
749
761
  if (hasSingleUnderscore()) {
750
762
  context.report({
@@ -758,7 +770,7 @@ function createValidator(type, context, allConfigs) {
758
770
  });
759
771
  return;
760
772
  }
761
- return name$1;
773
+ return name;
762
774
  case UnderscoreOption.require:
763
775
  if (!hasSingleUnderscore()) {
764
776
  context.report({
@@ -789,12 +801,12 @@ function createValidator(type, context, allConfigs) {
789
801
  return trimDoubleUnderscore();
790
802
  }
791
803
  }
792
- function validateAffix({ config, name: name$1, node, originalName, position }) {
804
+ function validateAffix({ name, config, node, originalName, position }) {
793
805
  const affixes = config[position];
794
- if (!affixes || affixes.length === 0) return name$1;
806
+ if (!affixes || affixes.length === 0) return name;
795
807
  for (const affix of affixes) {
796
- const hasAffix = position === "prefix" ? name$1.startsWith(affix) : name$1.endsWith(affix);
797
- const trimAffix = position === "prefix" ? () => name$1.slice(affix.length) : () => name$1.slice(0, -affix.length);
808
+ const hasAffix = position === "prefix" ? name.startsWith(affix) : name.endsWith(affix);
809
+ const trimAffix = position === "prefix" ? () => name.slice(affix.length) : () => name.slice(0, -affix.length);
798
810
  if (hasAffix) return trimAffix();
799
811
  }
800
812
  context.report({
@@ -807,10 +819,10 @@ function createValidator(type, context, allConfigs) {
807
819
  node
808
820
  });
809
821
  }
810
- function validateCustom({ config, name: name$1, node, originalName }) {
822
+ function validateCustom({ name, config, node, originalName }) {
811
823
  const { custom } = config;
812
824
  if (!custom) return true;
813
- const result = custom.regex.test(name$1);
825
+ const result = custom.regex.test(name);
814
826
  if (custom.match && result) return true;
815
827
  if (!custom.match && !result) return true;
816
828
  context.report({
@@ -823,20 +835,20 @@ function createValidator(type, context, allConfigs) {
823
835
  });
824
836
  return false;
825
837
  }
826
- function validatePredefinedFormat({ config, modifiers, name: name$1, node, originalName }) {
838
+ function validatePredefinedFormat({ name, config, modifiers, node, originalName }) {
827
839
  const formats = config.format;
828
840
  if (!formats || formats.length === 0) return true;
829
841
  if (!modifiers.has(Modifier.requiresQuotes)) for (const format of formats) {
830
842
  const checker = FormatCheckersMap[format];
831
- if (checker(name$1)) return true;
843
+ if (checker(name)) return true;
832
844
  }
833
845
  context.report({
834
846
  data: formatReportData({
835
847
  formats,
836
848
  originalName,
837
- processedName: name$1
849
+ processedName: name
838
850
  }),
839
- messageId: originalName === name$1 ? "doesNotMatchFormat" : "doesNotMatchFormatTrimmed",
851
+ messageId: originalName === name ? "doesNotMatchFormat" : "doesNotMatchFormatTrimmed",
840
852
  node
841
853
  });
842
854
  return false;
@@ -859,19 +871,15 @@ function isCorrectType(node, config, context, selector) {
859
871
  break;
860
872
  case TypeModifier.boolean:
861
873
  case TypeModifier.number:
862
- case TypeModifier.string: {
863
- const typeString = checker.typeToString(checker.getWidenedType(checker.getBaseTypeOfLiteralType(type)));
864
- const allowedTypeString = TypeModifierValueToKey[allowedType];
865
- if (typeString === allowedTypeString) return true;
874
+ case TypeModifier.string:
875
+ if (checker.typeToString(checker.getWidenedType(checker.getBaseTypeOfLiteralType(type))) === TypeModifierValueToKey[allowedType]) return true;
866
876
  break;
867
- }
868
877
  case TypeModifier.function:
869
878
  if (isAllTypesMatch(type, (inner) => inner.getCallSignatures().length > 0)) return true;
870
879
  break;
871
880
  }
872
881
  return false;
873
882
  }
874
-
875
883
  //#endregion
876
884
  //#region src/rules/naming-convention/utils/parse-options.ts
877
885
  function parseOptions(context) {
@@ -911,7 +919,6 @@ function normalizeOption(option) {
911
919
  };
912
920
  });
913
921
  }
914
-
915
922
  //#endregion
916
923
  //#region src/rules/naming-convention/utils/schema.ts
917
924
  const $DEFS = {
@@ -1182,11 +1189,10 @@ const SCHEMA = {
1182
1189
  ] },
1183
1190
  type: "array"
1184
1191
  };
1185
-
1186
1192
  //#endregion
1187
1193
  //#region src/rules/naming-convention/rule.ts
1188
- const RULE_NAME = "naming-convention";
1189
- const messages = {
1194
+ const RULE_NAME$2 = "naming-convention";
1195
+ const messages$2 = {
1190
1196
  doesNotMatchFormat: "{{type}} name `{{name}}` must match one of the following formats: {{formats}}",
1191
1197
  doesNotMatchFormatTrimmed: "{{type}} name `{{name}}` trimmed as `{{processedName}}` must match one of the following formats: {{formats}}",
1192
1198
  missingAffix: "{{type}} name `{{name}}` must have one of the following {{position}}es: {{affixes}}",
@@ -1216,7 +1222,7 @@ const camelCaseNamingConfig = [
1216
1222
  selector: "typeLike"
1217
1223
  }
1218
1224
  ];
1219
- function create(contextWithoutDefaults) {
1225
+ function create$2(contextWithoutDefaults) {
1220
1226
  const context = contextWithoutDefaults.options.length > 0 ? contextWithoutDefaults : Object.setPrototypeOf({ options: camelCaseNamingConfig }, contextWithoutDefaults);
1221
1227
  const validators = parseOptions(context);
1222
1228
  const compilerOptions = getParserServices(context, true).program?.getCompilerOptions() ?? {};
@@ -1237,11 +1243,11 @@ function create(contextWithoutDefaults) {
1237
1243
  return modifiers;
1238
1244
  }
1239
1245
  const { unusedVariables } = collectVariables(context);
1240
- function isUnused(name$1, initialScope) {
1246
+ function isUnused(name, initialScope) {
1241
1247
  let variable = null;
1242
1248
  let scope = initialScope;
1243
1249
  while (scope) {
1244
- variable = scope.set.get(name$1) ?? null;
1250
+ variable = scope.set.get(name) ?? null;
1245
1251
  if (variable) break;
1246
1252
  scope = scope.upper;
1247
1253
  }
@@ -1323,22 +1329,19 @@ function create(contextWithoutDefaults) {
1323
1329
  const selectors = {
1324
1330
  ":matches(PropertyDefinition, TSAbstractPropertyDefinition)[computed = false][value.type != \"ArrowFunctionExpression\"][value.type != \"FunctionExpression\"][value.type != \"TSEmptyBodyFunctionExpression\"]": {
1325
1331
  handler: (node, validator) => {
1326
- const modifiers = getMemberModifiers(node);
1327
- handleMember(validator, node, modifiers);
1332
+ handleMember(validator, node, getMemberModifiers(node));
1328
1333
  },
1329
1334
  validator: validators.classProperty
1330
1335
  },
1331
1336
  ":not(ObjectPattern) > Property[computed = false][kind = \"init\"][value.type != \"ArrowFunctionExpression\"][value.type != \"FunctionExpression\"][value.type != \"TSEmptyBodyFunctionExpression\"]": {
1332
1337
  handler: (node, validator) => {
1333
- const modifiers = new Set([Modifier.public]);
1334
- handleMember(validator, node, modifiers);
1338
+ handleMember(validator, node, /* @__PURE__ */ new Set([Modifier.public]));
1335
1339
  },
1336
1340
  validator: validators.objectLiteralProperty
1337
1341
  },
1338
1342
  [["TSMethodSignature[computed = false]", "TSPropertySignature[computed = false][typeAnnotation.typeAnnotation.type = \"TSFunctionType\"]"].join(", ")]: {
1339
1343
  handler: (node, validator) => {
1340
- const modifiers = new Set([Modifier.public]);
1341
- handleMember(validator, node, modifiers);
1344
+ handleMember(validator, node, /* @__PURE__ */ new Set([Modifier.public]));
1342
1345
  },
1343
1346
  validator: validators.typeMethod
1344
1347
  },
@@ -1358,8 +1361,7 @@ function create(contextWithoutDefaults) {
1358
1361
  },
1359
1362
  [["MethodDefinition[computed = false]:matches([kind = \"get\"], [kind = \"set\"])", "TSAbstractMethodDefinition[computed = false]:matches([kind=\"get\"], [kind=\"set\"])"].join(", ")]: {
1360
1363
  handler: (node, validator) => {
1361
- const modifiers = getMemberModifiers(node);
1362
- handleMember(validator, node, modifiers);
1364
+ handleMember(validator, node, getMemberModifiers(node));
1363
1365
  },
1364
1366
  validator: validators.classicAccessor
1365
1367
  },
@@ -1369,7 +1371,7 @@ function create(contextWithoutDefaults) {
1369
1371
  "Property[computed = false][kind = \"init\"][value.type = \"TSEmptyBodyFunctionExpression\"]"
1370
1372
  ].join(", ")]: {
1371
1373
  handler: (node, validator) => {
1372
- const modifiers = new Set([Modifier.public]);
1374
+ const modifiers = /* @__PURE__ */ new Set([Modifier.public]);
1373
1375
  if (isAsyncMemberOrProperty(node)) modifiers.add(Modifier.async);
1374
1376
  handleMember(validator, node, modifiers);
1375
1377
  },
@@ -1377,14 +1379,13 @@ function create(contextWithoutDefaults) {
1377
1379
  },
1378
1380
  [[AST_NODE_TYPES.AccessorProperty, AST_NODE_TYPES.TSAbstractAccessorProperty].join(", ")]: {
1379
1381
  handler: (node, validator) => {
1380
- const modifiers = getMemberModifiers(node);
1381
- handleMember(validator, node, modifiers);
1382
+ handleMember(validator, node, getMemberModifiers(node));
1382
1383
  },
1383
1384
  validator: validators.autoAccessor
1384
1385
  },
1385
1386
  "ClassDeclaration, ClassExpression": {
1386
1387
  handler: (node, validator) => {
1387
- const { abstract, id } = node;
1388
+ const { id, abstract } = node;
1388
1389
  if (id === null) return;
1389
1390
  const modifiers = /* @__PURE__ */ new Set();
1390
1391
  const scope = context.sourceCode.getScope(node).upper;
@@ -1444,8 +1445,7 @@ function create(contextWithoutDefaults) {
1444
1445
  },
1445
1446
  "Property[computed = false]:matches([kind = \"get\"], [kind = \"set\"])": {
1446
1447
  handler: (node, validator) => {
1447
- const modifiers = new Set([Modifier.public]);
1448
- handleMember(validator, node, modifiers);
1448
+ handleMember(validator, node, /* @__PURE__ */ new Set([Modifier.public]));
1449
1449
  },
1450
1450
  validator: validators.classicAccessor
1451
1451
  },
@@ -1488,7 +1488,7 @@ function create(contextWithoutDefaults) {
1488
1488
  },
1489
1489
  "TSPropertySignature[computed = false][typeAnnotation.typeAnnotation.type != \"TSFunctionType\"]": {
1490
1490
  handler: (node, validator) => {
1491
- const modifiers = new Set([Modifier.public]);
1491
+ const modifiers = /* @__PURE__ */ new Set([Modifier.public]);
1492
1492
  if (node.readonly) modifiers.add(Modifier.readonly);
1493
1493
  handleMember(validator, node, modifiers);
1494
1494
  },
@@ -1542,7 +1542,8 @@ function create(contextWithoutDefaults) {
1542
1542
  }));
1543
1543
  }
1544
1544
  const namingConvention = createEslintRule({
1545
- create,
1545
+ name: RULE_NAME$2,
1546
+ create: create$2,
1546
1547
  defaultOptions: camelCaseNamingConfig,
1547
1548
  meta: {
1548
1549
  docs: {
@@ -1552,21 +1553,22 @@ const namingConvention = createEslintRule({
1552
1553
  },
1553
1554
  fixable: void 0,
1554
1555
  hasSuggestions: false,
1555
- messages,
1556
+ messages: messages$2,
1556
1557
  schema: SCHEMA,
1557
1558
  type: "suggestion"
1558
- },
1559
- name: RULE_NAME
1559
+ }
1560
1560
  });
1561
1561
  function getIdentifiersFromPattern(pattern) {
1562
1562
  const identifiers = [];
1563
- new PatternVisitor({}, pattern, (id) => identifiers.push(id)).visit(pattern);
1563
+ new PatternVisitor({}, pattern, (id) => {
1564
+ identifiers.push(id);
1565
+ }).visit(pattern);
1564
1566
  return identifiers;
1565
1567
  }
1566
- function isExported(node, name$1, scope) {
1568
+ function isExported(node, name, scope) {
1567
1569
  if (node?.parent?.type === AST_NODE_TYPES.ExportDefaultDeclaration || node?.parent?.type === AST_NODE_TYPES.ExportNamedDeclaration) return true;
1568
1570
  if (scope === null) return false;
1569
- const variable = scope.set.get(name$1);
1571
+ const variable = scope.set.get(name);
1570
1572
  if (variable) for (const ref of variable.references) {
1571
1573
  const refParent = ref.identifier.parent;
1572
1574
  if (refParent.type === AST_NODE_TYPES.ExportDefaultDeclaration || refParent.type === AST_NODE_TYPES.ExportSpecifier) return true;
@@ -1578,10 +1580,285 @@ function isGlobal(scope) {
1578
1580
  return scope.type === TSESLint.Scope.ScopeType.global || scope.type === TSESLint.Scope.ScopeType.module;
1579
1581
  }
1580
1582
  function requiresQuoting$1(node, target) {
1581
- const name$1 = node.type === AST_NODE_TYPES.Identifier || node.type === AST_NODE_TYPES.PrivateIdentifier ? node.name : `${node.value}`;
1582
- return requiresQuoting(name$1, target);
1583
+ return requiresQuoting(node.type === AST_NODE_TYPES.Identifier || node.type === AST_NODE_TYPES.PrivateIdentifier ? node.name : `${node.value}`, target);
1584
+ }
1585
+ //#endregion
1586
+ //#region src/rules/toml-sort-keys/rule.ts
1587
+ const RULE_NAME$1 = "toml-sort-keys";
1588
+ const MESSAGE_ID = "unsorted";
1589
+ const messages$1 = { [MESSAGE_ID]: "Expected {{target}} to follow the configured order." };
1590
+ const schema = {
1591
+ items: {
1592
+ additionalProperties: false,
1593
+ properties: {
1594
+ order: { oneOf: [{
1595
+ items: { type: "string" },
1596
+ type: "array"
1597
+ }, {
1598
+ additionalProperties: false,
1599
+ properties: {
1600
+ caseSensitive: { type: "boolean" },
1601
+ natural: { type: "boolean" },
1602
+ type: {
1603
+ enum: ["asc", "desc"],
1604
+ type: "string"
1605
+ }
1606
+ },
1607
+ type: "object"
1608
+ }] },
1609
+ pathPattern: { type: "string" }
1610
+ },
1611
+ required: ["order", "pathPattern"],
1612
+ type: "object"
1613
+ },
1614
+ type: "array"
1615
+ };
1616
+ function isTable(entry) {
1617
+ return entry.type === "TOMLTable";
1618
+ }
1619
+ /**
1620
+ * The dotted name of an entry, e.g. `settings`, `settings.node`, `_.path`.
1621
+ *
1622
+ * @param entry - The table header or key-value entry.
1623
+ * @returns The dotted key path as a string.
1624
+ */
1625
+ function nameOf(entry) {
1626
+ return getStaticTOMLValue(entry.key).join(".");
1627
+ }
1628
+ /**
1629
+ * Builds a rank function from an explicit order list. An entry matches an order
1630
+ * item when it equals it or is a dotted child of it (`settings.node` matches
1631
+ * `settings`), which keeps sub-tables grouped under their parent's slot.
1632
+ *
1633
+ * @param order - The configured names in their desired order.
1634
+ * @returns A function giving a name's rank, or `Infinity` when unlisted.
1635
+ */
1636
+ function makeRank(order) {
1637
+ return (name) => {
1638
+ for (const [index, item] of order.entries()) if (name === item || name.startsWith(`${item}.`)) return index;
1639
+ return Number.POSITIVE_INFINITY;
1640
+ };
1641
+ }
1642
+ function makeFallback(config) {
1643
+ const { caseSensitive = true, natural = false, type = "asc" } = config;
1644
+ const sensitivity = caseSensitive ? "variant" : "accent";
1645
+ return (a, b) => {
1646
+ const result = a.localeCompare(b, "en", {
1647
+ numeric: natural,
1648
+ sensitivity
1649
+ });
1650
+ return type === "desc" ? -result : result;
1651
+ };
1583
1652
  }
1584
-
1653
+ /**
1654
+ * Builds a comparator for one table body. Explicit-order entries sort first by
1655
+ * their listed position, then unlisted entries fall back to a natural/asc sort
1656
+ * (mirroring `yaml/sort-keys`). At the top level, bare key-values are always
1657
+ * kept before any `[table]` header, since TOML would otherwise re-scope them.
1658
+ *
1659
+ * @param order - The order configuration for this table's path.
1660
+ * @param isTopLevel - Whether the body is the top-level table.
1661
+ * @returns A comparator over two entries.
1662
+ */
1663
+ function makeComparator(order, isTopLevel) {
1664
+ const rank = Array.isArray(order) ? makeRank(order) : void 0;
1665
+ const fallback = makeFallback(Array.isArray(order) ? {
1666
+ natural: true,
1667
+ type: "asc"
1668
+ } : order);
1669
+ return (a, b) => {
1670
+ if (isTopLevel) {
1671
+ const aRank = isTable(a) ? 1 : 0;
1672
+ const bRank = isTable(b) ? 1 : 0;
1673
+ if (aRank !== bRank) return aRank - bRank;
1674
+ }
1675
+ if (rank !== void 0) {
1676
+ const aRank = rank(nameOf(a));
1677
+ const bRank = rank(nameOf(b));
1678
+ if (aRank !== bRank) {
1679
+ if (aRank === Number.POSITIVE_INFINITY) return 1;
1680
+ if (bRank === Number.POSITIVE_INFINITY) return -1;
1681
+ return aRank - bRank;
1682
+ }
1683
+ }
1684
+ return fallback(nameOf(a), nameOf(b));
1685
+ };
1686
+ }
1687
+ function create$1(context) {
1688
+ const { options, sourceCode } = context;
1689
+ if (sourceCode.parserServices.isTOML !== true) return {};
1690
+ const specs = options.map(({ order, pathPattern }) => {
1691
+ return {
1692
+ order,
1693
+ pattern: new RegExp(pathPattern, "u")
1694
+ };
1695
+ });
1696
+ if (specs.length === 0) return {};
1697
+ function resolveOrder(path) {
1698
+ for (const spec of specs) if (spec.pattern.test(path)) return spec.order;
1699
+ }
1700
+ /**
1701
+ * A comment counts as attached to an entry when it sits on its own line
1702
+ * directly above it (no blank line, no trailing code). Such comments travel
1703
+ * with the entry when it moves.
1704
+ *
1705
+ * @param comment - The comment to classify.
1706
+ * @returns Whether the comment stands alone on its line.
1707
+ */
1708
+ function isOwnLineComment(comment) {
1709
+ const before = sourceCode.getTokenBefore(comment, { includeComments: true });
1710
+ return before === null || before.loc.end.line < comment.loc.start.line;
1711
+ }
1712
+ function leadingStart(entry) {
1713
+ const comments = sourceCode.getCommentsBefore(entry);
1714
+ let start = entry.range[0];
1715
+ let boundaryLine = entry.loc.start.line;
1716
+ for (let index = comments.length - 1; index >= 0; index -= 1) {
1717
+ const comment = comments[index];
1718
+ if (comment === void 0) break;
1719
+ if (comment.loc.end.line !== boundaryLine - 1 || !isOwnLineComment(comment)) break;
1720
+ start = comment.range[0];
1721
+ boundaryLine = comment.loc.start.line;
1722
+ }
1723
+ return start;
1724
+ }
1725
+ function trailingEnd(entry) {
1726
+ const [comment] = sourceCode.getCommentsAfter(entry);
1727
+ if (comment?.loc.start.line === entry.loc.end.line) return comment.range[1];
1728
+ return entry.range[1];
1729
+ }
1730
+ function buildFix(entries, target) {
1731
+ const text = sourceCode.getText();
1732
+ const blocks = entries.map((entry) => {
1733
+ return {
1734
+ end: trailingEnd(entry),
1735
+ entry,
1736
+ start: leadingStart(entry)
1737
+ };
1738
+ });
1739
+ for (let index = 1; index < blocks.length; index += 1) {
1740
+ const previous = blocks[index - 1];
1741
+ const current = blocks[index];
1742
+ if (previous === void 0 || current === void 0) return;
1743
+ if (/\S/u.test(text.slice(previous.end, current.start))) return;
1744
+ }
1745
+ const first = blocks[0];
1746
+ const last = blocks[blocks.length - 1];
1747
+ if (first === void 0 || last === void 0) return;
1748
+ const textByEntry = new Map(blocks.map((block) => [block.entry, text.slice(block.start, block.end)]));
1749
+ const parts = [];
1750
+ for (const [index, entry] of target.entries()) {
1751
+ const previous = target[index - 1];
1752
+ if (previous !== void 0) parts.push(isTable(entry) || isTable(previous) ? "\n\n" : "\n");
1753
+ parts.push(textByEntry.get(entry) ?? "");
1754
+ }
1755
+ const sortedText = parts.join("");
1756
+ return (fixer) => fixer.replaceTextRange([first.start, last.end], sortedText);
1757
+ }
1758
+ function verify(path, body, isTopLevel) {
1759
+ if (body.length < 2) return;
1760
+ const order = resolveOrder(path);
1761
+ if (order === void 0) return;
1762
+ const entries = [...body];
1763
+ const target = [...entries].sort(makeComparator(order, isTopLevel));
1764
+ let outOfPlace;
1765
+ for (const [index, entry] of entries.entries()) if (entry !== target[index]) {
1766
+ outOfPlace = entry;
1767
+ break;
1768
+ }
1769
+ if (outOfPlace === void 0) return;
1770
+ context.report({
1771
+ data: { target: path === "" ? "top-level tables" : `keys in "${path}"` },
1772
+ fix: buildFix(entries, target),
1773
+ loc: outOfPlace.key.loc,
1774
+ messageId: MESSAGE_ID
1775
+ });
1776
+ }
1777
+ return {
1778
+ TOMLTable(node) {
1779
+ verify(getStaticTOMLValue(node.key).join("."), node.body, false);
1780
+ },
1781
+ TOMLTopLevelTable(node) {
1782
+ verify("", node.body, true);
1783
+ }
1784
+ };
1785
+ }
1786
+ const tomlSortKeys = createEslintRule({
1787
+ name: RULE_NAME$1,
1788
+ create: create$1,
1789
+ defaultOptions: [],
1790
+ meta: {
1791
+ defaultOptions: [],
1792
+ docs: {
1793
+ description: "Enforce a configured sort order for TOML keys and tables",
1794
+ recommended: false,
1795
+ requiresTypeChecking: false
1796
+ },
1797
+ fixable: "code",
1798
+ hasSuggestions: false,
1799
+ messages: messages$1,
1800
+ schema,
1801
+ type: "layout"
1802
+ }
1803
+ });
1804
+ //#endregion
1805
+ //#region src/rules/yaml-block-key-blank-lines/rule.ts
1806
+ const RULE_NAME = "yaml-block-key-blank-lines";
1807
+ const messages = { blankLine: "Expected {{count}} blank line(s) around this top-level key." };
1808
+ /**
1809
+ * Determines whether a pair value is a block collection (block mapping or block
1810
+ * sequence). Flow collections (`{ ... }` / `[ ... ]`) count as scalars.
1811
+ *
1812
+ * @param value - The value node of a YAML pair.
1813
+ * @returns True if the value is a block mapping or block sequence.
1814
+ */
1815
+ function isBlock(value) {
1816
+ return value !== null && (value.type === "YAMLMapping" || value.type === "YAMLSequence") && value.style === "block";
1817
+ }
1818
+ function create(context) {
1819
+ const { sourceCode } = context;
1820
+ if (sourceCode.parserServices.isYAML !== true) return {};
1821
+ return { YAMLMapping(yamlNode) {
1822
+ if (yamlNode.parent.type !== "YAMLDocument") return;
1823
+ const { pairs } = yamlNode;
1824
+ for (let index = 1; index < pairs.length; index += 1) {
1825
+ const previous = pairs[index - 1];
1826
+ const current = pairs[index];
1827
+ if (previous === void 0 || current === void 0) continue;
1828
+ const left = sourceCode.getLastToken(previous);
1829
+ const right = sourceCode.getFirstToken(current);
1830
+ if (sourceCode.commentsExistBetween(left, right)) continue;
1831
+ const blanks = right.loc.start.line - left.loc.end.line - 1;
1832
+ const want = isBlock(previous.value) || isBlock(current.value) ? 1 : 0;
1833
+ if (blanks === want) continue;
1834
+ context.report({
1835
+ data: { count: want },
1836
+ fix(fixer) {
1837
+ return fixer.replaceTextRange([left.range[1], right.range[0]], "\n".repeat(want + 1));
1838
+ },
1839
+ loc: (current.key ?? current).loc,
1840
+ messageId: "blankLine"
1841
+ });
1842
+ }
1843
+ } };
1844
+ }
1845
+ const yamlBlockKeyBlankLines = createEslintRule({
1846
+ name: RULE_NAME,
1847
+ create,
1848
+ defaultOptions: [],
1849
+ meta: {
1850
+ docs: {
1851
+ description: "Enforce blank lines around top-level YAML block collection keys",
1852
+ recommended: false,
1853
+ requiresTypeChecking: false
1854
+ },
1855
+ fixable: "whitespace",
1856
+ hasSuggestions: false,
1857
+ messages,
1858
+ schema: [],
1859
+ type: "layout"
1860
+ }
1861
+ });
1585
1862
  //#endregion
1586
1863
  //#region src/plugin.ts
1587
1864
  const PLUGIN_NAME = name.replace(/^eslint-plugin-/, "");
@@ -1600,23 +1877,24 @@ const plugin = {
1600
1877
  name: PLUGIN_NAME,
1601
1878
  version
1602
1879
  },
1603
- rules: { "naming-convention": namingConvention }
1880
+ rules: {
1881
+ "naming-convention": namingConvention,
1882
+ "toml-sort-keys": tomlSortKeys,
1883
+ "yaml-block-key-blank-lines": yamlBlockKeyBlankLines
1884
+ }
1604
1885
  };
1605
- const allRules = getRules(PLUGIN_NAME, plugin.rules);
1606
-
1886
+ getRules(PLUGIN_NAME, plugin.rules);
1607
1887
  //#endregion
1608
1888
  //#region src/configs/index.ts
1609
1889
  const configs = { recommended: {
1610
1890
  plugins: { [PLUGIN_NAME]: plugin },
1611
1891
  rules: {}
1612
1892
  } };
1613
-
1614
1893
  //#endregion
1615
1894
  //#region src/index.ts
1616
1895
  var src_default = {
1617
1896
  ...plugin,
1618
1897
  configs
1619
1898
  };
1620
-
1621
1899
  //#endregion
1622
- export { src_default as default };
1900
+ export { src_default as default };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-flawless",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "Your ESLint plugin description",
5
5
  "keywords": [
6
6
  "eslint",
@@ -34,44 +34,47 @@
34
34
  "*": "eslint --fix --cache"
35
35
  },
36
36
  "dependencies": {
37
- "@typescript-eslint/scope-manager": "8.44.0",
38
- "@typescript-eslint/type-utils": "^8.44.0",
39
- "@typescript-eslint/utils": "8.44.0"
37
+ "@typescript-eslint/scope-manager": "8.62.1",
38
+ "@typescript-eslint/type-utils": "8.62.1",
39
+ "@typescript-eslint/utils": "8.62.1",
40
+ "toml-eslint-parser": "1.0.3"
40
41
  },
41
42
  "devDependencies": {
42
- "@antfu/ni": "25.0.0",
43
- "@isentinel/eslint-config": "3.0.0",
44
- "@isentinel/tsconfig": "1.0.0",
43
+ "@antfu/ni": "30.2.0",
44
+ "@isentinel/eslint-config": "5.2.0",
45
+ "@isentinel/tsconfig": "1.2.0",
45
46
  "@types/node": "24.0.13",
46
- "@typescript-eslint/eslint-plugin": "8.44.0",
47
- "@typescript-eslint/parser": "8.44.0",
48
- "@vitest/eslint-plugin": "1.3.4",
49
- "bumpp": "10.2.2",
50
- "eslint": "9.35.0",
51
- "eslint-doc-generator": "2.2.2",
52
- "eslint-plugin-eslint-plugin": "7.0.0",
53
- "eslint-plugin-n": "17.23.1",
54
- "eslint-plugin-pnpm": "1.1.0",
55
- "eslint-vitest-rule-tester": "2.2.1",
56
- "jiti": "2.5.1",
57
- "lint-staged": "16.1.4",
58
- "publint": "0.3.12",
47
+ "@typescript-eslint/eslint-plugin": "8.62.1",
48
+ "@typescript-eslint/parser": "8.62.1",
49
+ "@vitest/eslint-plugin": "1.6.20",
50
+ "bumpp": "11.1.0",
51
+ "eslint": "9.39.4",
52
+ "eslint-doc-generator": "3.6.0",
53
+ "eslint-plugin-eslint-plugin": "7.4.1",
54
+ "eslint-plugin-n": "18.2.1",
55
+ "eslint-plugin-pnpm": "1.6.1",
56
+ "eslint-plugin-toml": "1.4.0",
57
+ "eslint-plugin-yml": "3.5.0",
58
+ "eslint-vitest-rule-tester": "3.1.0",
59
+ "jiti": "2.7.0",
60
+ "lint-staged": "17.0.8",
61
+ "publint": "0.3.21",
59
62
  "simple-git-hooks": "2.13.1",
60
- "ts-api-utils": "2.1.0",
61
- "tsdown": "0.13.3",
62
- "tsx": "4.20.3",
63
- "typescript": "5.8.3",
64
- "unplugin-unused": "0.5.1",
65
- "vitest": "3.2.4"
63
+ "ts-api-utils": "2.5.0",
64
+ "tsdown": "0.22.3",
65
+ "tsx": "4.22.4",
66
+ "typescript": "6.0.3",
67
+ "unplugin-unused": "0.5.7",
68
+ "vitest": "4.1.9",
69
+ "yaml-eslint-parser": "2.0.0"
66
70
  },
67
71
  "peerDependencies": {
68
72
  "eslint": ">=9.15.0"
69
73
  },
70
74
  "engines": {
71
- "node": ">=20.0.0"
75
+ "node": ">=24.12.0"
72
76
  },
73
77
  "publishConfig": {
74
- "access": "public",
75
78
  "provenance": true
76
79
  },
77
80
  "scripts": {