@borela-tech/eslint-config 2.2.0 → 2.2.1

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/dist/index.mjs ADDED
@@ -0,0 +1,872 @@
1
+ import eslint from "@eslint/js";
2
+ import react from "eslint-plugin-react";
3
+ import reactHooks from "eslint-plugin-react-hooks";
4
+ import stylistic from "@stylistic/eslint-plugin";
5
+ import typescript from "typescript-eslint";
6
+ //#region src/rules/importsAndReExportsAtTop/getStatementType.ts
7
+ function getStatementType(statement) {
8
+ if (statement.type === "ImportDeclaration") return "import";
9
+ if (statement.type === "ExportAllDeclaration") return "re-export";
10
+ if (statement.type === "ExportNamedDeclaration") {
11
+ if (statement.source !== null) return "re-export";
12
+ }
13
+ return "other";
14
+ }
15
+ //#endregion
16
+ //#region src/rules/importsAndReExportsAtTop/isImportDeclaration.ts
17
+ function isImportDeclaration(statement) {
18
+ return statement.type === "ImportDeclaration";
19
+ }
20
+ //#endregion
21
+ //#region src/rules/importsAndReExportsAtTop/isReExport.ts
22
+ function isReExport(statement) {
23
+ if (statement.type === "ExportAllDeclaration") return true;
24
+ if (statement.type === "ExportNamedDeclaration") return statement.source !== null;
25
+ return false;
26
+ }
27
+ //#endregion
28
+ //#region src/rules/importsAndReExportsAtTop/categorizeStatements.ts
29
+ function categorizeStatements(statements) {
30
+ const result = {
31
+ imports: [],
32
+ reExports: [],
33
+ other: []
34
+ };
35
+ for (const statement of statements) {
36
+ const type = getStatementType(statement);
37
+ if (type === "import" && isImportDeclaration(statement)) result.imports.push(statement);
38
+ else if (type === "re-export" && isReExport(statement)) result.reExports.push(statement);
39
+ else result.other.push(statement);
40
+ }
41
+ return result;
42
+ }
43
+ //#endregion
44
+ //#region src/rules/importsAndReExportsAtTop/findStatementIndices.ts
45
+ function findStatementIndices(statements) {
46
+ let firstRegularStatement = -1;
47
+ let lastImport = -1;
48
+ let lastReExport = -1;
49
+ for (let i = 0; i < statements.length; i++) {
50
+ const type = getStatementType(statements[i]);
51
+ if (type === "import") lastImport = i;
52
+ else if (type === "re-export") lastReExport = i;
53
+ else if (type === "other" && firstRegularStatement === -1) firstRegularStatement = i;
54
+ }
55
+ return {
56
+ firstRegularStatement,
57
+ lastImport,
58
+ lastReExport
59
+ };
60
+ }
61
+ //#endregion
62
+ //#region src/rules/importsAndReExportsAtTop/generateSortedText.ts
63
+ function generateSortedText(context, categories) {
64
+ return [
65
+ ...categories.imports,
66
+ ...categories.reExports,
67
+ ...categories.other
68
+ ].map((node) => context.sourceCode.getText(node)).join("\n");
69
+ }
70
+ //#endregion
71
+ //#region src/rules/importsAndReExportsAtTop/hasViolation.ts
72
+ function hasViolation(indices, categories) {
73
+ const { firstRegularStatement, lastImport, lastReExport } = indices;
74
+ if (categories.imports.length === 0 && categories.reExports.length === 0) return false;
75
+ const hasImportAfterRegularStatement = categories.imports.length > 0 && firstRegularStatement !== -1 && lastImport > firstRegularStatement;
76
+ const hasReExportAfterRegularStatement = categories.reExports.length > 0 && firstRegularStatement !== -1 && lastReExport > firstRegularStatement;
77
+ return hasImportAfterRegularStatement || hasReExportAfterRegularStatement;
78
+ }
79
+ //#endregion
80
+ //#region src/rules/importsAndReExportsAtTop/index.ts
81
+ const importsAndReExportsAtTop = {
82
+ meta: {
83
+ type: "suggestion",
84
+ docs: { description: "Enforce imports and re-exports at the top of the file" },
85
+ fixable: "code",
86
+ messages: { importsAndReExportsAtTop: "Imports and re-exports should be at the top of the file." },
87
+ schema: []
88
+ },
89
+ create(context) {
90
+ return { Program(node) {
91
+ const statements = node.body;
92
+ const categories = categorizeStatements(statements);
93
+ if (!hasViolation(findStatementIndices(statements), categories)) return;
94
+ context.report({
95
+ node,
96
+ messageId: "importsAndReExportsAtTop",
97
+ fix(fixer) {
98
+ const sortedText = generateSortedText(context, categories);
99
+ return fixer.replaceText(node, sortedText);
100
+ }
101
+ });
102
+ } };
103
+ }
104
+ };
105
+ //#endregion
106
+ //#region src/rules/individualImports.ts
107
+ const individualImports = {
108
+ meta: {
109
+ docs: { description: "Enforce individual imports instead of grouped imports" },
110
+ fixable: "code",
111
+ messages: { individualImports: "Use individual imports instead of grouped imports." },
112
+ schema: [],
113
+ type: "suggestion"
114
+ },
115
+ create(context) {
116
+ return { ImportDeclaration(node) {
117
+ if (node.specifiers.length <= 1) return;
118
+ context.report({
119
+ node,
120
+ messageId: "individualImports",
121
+ fix(fixer) {
122
+ const source = node.source.raw;
123
+ const specifiers = node.specifiers.filter((s) => s.type === "ImportSpecifier").map((s) => `import {${s.local.name}} from ${source}`).join("\n");
124
+ return fixer.replaceText(node, specifiers);
125
+ }
126
+ });
127
+ } };
128
+ }
129
+ };
130
+ //#endregion
131
+ //#region src/rules/individualReExports.ts
132
+ const individualReExports = {
133
+ meta: {
134
+ docs: { description: "Enforce individual exports instead of grouped exports" },
135
+ fixable: "code",
136
+ messages: { individualReExports: "Use individual exports instead of grouped exports." },
137
+ schema: [],
138
+ type: "suggestion"
139
+ },
140
+ create(context) {
141
+ return { ExportNamedDeclaration(node) {
142
+ if (!node.source || node.specifiers.length <= 1) return;
143
+ context.report({
144
+ node,
145
+ messageId: "individualReExports",
146
+ fix(fixer) {
147
+ const source = node.source.value;
148
+ const typeKeyword = node.exportKind === "type" ? "type " : "";
149
+ const specifiers = node.specifiers.map((s) => {
150
+ const localName = s.local.type === "Identifier" ? s.local.name : s.local.value;
151
+ const exportedName = s.exported.type === "Identifier" ? s.exported.name : s.exported.value;
152
+ return `export ${typeKeyword}{${localName === exportedName ? localName : `${localName} as ${exportedName}`}} from '${source}'`;
153
+ }).join("\n");
154
+ return fixer.replaceText(node, specifiers);
155
+ }
156
+ });
157
+ } };
158
+ }
159
+ };
160
+ //#endregion
161
+ //#region src/rules/multilineUnionTypes/createFix.ts
162
+ function createFix$4(fixer, node, sourceCode) {
163
+ const result = `\n${node.types.map((t) => sourceCode.getText(t)).map((t) => ` | ${t}`).join("\n")}`;
164
+ return fixer.replaceText(node, result);
165
+ }
166
+ //#endregion
167
+ //#region src/rules/multilineUnionTypes/isMultiline.ts
168
+ function isMultiline$2(unionType) {
169
+ const { start, end } = unionType.loc ?? {};
170
+ return start?.line !== end?.line;
171
+ }
172
+ //#endregion
173
+ //#region src/rules/multilineUnionTypes/index.ts
174
+ const multilineUnionTypes = {
175
+ meta: {
176
+ docs: { description: "Enforce union types with multiple members to be on multiple lines" },
177
+ fixable: "code",
178
+ messages: {
179
+ singleLine: "Union types with multiple members should be on multiple lines",
180
+ missingPipes: "Multiline union types should have leading pipes on each member"
181
+ },
182
+ schema: [],
183
+ type: "layout"
184
+ },
185
+ create(context) {
186
+ return { TSUnionType(node) {
187
+ if (node.types.length < 2) return;
188
+ const sourceCode = context.sourceCode;
189
+ if (sourceCode.getText(node).trim().startsWith("|")) return;
190
+ if (!isMultiline$2(node)) {
191
+ context.report({
192
+ node,
193
+ messageId: "singleLine",
194
+ fix: (fixer) => createFix$4(fixer, node, sourceCode)
195
+ });
196
+ return;
197
+ }
198
+ context.report({
199
+ node,
200
+ messageId: "missingPipes",
201
+ fix: (fixer) => createFix$4(fixer, node, sourceCode)
202
+ });
203
+ } };
204
+ }
205
+ };
206
+ //#endregion
207
+ //#region src/rules/singleLineImports/formatAttributes.ts
208
+ function formatAttributes(attributes) {
209
+ if (attributes.length === 0) return "";
210
+ return ` with {${attributes.map((attr) => {
211
+ return `${attr.key.type === "Identifier" ? attr.key.name : attr.key.value}: '${attr.value.value}'`;
212
+ }).join(", ")}}`;
213
+ }
214
+ //#endregion
215
+ //#region src/rules/singleLineImports/formatNamed.ts
216
+ function formatNamed(specifiers, sourceCode) {
217
+ return specifiers.map((s) => sourceCode.getText(s)).join(", ");
218
+ }
219
+ //#endregion
220
+ //#region src/rules/singleLineImports/formatSpecifiers.ts
221
+ function formatSpecifiers(declaration, sourceCode) {
222
+ const defaultSpecifier = declaration.specifiers.find((s) => s.type === "ImportDefaultSpecifier");
223
+ const namespaceSpecifier = declaration.specifiers.find((s) => s.type === "ImportNamespaceSpecifier");
224
+ const namedSpecifiers = declaration.specifiers.filter((s) => s.type === "ImportSpecifier");
225
+ if (namespaceSpecifier) return `* as ${namespaceSpecifier.local.name}`;
226
+ if (defaultSpecifier && namedSpecifiers.length > 0) return `${defaultSpecifier.local.name}, {${formatNamed(namedSpecifiers, sourceCode)}}`;
227
+ if (defaultSpecifier) return defaultSpecifier.local.name;
228
+ if (namedSpecifiers.length === 0) return "";
229
+ return `{${formatNamed(namedSpecifiers, sourceCode)}}`;
230
+ }
231
+ //#endregion
232
+ //#region src/rules/singleLineImports/createFix.ts
233
+ function createFix$3(fixer, declaration, sourceCode) {
234
+ const source = declaration.source.value;
235
+ const prefix = declaration.importKind === "type" ? "import type " : "import ";
236
+ const specifiers = formatSpecifiers(declaration, sourceCode);
237
+ const attributes = formatAttributes(declaration.attributes);
238
+ if (specifiers === "") {
239
+ const result = `${prefix}'${source}'${attributes}`;
240
+ return fixer.replaceText(declaration, result);
241
+ }
242
+ const result = `${prefix}${specifiers} from '${source}'${attributes}`;
243
+ return fixer.replaceText(declaration, result);
244
+ }
245
+ //#endregion
246
+ //#region src/rules/singleLineImports/isMultiline.ts
247
+ function isMultiline$1(declaration) {
248
+ const { start, end } = declaration.loc ?? {};
249
+ return start?.line !== end?.line;
250
+ }
251
+ //#endregion
252
+ //#region src/rules/singleLineImports/index.ts
253
+ const singleLineImports = {
254
+ meta: {
255
+ docs: { description: "Enforce imports to be on a single line" },
256
+ fixable: "code",
257
+ messages: { multiline: "Import should be on a single line" },
258
+ schema: [],
259
+ type: "layout"
260
+ },
261
+ create(context) {
262
+ return { ImportDeclaration(node) {
263
+ if (!isMultiline$1(node)) return;
264
+ context.report({
265
+ node,
266
+ messageId: "multiline",
267
+ fix: (fixer) => createFix$3(fixer, node, context.sourceCode)
268
+ });
269
+ } };
270
+ }
271
+ };
272
+ //#endregion
273
+ //#region src/rules/singleLineReExports/createFix.ts
274
+ function createFix$2(fixer, declaration, sourceCode) {
275
+ const source = declaration.source.value;
276
+ if (declaration.type === "ExportAllDeclaration") {
277
+ const result = `export ${declaration.exported ? `* as ${sourceCode.getText(declaration.exported)}` : "*"} from '${source}'${formatAttributes(declaration.attributes)}`;
278
+ return fixer.replaceText(declaration, result);
279
+ }
280
+ const result = `${declaration.exportKind === "type" ? "export type " : "export "}{${declaration.specifiers.map((s) => sourceCode.getText(s)).join(", ")}} from '${source}'${formatAttributes(declaration.attributes)}`;
281
+ return fixer.replaceText(declaration, result);
282
+ }
283
+ //#endregion
284
+ //#region src/rules/singleLineReExports/isMultiline.ts
285
+ function isMultiline(declaration) {
286
+ const { start, end } = declaration.loc ?? {};
287
+ return start?.line !== end?.line;
288
+ }
289
+ //#endregion
290
+ //#region src/rules/singleLineReExports/index.ts
291
+ const singleLineReExports = {
292
+ meta: {
293
+ docs: { description: "Enforce re-exports to be on a single line" },
294
+ fixable: "code",
295
+ messages: { multiline: "Re-export should be on a single line" },
296
+ schema: [],
297
+ type: "layout"
298
+ },
299
+ create(context) {
300
+ const checkDeclaration = (node, declaration) => {
301
+ if (!declaration.source) return;
302
+ if (!isMultiline(declaration)) return;
303
+ context.report({
304
+ node,
305
+ messageId: "multiline",
306
+ fix: (fixer) => createFix$2(fixer, declaration, context.sourceCode)
307
+ });
308
+ };
309
+ return {
310
+ ExportNamedDeclaration: (node) => checkDeclaration(node, node),
311
+ ExportAllDeclaration: (node) => checkDeclaration(node, node)
312
+ };
313
+ }
314
+ };
315
+ //#endregion
316
+ //#region src/rules/sortedImports/categorizeImport.ts
317
+ function categorizeImport(declaration) {
318
+ if (declaration.importKind === "type") return "type";
319
+ if (declaration.specifiers.length === 0) return "side-effect";
320
+ if (declaration.specifiers.some((s) => s.type === "ImportNamespaceSpecifier")) return "namespace";
321
+ if (declaration.specifiers.some((s) => s.type === "ImportDefaultSpecifier")) return "default";
322
+ return "named";
323
+ }
324
+ //#endregion
325
+ //#region src/rules/sortedImports/getSortKey.ts
326
+ function getSortKey$1(declaration) {
327
+ const group = categorizeImport(declaration);
328
+ if (group === "side-effect") return declaration.source.value;
329
+ if (group === "namespace") return `*${declaration.specifiers.find((s) => s.type === "ImportNamespaceSpecifier")?.local.name ?? ""}`;
330
+ if (group === "default") return declaration.specifiers.find((s) => s.type === "ImportDefaultSpecifier")?.local.name ?? "";
331
+ return declaration.specifiers[0].local.name;
332
+ }
333
+ //#endregion
334
+ //#region src/rules/sortedImports/categorizeImports.ts
335
+ function categorizeImports(declarations) {
336
+ return declarations.map((declaration) => ({
337
+ declaration,
338
+ group: categorizeImport(declaration),
339
+ sortKey: getSortKey$1(declaration)
340
+ }));
341
+ }
342
+ //#endregion
343
+ //#region src/lib/compare.ts
344
+ function compare(a, b) {
345
+ return a.localeCompare(b, "en", { sensitivity: "case" });
346
+ }
347
+ //#endregion
348
+ //#region src/rules/sortedImports/ImportGroupOrder.ts
349
+ const importGroupOrder = [
350
+ "side-effect",
351
+ "namespace",
352
+ "default",
353
+ "named",
354
+ "type"
355
+ ];
356
+ //#endregion
357
+ //#region src/rules/sortedImports/checkAlphabeticalSorting.ts
358
+ function checkAlphabeticalSorting$1(categorized) {
359
+ const errors = [];
360
+ for (const group of importGroupOrder) {
361
+ const groupImports = categorized.filter((c) => c.group === group);
362
+ const sorted = [...groupImports].sort((a, b) => compare(a.sortKey, b.sortKey));
363
+ for (let i = 0; i < groupImports.length; i++) if (groupImports[i] !== sorted[i]) errors.push({
364
+ node: groupImports[i].declaration,
365
+ messageId: "sortedImports"
366
+ });
367
+ }
368
+ return errors;
369
+ }
370
+ //#endregion
371
+ //#region src/rules/sortedImports/checkGroupOrdering.ts
372
+ function checkGroupOrdering$1(categorized) {
373
+ const errors = [];
374
+ let currentGroupIndex = -1;
375
+ for (const { declaration, group } of categorized) {
376
+ const groupIndex = importGroupOrder.indexOf(group);
377
+ if (groupIndex < currentGroupIndex) errors.push({
378
+ node: declaration,
379
+ messageId: "wrongGroup"
380
+ });
381
+ else currentGroupIndex = groupIndex;
382
+ }
383
+ return errors;
384
+ }
385
+ //#endregion
386
+ //#region src/rules/sortedImports/getSpecifierName.ts
387
+ function getSpecifierName$1(specifier) {
388
+ return specifier.imported.type === "Identifier" ? specifier.imported.name : String(specifier.imported.value);
389
+ }
390
+ //#endregion
391
+ //#region src/rules/sortedImports/areSpecifiersSorted.ts
392
+ function areSpecifiersSorted$1(specifiers) {
393
+ const names = specifiers.map((s) => getSpecifierName$1(s));
394
+ const sorted = [...names].sort((a, b) => compare(a, b));
395
+ return names.every((name, i) => name === sorted[i]);
396
+ }
397
+ //#endregion
398
+ //#region src/rules/sortedImports/getNamedSpecifiers.ts
399
+ function getNamedSpecifiers$1(declaration) {
400
+ return declaration.specifiers.filter((s) => s.type === "ImportSpecifier");
401
+ }
402
+ //#endregion
403
+ //#region src/rules/sortedImports/checkSpecifiersSorting.ts
404
+ function checkSpecifiersSorting$1(categorized) {
405
+ const errors = [];
406
+ const namedImports = categorized.filter((c) => c.group === "named");
407
+ for (const { declaration } of namedImports) {
408
+ const specifiers = getNamedSpecifiers$1(declaration);
409
+ if (specifiers.length > 1 && !areSpecifiersSorted$1(specifiers)) errors.push({
410
+ node: declaration,
411
+ messageId: "sortedNames"
412
+ });
413
+ }
414
+ return errors;
415
+ }
416
+ //#endregion
417
+ //#region src/rules/sortedImports/sortSpecifiersText.ts
418
+ function sortSpecifiersText$1(specifiers, sourceCode) {
419
+ return [...specifiers].sort((a, b) => {
420
+ return compare(getSpecifierName$1(a), getSpecifierName$1(b));
421
+ }).map((s) => sourceCode.getText(s)).join(", ");
422
+ }
423
+ //#endregion
424
+ //#region src/rules/sortedImports/createFix/formatNamedImport.ts
425
+ function formatNamedImport(declaration, sourceCode) {
426
+ const specifiers = getNamedSpecifiers$1(declaration);
427
+ if (specifiers.length > 1 && !areSpecifiersSorted$1(specifiers)) {
428
+ const sortedSpecifiers = sortSpecifiersText$1(specifiers, sourceCode);
429
+ const source = declaration.source.value;
430
+ return `${declaration.importKind === "type" ? "import type " : "import "}{${sortedSpecifiers}} from '${source}'`;
431
+ }
432
+ return sourceCode.getText(declaration);
433
+ }
434
+ //#endregion
435
+ //#region src/rules/sortedImports/createFix/buildSortedCode.ts
436
+ function buildSortedCode$1(grouped, sourceCode) {
437
+ const sortedCode = [];
438
+ for (const group of importGroupOrder) for (const { declaration } of grouped[group]) if (group === "named" || group === "type") sortedCode.push(formatNamedImport(declaration, sourceCode));
439
+ else sortedCode.push(sourceCode.getText(declaration));
440
+ return sortedCode;
441
+ }
442
+ //#endregion
443
+ //#region src/rules/sortedImports/createFix/groupImportsByType.ts
444
+ function groupImportsByType(categorized) {
445
+ const grouped = {
446
+ "side-effect": [],
447
+ namespace: [],
448
+ default: [],
449
+ named: [],
450
+ type: []
451
+ };
452
+ for (const item of categorized) grouped[item.group].push(item);
453
+ return grouped;
454
+ }
455
+ //#endregion
456
+ //#region src/rules/sortedImports/createFix/sortImportGroups.ts
457
+ function sortImportGroups(grouped) {
458
+ grouped["side-effect"].sort((a, b) => compare(a.sortKey, b.sortKey));
459
+ grouped["namespace"].sort((a, b) => compare(a.sortKey, b.sortKey));
460
+ grouped["default"].sort((a, b) => compare(a.sortKey, b.sortKey));
461
+ grouped["named"].sort((a, b) => compare(a.sortKey, b.sortKey));
462
+ grouped["type"].sort((a, b) => compare(a.sortKey, b.sortKey));
463
+ }
464
+ //#endregion
465
+ //#region src/rules/sortedImports/createFix/index.ts
466
+ function createFixForGroup$1(fixer, importDeclarations, sourceCode) {
467
+ if (importDeclarations.length === 0) return null;
468
+ const grouped = groupImportsByType(categorizeImports(importDeclarations));
469
+ sortImportGroups(grouped);
470
+ const sortedCode = buildSortedCode$1(grouped, sourceCode).join("\n");
471
+ const firstImport = importDeclarations[0];
472
+ const lastImport = importDeclarations[importDeclarations.length - 1];
473
+ return fixer.replaceTextRange([firstImport.range[0], lastImport.range[1]], sortedCode);
474
+ }
475
+ function createFix$1(fixer, importGroups, sourceCode) {
476
+ const fixes = [];
477
+ for (const group of importGroups) {
478
+ const fix = createFixForGroup$1(fixer, group, sourceCode);
479
+ if (fix) fixes.push(fix);
480
+ }
481
+ return fixes;
482
+ }
483
+ //#endregion
484
+ //#region src/rules/sortedImports/getImportGroups.ts
485
+ function getImportGroups(programBody) {
486
+ const groups = [];
487
+ let currentGroup = [];
488
+ for (const statement of programBody) {
489
+ if (statement.type === "ImportDeclaration") {
490
+ currentGroup.push(statement);
491
+ continue;
492
+ }
493
+ if (currentGroup.length > 0) {
494
+ groups.push(currentGroup);
495
+ currentGroup = [];
496
+ }
497
+ }
498
+ if (currentGroup.length > 0) groups.push(currentGroup);
499
+ return groups;
500
+ }
501
+ //#endregion
502
+ //#region src/rules/sortedImports/index.ts
503
+ const sortedImports = {
504
+ meta: {
505
+ docs: { description: "Enforce sorted imports alphabetically" },
506
+ fixable: "code",
507
+ messages: {
508
+ sortedImports: "Imports should be sorted alphabetically",
509
+ sortedNames: "Named imports should be sorted alphabetically",
510
+ wrongGroup: "Import is in wrong group"
511
+ },
512
+ schema: [],
513
+ type: "suggestion"
514
+ },
515
+ create(context) {
516
+ return { Program(node) {
517
+ const body = node.body;
518
+ const importGroups = getImportGroups(body);
519
+ if (importGroups.length === 0) return;
520
+ const allErrors = [];
521
+ for (const group of importGroups) {
522
+ const categorized = categorizeImports(group);
523
+ const errors = [
524
+ ...checkGroupOrdering$1(categorized),
525
+ ...checkAlphabeticalSorting$1(categorized),
526
+ ...checkSpecifiersSorting$1(categorized)
527
+ ];
528
+ allErrors.push(...errors);
529
+ }
530
+ for (const error of allErrors) context.report({
531
+ node: error.node,
532
+ messageId: error.messageId,
533
+ fix(fixer) {
534
+ const sourceCode = context.sourceCode;
535
+ return createFix$1(fixer, importGroups, sourceCode);
536
+ }
537
+ });
538
+ } };
539
+ }
540
+ };
541
+ //#endregion
542
+ //#region src/rules/sortedReExports/categorizeReExport.ts
543
+ function categorizeReExport(declaration) {
544
+ if (declaration.type === "ExportAllDeclaration") {
545
+ if (declaration.exported) return "re-export-namespace";
546
+ return "re-export-all";
547
+ }
548
+ if (declaration.exportKind === "type") return "re-export-type";
549
+ return "re-export-named";
550
+ }
551
+ //#endregion
552
+ //#region src/rules/sortedReExports/getSortKey.ts
553
+ function getSortKey(declaration) {
554
+ const group = categorizeReExport(declaration);
555
+ if (declaration.type === "ExportAllDeclaration") {
556
+ if (group === "re-export-namespace") {
557
+ if (declaration.exported?.type === "Identifier") return `*${declaration.exported.name}`;
558
+ }
559
+ return declaration.source.value;
560
+ }
561
+ const specifier = declaration.specifiers[0];
562
+ if (!specifier) return "";
563
+ return specifier.local.type === "Identifier" ? specifier.local.name : specifier.local.value;
564
+ }
565
+ //#endregion
566
+ //#region src/rules/sortedReExports/categorizeReExports.ts
567
+ function categorizeReExports(declarations) {
568
+ return declarations.map((declaration) => {
569
+ return {
570
+ declaration,
571
+ group: categorizeReExport(declaration),
572
+ sortKey: getSortKey(declaration)
573
+ };
574
+ });
575
+ }
576
+ //#endregion
577
+ //#region src/rules/sortedReExports/ReExportGroupOrder.ts
578
+ const reExportGroupOrder = [
579
+ "re-export-all",
580
+ "re-export-namespace",
581
+ "re-export-named",
582
+ "re-export-type"
583
+ ];
584
+ //#endregion
585
+ //#region src/rules/sortedReExports/checkAlphabeticalSorting.ts
586
+ function checkAlphabeticalSorting(categorized) {
587
+ const errors = [];
588
+ for (const group of reExportGroupOrder) {
589
+ const groupReExports = categorized.filter((c) => c.group === group);
590
+ const sorted = [...groupReExports].sort((a, b) => compare(a.sortKey, b.sortKey));
591
+ for (let i = 0; i < groupReExports.length; i++) if (groupReExports[i] !== sorted[i]) errors.push({
592
+ node: groupReExports[i].declaration,
593
+ messageId: "sortedReExports"
594
+ });
595
+ }
596
+ return errors;
597
+ }
598
+ //#endregion
599
+ //#region src/rules/sortedReExports/checkGroupOrdering.ts
600
+ function checkGroupOrdering(categorized) {
601
+ const errors = [];
602
+ let currentGroupIndex = -1;
603
+ for (const { declaration, group } of categorized) {
604
+ const groupIndex = reExportGroupOrder.indexOf(group);
605
+ if (groupIndex < currentGroupIndex) errors.push({
606
+ node: declaration,
607
+ messageId: "wrongGroup"
608
+ });
609
+ else currentGroupIndex = groupIndex;
610
+ }
611
+ return errors;
612
+ }
613
+ //#endregion
614
+ //#region src/rules/sortedReExports/getSpecifierName.ts
615
+ function getSpecifierName(specifier) {
616
+ return specifier.local.type === "Identifier" ? specifier.local.name : String(specifier.local.value);
617
+ }
618
+ //#endregion
619
+ //#region src/rules/sortedReExports/areSpecifiersSorted.ts
620
+ function areSpecifiersSorted(specifiers) {
621
+ const names = specifiers.map((s) => getSpecifierName(s));
622
+ const sorted = [...names].sort((a, b) => compare(a, b));
623
+ return names.every((name, i) => name === sorted[i]);
624
+ }
625
+ //#endregion
626
+ //#region src/rules/sortedReExports/getNamedSpecifiers.ts
627
+ function getNamedSpecifiers(declaration) {
628
+ return declaration.specifiers.filter((s) => s.type === "ExportSpecifier" && s.local.type === "Identifier");
629
+ }
630
+ //#endregion
631
+ //#region src/rules/sortedReExports/isNamedReExport.ts
632
+ function isNamedReExport(x) {
633
+ return x.group !== "re-export-all" && x.group !== "re-export-namespace";
634
+ }
635
+ //#endregion
636
+ //#region src/rules/sortedReExports/checkSpecifiersSorting.ts
637
+ function checkSpecifiersSorting(categorized) {
638
+ const errors = [];
639
+ const namedReExports = categorized.filter(isNamedReExport);
640
+ for (const { declaration } of namedReExports) {
641
+ const specifiers = getNamedSpecifiers(declaration);
642
+ const isSorted = areSpecifiersSorted(specifiers);
643
+ if (specifiers.length > 1 && !isSorted) errors.push({
644
+ node: declaration,
645
+ messageId: "sortedNames"
646
+ });
647
+ }
648
+ return errors;
649
+ }
650
+ //#endregion
651
+ //#region src/rules/sortedReExports/sortSpecifiersText.ts
652
+ function sortSpecifiersText(specifiers, sourceCode) {
653
+ return [...specifiers].sort((a, b) => {
654
+ return compare(getSpecifierName(a), getSpecifierName(b));
655
+ }).map((s) => sourceCode.getText(s)).join(", ");
656
+ }
657
+ //#endregion
658
+ //#region src/rules/sortedReExports/createFix/formatNamedReExport.ts
659
+ function formatNamedReExport(declaration, sourceCode) {
660
+ const specifiers = getNamedSpecifiers(declaration);
661
+ if (specifiers.length > 1 && !areSpecifiersSorted(specifiers)) {
662
+ const sortedSpecifiers = sortSpecifiersText(specifiers, sourceCode);
663
+ const source = declaration.source.value;
664
+ return `${declaration.exportKind === "type" ? "export type " : "export "}{${sortedSpecifiers}} from '${source}'`;
665
+ }
666
+ return sourceCode.getText(declaration);
667
+ }
668
+ //#endregion
669
+ //#region src/rules/sortedReExports/createFix/buildSortedCode.ts
670
+ function buildSortedCode(grouped, sourceCode) {
671
+ const sortedCode = [];
672
+ for (const group of reExportGroupOrder) for (const item of grouped[group]) if (isNamedReExport(item)) sortedCode.push(formatNamedReExport(item.declaration, sourceCode));
673
+ else sortedCode.push(sourceCode.getText(item.declaration));
674
+ return sortedCode;
675
+ }
676
+ //#endregion
677
+ //#region src/rules/sortedReExports/createFix/groupReExportsByType.ts
678
+ function groupReExportsByType(categorized) {
679
+ const grouped = {
680
+ "re-export-all": [],
681
+ "re-export-namespace": [],
682
+ "re-export-named": [],
683
+ "re-export-type": []
684
+ };
685
+ for (const item of categorized) grouped[item.group].push(item);
686
+ return grouped;
687
+ }
688
+ //#endregion
689
+ //#region src/rules/sortedReExports/createFix/sortExportGroups.ts
690
+ function sortExportGroups(grouped) {
691
+ grouped["re-export-all"].sort((a, b) => compare(a.sortKey, b.sortKey));
692
+ grouped["re-export-namespace"].sort((a, b) => compare(a.sortKey, b.sortKey));
693
+ grouped["re-export-named"].sort((a, b) => compare(a.sortKey, b.sortKey));
694
+ grouped["re-export-type"].sort((a, b) => compare(a.sortKey, b.sortKey));
695
+ }
696
+ //#endregion
697
+ //#region src/rules/sortedReExports/createFix/index.ts
698
+ function createFixForGroup(fixer, reExportDeclarations, sourceCode) {
699
+ if (reExportDeclarations.length === 0) return null;
700
+ const grouped = groupReExportsByType(categorizeReExports(reExportDeclarations));
701
+ sortExportGroups(grouped);
702
+ const sortedCode = buildSortedCode(grouped, sourceCode).join("\n");
703
+ const firstReExport = reExportDeclarations[0];
704
+ const lastReExport = reExportDeclarations[reExportDeclarations.length - 1];
705
+ return fixer.replaceTextRange([firstReExport.range[0], lastReExport.range[1]], sortedCode);
706
+ }
707
+ function createFix(fixer, reExportGroups, sourceCode) {
708
+ const fixes = [];
709
+ for (const group of reExportGroups) {
710
+ const fix = createFixForGroup(fixer, group, sourceCode);
711
+ if (fix) fixes.push(fix);
712
+ }
713
+ return fixes;
714
+ }
715
+ //#endregion
716
+ //#region src/rules/sortedReExports/getReExportGroups.ts
717
+ function isReExportDeclaration(statement) {
718
+ return statement.type === "ExportNamedDeclaration" && statement.source !== null || statement.type === "ExportAllDeclaration";
719
+ }
720
+ function getReExportGroups(programBody) {
721
+ const groups = [];
722
+ let currentGroup = [];
723
+ for (const statement of programBody) {
724
+ if (isReExportDeclaration(statement)) {
725
+ currentGroup.push(statement);
726
+ continue;
727
+ }
728
+ if (currentGroup.length > 0) {
729
+ groups.push(currentGroup);
730
+ currentGroup = [];
731
+ }
732
+ }
733
+ if (currentGroup.length > 0) groups.push(currentGroup);
734
+ return groups;
735
+ }
736
+ //#endregion
737
+ //#region src/rules/sortedReExports/index.ts
738
+ const sortedReExports = {
739
+ meta: {
740
+ docs: { description: "Enforce sorted exports alphabetically" },
741
+ fixable: "code",
742
+ messages: {
743
+ sortedReExports: "Exports should be sorted alphabetically",
744
+ sortedNames: "Named exports should be sorted alphabetically",
745
+ wrongGroup: "Export is in wrong group"
746
+ },
747
+ schema: [],
748
+ type: "suggestion"
749
+ },
750
+ create(context) {
751
+ return { Program(node) {
752
+ const body = node.body;
753
+ const reExportGroups = getReExportGroups(body);
754
+ if (reExportGroups.length === 0) return;
755
+ const allErrors = [];
756
+ for (const group of reExportGroups) {
757
+ const categorized = categorizeReExports(group);
758
+ const errors = [
759
+ ...checkGroupOrdering(categorized),
760
+ ...checkAlphabeticalSorting(categorized),
761
+ ...checkSpecifiersSorting(categorized)
762
+ ];
763
+ allErrors.push(...errors);
764
+ }
765
+ for (const error of allErrors) context.report({
766
+ node: error.node,
767
+ messageId: error.messageId,
768
+ fix(fixer) {
769
+ const sourceCode = context.sourceCode;
770
+ return createFix(fixer, reExportGroups, sourceCode);
771
+ }
772
+ });
773
+ } };
774
+ }
775
+ };
776
+ //#endregion
777
+ //#region src/index.ts
778
+ const CONFIG = [
779
+ { ignores: [
780
+ "src/graphql/sdk.ts",
781
+ "**/node_modules/**",
782
+ "**/dist/**"
783
+ ] },
784
+ { settings: { react: { version: "19" } } },
785
+ eslint.configs.recommended,
786
+ react.configs.flat.recommended,
787
+ stylistic.configs.recommended,
788
+ ...typescript.configs.recommended,
789
+ ...typescript.configs.stylistic,
790
+ {
791
+ plugins: { "react-hooks": reactHooks },
792
+ rules: reactHooks.configs.recommended.rules
793
+ },
794
+ {
795
+ plugins: { "@borela-tech": { rules: {
796
+ "imports-and-re-exports-at-top": importsAndReExportsAtTop,
797
+ "individual-imports": individualImports,
798
+ "individual-re-exports": individualReExports,
799
+ "multiline-union-types": multilineUnionTypes,
800
+ "single-line-imports": singleLineImports,
801
+ "single-line-re-exports": singleLineReExports,
802
+ "sorted-imports": sortedImports,
803
+ "sorted-re-exports": sortedReExports
804
+ } } },
805
+ rules: {
806
+ "@borela-tech/imports-and-re-exports-at-top": "error",
807
+ "@borela-tech/individual-imports": "error",
808
+ "@borela-tech/individual-re-exports": "error",
809
+ "@borela-tech/multiline-union-types": "error",
810
+ "@borela-tech/single-line-imports": "error",
811
+ "@borela-tech/single-line-re-exports": "error",
812
+ "@borela-tech/sorted-imports": "error",
813
+ "@borela-tech/sorted-re-exports": "error"
814
+ }
815
+ },
816
+ { rules: {
817
+ "capitalized-comments": [
818
+ "error",
819
+ "always",
820
+ { ignoreConsecutiveComments: true }
821
+ ],
822
+ "react/react-in-jsx-scope": "off",
823
+ "@stylistic/arrow-parens": ["error", "as-needed"],
824
+ "@stylistic/array-bracket-newline": ["error", "consistent"],
825
+ "@stylistic/array-bracket-spacing": ["error", "never"],
826
+ "@stylistic/array-element-newline": ["error", "consistent"],
827
+ "@stylistic/block-spacing": "off",
828
+ "@stylistic/brace-style": [
829
+ "error",
830
+ "1tbs",
831
+ { allowSingleLine: true }
832
+ ],
833
+ "@stylistic/indent": [
834
+ "error",
835
+ 2,
836
+ { ignoredNodes: ["TSMappedType > *"] }
837
+ ],
838
+ "@stylistic/jsx-tag-spacing": ["error", {
839
+ afterOpening: "never",
840
+ beforeClosing: "never",
841
+ beforeSelfClosing: "never",
842
+ closingSlash: "never"
843
+ }],
844
+ "@stylistic/jsx-wrap-multilines": "off",
845
+ "@stylistic/lines-between-class-members": "off",
846
+ "@stylistic/object-curly-newline": ["error", { consistent: true }],
847
+ "@stylistic/object-curly-spacing": ["error", "never"],
848
+ "@stylistic/operator-linebreak": [
849
+ "error",
850
+ "before",
851
+ { overrides: { "=": "after" } }
852
+ ],
853
+ "@stylistic/quotes": [
854
+ "error",
855
+ "single",
856
+ { avoidEscape: true }
857
+ ],
858
+ "@stylistic/quote-props": ["error", "as-needed"],
859
+ "@stylistic/semi": [
860
+ "error",
861
+ "never",
862
+ { beforeStatementContinuationChars: "always" }
863
+ ],
864
+ "@typescript-eslint/no-empty-function": "off",
865
+ "@typescript-eslint/consistent-indexed-object-style": "off",
866
+ "@typescript-eslint/consistent-type-imports": ["error", { fixStyle: "separate-type-imports" }]
867
+ } }
868
+ ];
869
+ //#endregion
870
+ export { CONFIG };
871
+
872
+ //# sourceMappingURL=index.mjs.map