@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
@@ -0,0 +1,21 @@
1
+ import {reExportGroupOrder} from './ReExportGroupOrder'
2
+ import type {CategorizedReExport} from './CategorizedReExport'
3
+ import type {ReExportError} from './ReExportError'
4
+
5
+ export function checkGroupOrdering(categorized: CategorizedReExport[]): ReExportError[] {
6
+ const errors: ReExportError[] = []
7
+
8
+ let currentGroupIndex = -1
9
+ for (const {declaration, group} of categorized) {
10
+ const groupIndex = reExportGroupOrder.indexOf(group)
11
+ if (groupIndex < currentGroupIndex) {
12
+ errors.push({
13
+ node: declaration,
14
+ messageId: 'wrongGroup',
15
+ })
16
+ } else
17
+ currentGroupIndex = groupIndex
18
+ }
19
+
20
+ return errors
21
+ }
@@ -0,0 +1,23 @@
1
+ import {areSpecifiersSorted} from './areSpecifiersSorted'
2
+ import {getNamedSpecifiers} from './getNamedSpecifiers'
3
+ import {isNamedReExport} from './isNamedReExport'
4
+ import type {CategorizedReExport} from './CategorizedReExport'
5
+ import type {ReExportError} from './ReExportError'
6
+
7
+ export function checkSpecifiersSorting(categorized: CategorizedReExport[]): ReExportError[] {
8
+ const errors: ReExportError[] = []
9
+ const namedReExports = categorized.filter(isNamedReExport)
10
+
11
+ for (const {declaration} of namedReExports) {
12
+ const specifiers = getNamedSpecifiers(declaration)
13
+ const isSorted = areSpecifiersSorted(specifiers)
14
+ if (specifiers.length > 1 && !isSorted) {
15
+ errors.push({
16
+ node: declaration,
17
+ messageId: 'sortedNames',
18
+ })
19
+ }
20
+ }
21
+
22
+ return errors
23
+ }
@@ -0,0 +1,28 @@
1
+ import {formatNamedReExport} from './formatNamedReExport'
2
+ import {isNamedReExport} from '../isNamedReExport'
3
+ import {reExportGroupOrder} from '../ReExportGroupOrder'
4
+ import type {CategorizedReExport} from '../CategorizedReExport'
5
+ import type {ReExportGroup} from '../ReExportGroup'
6
+
7
+ export function buildSortedCode(
8
+ grouped: Record<ReExportGroup, CategorizedReExport[]>,
9
+ sourceCode: {getText: (node?: unknown) => string},
10
+ ): string[] {
11
+ const sortedCode: string[] = []
12
+
13
+ for (const group of reExportGroupOrder) {
14
+ for (const item of grouped[group]) {
15
+ if (isNamedReExport(item)) {
16
+ sortedCode.push(
17
+ formatNamedReExport(
18
+ item.declaration,
19
+ sourceCode,
20
+ ),
21
+ )
22
+ } else
23
+ sortedCode.push(sourceCode.getText(item.declaration))
24
+ }
25
+ }
26
+
27
+ return sortedCode
28
+ }
@@ -0,0 +1,11 @@
1
+ import type {TSESTree} from '@typescript-eslint/types'
2
+
3
+ export function findFirstExportIndex(programBody: TSESTree.ProgramStatement[]): number {
4
+ for (let i = 0; i < programBody.length; i++) {
5
+ if (programBody[i].type === 'ExportNamedDeclaration'
6
+ || programBody[i].type === 'ExportAllDeclaration') {
7
+ return i
8
+ }
9
+ }
10
+ return -1
11
+ }
@@ -0,0 +1,12 @@
1
+ import type {TSESTree} from '@typescript-eslint/types'
2
+
3
+ export function findLastExportIndex(programBody: TSESTree.ProgramStatement[]): number {
4
+ let lastIndex = -1
5
+ for (let i = 0; i < programBody.length; i++) {
6
+ if (programBody[i].type === 'ExportNamedDeclaration'
7
+ || programBody[i].type === 'ExportAllDeclaration') {
8
+ lastIndex = i
9
+ }
10
+ }
11
+ return lastIndex
12
+ }
@@ -0,0 +1,20 @@
1
+ import {areSpecifiersSorted} from '../areSpecifiersSorted'
2
+ import {getNamedSpecifiers} from '../getNamedSpecifiers'
3
+ import {sortSpecifiersText} from '../sortSpecifiersText'
4
+ import type {TSESTree} from '@typescript-eslint/types'
5
+
6
+ export function formatNamedReExport(
7
+ declaration: TSESTree.ExportNamedDeclaration,
8
+ sourceCode: {getText: (node?: unknown) => string},
9
+ ): string {
10
+ const specifiers = getNamedSpecifiers(declaration)
11
+
12
+ if (specifiers.length > 1 && !areSpecifiersSorted(specifiers)) {
13
+ const sortedSpecifiers = sortSpecifiersText(specifiers, sourceCode)
14
+ const source = declaration.source!.value
15
+ const prefix = declaration.exportKind === 'type' ? 'export type ' : 'export '
16
+ return `${prefix}{${sortedSpecifiers}} from '${source}'`
17
+ }
18
+
19
+ return sourceCode.getText(declaration)
20
+ }
@@ -0,0 +1,22 @@
1
+ import {findFirstExportIndex} from './findFirstExportIndex'
2
+ import {findLastExportIndex} from './findLastExportIndex'
3
+ import type {ReplacementRange} from '../../../lib/ReplacementRange'
4
+ import type {TSESTree} from '@typescript-eslint/types'
5
+
6
+ export function getReplacementRange(
7
+ programBody: TSESTree.ProgramStatement[],
8
+ ): ReplacementRange {
9
+ const firstIndex = findFirstExportIndex(programBody)
10
+ const lastIndex = findLastExportIndex(programBody)
11
+
12
+ if (firstIndex === -1 || lastIndex === -1)
13
+ return {start: 0, end: 0}
14
+
15
+ const firstExport = programBody[firstIndex]
16
+ const lastExport = programBody[lastIndex]
17
+
18
+ const start = firstExport.range[0]
19
+ const end = lastExport.range[1]
20
+
21
+ return {start, end}
22
+ }
@@ -0,0 +1,17 @@
1
+ import type {CategorizedReExport} from '../CategorizedReExport'
2
+ import type {ReExportGroup} from '../ReExportGroup'
3
+
4
+ export function groupReExportsByType(
5
+ categorized: CategorizedReExport[],
6
+ ): Record<ReExportGroup, CategorizedReExport[]> {
7
+ const grouped: Record<ReExportGroup, CategorizedReExport[]> = {
8
+ 're-export-all': [],
9
+ 're-export-named': [],
10
+ 're-export-type': [],
11
+ }
12
+
13
+ for (const item of categorized)
14
+ grouped[item.group].push(item)
15
+
16
+ return grouped
17
+ }
@@ -0,0 +1,29 @@
1
+ import {buildSortedCode} from './buildSortedCode'
2
+ import {categorizeReExports} from '../categorizeReExports'
3
+ import {getReplacementRange} from './getReplacementRange'
4
+ import {groupReExportsByType} from './groupReExportsByType'
5
+ import {ReExportDeclaration} from '../ReExportDeclaration'
6
+ import {sortExportGroups} from './sortExportGroups'
7
+ import type {Rule} from 'eslint'
8
+ import type {TSESTree} from '@typescript-eslint/types'
9
+
10
+ export function createFix(
11
+ fixer: Rule.RuleFixer,
12
+ reExportDeclarations: ReExportDeclaration[],
13
+ sourceCode: {getText: (node?: unknown) => string},
14
+ programBody: TSESTree.ProgramStatement[],
15
+ ) {
16
+ const range = getReplacementRange(programBody)
17
+ const categorized = categorizeReExports(reExportDeclarations)
18
+ const grouped = groupReExportsByType(categorized)
19
+
20
+ sortExportGroups(grouped)
21
+
22
+ const sortedCode = buildSortedCode(grouped, sourceCode)
23
+ .join('\n')
24
+
25
+ return fixer.replaceTextRange(
26
+ [range.start, range.end],
27
+ sortedCode,
28
+ )
29
+ }
@@ -0,0 +1,11 @@
1
+ import {compare} from '../../../lib/compare'
2
+ import type {CategorizedReExport} from '../CategorizedReExport'
3
+ import type {ReExportGroup} from '../ReExportGroup'
4
+
5
+ export function sortExportGroups(
6
+ grouped: Record<ReExportGroup, CategorizedReExport[]>,
7
+ ): void {
8
+ grouped['re-export-all'].sort((a, b) => compare(a.sortKey, b.sortKey))
9
+ grouped['re-export-named'].sort((a, b) => compare(a.sortKey, b.sortKey))
10
+ grouped['re-export-type'].sort((a, b) => compare(a.sortKey, b.sortKey))
11
+ }
@@ -0,0 +1,9 @@
1
+ import type {TSESTree} from '@typescript-eslint/types'
2
+
3
+ export function getNamedSpecifiers(
4
+ declaration: TSESTree.ExportNamedDeclaration,
5
+ ): TSESTree.ExportSpecifier[] {
6
+ return declaration.specifiers.filter(
7
+ s => s.type === 'ExportSpecifier' && s.local.type === 'Identifier',
8
+ ) as TSESTree.ExportSpecifier[]
9
+ }
@@ -0,0 +1,12 @@
1
+ import type {ReExportDeclaration} from './ReExportDeclaration'
2
+ import type {TSESTree} from '@typescript-eslint/types'
3
+
4
+ export function getReExportDeclarations(
5
+ programBody: TSESTree.ProgramStatement[],
6
+ ): ReExportDeclaration[] {
7
+ return programBody.filter(
8
+ (statement): statement is ReExportDeclaration =>
9
+ (statement.type === 'ExportNamedDeclaration' && statement.source !== null)
10
+ || statement.type === 'ExportAllDeclaration',
11
+ )
12
+ }
@@ -0,0 +1,16 @@
1
+ import type {TSESTree} from '@typescript-eslint/types'
2
+
3
+ export function getSortKey(
4
+ declaration: TSESTree.ExportNamedDeclaration | TSESTree.ExportAllDeclaration,
5
+ ): string {
6
+ if (declaration.type === 'ExportAllDeclaration')
7
+ return declaration.source.value
8
+
9
+ const specifier = declaration.specifiers[0]
10
+ if (!specifier)
11
+ return ''
12
+
13
+ return specifier.local.type === 'Identifier'
14
+ ? specifier.local.name
15
+ : specifier.local.value
16
+ }
@@ -0,0 +1,7 @@
1
+ import type {TSESTree} from '@typescript-eslint/types'
2
+
3
+ export function getSpecifierName(specifier: TSESTree.ExportSpecifier): string {
4
+ return specifier.local.type === 'Identifier'
5
+ ? specifier.local.name
6
+ : String(specifier.local.value)
7
+ }
@@ -0,0 +1,54 @@
1
+ import {categorizeReExports} from './categorizeReExports'
2
+ import {checkAlphabeticalSorting} from './checkAlphabeticalSorting'
3
+ import {checkGroupOrdering} from './checkGroupOrdering'
4
+ import {checkSpecifiersSorting} from './checkSpecifiersSorting'
5
+ import {createFix} from './createFix'
6
+ import {getReExportDeclarations} from './getReExportDeclarations'
7
+ import type {ReExportError} from './ReExportError'
8
+ import type {Rule} from 'eslint'
9
+ import type {TSESTree} from '@typescript-eslint/types'
10
+
11
+ export const sortedReExports: Rule.RuleModule = {
12
+ meta: {
13
+ docs: {
14
+ description: 'Enforce sorted exports alphabetically',
15
+ recommended: true,
16
+ },
17
+ fixable: 'code',
18
+ messages: {
19
+ sortedReExports: 'Exports should be sorted alphabetically',
20
+ sortedNames: 'Named exports should be sorted alphabetically',
21
+ wrongGroup: 'Export is in wrong group',
22
+ },
23
+ schema: [],
24
+ type: 'suggestion',
25
+ },
26
+ create(context) {
27
+ return {
28
+ Program(node) {
29
+ const body = node.body as TSESTree.ProgramStatement[]
30
+ const declarations = getReExportDeclarations(body)
31
+ if (declarations.length === 0)
32
+ return
33
+
34
+ const categorized = categorizeReExports(declarations)
35
+ const errors: ReExportError[] = [
36
+ ...checkGroupOrdering(categorized),
37
+ ...checkAlphabeticalSorting(categorized),
38
+ ...checkSpecifiersSorting(categorized),
39
+ ]
40
+
41
+ for (const error of errors) {
42
+ context.report({
43
+ node: error.node,
44
+ messageId: error.messageId,
45
+ fix(fixer) {
46
+ const sourceCode = context.sourceCode
47
+ return createFix(fixer, declarations, sourceCode, body)
48
+ },
49
+ })
50
+ }
51
+ },
52
+ }
53
+ },
54
+ }
@@ -0,0 +1,6 @@
1
+ import {CategorizedNamedReExport} from './CategorizedNamedReExport'
2
+ import type {CategorizedReExport} from './CategorizedReExport'
3
+
4
+ export function isNamedReExport(x: CategorizedReExport): x is CategorizedNamedReExport {
5
+ return x.group !== 're-export-all'
6
+ }
@@ -0,0 +1,15 @@
1
+ import {compare} from '../../lib/compare'
2
+ import {getSpecifierName} from './getSpecifierName'
3
+ import type {TSESTree} from '@typescript-eslint/types'
4
+
5
+ export function sortSpecifiersText(
6
+ specifiers: TSESTree.ExportSpecifier[],
7
+ sourceCode: {getText: (node?: unknown) => string},
8
+ ): string {
9
+ const sorted = [...specifiers].sort((a, b) => {
10
+ const nameA = getSpecifierName(a)
11
+ const nameB = getSpecifierName(b)
12
+ return compare(nameA, nameB)
13
+ })
14
+ return sorted.map(s => sourceCode.getText(s)).join(', ')
15
+ }