@borela-tech/eslint-config 2.0.0 → 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 (77) hide show
  1. package/.github/workflows/ci.yml +82 -0
  2. package/README.md +67 -0
  3. package/bin/lint +2 -1
  4. package/bin/typecheck +8 -0
  5. package/dist/index.js +478 -51
  6. package/dist/index.js.map +1 -1
  7. package/package.json +8 -1
  8. package/src/index.ts +9 -0
  9. package/src/lib/compare.ts +3 -0
  10. package/src/rules/__tests__/importsAndReExportsAtTop.test.ts +118 -0
  11. package/src/rules/__tests__/individualImports.test.ts +2 -2
  12. package/src/rules/__tests__/individualReExports.test.ts +62 -0
  13. package/src/rules/__tests__/sortedImports.test.ts +38 -4
  14. package/src/rules/__tests__/sortedReExports.test.ts +151 -0
  15. package/src/rules/importsAndReExportsAtTop/CategorizedStatements.ts +8 -0
  16. package/src/rules/importsAndReExportsAtTop/ReExport.ts +5 -0
  17. package/src/rules/importsAndReExportsAtTop/StatementIndices.ts +5 -0
  18. package/src/rules/importsAndReExportsAtTop/categorizeStatements.ts +27 -0
  19. package/src/rules/importsAndReExportsAtTop/findFirstIndices.ts +25 -0
  20. package/src/rules/importsAndReExportsAtTop/generateSortedText.ts +18 -0
  21. package/src/rules/importsAndReExportsAtTop/getStatementType.ts +17 -0
  22. package/src/rules/importsAndReExportsAtTop/hasViolation.ts +29 -0
  23. package/src/rules/importsAndReExportsAtTop/index.ts +45 -0
  24. package/src/rules/importsAndReExportsAtTop/statementType.ts +4 -0
  25. package/src/rules/individualImports.ts +5 -14
  26. package/src/rules/individualReExports.ts +52 -0
  27. package/src/rules/sortedImports/CategorizedImport.ts +2 -2
  28. package/src/rules/sortedImports/ImportError.ts +2 -2
  29. package/src/rules/sortedImports/ImportGroup.ts +5 -1
  30. package/src/rules/sortedImports/ImportGroupOrder.ts +8 -0
  31. package/src/rules/sortedImports/areSpecifiersSorted.ts +4 -5
  32. package/src/rules/sortedImports/categorizeImport.ts +9 -2
  33. package/src/rules/sortedImports/categorizeImports.ts +3 -3
  34. package/src/rules/sortedImports/checkAlphabeticalSorting.ts +4 -3
  35. package/src/rules/sortedImports/checkGroupOrdering.ts +2 -3
  36. package/src/rules/sortedImports/checkSpecifiersSorting.ts +2 -2
  37. package/src/rules/sortedImports/createFix/buildSortedCode.ts +4 -4
  38. package/src/rules/sortedImports/createFix/findLastImportIndex.ts +2 -2
  39. package/src/rules/sortedImports/createFix/formatNamedImport.ts +5 -8
  40. package/src/rules/sortedImports/createFix/getReplacementRange.ts +6 -18
  41. package/src/rules/sortedImports/createFix/groupImportsByType.ts +1 -0
  42. package/src/rules/sortedImports/createFix/index.ts +5 -6
  43. package/src/rules/sortedImports/createFix/sortImportGroups.ts +5 -2
  44. package/src/rules/sortedImports/getImportDeclarations.ts +3 -4
  45. package/src/rules/sortedImports/getNamedSpecifiers.ts +3 -4
  46. package/src/rules/sortedImports/getSortKey.ts +7 -7
  47. package/src/rules/sortedImports/getSpecifierName.ts +2 -2
  48. package/src/rules/sortedImports/index.ts +6 -4
  49. package/src/rules/sortedImports/sortSpecifiersText.ts +7 -6
  50. package/src/rules/sortedReExports/CategorizedNamedReExport.ts +6 -0
  51. package/src/rules/sortedReExports/CategorizedReExport.ts +15 -0
  52. package/src/rules/sortedReExports/ReExportDeclaration.ts +5 -0
  53. package/src/rules/sortedReExports/ReExportError.ts +6 -0
  54. package/src/rules/sortedReExports/ReExportGroup.ts +4 -0
  55. package/src/rules/sortedReExports/ReExportGroupOrder.ts +7 -0
  56. package/src/rules/sortedReExports/areSpecifiersSorted.ts +9 -0
  57. package/src/rules/sortedReExports/categorizeReExport.ts +17 -0
  58. package/src/rules/sortedReExports/categorizeReExports.ts +14 -0
  59. package/src/rules/sortedReExports/checkAlphabeticalSorting.ts +25 -0
  60. package/src/rules/sortedReExports/checkGroupOrdering.ts +21 -0
  61. package/src/rules/sortedReExports/checkSpecifiersSorting.ts +23 -0
  62. package/src/rules/sortedReExports/createFix/buildSortedCode.ts +28 -0
  63. package/src/rules/sortedReExports/createFix/findFirstExportIndex.ts +11 -0
  64. package/src/rules/sortedReExports/createFix/findLastExportIndex.ts +12 -0
  65. package/src/rules/sortedReExports/createFix/formatNamedReExport.ts +20 -0
  66. package/src/rules/sortedReExports/createFix/getReplacementRange.ts +22 -0
  67. package/src/rules/sortedReExports/createFix/groupReExportsByType.ts +17 -0
  68. package/src/rules/sortedReExports/createFix/index.ts +29 -0
  69. package/src/rules/sortedReExports/createFix/sortExportGroups.ts +11 -0
  70. package/src/rules/sortedReExports/getNamedSpecifiers.ts +9 -0
  71. package/src/rules/sortedReExports/getReExportDeclarations.ts +12 -0
  72. package/src/rules/sortedReExports/getSortKey.ts +16 -0
  73. package/src/rules/sortedReExports/getSpecifierName.ts +7 -0
  74. package/src/rules/sortedReExports/index.ts +54 -0
  75. package/src/rules/sortedReExports/isNamedReExport.ts +6 -0
  76. package/src/rules/sortedReExports/sortSpecifiersText.ts +15 -0
  77. /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
  }
@@ -50,6 +193,8 @@ var individualImports = {
50
193
 
51
194
  // src/rules/sortedImports/categorizeImport.ts
52
195
  function categorizeImport(declaration) {
196
+ if (declaration.importKind === "type")
197
+ return "type";
53
198
  if (declaration.specifiers.length === 0)
54
199
  return "side-effect";
55
200
  if (declaration.specifiers.some((s) => s.type === "ImportDefaultSpecifier"))
@@ -61,14 +206,15 @@ function categorizeImport(declaration) {
61
206
  function getSortKey(declaration) {
62
207
  const group = categorizeImport(declaration);
63
208
  if (group === "side-effect")
64
- return declaration.source.value.toLowerCase();
209
+ return declaration.source.value;
65
210
  if (group === "default") {
66
211
  const defaultSpecifier = declaration.specifiers.find(
67
212
  (s) => s.type === "ImportDefaultSpecifier"
68
213
  );
69
- return defaultSpecifier?.local.name.toLowerCase() ?? "";
214
+ return defaultSpecifier?.local.name ?? "";
70
215
  }
71
- return "";
216
+ const specifier = declaration.specifiers[0];
217
+ return specifier.local.name;
72
218
  }
73
219
 
74
220
  // src/rules/sortedImports/categorizeImports.ts
@@ -80,12 +226,25 @@ function categorizeImports(declarations) {
80
226
  }));
81
227
  }
82
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
+
83
242
  // src/rules/sortedImports/checkAlphabeticalSorting.ts
84
243
  function checkAlphabeticalSorting(categorized) {
85
244
  const errors = [];
86
- for (const group of ["side-effect", "default"]) {
245
+ for (const group of importGroupOrder) {
87
246
  const groupImports = categorized.filter((c) => c.group === group);
88
- const sorted = [...groupImports].sort((a, b) => a.sortKey.localeCompare(b.sortKey));
247
+ const sorted = [...groupImports].sort((a, b) => compare(a.sortKey, b.sortKey));
89
248
  for (let i = 0; i < groupImports.length; i++) {
90
249
  if (groupImports[i] !== sorted[i]) {
91
250
  errors.push({
@@ -100,11 +259,10 @@ function checkAlphabeticalSorting(categorized) {
100
259
 
101
260
  // src/rules/sortedImports/checkGroupOrdering.ts
102
261
  function checkGroupOrdering(categorized) {
103
- const groupOrder = ["side-effect", "default", "named"];
104
262
  const errors = [];
105
263
  let currentGroupIndex = -1;
106
264
  for (const { declaration, group } of categorized) {
107
- const groupIndex = groupOrder.indexOf(group);
265
+ const groupIndex = importGroupOrder.indexOf(group);
108
266
  if (groupIndex < currentGroupIndex) {
109
267
  errors.push({
110
268
  node: declaration,
@@ -124,9 +282,7 @@ function getSpecifierName(specifier) {
124
282
  // src/rules/sortedImports/areSpecifiersSorted.ts
125
283
  function areSpecifiersSorted(specifiers) {
126
284
  const names = specifiers.map((s) => getSpecifierName(s));
127
- const sorted = [...names].sort(
128
- (a, b) => a.toLowerCase().localeCompare(b.toLowerCase())
129
- );
285
+ const sorted = [...names].sort((a, b) => compare(a, b));
130
286
  return names.every((name, i) => name === sorted[i]);
131
287
  }
132
288
 
@@ -156,9 +312,9 @@ function checkSpecifiersSorting(categorized) {
156
312
  // src/rules/sortedImports/sortSpecifiersText.ts
157
313
  function sortSpecifiersText(specifiers, sourceCode) {
158
314
  const sorted = [...specifiers].sort((a, b) => {
159
- const lowerA = getSpecifierName(a).toLowerCase();
160
- const lowerB = getSpecifierName(b).toLowerCase();
161
- return lowerA.localeCompare(lowerB);
315
+ const nameA = getSpecifierName(a);
316
+ const nameB = getSpecifierName(b);
317
+ return compare(nameA, nameB);
162
318
  });
163
319
  return sorted.map((s) => sourceCode.getText(s)).join(", ");
164
320
  }
@@ -167,24 +323,20 @@ function sortSpecifiersText(specifiers, sourceCode) {
167
323
  function formatNamedImport(declaration, sourceCode) {
168
324
  const specifiers = getNamedSpecifiers(declaration);
169
325
  if (specifiers.length > 1 && !areSpecifiersSorted(specifiers)) {
170
- const importText = sourceCode.getText(declaration);
171
- const specifiersStart = importText.indexOf("{");
172
- const specifiersEnd = importText.lastIndexOf("}");
173
- const before = importText.substring(0, specifiersStart + 1);
174
- const after = importText.substring(specifiersEnd);
175
326
  const sortedSpecifiers = sortSpecifiersText(specifiers, sourceCode);
176
- 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}'`;
177
330
  }
178
331
  return sourceCode.getText(declaration);
179
332
  }
180
333
 
181
334
  // src/rules/sortedImports/createFix/buildSortedCode.ts
182
335
  function buildSortedCode(grouped, sourceCode) {
183
- const groupOrder = ["side-effect", "default", "named"];
184
336
  const sortedCode = [];
185
- for (const group of groupOrder) {
337
+ for (const group of importGroupOrder) {
186
338
  for (const { declaration } of grouped[group]) {
187
- if (group === "named")
339
+ if (group === "named" || group === "type")
188
340
  sortedCode.push(formatNamedImport(declaration, sourceCode));
189
341
  else
190
342
  sortedCode.push(sourceCode.getText(declaration));
@@ -206,20 +358,12 @@ function findLastImportIndex(programBody) {
206
358
  }
207
359
 
208
360
  // src/rules/sortedImports/createFix/getReplacementRange.ts
209
- function getReplacementRange(programBody, sourceCode) {
210
- const fullText = sourceCode.getText();
361
+ function getReplacementRange(programBody) {
211
362
  const lastIndex = findLastImportIndex(programBody);
212
363
  const firstImport = programBody[0];
213
364
  const lastImport = programBody[lastIndex];
214
365
  const start = firstImport.range[0];
215
- let end = lastImport.range[1];
216
- for (let i = end; i < fullText.length; i++) {
217
- const char = fullText[i];
218
- if (char === "\n" || char === " " || char === " ")
219
- end++;
220
- else
221
- break;
222
- }
366
+ const end = lastImport.range[1];
223
367
  return { start, end };
224
368
  }
225
369
 
@@ -228,7 +372,8 @@ function groupImportsByType(categorized) {
228
372
  const grouped = {
229
373
  "side-effect": [],
230
374
  default: [],
231
- named: []
375
+ named: [],
376
+ type: []
232
377
  };
233
378
  for (const item of categorized)
234
379
  grouped[item.group].push(item);
@@ -237,13 +382,15 @@ function groupImportsByType(categorized) {
237
382
 
238
383
  // src/rules/sortedImports/createFix/sortImportGroups.ts
239
384
  function sortImportGroups(grouped) {
240
- grouped["side-effect"].sort((a, b) => a.sortKey.localeCompare(b.sortKey));
241
- grouped["default"].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));
242
389
  }
243
390
 
244
391
  // src/rules/sortedImports/createFix/index.ts
245
392
  function createFix(fixer, importDeclarations, sourceCode, programBody) {
246
- const range = getReplacementRange(programBody, sourceCode);
393
+ const range = getReplacementRange(programBody);
247
394
  const categorized = categorizeImports(importDeclarations);
248
395
  const grouped = groupImportsByType(categorized);
249
396
  sortImportGroups(grouped);
@@ -280,7 +427,8 @@ var sortedImports = {
280
427
  create(context) {
281
428
  return {
282
429
  Program(node) {
283
- const declarations = getImportDeclarations(node.body);
430
+ const body = node.body;
431
+ const declarations = getImportDeclarations(body);
284
432
  if (declarations.length === 0)
285
433
  return;
286
434
  const categorized = categorizeImports(declarations);
@@ -295,7 +443,280 @@ var sortedImports = {
295
443
  messageId: error.messageId,
296
444
  fix(fixer) {
297
445
  const sourceCode = context.sourceCode;
298
- return createFix(fixer, declarations, sourceCode, node.body);
446
+ return createFix(fixer, declarations, sourceCode, body);
447
+ }
448
+ });
449
+ }
450
+ }
451
+ };
452
+ }
453
+ };
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);
299
720
  }
300
721
  });
301
722
  }
@@ -335,14 +756,20 @@ var CONFIG = defineConfig([
335
756
  plugins: {
336
757
  "@borela-tech": {
337
758
  rules: {
759
+ "imports-and-re-exports-at-top": importsAndReExportsAtTop,
338
760
  "individual-imports": individualImports,
339
- "sorted-imports": sortedImports
761
+ "individual-re-exports": individualReExports,
762
+ "sorted-imports": sortedImports,
763
+ "sorted-re-exports": sortedReExports
340
764
  }
341
765
  }
342
766
  },
343
767
  rules: {
768
+ "@borela-tech/imports-and-re-exports-at-top": "error",
344
769
  "@borela-tech/individual-imports": "error",
345
- "@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"
346
773
  }
347
774
  },
348
775
  {