@borela-tech/eslint-config 2.0.1 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/.github/workflows/ci.yml +82 -0
  2. package/README.md +67 -0
  3. package/dist/index.js +466 -38
  4. package/dist/index.js.map +1 -1
  5. package/package.json +8 -1
  6. package/src/index.ts +9 -0
  7. package/src/lib/compare.ts +3 -0
  8. package/src/rules/__tests__/importsAndReExportsAtTop.test.ts +118 -0
  9. package/src/rules/__tests__/individualReExports.test.ts +62 -0
  10. package/src/rules/__tests__/sortedImports.test.ts +3 -3
  11. package/src/rules/__tests__/sortedReExports.test.ts +151 -0
  12. package/src/rules/importsAndReExportsAtTop/CategorizedStatements.ts +8 -0
  13. package/src/rules/importsAndReExportsAtTop/ReExport.ts +5 -0
  14. package/src/rules/importsAndReExportsAtTop/StatementIndices.ts +5 -0
  15. package/src/rules/importsAndReExportsAtTop/categorizeStatements.ts +27 -0
  16. package/src/rules/importsAndReExportsAtTop/findFirstIndices.ts +25 -0
  17. package/src/rules/importsAndReExportsAtTop/generateSortedText.ts +18 -0
  18. package/src/rules/importsAndReExportsAtTop/getStatementType.ts +17 -0
  19. package/src/rules/importsAndReExportsAtTop/hasViolation.ts +29 -0
  20. package/src/rules/importsAndReExportsAtTop/index.ts +45 -0
  21. package/src/rules/importsAndReExportsAtTop/statementType.ts +4 -0
  22. package/src/rules/individualImports.ts +5 -14
  23. package/src/rules/individualReExports.ts +52 -0
  24. package/src/rules/sortedImports/CategorizedImport.ts +2 -2
  25. package/src/rules/sortedImports/ImportError.ts +2 -2
  26. package/src/rules/sortedImports/ImportGroup.ts +5 -1
  27. package/src/rules/sortedImports/ImportGroupOrder.ts +8 -0
  28. package/src/rules/sortedImports/areSpecifiersSorted.ts +4 -5
  29. package/src/rules/sortedImports/categorizeImport.ts +4 -0
  30. package/src/rules/sortedImports/checkAlphabeticalSorting.ts +4 -3
  31. package/src/rules/sortedImports/checkGroupOrdering.ts +2 -3
  32. package/src/rules/sortedImports/createFix/buildSortedCode.ts +2 -2
  33. package/src/rules/sortedImports/createFix/formatNamedImport.ts +5 -8
  34. package/src/rules/sortedImports/createFix/getReplacementRange.ts +1 -1
  35. package/src/rules/sortedImports/createFix/sortImportGroups.ts +5 -4
  36. package/src/rules/sortedImports/getNamedSpecifiers.ts +3 -4
  37. package/src/rules/sortedImports/getSortKey.ts +3 -3
  38. package/src/rules/sortedImports/getSpecifierName.ts +2 -2
  39. package/src/rules/sortedImports/sortSpecifiersText.ts +7 -6
  40. package/src/rules/sortedReExports/CategorizedNamedReExport.ts +6 -0
  41. package/src/rules/sortedReExports/CategorizedReExport.ts +15 -0
  42. package/src/rules/sortedReExports/ReExportDeclaration.ts +5 -0
  43. package/src/rules/sortedReExports/ReExportError.ts +6 -0
  44. package/src/rules/sortedReExports/ReExportGroup.ts +4 -0
  45. package/src/rules/sortedReExports/ReExportGroupOrder.ts +7 -0
  46. package/src/rules/sortedReExports/areSpecifiersSorted.ts +9 -0
  47. package/src/rules/sortedReExports/categorizeReExport.ts +17 -0
  48. package/src/rules/sortedReExports/categorizeReExports.ts +14 -0
  49. package/src/rules/sortedReExports/checkAlphabeticalSorting.ts +25 -0
  50. package/src/rules/sortedReExports/checkGroupOrdering.ts +21 -0
  51. package/src/rules/sortedReExports/checkSpecifiersSorting.ts +23 -0
  52. package/src/rules/sortedReExports/createFix/buildSortedCode.ts +28 -0
  53. package/src/rules/sortedReExports/createFix/findFirstExportIndex.ts +11 -0
  54. package/src/rules/sortedReExports/createFix/findLastExportIndex.ts +12 -0
  55. package/src/rules/sortedReExports/createFix/formatNamedReExport.ts +20 -0
  56. package/src/rules/sortedReExports/createFix/getReplacementRange.ts +22 -0
  57. package/src/rules/sortedReExports/createFix/groupReExportsByType.ts +17 -0
  58. package/src/rules/sortedReExports/createFix/index.ts +29 -0
  59. package/src/rules/sortedReExports/createFix/sortExportGroups.ts +11 -0
  60. package/src/rules/sortedReExports/getNamedSpecifiers.ts +9 -0
  61. package/src/rules/sortedReExports/getReExportDeclarations.ts +12 -0
  62. package/src/rules/sortedReExports/getSortKey.ts +16 -0
  63. package/src/rules/sortedReExports/getSpecifierName.ts +7 -0
  64. package/src/rules/sortedReExports/index.ts +54 -0
  65. package/src/rules/sortedReExports/isNamedReExport.ts +6 -0
  66. package/src/rules/sortedReExports/sortSpecifiersText.ts +15 -0
  67. /package/src/{rules/sortedImports/createFix → lib}/ReplacementRange.ts +0 -0
package/dist/index.js CHANGED
@@ -6,6 +6,118 @@ import stylistic from "@stylistic/eslint-plugin";
6
6
  import typescript from "typescript-eslint";
7
7
  import { defineConfig } from "eslint/config";
8
8
 
9
+ // src/rules/importsAndReExportsAtTop/getStatementType.ts
10
+ function getStatementType(statement) {
11
+ if (statement.type === "ImportDeclaration")
12
+ return "import";
13
+ if (statement.type === "ExportAllDeclaration")
14
+ return "re-export";
15
+ if (statement.type === "ExportNamedDeclaration") {
16
+ if (statement.source !== null)
17
+ return "re-export";
18
+ }
19
+ return "other";
20
+ }
21
+
22
+ // src/rules/importsAndReExportsAtTop/categorizeStatements.ts
23
+ function categorizeStatements(statements) {
24
+ const result = {
25
+ imports: [],
26
+ reExports: [],
27
+ other: []
28
+ };
29
+ for (const statement of statements) {
30
+ const type = getStatementType(statement);
31
+ if (type === "import")
32
+ result.imports.push(statement);
33
+ else if (type === "re-export")
34
+ result.reExports.push(statement);
35
+ else
36
+ result.other.push(statement);
37
+ }
38
+ return result;
39
+ }
40
+
41
+ // src/rules/importsAndReExportsAtTop/findFirstIndices.ts
42
+ function findFirstIndices(statements) {
43
+ let firstImport = Infinity;
44
+ let firstReExport = Infinity;
45
+ let firstOther = -1;
46
+ for (let i = 0; i < statements.length; i++) {
47
+ const type = getStatementType(statements[i]);
48
+ if (type === "import" && firstImport === Infinity)
49
+ firstImport = i;
50
+ else if (type === "re-export" && firstReExport === Infinity)
51
+ firstReExport = i;
52
+ else if (type === "other" && firstOther === -1)
53
+ firstOther = i;
54
+ }
55
+ return { firstImport, firstReExport, firstOther };
56
+ }
57
+
58
+ // src/rules/importsAndReExportsAtTop/generateSortedText.ts
59
+ function generateSortedText(context, categories) {
60
+ const allStatements = [
61
+ ...categories.imports,
62
+ ...categories.reExports,
63
+ ...categories.other
64
+ ];
65
+ return allStatements.map(
66
+ (node) => context.sourceCode.getText(node)
67
+ ).join("\n");
68
+ }
69
+
70
+ // src/rules/importsAndReExportsAtTop/hasViolation.ts
71
+ function hasViolation(indices, categories) {
72
+ const {
73
+ firstImport,
74
+ firstReExport,
75
+ firstOther
76
+ } = indices;
77
+ if (categories.imports.length === 0 || categories.reExports.length === 0)
78
+ return false;
79
+ const firstImportOrReExport = Math.min(firstImport, firstReExport);
80
+ const hasOtherBeforeImportOrReExport = firstOther !== -1 && firstOther < firstImportOrReExport;
81
+ if (hasOtherBeforeImportOrReExport || firstImport > firstReExport)
82
+ return true;
83
+ return false;
84
+ }
85
+
86
+ // src/rules/importsAndReExportsAtTop/index.ts
87
+ var importsAndReExportsAtTop = {
88
+ meta: {
89
+ type: "suggestion",
90
+ docs: {
91
+ description: "Enforce imports and re-exports at the top of the file",
92
+ recommended: false
93
+ },
94
+ fixable: "code",
95
+ messages: {
96
+ importsAndReExportsAtTop: "Imports and re-exports should be at the top of the file."
97
+ },
98
+ schema: []
99
+ },
100
+ create(context) {
101
+ return {
102
+ Program(node) {
103
+ const statements = node.body;
104
+ const categories = categorizeStatements(statements);
105
+ const indices = findFirstIndices(statements);
106
+ if (!hasViolation(indices, categories))
107
+ return;
108
+ context.report({
109
+ node,
110
+ messageId: "importsAndReExportsAtTop",
111
+ fix(fixer) {
112
+ const sortedText = generateSortedText(context, categories);
113
+ return fixer.replaceText(node, sortedText);
114
+ }
115
+ });
116
+ }
117
+ };
118
+ }
119
+ };
120
+
9
121
  // src/rules/individualImports.ts
10
122
  var individualImports = {
11
123
  meta: {
@@ -30,17 +142,48 @@ var individualImports = {
30
142
  messageId: "individualImports",
31
143
  fix(fixer) {
32
144
  const source = node.source.raw;
33
- const specifiers = node.specifiers.map((importSpecifier) => {
34
- if (importSpecifier.type === "ImportSpecifier")
35
- return `import {${importSpecifier.local.name}} from ${source}`;
36
- return null;
37
- }).filter(Boolean);
38
- if (specifiers.length !== node.specifiers.length)
39
- return null;
40
- return fixer.replaceText(
41
- node,
42
- specifiers.join("\n")
43
- );
145
+ const specifiers = node.specifiers.filter((s) => s.type === "ImportSpecifier").map((s) => `import {${s.local.name}} from ${source}`).join("\n");
146
+ return fixer.replaceText(node, specifiers);
147
+ }
148
+ });
149
+ }
150
+ };
151
+ }
152
+ };
153
+
154
+ // src/rules/individualReExports.ts
155
+ var individualReExports = {
156
+ meta: {
157
+ docs: {
158
+ description: "Enforce individual exports instead of grouped exports",
159
+ recommended: true
160
+ },
161
+ fixable: "code",
162
+ messages: {
163
+ individualReExports: "Use individual exports instead of grouped exports."
164
+ },
165
+ schema: [],
166
+ type: "suggestion"
167
+ },
168
+ create(context) {
169
+ return {
170
+ ExportNamedDeclaration(node) {
171
+ const exportNode = node;
172
+ if (!exportNode.source || exportNode.specifiers.length <= 1)
173
+ return;
174
+ context.report({
175
+ node,
176
+ messageId: "individualReExports",
177
+ fix(fixer) {
178
+ const source = exportNode.source.value;
179
+ const typeKeyword = exportNode.exportKind === "type" ? "type " : "";
180
+ const specifiers = exportNode.specifiers.map((s) => {
181
+ const localName = s.local.type === "Identifier" ? s.local.name : s.local.value;
182
+ const exportedName = s.exported.type === "Identifier" ? s.exported.name : s.exported.value;
183
+ const name = localName === exportedName ? localName : `${localName} as ${exportedName}`;
184
+ return `export ${typeKeyword}{${name}} from '${source}'`;
185
+ }).join("\n");
186
+ return fixer.replaceText(node, specifiers);
44
187
  }
45
188
  });
46
189
  }
@@ -63,15 +206,15 @@ function categorizeImport(declaration) {
63
206
  function getSortKey(declaration) {
64
207
  const group = categorizeImport(declaration);
65
208
  if (group === "side-effect")
66
- return declaration.source.value.toLowerCase();
209
+ return declaration.source.value;
67
210
  if (group === "default") {
68
211
  const defaultSpecifier = declaration.specifiers.find(
69
212
  (s) => s.type === "ImportDefaultSpecifier"
70
213
  );
71
- return defaultSpecifier?.local.name.toLowerCase() ?? "";
214
+ return defaultSpecifier?.local.name ?? "";
72
215
  }
73
216
  const specifier = declaration.specifiers[0];
74
- return specifier.local.name.toLowerCase();
217
+ return specifier.local.name;
75
218
  }
76
219
 
77
220
  // src/rules/sortedImports/categorizeImports.ts
@@ -83,12 +226,25 @@ function categorizeImports(declarations) {
83
226
  }));
84
227
  }
85
228
 
229
+ // src/lib/compare.ts
230
+ function compare(a, b) {
231
+ return a.localeCompare(b, "en", { sensitivity: "case" });
232
+ }
233
+
234
+ // src/rules/sortedImports/ImportGroupOrder.ts
235
+ var importGroupOrder = [
236
+ "side-effect",
237
+ "default",
238
+ "named",
239
+ "type"
240
+ ];
241
+
86
242
  // src/rules/sortedImports/checkAlphabeticalSorting.ts
87
243
  function checkAlphabeticalSorting(categorized) {
88
244
  const errors = [];
89
- for (const group of ["side-effect", "default", "named", "type"]) {
245
+ for (const group of importGroupOrder) {
90
246
  const groupImports = categorized.filter((c) => c.group === group);
91
- const sorted = [...groupImports].sort((a, b) => a.sortKey.localeCompare(b.sortKey));
247
+ const sorted = [...groupImports].sort((a, b) => compare(a.sortKey, b.sortKey));
92
248
  for (let i = 0; i < groupImports.length; i++) {
93
249
  if (groupImports[i] !== sorted[i]) {
94
250
  errors.push({
@@ -103,11 +259,10 @@ function checkAlphabeticalSorting(categorized) {
103
259
 
104
260
  // src/rules/sortedImports/checkGroupOrdering.ts
105
261
  function checkGroupOrdering(categorized) {
106
- const groupOrder = ["side-effect", "default", "named", "type"];
107
262
  const errors = [];
108
263
  let currentGroupIndex = -1;
109
264
  for (const { declaration, group } of categorized) {
110
- const groupIndex = groupOrder.indexOf(group);
265
+ const groupIndex = importGroupOrder.indexOf(group);
111
266
  if (groupIndex < currentGroupIndex) {
112
267
  errors.push({
113
268
  node: declaration,
@@ -127,9 +282,7 @@ function getSpecifierName(specifier) {
127
282
  // src/rules/sortedImports/areSpecifiersSorted.ts
128
283
  function areSpecifiersSorted(specifiers) {
129
284
  const names = specifiers.map((s) => getSpecifierName(s));
130
- const sorted = [...names].sort(
131
- (a, b) => a.toLowerCase().localeCompare(b.toLowerCase())
132
- );
285
+ const sorted = [...names].sort((a, b) => compare(a, b));
133
286
  return names.every((name, i) => name === sorted[i]);
134
287
  }
135
288
 
@@ -159,9 +312,9 @@ function checkSpecifiersSorting(categorized) {
159
312
  // src/rules/sortedImports/sortSpecifiersText.ts
160
313
  function sortSpecifiersText(specifiers, sourceCode) {
161
314
  const sorted = [...specifiers].sort((a, b) => {
162
- const lowerA = getSpecifierName(a).toLowerCase();
163
- const lowerB = getSpecifierName(b).toLowerCase();
164
- return lowerA.localeCompare(lowerB);
315
+ const nameA = getSpecifierName(a);
316
+ const nameB = getSpecifierName(b);
317
+ return compare(nameA, nameB);
165
318
  });
166
319
  return sorted.map((s) => sourceCode.getText(s)).join(", ");
167
320
  }
@@ -170,22 +323,18 @@ function sortSpecifiersText(specifiers, sourceCode) {
170
323
  function formatNamedImport(declaration, sourceCode) {
171
324
  const specifiers = getNamedSpecifiers(declaration);
172
325
  if (specifiers.length > 1 && !areSpecifiersSorted(specifiers)) {
173
- const importText = sourceCode.getText(declaration);
174
- const specifiersStart = importText.indexOf("{");
175
- const specifiersEnd = importText.lastIndexOf("}");
176
- const before = importText.substring(0, specifiersStart + 1);
177
- const after = importText.substring(specifiersEnd);
178
326
  const sortedSpecifiers = sortSpecifiersText(specifiers, sourceCode);
179
- return before + " " + sortedSpecifiers + " " + after;
327
+ const source = declaration.source.value;
328
+ const prefix = declaration.importKind === "type" ? "import type " : "import ";
329
+ return `${prefix}{${sortedSpecifiers}} from '${source}'`;
180
330
  }
181
331
  return sourceCode.getText(declaration);
182
332
  }
183
333
 
184
334
  // src/rules/sortedImports/createFix/buildSortedCode.ts
185
335
  function buildSortedCode(grouped, sourceCode) {
186
- const groupOrder = ["side-effect", "default", "named", "type"];
187
336
  const sortedCode = [];
188
- for (const group of groupOrder) {
337
+ for (const group of importGroupOrder) {
189
338
  for (const { declaration } of grouped[group]) {
190
339
  if (group === "named" || group === "type")
191
340
  sortedCode.push(formatNamedImport(declaration, sourceCode));
@@ -233,10 +382,10 @@ function groupImportsByType(categorized) {
233
382
 
234
383
  // src/rules/sortedImports/createFix/sortImportGroups.ts
235
384
  function sortImportGroups(grouped) {
236
- grouped["side-effect"].sort((a, b) => a.sortKey.localeCompare(b.sortKey));
237
- grouped["default"].sort((a, b) => a.sortKey.localeCompare(b.sortKey));
238
- grouped["named"].sort((a, b) => a.sortKey.localeCompare(b.sortKey));
239
- grouped["type"].sort((a, b) => a.sortKey.localeCompare(b.sortKey));
385
+ grouped["side-effect"].sort((a, b) => compare(a.sortKey, b.sortKey));
386
+ grouped["default"].sort((a, b) => compare(a.sortKey, b.sortKey));
387
+ grouped["named"].sort((a, b) => compare(a.sortKey, b.sortKey));
388
+ grouped["type"].sort((a, b) => compare(a.sortKey, b.sortKey));
240
389
  }
241
390
 
242
391
  // src/rules/sortedImports/createFix/index.ts
@@ -303,6 +452,279 @@ var sortedImports = {
303
452
  }
304
453
  };
305
454
 
455
+ // src/rules/sortedReExports/categorizeReExport.ts
456
+ function categorizeReExport(declaration) {
457
+ if (declaration.type === "ExportAllDeclaration")
458
+ return "re-export-all";
459
+ if (declaration.exportKind === "type")
460
+ return "re-export-type";
461
+ return "re-export-named";
462
+ }
463
+
464
+ // src/rules/sortedReExports/getSortKey.ts
465
+ function getSortKey2(declaration) {
466
+ if (declaration.type === "ExportAllDeclaration")
467
+ return declaration.source.value;
468
+ const specifier = declaration.specifiers[0];
469
+ if (!specifier)
470
+ return "";
471
+ return specifier.local.type === "Identifier" ? specifier.local.name : specifier.local.value;
472
+ }
473
+
474
+ // src/rules/sortedReExports/categorizeReExports.ts
475
+ function categorizeReExports(declarations) {
476
+ return declarations.map((declaration) => {
477
+ return {
478
+ declaration,
479
+ group: categorizeReExport(declaration),
480
+ sortKey: getSortKey2(declaration)
481
+ };
482
+ });
483
+ }
484
+
485
+ // src/rules/sortedReExports/ReExportGroupOrder.ts
486
+ var reExportGroupOrder = [
487
+ "re-export-all",
488
+ "re-export-named",
489
+ "re-export-type"
490
+ ];
491
+
492
+ // src/rules/sortedReExports/checkAlphabeticalSorting.ts
493
+ function checkAlphabeticalSorting2(categorized) {
494
+ const errors = [];
495
+ for (const group of reExportGroupOrder) {
496
+ const groupReExports = categorized.filter((c) => c.group === group);
497
+ const sorted = [...groupReExports].sort(
498
+ (a, b) => compare(a.sortKey, b.sortKey)
499
+ );
500
+ for (let i = 0; i < groupReExports.length; i++) {
501
+ if (groupReExports[i] !== sorted[i]) {
502
+ errors.push({
503
+ node: groupReExports[i].declaration,
504
+ messageId: "sortedReExports"
505
+ });
506
+ }
507
+ }
508
+ }
509
+ return errors;
510
+ }
511
+
512
+ // src/rules/sortedReExports/checkGroupOrdering.ts
513
+ function checkGroupOrdering2(categorized) {
514
+ const errors = [];
515
+ let currentGroupIndex = -1;
516
+ for (const { declaration, group } of categorized) {
517
+ const groupIndex = reExportGroupOrder.indexOf(group);
518
+ if (groupIndex < currentGroupIndex) {
519
+ errors.push({
520
+ node: declaration,
521
+ messageId: "wrongGroup"
522
+ });
523
+ } else
524
+ currentGroupIndex = groupIndex;
525
+ }
526
+ return errors;
527
+ }
528
+
529
+ // src/rules/sortedReExports/getSpecifierName.ts
530
+ function getSpecifierName2(specifier) {
531
+ return specifier.local.type === "Identifier" ? specifier.local.name : String(specifier.local.value);
532
+ }
533
+
534
+ // src/rules/sortedReExports/areSpecifiersSorted.ts
535
+ function areSpecifiersSorted2(specifiers) {
536
+ const names = specifiers.map((s) => getSpecifierName2(s));
537
+ const sorted = [...names].sort((a, b) => compare(a, b));
538
+ return names.every((name, i) => name === sorted[i]);
539
+ }
540
+
541
+ // src/rules/sortedReExports/getNamedSpecifiers.ts
542
+ function getNamedSpecifiers2(declaration) {
543
+ return declaration.specifiers.filter(
544
+ (s) => s.type === "ExportSpecifier" && s.local.type === "Identifier"
545
+ );
546
+ }
547
+
548
+ // src/rules/sortedReExports/isNamedReExport.ts
549
+ function isNamedReExport(x) {
550
+ return x.group !== "re-export-all";
551
+ }
552
+
553
+ // src/rules/sortedReExports/checkSpecifiersSorting.ts
554
+ function checkSpecifiersSorting2(categorized) {
555
+ const errors = [];
556
+ const namedReExports = categorized.filter(isNamedReExport);
557
+ for (const { declaration } of namedReExports) {
558
+ const specifiers = getNamedSpecifiers2(declaration);
559
+ const isSorted = areSpecifiersSorted2(specifiers);
560
+ if (specifiers.length > 1 && !isSorted) {
561
+ errors.push({
562
+ node: declaration,
563
+ messageId: "sortedNames"
564
+ });
565
+ }
566
+ }
567
+ return errors;
568
+ }
569
+
570
+ // src/rules/sortedReExports/sortSpecifiersText.ts
571
+ function sortSpecifiersText2(specifiers, sourceCode) {
572
+ const sorted = [...specifiers].sort((a, b) => {
573
+ const nameA = getSpecifierName2(a);
574
+ const nameB = getSpecifierName2(b);
575
+ return compare(nameA, nameB);
576
+ });
577
+ return sorted.map((s) => sourceCode.getText(s)).join(", ");
578
+ }
579
+
580
+ // src/rules/sortedReExports/createFix/formatNamedReExport.ts
581
+ function formatNamedReExport(declaration, sourceCode) {
582
+ const specifiers = getNamedSpecifiers2(declaration);
583
+ if (specifiers.length > 1 && !areSpecifiersSorted2(specifiers)) {
584
+ const sortedSpecifiers = sortSpecifiersText2(specifiers, sourceCode);
585
+ const source = declaration.source.value;
586
+ const prefix = declaration.exportKind === "type" ? "export type " : "export ";
587
+ return `${prefix}{${sortedSpecifiers}} from '${source}'`;
588
+ }
589
+ return sourceCode.getText(declaration);
590
+ }
591
+
592
+ // src/rules/sortedReExports/createFix/buildSortedCode.ts
593
+ function buildSortedCode2(grouped, sourceCode) {
594
+ const sortedCode = [];
595
+ for (const group of reExportGroupOrder) {
596
+ for (const item of grouped[group]) {
597
+ if (isNamedReExport(item)) {
598
+ sortedCode.push(
599
+ formatNamedReExport(
600
+ item.declaration,
601
+ sourceCode
602
+ )
603
+ );
604
+ } else
605
+ sortedCode.push(sourceCode.getText(item.declaration));
606
+ }
607
+ }
608
+ return sortedCode;
609
+ }
610
+
611
+ // src/rules/sortedReExports/createFix/findFirstExportIndex.ts
612
+ function findFirstExportIndex(programBody) {
613
+ for (let i = 0; i < programBody.length; i++) {
614
+ if (programBody[i].type === "ExportNamedDeclaration" || programBody[i].type === "ExportAllDeclaration") {
615
+ return i;
616
+ }
617
+ }
618
+ return -1;
619
+ }
620
+
621
+ // src/rules/sortedReExports/createFix/findLastExportIndex.ts
622
+ function findLastExportIndex(programBody) {
623
+ let lastIndex = -1;
624
+ for (let i = 0; i < programBody.length; i++) {
625
+ if (programBody[i].type === "ExportNamedDeclaration" || programBody[i].type === "ExportAllDeclaration") {
626
+ lastIndex = i;
627
+ }
628
+ }
629
+ return lastIndex;
630
+ }
631
+
632
+ // src/rules/sortedReExports/createFix/getReplacementRange.ts
633
+ function getReplacementRange2(programBody) {
634
+ const firstIndex = findFirstExportIndex(programBody);
635
+ const lastIndex = findLastExportIndex(programBody);
636
+ if (firstIndex === -1 || lastIndex === -1)
637
+ return { start: 0, end: 0 };
638
+ const firstExport = programBody[firstIndex];
639
+ const lastExport = programBody[lastIndex];
640
+ const start = firstExport.range[0];
641
+ const end = lastExport.range[1];
642
+ return { start, end };
643
+ }
644
+
645
+ // src/rules/sortedReExports/createFix/groupReExportsByType.ts
646
+ function groupReExportsByType(categorized) {
647
+ const grouped = {
648
+ "re-export-all": [],
649
+ "re-export-named": [],
650
+ "re-export-type": []
651
+ };
652
+ for (const item of categorized)
653
+ grouped[item.group].push(item);
654
+ return grouped;
655
+ }
656
+
657
+ // src/rules/sortedReExports/createFix/sortExportGroups.ts
658
+ function sortExportGroups(grouped) {
659
+ grouped["re-export-all"].sort((a, b) => compare(a.sortKey, b.sortKey));
660
+ grouped["re-export-named"].sort((a, b) => compare(a.sortKey, b.sortKey));
661
+ grouped["re-export-type"].sort((a, b) => compare(a.sortKey, b.sortKey));
662
+ }
663
+
664
+ // src/rules/sortedReExports/createFix/index.ts
665
+ function createFix2(fixer, reExportDeclarations, sourceCode, programBody) {
666
+ const range = getReplacementRange2(programBody);
667
+ const categorized = categorizeReExports(reExportDeclarations);
668
+ const grouped = groupReExportsByType(categorized);
669
+ sortExportGroups(grouped);
670
+ const sortedCode = buildSortedCode2(grouped, sourceCode).join("\n");
671
+ return fixer.replaceTextRange(
672
+ [range.start, range.end],
673
+ sortedCode
674
+ );
675
+ }
676
+
677
+ // src/rules/sortedReExports/getReExportDeclarations.ts
678
+ function getReExportDeclarations(programBody) {
679
+ return programBody.filter(
680
+ (statement) => statement.type === "ExportNamedDeclaration" && statement.source !== null || statement.type === "ExportAllDeclaration"
681
+ );
682
+ }
683
+
684
+ // src/rules/sortedReExports/index.ts
685
+ var sortedReExports = {
686
+ meta: {
687
+ docs: {
688
+ description: "Enforce sorted exports alphabetically",
689
+ recommended: true
690
+ },
691
+ fixable: "code",
692
+ messages: {
693
+ sortedReExports: "Exports should be sorted alphabetically",
694
+ sortedNames: "Named exports should be sorted alphabetically",
695
+ wrongGroup: "Export is in wrong group"
696
+ },
697
+ schema: [],
698
+ type: "suggestion"
699
+ },
700
+ create(context) {
701
+ return {
702
+ Program(node) {
703
+ const body = node.body;
704
+ const declarations = getReExportDeclarations(body);
705
+ if (declarations.length === 0)
706
+ return;
707
+ const categorized = categorizeReExports(declarations);
708
+ const errors = [
709
+ ...checkGroupOrdering2(categorized),
710
+ ...checkAlphabeticalSorting2(categorized),
711
+ ...checkSpecifiersSorting2(categorized)
712
+ ];
713
+ for (const error of errors) {
714
+ context.report({
715
+ node: error.node,
716
+ messageId: error.messageId,
717
+ fix(fixer) {
718
+ const sourceCode = context.sourceCode;
719
+ return createFix2(fixer, declarations, sourceCode, body);
720
+ }
721
+ });
722
+ }
723
+ }
724
+ };
725
+ }
726
+ };
727
+
306
728
  // src/index.ts
307
729
  var CONFIG = defineConfig([
308
730
  {
@@ -334,14 +756,20 @@ var CONFIG = defineConfig([
334
756
  plugins: {
335
757
  "@borela-tech": {
336
758
  rules: {
759
+ "imports-and-re-exports-at-top": importsAndReExportsAtTop,
337
760
  "individual-imports": individualImports,
338
- "sorted-imports": sortedImports
761
+ "individual-re-exports": individualReExports,
762
+ "sorted-imports": sortedImports,
763
+ "sorted-re-exports": sortedReExports
339
764
  }
340
765
  }
341
766
  },
342
767
  rules: {
768
+ "@borela-tech/imports-and-re-exports-at-top": "error",
343
769
  "@borela-tech/individual-imports": "error",
344
- "@borela-tech/sorted-imports": "error"
770
+ "@borela-tech/individual-re-exports": "error",
771
+ "@borela-tech/sorted-imports": "error",
772
+ "@borela-tech/sorted-re-exports": "error"
345
773
  }
346
774
  },
347
775
  {