@borela-tech/eslint-config 2.0.0 → 2.0.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/bin/lint CHANGED
@@ -8,6 +8,7 @@ npx eslint \
8
8
  --ext ts \
9
9
  --max-warnings 0 \
10
10
  --report-unused-disable-directives \
11
- .
11
+ . \
12
+ $@
12
13
 
13
14
  popd > /dev/null
package/bin/typecheck ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env -S bash -e -o pipefail
2
+
3
+ SCRIPT_DIR=$(dirname -- "${BASH_SOURCE[0]}")
4
+ pushd "$SCRIPT_DIR/.." > /dev/null
5
+
6
+ npx tsc --noEmit
7
+
8
+ popd > /dev/null
package/dist/index.js CHANGED
@@ -50,6 +50,8 @@ var individualImports = {
50
50
 
51
51
  // src/rules/sortedImports/categorizeImport.ts
52
52
  function categorizeImport(declaration) {
53
+ if (declaration.importKind === "type")
54
+ return "type";
53
55
  if (declaration.specifiers.length === 0)
54
56
  return "side-effect";
55
57
  if (declaration.specifiers.some((s) => s.type === "ImportDefaultSpecifier"))
@@ -68,7 +70,8 @@ function getSortKey(declaration) {
68
70
  );
69
71
  return defaultSpecifier?.local.name.toLowerCase() ?? "";
70
72
  }
71
- return "";
73
+ const specifier = declaration.specifiers[0];
74
+ return specifier.local.name.toLowerCase();
72
75
  }
73
76
 
74
77
  // src/rules/sortedImports/categorizeImports.ts
@@ -83,7 +86,7 @@ function categorizeImports(declarations) {
83
86
  // src/rules/sortedImports/checkAlphabeticalSorting.ts
84
87
  function checkAlphabeticalSorting(categorized) {
85
88
  const errors = [];
86
- for (const group of ["side-effect", "default"]) {
89
+ for (const group of ["side-effect", "default", "named", "type"]) {
87
90
  const groupImports = categorized.filter((c) => c.group === group);
88
91
  const sorted = [...groupImports].sort((a, b) => a.sortKey.localeCompare(b.sortKey));
89
92
  for (let i = 0; i < groupImports.length; i++) {
@@ -100,7 +103,7 @@ function checkAlphabeticalSorting(categorized) {
100
103
 
101
104
  // src/rules/sortedImports/checkGroupOrdering.ts
102
105
  function checkGroupOrdering(categorized) {
103
- const groupOrder = ["side-effect", "default", "named"];
106
+ const groupOrder = ["side-effect", "default", "named", "type"];
104
107
  const errors = [];
105
108
  let currentGroupIndex = -1;
106
109
  for (const { declaration, group } of categorized) {
@@ -180,11 +183,11 @@ function formatNamedImport(declaration, sourceCode) {
180
183
 
181
184
  // src/rules/sortedImports/createFix/buildSortedCode.ts
182
185
  function buildSortedCode(grouped, sourceCode) {
183
- const groupOrder = ["side-effect", "default", "named"];
186
+ const groupOrder = ["side-effect", "default", "named", "type"];
184
187
  const sortedCode = [];
185
188
  for (const group of groupOrder) {
186
189
  for (const { declaration } of grouped[group]) {
187
- if (group === "named")
190
+ if (group === "named" || group === "type")
188
191
  sortedCode.push(formatNamedImport(declaration, sourceCode));
189
192
  else
190
193
  sortedCode.push(sourceCode.getText(declaration));
@@ -206,20 +209,12 @@ function findLastImportIndex(programBody) {
206
209
  }
207
210
 
208
211
  // src/rules/sortedImports/createFix/getReplacementRange.ts
209
- function getReplacementRange(programBody, sourceCode) {
210
- const fullText = sourceCode.getText();
212
+ function getReplacementRange(programBody) {
211
213
  const lastIndex = findLastImportIndex(programBody);
212
214
  const firstImport = programBody[0];
213
215
  const lastImport = programBody[lastIndex];
214
216
  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
- }
217
+ const end = lastImport.range[1];
223
218
  return { start, end };
224
219
  }
225
220
 
@@ -228,7 +223,8 @@ function groupImportsByType(categorized) {
228
223
  const grouped = {
229
224
  "side-effect": [],
230
225
  default: [],
231
- named: []
226
+ named: [],
227
+ type: []
232
228
  };
233
229
  for (const item of categorized)
234
230
  grouped[item.group].push(item);
@@ -239,11 +235,13 @@ function groupImportsByType(categorized) {
239
235
  function sortImportGroups(grouped) {
240
236
  grouped["side-effect"].sort((a, b) => a.sortKey.localeCompare(b.sortKey));
241
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));
242
240
  }
243
241
 
244
242
  // src/rules/sortedImports/createFix/index.ts
245
243
  function createFix(fixer, importDeclarations, sourceCode, programBody) {
246
- const range = getReplacementRange(programBody, sourceCode);
244
+ const range = getReplacementRange(programBody);
247
245
  const categorized = categorizeImports(importDeclarations);
248
246
  const grouped = groupImportsByType(categorized);
249
247
  sortImportGroups(grouped);
@@ -280,7 +278,8 @@ var sortedImports = {
280
278
  create(context) {
281
279
  return {
282
280
  Program(node) {
283
- const declarations = getImportDeclarations(node.body);
281
+ const body = node.body;
282
+ const declarations = getImportDeclarations(body);
284
283
  if (declarations.length === 0)
285
284
  return;
286
285
  const categorized = categorizeImports(declarations);
@@ -295,7 +294,7 @@ var sortedImports = {
295
294
  messageId: error.messageId,
296
295
  fix(fixer) {
297
296
  const sourceCode = context.sourceCode;
298
- return createFix(fixer, declarations, sourceCode, node.body);
297
+ return createFix(fixer, declarations, sourceCode, body);
299
298
  }
300
299
  });
301
300
  }
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/rules/individualImports.ts","../src/rules/sortedImports/categorizeImport.ts","../src/rules/sortedImports/getSortKey.ts","../src/rules/sortedImports/categorizeImports.ts","../src/rules/sortedImports/checkAlphabeticalSorting.ts","../src/rules/sortedImports/checkGroupOrdering.ts","../src/rules/sortedImports/getSpecifierName.ts","../src/rules/sortedImports/areSpecifiersSorted.ts","../src/rules/sortedImports/getNamedSpecifiers.ts","../src/rules/sortedImports/checkSpecifiersSorting.ts","../src/rules/sortedImports/sortSpecifiersText.ts","../src/rules/sortedImports/createFix/formatNamedImport.ts","../src/rules/sortedImports/createFix/buildSortedCode.ts","../src/rules/sortedImports/createFix/findLastImportIndex.ts","../src/rules/sortedImports/createFix/getReplacementRange.ts","../src/rules/sortedImports/createFix/groupImportsByType.ts","../src/rules/sortedImports/createFix/sortImportGroups.ts","../src/rules/sortedImports/createFix/index.ts","../src/rules/sortedImports/getImportDeclarations.ts","../src/rules/sortedImports/index.ts"],"sourcesContent":["import eslint from '@eslint/js'\nimport react from 'eslint-plugin-react'\nimport reactHooks from 'eslint-plugin-react-hooks'\nimport stylistic from '@stylistic/eslint-plugin'\nimport typescript from 'typescript-eslint'\nimport {defineConfig} from 'eslint/config'\nimport {individualImports} from './rules/individualImports'\nimport {sortedImports} from './rules/sortedImports'\n\nexport const CONFIG = defineConfig([\n {\n ignores: [\n 'src/graphql/sdk.ts',\n '**/node_modules/**',\n '**/dist/**',\n ],\n },\n {\n settings: {\n react: {\n version: '19',\n },\n },\n },\n eslint.configs.recommended,\n react.configs.flat.recommended,\n stylistic.configs.recommended,\n typescript.configs.recommended,\n typescript.configs.stylistic,\n {\n plugins: {\n 'react-hooks': reactHooks,\n },\n rules: reactHooks.configs.recommended.rules,\n },\n {\n plugins: {\n '@borela-tech': {\n rules: {\n 'individual-imports': individualImports,\n 'sorted-imports': sortedImports,\n },\n },\n },\n rules: {\n '@borela-tech/individual-imports': 'error',\n '@borela-tech/sorted-imports': 'error',\n },\n },\n {\n rules: {\n 'capitalized-comments': [\n 'error',\n 'always',\n {ignoreConsecutiveComments: true},\n ],\n 'react/react-in-jsx-scope': 'off',\n '@stylistic/arrow-parens': [\n 'error',\n 'as-needed',\n ],\n '@stylistic/array-bracket-newline': [\n 'error',\n 'consistent',\n ],\n '@stylistic/array-bracket-spacing': [\n 'error',\n 'never',\n ],\n '@stylistic/array-element-newline': [\n 'error',\n 'consistent',\n ],\n '@stylistic/block-spacing': 'off',\n '@stylistic/brace-style': [\n 'error',\n '1tbs',\n {allowSingleLine: true},\n ],\n '@stylistic/indent': [\n 'error',\n 2,\n {ignoredNodes: ['TSMappedType > *']},\n ],\n '@stylistic/jsx-tag-spacing': [\n 'error',\n {\n afterOpening: 'never',\n beforeClosing: 'never',\n beforeSelfClosing: 'never',\n closingSlash: 'never',\n },\n ],\n '@stylistic/jsx-wrap-multilines': 'off',\n '@stylistic/lines-between-class-members': 'off',\n '@stylistic/object-curly-newline': [\n 'error',\n {consistent: true},\n ],\n '@stylistic/object-curly-spacing': [\n 'error',\n 'never',\n ],\n '@stylistic/operator-linebreak': [\n 'error',\n 'before',\n {overrides: {'=': 'after'}},\n ],\n '@stylistic/quotes': [\n 'error',\n 'single',\n {avoidEscape: true},\n ],\n '@stylistic/quote-props': [\n 'error',\n 'as-needed',\n ],\n '@stylistic/semi': [\n 'error',\n 'never',\n {beforeStatementContinuationChars: 'always'},\n ],\n '@typescript-eslint/no-empty-function': 'off',\n '@typescript-eslint/consistent-indexed-object-style': 'off',\n },\n },\n])\n","import type {Rule} from 'eslint'\n\nexport const individualImports: Rule.RuleModule = {\n meta: {\n docs: {\n description: 'Enforce individual imports instead of grouped imports',\n recommended: true,\n },\n fixable: 'code',\n messages: {\n individualImports: 'Use individual imports instead of grouped imports.',\n },\n schema: [],\n type: 'suggestion',\n },\n create(context) {\n return {\n ImportDeclaration(node) {\n if (node.specifiers.length <= 1)\n return\n context.report({\n node,\n messageId: 'individualImports',\n fix(fixer) {\n const source = node.source.raw\n const specifiers = node.specifiers\n .map(importSpecifier => {\n if (importSpecifier.type === 'ImportSpecifier')\n return `import {${importSpecifier.local.name}} from ${source}`\n return null\n })\n .filter(Boolean)\n\n if (specifiers.length !== node.specifiers.length)\n return null\n\n return fixer.replaceText(\n node,\n specifiers.join('\\n'),\n )\n },\n })\n },\n }\n },\n}\n","import type {ImportDeclaration} from 'estree'\nimport type {ImportGroup} from './ImportGroup'\n\nexport function categorizeImport(declaration: ImportDeclaration): ImportGroup {\n if (declaration.specifiers.length === 0)\n return 'side-effect'\n\n if (declaration.specifiers.some(s => s.type === 'ImportDefaultSpecifier'))\n return 'default'\n\n return 'named'\n}\n","import type {ImportDeclaration} from 'estree'\nimport type {ImportDefaultSpecifier} from 'estree'\nimport {categorizeImport} from './categorizeImport'\n\nexport function getSortKey(declaration: ImportDeclaration): string {\n const group = categorizeImport(declaration)\n\n if (group === 'side-effect')\n return (declaration.source.value as string).toLowerCase()\n\n if (group === 'default') {\n const defaultSpecifier = declaration.specifiers.find(\n s => s.type === 'ImportDefaultSpecifier',\n ) as ImportDefaultSpecifier | undefined\n\n return defaultSpecifier?.local.name.toLowerCase() ?? ''\n }\n\n return ''\n}\n","import type {ImportDeclaration} from 'estree'\nimport type {CategorizedImport} from './CategorizedImport'\nimport {categorizeImport} from './categorizeImport'\nimport {getSortKey} from './getSortKey'\n\nexport function categorizeImports(declarations: ImportDeclaration[]): CategorizedImport[] {\n return declarations.map(declaration => ({\n declaration,\n group: categorizeImport(declaration),\n sortKey: getSortKey(declaration),\n }))\n}\n","import type {CategorizedImport} from './CategorizedImport'\nimport type {ImportError} from './ImportError'\nimport type {ImportGroup} from './ImportGroup'\n\nexport function checkAlphabeticalSorting(categorized: CategorizedImport[]): ImportError[] {\n const errors: ImportError[] = []\n\n for (const group of ['side-effect', 'default'] as ImportGroup[]) {\n const groupImports = categorized.filter(c => c.group === group)\n const sorted = [...groupImports].sort((a, b) => a.sortKey.localeCompare(b.sortKey))\n for (let i = 0; i < groupImports.length; i++) {\n if (groupImports[i] !== sorted[i]) {\n errors.push({\n node: groupImports[i].declaration,\n messageId: 'sortedImports',\n })\n }\n }\n }\n\n return errors\n}\n","import type {CategorizedImport} from './CategorizedImport'\nimport type {ImportError} from './ImportError'\nimport type {ImportGroup} from './ImportGroup'\n\nexport function checkGroupOrdering(categorized: CategorizedImport[]): ImportError[] {\n const groupOrder: ImportGroup[] = ['side-effect', 'default', 'named']\n const errors: ImportError[] = []\n\n let currentGroupIndex = -1\n for (const {declaration, group} of categorized) {\n const groupIndex = groupOrder.indexOf(group)\n if (groupIndex < currentGroupIndex) {\n errors.push({\n node: declaration,\n messageId: 'wrongGroup',\n })\n } else\n currentGroupIndex = groupIndex\n }\n\n return errors\n}\n","import type {ImportSpecifier} from 'estree'\n\nexport function getSpecifierName(specifier: ImportSpecifier): string {\n return specifier.imported.type === 'Identifier'\n ? specifier.imported.name\n : String(specifier.imported.value)\n}\n","import type {ImportSpecifier} from 'estree'\nimport {getSpecifierName} from './getSpecifierName'\n\nexport function areSpecifiersSorted(specifiers: ImportSpecifier[]): boolean {\n const names = specifiers.map(s => getSpecifierName(s))\n const sorted = [...names].sort((a, b) =>\n a.toLowerCase().localeCompare(b.toLowerCase()),\n )\n return names.every((name, i) => name === sorted[i])\n}\n","import type {ImportDeclaration} from 'estree'\nimport type {ImportSpecifier} from 'estree'\n\nexport function getNamedSpecifiers(declaration: ImportDeclaration): ImportSpecifier[] {\n return declaration.specifiers.filter(\n (s): s is ImportSpecifier => s.type === 'ImportSpecifier',\n )\n}\n","import type {CategorizedImport} from './CategorizedImport'\nimport type {ImportError} from './ImportError'\nimport {areSpecifiersSorted} from './areSpecifiersSorted'\nimport {getNamedSpecifiers} from './getNamedSpecifiers'\n\nexport function checkSpecifiersSorting(categorized: CategorizedImport[]): ImportError[] {\n const errors: ImportError[] = []\n const namedImports = categorized.filter(c => c.group === 'named')\n\n for (const {declaration} of namedImports) {\n const specifiers = getNamedSpecifiers(declaration)\n if (specifiers.length > 1 && !areSpecifiersSorted(specifiers)) {\n errors.push({\n node: declaration,\n messageId: 'sortedNames',\n })\n }\n }\n\n return errors\n}\n","import type {ImportSpecifier} from 'estree'\nimport {getSpecifierName} from './getSpecifierName'\n\nexport function sortSpecifiersText(\n specifiers: ImportSpecifier[],\n sourceCode: {getText: (node: ImportSpecifier) => string},\n): string {\n const sorted = [...specifiers].sort((a, b) => {\n const lowerA = getSpecifierName(a).toLowerCase()\n const lowerB = getSpecifierName(b).toLowerCase()\n return lowerA.localeCompare(lowerB)\n })\n return sorted.map(s => sourceCode.getText(s)).join(', ')\n}\n","import type {ImportDeclaration} from 'estree'\nimport {areSpecifiersSorted} from '../areSpecifiersSorted'\nimport {getNamedSpecifiers} from '../getNamedSpecifiers'\nimport {sortSpecifiersText} from '../sortSpecifiersText'\n\nexport function formatNamedImport(\n declaration: ImportDeclaration,\n sourceCode: {getText: (node?: unknown) => string},\n): string {\n const specifiers = getNamedSpecifiers(declaration)\n\n if (specifiers.length > 1 && !areSpecifiersSorted(specifiers)) {\n const importText = sourceCode.getText(declaration)\n const specifiersStart = importText.indexOf('{')\n const specifiersEnd = importText.lastIndexOf('}')\n const before = importText.substring(0, specifiersStart + 1)\n const after = importText.substring(specifiersEnd)\n const sortedSpecifiers = sortSpecifiersText(specifiers, sourceCode)\n return before + ' ' + sortedSpecifiers + ' ' + after\n }\n\n return sourceCode.getText(declaration)\n}\n","import type {CategorizedImport} from '../CategorizedImport'\nimport type {ImportGroup} from '../ImportGroup'\nimport {formatNamedImport} from './formatNamedImport'\n\nexport function buildSortedCode(\n grouped: Record<ImportGroup, CategorizedImport[]>,\n sourceCode: {getText: (node?: unknown) => string},\n): string[] {\n const groupOrder: ImportGroup[] = ['side-effect', 'default', 'named']\n const sortedCode: string[] = []\n\n for (const group of groupOrder) {\n for (const {declaration} of grouped[group]) {\n if (group === 'named')\n sortedCode.push(formatNamedImport(declaration, sourceCode))\n else\n sortedCode.push(sourceCode.getText(declaration))\n }\n }\n\n return sortedCode\n}\n","import type {Program} from 'estree'\n\nexport function findLastImportIndex(programBody: Program['body']): number {\n let lastIndex = 0\n for (let i = 0; i < programBody.length; i++) {\n if (programBody[i].type === 'ImportDeclaration')\n lastIndex = i\n else\n break\n }\n return lastIndex\n}\n","import type {ImportDeclaration} from 'estree'\nimport type {Program} from 'estree'\nimport type {ReplacementRange} from './ReplacementRange'\nimport {findLastImportIndex} from './findLastImportIndex'\n\nexport function getReplacementRange(\n programBody: Program['body'],\n sourceCode: {getText: () => string},\n): ReplacementRange {\n const fullText = sourceCode.getText()\n const lastIndex = findLastImportIndex(programBody)\n const firstImport = programBody[0] as ImportDeclaration\n const lastImport = programBody[lastIndex] as ImportDeclaration\n const start = firstImport.range![0]\n let end = lastImport.range![1]\n\n for (let i = end; i < fullText.length; i++) {\n const char = fullText[i]\n if (char === '\\n' || char === ' ' || char === '\\t')\n end++\n else\n break\n }\n\n return {start, end}\n}\n","import type {CategorizedImport} from '../CategorizedImport'\nimport type {ImportGroup} from '../ImportGroup'\n\nexport function groupImportsByType(\n categorized: CategorizedImport[],\n): Record<ImportGroup, CategorizedImport[]> {\n const grouped: Record<ImportGroup, CategorizedImport[]> = {\n 'side-effect': [],\n default: [],\n named: [],\n }\n\n for (const item of categorized)\n grouped[item.group].push(item)\n\n return grouped\n}\n","import type {CategorizedImport} from '../CategorizedImport'\nimport type {ImportGroup} from '../ImportGroup'\n\nexport function sortImportGroups(\n grouped: Record<ImportGroup, CategorizedImport[]>,\n): void {\n grouped['side-effect'].sort((a, b) => a.sortKey.localeCompare(b.sortKey))\n grouped['default'].sort((a, b) => a.sortKey.localeCompare(b.sortKey))\n}\n","import type {Rule} from 'eslint'\nimport type {ImportDeclaration} from 'estree'\nimport type {Program} from 'estree'\nimport {buildSortedCode} from './buildSortedCode'\nimport {categorizeImports} from '../categorizeImports'\nimport {getReplacementRange} from './getReplacementRange'\nimport {groupImportsByType} from './groupImportsByType'\nimport {sortImportGroups} from './sortImportGroups'\n\nexport function createFix(\n fixer: Rule.RuleFixer,\n importDeclarations: ImportDeclaration[],\n sourceCode: {getText: (node?: unknown) => string},\n programBody: Program['body'],\n) {\n const range = getReplacementRange(programBody, sourceCode)\n const categorized = categorizeImports(importDeclarations)\n const grouped = groupImportsByType(categorized)\n\n sortImportGroups(grouped)\n\n const sortedCode = buildSortedCode(grouped, sourceCode)\n .join('\\n')\n\n return fixer.replaceTextRange(\n [range.start, range.end],\n sortedCode,\n )\n}\n","import type {ImportDeclaration} from 'estree'\nimport type {Program} from 'estree'\n\nexport function getImportDeclarations(programBody: Program['body']): ImportDeclaration[] {\n return programBody.filter(\n (statement): statement is ImportDeclaration =>\n statement.type === 'ImportDeclaration',\n )\n}\n","import type {Rule} from 'eslint'\nimport type {ImportError} from './ImportError'\nimport {categorizeImports} from './categorizeImports'\nimport {checkAlphabeticalSorting} from './checkAlphabeticalSorting'\nimport {checkGroupOrdering} from './checkGroupOrdering'\nimport {checkSpecifiersSorting} from './checkSpecifiersSorting'\nimport {createFix} from './createFix'\nimport {getImportDeclarations} from './getImportDeclarations'\n\nexport const sortedImports: Rule.RuleModule = {\n meta: {\n docs: {\n description: 'Enforce sorted imports alphabetically',\n recommended: true,\n },\n fixable: 'code',\n messages: {\n sortedImports: 'Imports should be sorted alphabetically',\n sortedNames: 'Named imports should be sorted alphabetically',\n wrongGroup: 'Import is in wrong group',\n },\n schema: [],\n type: 'suggestion',\n },\n create(context) {\n return {\n Program(node) {\n const declarations = getImportDeclarations(node.body)\n if (declarations.length === 0)\n return\n\n const categorized = categorizeImports(declarations)\n const errors: ImportError[] = [\n ...checkGroupOrdering(categorized),\n ...checkAlphabeticalSorting(categorized),\n ...checkSpecifiersSorting(categorized),\n ]\n\n for (const error of errors) {\n context.report({\n node: error.node,\n messageId: error.messageId,\n fix(fixer) {\n const sourceCode = context.sourceCode\n return createFix(fixer, declarations, sourceCode, node.body)\n },\n })\n }\n },\n }\n },\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/index.ts","../src/rules/individualImports.ts","../src/rules/sortedImports/categorizeImport.ts","../src/rules/sortedImports/getSortKey.ts","../src/rules/sortedImports/categorizeImports.ts","../src/rules/sortedImports/checkAlphabeticalSorting.ts","../src/rules/sortedImports/checkGroupOrdering.ts","../src/rules/sortedImports/getSpecifierName.ts","../src/rules/sortedImports/areSpecifiersSorted.ts","../src/rules/sortedImports/getNamedSpecifiers.ts","../src/rules/sortedImports/checkSpecifiersSorting.ts","../src/rules/sortedImports/sortSpecifiersText.ts","../src/rules/sortedImports/createFix/formatNamedImport.ts","../src/rules/sortedImports/createFix/buildSortedCode.ts","../src/rules/sortedImports/createFix/findLastImportIndex.ts","../src/rules/sortedImports/createFix/getReplacementRange.ts","../src/rules/sortedImports/createFix/groupImportsByType.ts","../src/rules/sortedImports/createFix/sortImportGroups.ts","../src/rules/sortedImports/createFix/index.ts","../src/rules/sortedImports/getImportDeclarations.ts","../src/rules/sortedImports/index.ts"],"sourcesContent":["import eslint from '@eslint/js'\nimport react from 'eslint-plugin-react'\nimport reactHooks from 'eslint-plugin-react-hooks'\nimport stylistic from '@stylistic/eslint-plugin'\nimport typescript from 'typescript-eslint'\nimport {defineConfig} from 'eslint/config'\nimport {individualImports} from './rules/individualImports'\nimport {sortedImports} from './rules/sortedImports'\n\nexport const CONFIG = defineConfig([\n {\n ignores: [\n 'src/graphql/sdk.ts',\n '**/node_modules/**',\n '**/dist/**',\n ],\n },\n {\n settings: {\n react: {\n version: '19',\n },\n },\n },\n eslint.configs.recommended,\n react.configs.flat.recommended,\n stylistic.configs.recommended,\n typescript.configs.recommended,\n typescript.configs.stylistic,\n {\n plugins: {\n 'react-hooks': reactHooks,\n },\n rules: reactHooks.configs.recommended.rules,\n },\n {\n plugins: {\n '@borela-tech': {\n rules: {\n 'individual-imports': individualImports,\n 'sorted-imports': sortedImports,\n },\n },\n },\n rules: {\n '@borela-tech/individual-imports': 'error',\n '@borela-tech/sorted-imports': 'error',\n },\n },\n {\n rules: {\n 'capitalized-comments': [\n 'error',\n 'always',\n {ignoreConsecutiveComments: true},\n ],\n 'react/react-in-jsx-scope': 'off',\n '@stylistic/arrow-parens': [\n 'error',\n 'as-needed',\n ],\n '@stylistic/array-bracket-newline': [\n 'error',\n 'consistent',\n ],\n '@stylistic/array-bracket-spacing': [\n 'error',\n 'never',\n ],\n '@stylistic/array-element-newline': [\n 'error',\n 'consistent',\n ],\n '@stylistic/block-spacing': 'off',\n '@stylistic/brace-style': [\n 'error',\n '1tbs',\n {allowSingleLine: true},\n ],\n '@stylistic/indent': [\n 'error',\n 2,\n {ignoredNodes: ['TSMappedType > *']},\n ],\n '@stylistic/jsx-tag-spacing': [\n 'error',\n {\n afterOpening: 'never',\n beforeClosing: 'never',\n beforeSelfClosing: 'never',\n closingSlash: 'never',\n },\n ],\n '@stylistic/jsx-wrap-multilines': 'off',\n '@stylistic/lines-between-class-members': 'off',\n '@stylistic/object-curly-newline': [\n 'error',\n {consistent: true},\n ],\n '@stylistic/object-curly-spacing': [\n 'error',\n 'never',\n ],\n '@stylistic/operator-linebreak': [\n 'error',\n 'before',\n {overrides: {'=': 'after'}},\n ],\n '@stylistic/quotes': [\n 'error',\n 'single',\n {avoidEscape: true},\n ],\n '@stylistic/quote-props': [\n 'error',\n 'as-needed',\n ],\n '@stylistic/semi': [\n 'error',\n 'never',\n {beforeStatementContinuationChars: 'always'},\n ],\n '@typescript-eslint/no-empty-function': 'off',\n '@typescript-eslint/consistent-indexed-object-style': 'off',\n },\n },\n])\n","import type {Rule} from 'eslint'\n\nexport const individualImports: Rule.RuleModule = {\n meta: {\n docs: {\n description: 'Enforce individual imports instead of grouped imports',\n recommended: true,\n },\n fixable: 'code',\n messages: {\n individualImports: 'Use individual imports instead of grouped imports.',\n },\n schema: [],\n type: 'suggestion',\n },\n create(context) {\n return {\n ImportDeclaration(node) {\n if (node.specifiers.length <= 1)\n return\n context.report({\n node,\n messageId: 'individualImports',\n fix(fixer) {\n const source = node.source.raw\n const specifiers = node.specifiers\n .map(importSpecifier => {\n if (importSpecifier.type === 'ImportSpecifier')\n return `import {${importSpecifier.local.name}} from ${source}`\n return null\n })\n .filter(Boolean)\n\n if (specifiers.length !== node.specifiers.length)\n return null\n\n return fixer.replaceText(\n node,\n specifiers.join('\\n'),\n )\n },\n })\n },\n }\n },\n}\n","import type {ImportGroup} from './ImportGroup'\nimport type {TSESTree} from '@typescript-eslint/types'\n\nexport function categorizeImport(declaration: TSESTree.ImportDeclaration): ImportGroup {\n if (declaration.importKind === 'type')\n return 'type'\n\n if (declaration.specifiers.length === 0)\n return 'side-effect'\n\n if (declaration.specifiers.some(s => s.type === 'ImportDefaultSpecifier'))\n return 'default'\n\n return 'named'\n}\n","import {categorizeImport} from './categorizeImport'\nimport type {TSESTree} from '@typescript-eslint/types'\n\nexport function getSortKey(declaration: TSESTree.ImportDeclaration): string {\n const group = categorizeImport(declaration)\n\n if (group === 'side-effect')\n return (declaration.source.value as string).toLowerCase()\n\n if (group === 'default') {\n const defaultSpecifier = declaration.specifiers.find(\n s => s.type === 'ImportDefaultSpecifier',\n ) as TSESTree.ImportDefaultSpecifier | undefined\n\n return defaultSpecifier?.local.name.toLowerCase() ?? ''\n }\n\n const specifier = declaration.specifiers[0]\n return specifier.local.name.toLowerCase()\n}\n","import {categorizeImport} from './categorizeImport'\nimport {getSortKey} from './getSortKey'\nimport type {CategorizedImport} from './CategorizedImport'\nimport type {TSESTree} from '@typescript-eslint/types'\n\nexport function categorizeImports(declarations: TSESTree.ImportDeclaration[]): CategorizedImport[] {\n return declarations.map(declaration => ({\n declaration,\n group: categorizeImport(declaration),\n sortKey: getSortKey(declaration),\n }))\n}\n","import type {CategorizedImport} from './CategorizedImport'\nimport type {ImportError} from './ImportError'\nimport type {ImportGroup} from './ImportGroup'\n\nexport function checkAlphabeticalSorting(categorized: CategorizedImport[]): ImportError[] {\n const errors: ImportError[] = []\n\n for (const group of ['side-effect', 'default', 'named', 'type'] as ImportGroup[]) {\n const groupImports = categorized.filter(c => c.group === group)\n const sorted = [...groupImports].sort((a, b) => a.sortKey.localeCompare(b.sortKey))\n for (let i = 0; i < groupImports.length; i++) {\n if (groupImports[i] !== sorted[i]) {\n errors.push({\n node: groupImports[i].declaration,\n messageId: 'sortedImports',\n })\n }\n }\n }\n\n return errors\n}\n","import type {CategorizedImport} from './CategorizedImport'\nimport type {ImportError} from './ImportError'\nimport type {ImportGroup} from './ImportGroup'\n\nexport function checkGroupOrdering(categorized: CategorizedImport[]): ImportError[] {\n const groupOrder: ImportGroup[] = ['side-effect', 'default', 'named', 'type']\n const errors: ImportError[] = []\n\n let currentGroupIndex = -1\n for (const {declaration, group} of categorized) {\n const groupIndex = groupOrder.indexOf(group)\n if (groupIndex < currentGroupIndex) {\n errors.push({\n node: declaration,\n messageId: 'wrongGroup',\n })\n } else\n currentGroupIndex = groupIndex\n }\n\n return errors\n}\n","import type {ImportSpecifier} from 'estree'\n\nexport function getSpecifierName(specifier: ImportSpecifier): string {\n return specifier.imported.type === 'Identifier'\n ? specifier.imported.name\n : String(specifier.imported.value)\n}\n","import {getSpecifierName} from './getSpecifierName'\nimport type {ImportSpecifier} from 'estree'\n\nexport function areSpecifiersSorted(specifiers: ImportSpecifier[]): boolean {\n const names = specifiers.map(s => getSpecifierName(s))\n const sorted = [...names].sort((a, b) =>\n a.toLowerCase().localeCompare(b.toLowerCase()),\n )\n return names.every((name, i) => name === sorted[i])\n}\n","import type {ImportDeclaration} from 'estree'\nimport type {ImportSpecifier} from 'estree'\n\nexport function getNamedSpecifiers(declaration: ImportDeclaration): ImportSpecifier[] {\n return declaration.specifiers.filter(\n (s): s is ImportSpecifier => s.type === 'ImportSpecifier',\n )\n}\n","import {areSpecifiersSorted} from './areSpecifiersSorted'\nimport {getNamedSpecifiers} from './getNamedSpecifiers'\nimport type {CategorizedImport} from './CategorizedImport'\nimport type {ImportError} from './ImportError'\n\nexport function checkSpecifiersSorting(categorized: CategorizedImport[]): ImportError[] {\n const errors: ImportError[] = []\n const namedImports = categorized.filter(c => c.group === 'named')\n\n for (const {declaration} of namedImports) {\n const specifiers = getNamedSpecifiers(declaration)\n if (specifiers.length > 1 && !areSpecifiersSorted(specifiers)) {\n errors.push({\n node: declaration,\n messageId: 'sortedNames',\n })\n }\n }\n\n return errors\n}\n","import {getSpecifierName} from './getSpecifierName'\nimport type {ImportSpecifier} from 'estree'\n\nexport function sortSpecifiersText(\n specifiers: ImportSpecifier[],\n sourceCode: {getText: (node: ImportSpecifier) => string},\n): string {\n const sorted = [...specifiers].sort((a, b) => {\n const lowerA = getSpecifierName(a).toLowerCase()\n const lowerB = getSpecifierName(b).toLowerCase()\n return lowerA.localeCompare(lowerB)\n })\n return sorted.map(s => sourceCode.getText(s)).join(', ')\n}\n","import {areSpecifiersSorted} from '../areSpecifiersSorted'\nimport {getNamedSpecifiers} from '../getNamedSpecifiers'\nimport {sortSpecifiersText} from '../sortSpecifiersText'\nimport type {ImportDeclaration} from 'estree'\n\nexport function formatNamedImport(\n declaration: ImportDeclaration,\n sourceCode: {getText: (node?: unknown) => string},\n): string {\n const specifiers = getNamedSpecifiers(declaration)\n\n if (specifiers.length > 1 && !areSpecifiersSorted(specifiers)) {\n const importText = sourceCode.getText(declaration)\n const specifiersStart = importText.indexOf('{')\n const specifiersEnd = importText.lastIndexOf('}')\n const before = importText.substring(0, specifiersStart + 1)\n const after = importText.substring(specifiersEnd)\n const sortedSpecifiers = sortSpecifiersText(specifiers, sourceCode)\n return before + ' ' + sortedSpecifiers + ' ' + after\n }\n\n return sourceCode.getText(declaration)\n}\n","import {formatNamedImport} from './formatNamedImport'\nimport type {CategorizedImport} from '../CategorizedImport'\nimport type {ImportGroup} from '../ImportGroup'\n\nexport function buildSortedCode(\n grouped: Record<ImportGroup, CategorizedImport[]>,\n sourceCode: {getText: (node?: unknown) => string},\n): string[] {\n const groupOrder: ImportGroup[] = ['side-effect', 'default', 'named', 'type']\n const sortedCode: string[] = []\n\n for (const group of groupOrder) {\n for (const {declaration} of grouped[group]) {\n if (group === 'named' || group === 'type')\n sortedCode.push(formatNamedImport(declaration, sourceCode))\n else\n sortedCode.push(sourceCode.getText(declaration))\n }\n }\n\n return sortedCode\n}\n","import type {TSESTree} from '@typescript-eslint/types'\n\nexport function findLastImportIndex(programBody: TSESTree.ProgramStatement[]): number {\n let lastIndex = 0\n for (let i = 0; i < programBody.length; i++) {\n if (programBody[i].type === 'ImportDeclaration')\n lastIndex = i\n else\n break\n }\n return lastIndex\n}\n","import {findLastImportIndex} from './findLastImportIndex'\nimport type {ReplacementRange} from './ReplacementRange'\nimport type {TSESTree} from '@typescript-eslint/types'\n\nexport function getReplacementRange(\n programBody: TSESTree.ProgramStatement[],\n): ReplacementRange {\n const lastIndex = findLastImportIndex(programBody)\n const firstImport = programBody[0] as TSESTree.ImportDeclaration\n const lastImport = programBody[lastIndex] as TSESTree.ImportDeclaration\n const start = firstImport.range![0]\n const end = lastImport.range![1]\n return {start, end}\n}\n","import type {CategorizedImport} from '../CategorizedImport'\nimport type {ImportGroup} from '../ImportGroup'\n\nexport function groupImportsByType(\n categorized: CategorizedImport[],\n): Record<ImportGroup, CategorizedImport[]> {\n const grouped: Record<ImportGroup, CategorizedImport[]> = {\n 'side-effect': [],\n default: [],\n named: [],\n type: [],\n }\n\n for (const item of categorized)\n grouped[item.group].push(item)\n\n return grouped\n}\n","import type {CategorizedImport} from '../CategorizedImport'\nimport type {ImportGroup} from '../ImportGroup'\n\nexport function sortImportGroups(\n grouped: Record<ImportGroup, CategorizedImport[]>,\n): void {\n grouped['side-effect'].sort((a, b) => a.sortKey.localeCompare(b.sortKey))\n grouped['default'].sort((a, b) => a.sortKey.localeCompare(b.sortKey))\n grouped['named'].sort((a, b) => a.sortKey.localeCompare(b.sortKey))\n grouped['type'].sort((a, b) => a.sortKey.localeCompare(b.sortKey))\n}\n","import {buildSortedCode} from './buildSortedCode'\nimport {categorizeImports} from '../categorizeImports'\nimport {getReplacementRange} from './getReplacementRange'\nimport {groupImportsByType} from './groupImportsByType'\nimport {sortImportGroups} from './sortImportGroups'\nimport type {Rule} from 'eslint'\nimport type {TSESTree} from '@typescript-eslint/types'\n\nexport function createFix(\n fixer: Rule.RuleFixer,\n importDeclarations: TSESTree.ImportDeclaration[],\n sourceCode: {getText: (node?: unknown) => string},\n programBody: TSESTree.ProgramStatement[],\n) {\n const range = getReplacementRange(programBody)\n const categorized = categorizeImports(importDeclarations)\n const grouped = groupImportsByType(categorized)\n\n sortImportGroups(grouped)\n\n const sortedCode = buildSortedCode(grouped, sourceCode)\n .join('\\n')\n\n return fixer.replaceTextRange(\n [range.start, range.end],\n sortedCode,\n )\n}\n","import type {TSESTree} from '@typescript-eslint/types'\n\nexport function getImportDeclarations(programBody: TSESTree.ProgramStatement[]): TSESTree.ImportDeclaration[] {\n return programBody.filter(\n (statement): statement is TSESTree.ImportDeclaration =>\n statement.type === 'ImportDeclaration',\n )\n}\n","import {categorizeImports} from './categorizeImports'\nimport {checkAlphabeticalSorting} from './checkAlphabeticalSorting'\nimport {checkGroupOrdering} from './checkGroupOrdering'\nimport {checkSpecifiersSorting} from './checkSpecifiersSorting'\nimport {createFix} from './createFix'\nimport {getImportDeclarations} from './getImportDeclarations'\nimport type {ImportError} from './ImportError'\nimport type {Rule} from 'eslint'\nimport type {TSESTree} from '@typescript-eslint/types'\n\nexport const sortedImports: Rule.RuleModule = {\n meta: {\n docs: {\n description: 'Enforce sorted imports alphabetically',\n recommended: true,\n },\n fixable: 'code',\n messages: {\n sortedImports: 'Imports should be sorted alphabetically',\n sortedNames: 'Named imports should be sorted alphabetically',\n wrongGroup: 'Import is in wrong group',\n },\n schema: [],\n type: 'suggestion',\n },\n create(context) {\n return {\n Program(node) {\n const body = node.body as TSESTree.ProgramStatement[]\n const declarations = getImportDeclarations(body)\n if (declarations.length === 0)\n return\n\n const categorized = categorizeImports(declarations)\n const errors: ImportError[] = [\n ...checkGroupOrdering(categorized),\n ...checkAlphabeticalSorting(categorized),\n ...checkSpecifiersSorting(categorized),\n ]\n\n for (const error of errors) {\n context.report({\n node: error.node,\n messageId: error.messageId,\n fix(fixer) {\n const sourceCode = context.sourceCode\n return createFix(fixer, declarations, sourceCode, body)\n },\n })\n }\n },\n }\n },\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@borela-tech/eslint-config",
3
- "version": "2.0.0",
3
+ "version": "2.0.1",
4
4
  "description": "ESLint config used in Borela Tech projects.",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
@@ -1,7 +1,7 @@
1
1
  import typescript from 'typescript-eslint'
2
- import {RuleTester} from 'eslint'
3
- import {individualImports} from '../individualImports'
4
2
  import {dedent} from './dedent'
3
+ import {individualImports} from '../individualImports'
4
+ import {RuleTester} from 'eslint'
5
5
 
6
6
  const ruleTester = new RuleTester({
7
7
  languageOptions: {
@@ -1,7 +1,7 @@
1
1
  import typescript from 'typescript-eslint'
2
+ import {dedent} from './dedent'
2
3
  import {RuleTester} from 'eslint'
3
4
  import {sortedImports} from '../sortedImports'
4
- import {dedent} from './dedent'
5
5
 
6
6
  const ruleTester = new RuleTester({
7
7
  languageOptions: {
@@ -20,6 +20,8 @@ ruleTester.run('sorted-imports', sortedImports, {
20
20
  code: "import foo from 'bar'",
21
21
  }, {
22
22
  code: "import 'bar'",
23
+ }, {
24
+ code: "import type {Foo} from 'bar'",
23
25
  }, {
24
26
  code: dedent`
25
27
  import 'aaa'
@@ -28,6 +30,8 @@ ruleTester.run('sorted-imports', sortedImports, {
28
30
  import foo from 'aaa'
29
31
  import {a} from 'aaa'
30
32
  import {b} from 'bbb'
33
+ import type {X} from 'xxx'
34
+ import type {Y} from 'yyy'
31
35
  `,
32
36
  }, {
33
37
  code: dedent`
@@ -110,5 +114,35 @@ ruleTester.run('sorted-imports', sortedImports, {
110
114
  import foo from 'baz'
111
115
  import { a, b } from 'bar'
112
116
  `,
117
+ }, {
118
+ code: dedent`
119
+ import type {Y} from 'yyy'
120
+ import type {X} from 'xxx'
121
+ `,
122
+ errors: [{messageId: 'sortedImports'}, {messageId: 'sortedImports'}],
123
+ output: dedent`
124
+ import type {X} from 'xxx'
125
+ import type {Y} from 'yyy'
126
+ `,
127
+ }, {
128
+ code: dedent`
129
+ import type {Foo} from 'bar'
130
+ import {baz} from 'qux'
131
+ `,
132
+ errors: [{messageId: 'wrongGroup'}],
133
+ output: dedent`
134
+ import {baz} from 'qux'
135
+ import type {Foo} from 'bar'
136
+ `,
137
+ }, {
138
+ code: dedent`
139
+ import {existsSync} from 'fs'
140
+ import {basename} from 'path'
141
+ `,
142
+ errors: [{messageId: 'sortedImports'}, {messageId: 'sortedImports'}],
143
+ output: dedent`
144
+ import {basename} from 'path'
145
+ import {existsSync} from 'fs'
146
+ `,
113
147
  }],
114
148
  })
@@ -1 +1 @@
1
- export type ImportGroup = 'side-effect' | 'default' | 'named'
1
+ export type ImportGroup = 'side-effect' | 'default' | 'named' | 'type'
@@ -1,5 +1,5 @@
1
- import type {ImportSpecifier} from 'estree'
2
1
  import {getSpecifierName} from './getSpecifierName'
2
+ import type {ImportSpecifier} from 'estree'
3
3
 
4
4
  export function areSpecifiersSorted(specifiers: ImportSpecifier[]): boolean {
5
5
  const names = specifiers.map(s => getSpecifierName(s))
@@ -1,7 +1,10 @@
1
- import type {ImportDeclaration} from 'estree'
2
1
  import type {ImportGroup} from './ImportGroup'
2
+ import type {TSESTree} from '@typescript-eslint/types'
3
+
4
+ export function categorizeImport(declaration: TSESTree.ImportDeclaration): ImportGroup {
5
+ if (declaration.importKind === 'type')
6
+ return 'type'
3
7
 
4
- export function categorizeImport(declaration: ImportDeclaration): ImportGroup {
5
8
  if (declaration.specifiers.length === 0)
6
9
  return 'side-effect'
7
10
 
@@ -1,9 +1,9 @@
1
- import type {ImportDeclaration} from 'estree'
2
- import type {CategorizedImport} from './CategorizedImport'
3
1
  import {categorizeImport} from './categorizeImport'
4
2
  import {getSortKey} from './getSortKey'
3
+ import type {CategorizedImport} from './CategorizedImport'
4
+ import type {TSESTree} from '@typescript-eslint/types'
5
5
 
6
- export function categorizeImports(declarations: ImportDeclaration[]): CategorizedImport[] {
6
+ export function categorizeImports(declarations: TSESTree.ImportDeclaration[]): CategorizedImport[] {
7
7
  return declarations.map(declaration => ({
8
8
  declaration,
9
9
  group: categorizeImport(declaration),
@@ -5,7 +5,7 @@ import type {ImportGroup} from './ImportGroup'
5
5
  export function checkAlphabeticalSorting(categorized: CategorizedImport[]): ImportError[] {
6
6
  const errors: ImportError[] = []
7
7
 
8
- for (const group of ['side-effect', 'default'] as ImportGroup[]) {
8
+ for (const group of ['side-effect', 'default', 'named', 'type'] as ImportGroup[]) {
9
9
  const groupImports = categorized.filter(c => c.group === group)
10
10
  const sorted = [...groupImports].sort((a, b) => a.sortKey.localeCompare(b.sortKey))
11
11
  for (let i = 0; i < groupImports.length; i++) {
@@ -3,7 +3,7 @@ import type {ImportError} from './ImportError'
3
3
  import type {ImportGroup} from './ImportGroup'
4
4
 
5
5
  export function checkGroupOrdering(categorized: CategorizedImport[]): ImportError[] {
6
- const groupOrder: ImportGroup[] = ['side-effect', 'default', 'named']
6
+ const groupOrder: ImportGroup[] = ['side-effect', 'default', 'named', 'type']
7
7
  const errors: ImportError[] = []
8
8
 
9
9
  let currentGroupIndex = -1
@@ -1,7 +1,7 @@
1
- import type {CategorizedImport} from './CategorizedImport'
2
- import type {ImportError} from './ImportError'
3
1
  import {areSpecifiersSorted} from './areSpecifiersSorted'
4
2
  import {getNamedSpecifiers} from './getNamedSpecifiers'
3
+ import type {CategorizedImport} from './CategorizedImport'
4
+ import type {ImportError} from './ImportError'
5
5
 
6
6
  export function checkSpecifiersSorting(categorized: CategorizedImport[]): ImportError[] {
7
7
  const errors: ImportError[] = []
@@ -1,17 +1,17 @@
1
+ import {formatNamedImport} from './formatNamedImport'
1
2
  import type {CategorizedImport} from '../CategorizedImport'
2
3
  import type {ImportGroup} from '../ImportGroup'
3
- import {formatNamedImport} from './formatNamedImport'
4
4
 
5
5
  export function buildSortedCode(
6
6
  grouped: Record<ImportGroup, CategorizedImport[]>,
7
7
  sourceCode: {getText: (node?: unknown) => string},
8
8
  ): string[] {
9
- const groupOrder: ImportGroup[] = ['side-effect', 'default', 'named']
9
+ const groupOrder: ImportGroup[] = ['side-effect', 'default', 'named', 'type']
10
10
  const sortedCode: string[] = []
11
11
 
12
12
  for (const group of groupOrder) {
13
13
  for (const {declaration} of grouped[group]) {
14
- if (group === 'named')
14
+ if (group === 'named' || group === 'type')
15
15
  sortedCode.push(formatNamedImport(declaration, sourceCode))
16
16
  else
17
17
  sortedCode.push(sourceCode.getText(declaration))
@@ -1,6 +1,6 @@
1
- import type {Program} from 'estree'
1
+ import type {TSESTree} from '@typescript-eslint/types'
2
2
 
3
- export function findLastImportIndex(programBody: Program['body']): number {
3
+ export function findLastImportIndex(programBody: TSESTree.ProgramStatement[]): number {
4
4
  let lastIndex = 0
5
5
  for (let i = 0; i < programBody.length; i++) {
6
6
  if (programBody[i].type === 'ImportDeclaration')
@@ -1,7 +1,7 @@
1
- import type {ImportDeclaration} from 'estree'
2
1
  import {areSpecifiersSorted} from '../areSpecifiersSorted'
3
2
  import {getNamedSpecifiers} from '../getNamedSpecifiers'
4
3
  import {sortSpecifiersText} from '../sortSpecifiersText'
4
+ import type {ImportDeclaration} from 'estree'
5
5
 
6
6
  export function formatNamedImport(
7
7
  declaration: ImportDeclaration,
@@ -1,26 +1,14 @@
1
- import type {ImportDeclaration} from 'estree'
2
- import type {Program} from 'estree'
3
- import type {ReplacementRange} from './ReplacementRange'
4
1
  import {findLastImportIndex} from './findLastImportIndex'
2
+ import type {ReplacementRange} from './ReplacementRange'
3
+ import type {TSESTree} from '@typescript-eslint/types'
5
4
 
6
5
  export function getReplacementRange(
7
- programBody: Program['body'],
8
- sourceCode: {getText: () => string},
6
+ programBody: TSESTree.ProgramStatement[],
9
7
  ): ReplacementRange {
10
- const fullText = sourceCode.getText()
11
8
  const lastIndex = findLastImportIndex(programBody)
12
- const firstImport = programBody[0] as ImportDeclaration
13
- const lastImport = programBody[lastIndex] as ImportDeclaration
9
+ const firstImport = programBody[0] as TSESTree.ImportDeclaration
10
+ const lastImport = programBody[lastIndex] as TSESTree.ImportDeclaration
14
11
  const start = firstImport.range![0]
15
- let end = lastImport.range![1]
16
-
17
- for (let i = end; i < fullText.length; i++) {
18
- const char = fullText[i]
19
- if (char === '\n' || char === ' ' || char === '\t')
20
- end++
21
- else
22
- break
23
- }
24
-
12
+ const end = lastImport.range![1]
25
13
  return {start, end}
26
14
  }
@@ -8,6 +8,7 @@ export function groupImportsByType(
8
8
  'side-effect': [],
9
9
  default: [],
10
10
  named: [],
11
+ type: [],
11
12
  }
12
13
 
13
14
  for (const item of categorized)
@@ -1,19 +1,18 @@
1
- import type {Rule} from 'eslint'
2
- import type {ImportDeclaration} from 'estree'
3
- import type {Program} from 'estree'
4
1
  import {buildSortedCode} from './buildSortedCode'
5
2
  import {categorizeImports} from '../categorizeImports'
6
3
  import {getReplacementRange} from './getReplacementRange'
7
4
  import {groupImportsByType} from './groupImportsByType'
8
5
  import {sortImportGroups} from './sortImportGroups'
6
+ import type {Rule} from 'eslint'
7
+ import type {TSESTree} from '@typescript-eslint/types'
9
8
 
10
9
  export function createFix(
11
10
  fixer: Rule.RuleFixer,
12
- importDeclarations: ImportDeclaration[],
11
+ importDeclarations: TSESTree.ImportDeclaration[],
13
12
  sourceCode: {getText: (node?: unknown) => string},
14
- programBody: Program['body'],
13
+ programBody: TSESTree.ProgramStatement[],
15
14
  ) {
16
- const range = getReplacementRange(programBody, sourceCode)
15
+ const range = getReplacementRange(programBody)
17
16
  const categorized = categorizeImports(importDeclarations)
18
17
  const grouped = groupImportsByType(categorized)
19
18
 
@@ -6,4 +6,6 @@ export function sortImportGroups(
6
6
  ): void {
7
7
  grouped['side-effect'].sort((a, b) => a.sortKey.localeCompare(b.sortKey))
8
8
  grouped['default'].sort((a, b) => a.sortKey.localeCompare(b.sortKey))
9
+ grouped['named'].sort((a, b) => a.sortKey.localeCompare(b.sortKey))
10
+ grouped['type'].sort((a, b) => a.sortKey.localeCompare(b.sortKey))
9
11
  }
@@ -1,9 +1,8 @@
1
- import type {ImportDeclaration} from 'estree'
2
- import type {Program} from 'estree'
1
+ import type {TSESTree} from '@typescript-eslint/types'
3
2
 
4
- export function getImportDeclarations(programBody: Program['body']): ImportDeclaration[] {
3
+ export function getImportDeclarations(programBody: TSESTree.ProgramStatement[]): TSESTree.ImportDeclaration[] {
5
4
  return programBody.filter(
6
- (statement): statement is ImportDeclaration =>
5
+ (statement): statement is TSESTree.ImportDeclaration =>
7
6
  statement.type === 'ImportDeclaration',
8
7
  )
9
8
  }
@@ -1,8 +1,7 @@
1
- import type {ImportDeclaration} from 'estree'
2
- import type {ImportDefaultSpecifier} from 'estree'
3
1
  import {categorizeImport} from './categorizeImport'
2
+ import type {TSESTree} from '@typescript-eslint/types'
4
3
 
5
- export function getSortKey(declaration: ImportDeclaration): string {
4
+ export function getSortKey(declaration: TSESTree.ImportDeclaration): string {
6
5
  const group = categorizeImport(declaration)
7
6
 
8
7
  if (group === 'side-effect')
@@ -11,10 +10,11 @@ export function getSortKey(declaration: ImportDeclaration): string {
11
10
  if (group === 'default') {
12
11
  const defaultSpecifier = declaration.specifiers.find(
13
12
  s => s.type === 'ImportDefaultSpecifier',
14
- ) as ImportDefaultSpecifier | undefined
13
+ ) as TSESTree.ImportDefaultSpecifier | undefined
15
14
 
16
15
  return defaultSpecifier?.local.name.toLowerCase() ?? ''
17
16
  }
18
17
 
19
- return ''
18
+ const specifier = declaration.specifiers[0]
19
+ return specifier.local.name.toLowerCase()
20
20
  }
@@ -1,11 +1,12 @@
1
- import type {Rule} from 'eslint'
2
- import type {ImportError} from './ImportError'
3
1
  import {categorizeImports} from './categorizeImports'
4
2
  import {checkAlphabeticalSorting} from './checkAlphabeticalSorting'
5
3
  import {checkGroupOrdering} from './checkGroupOrdering'
6
4
  import {checkSpecifiersSorting} from './checkSpecifiersSorting'
7
5
  import {createFix} from './createFix'
8
6
  import {getImportDeclarations} from './getImportDeclarations'
7
+ import type {ImportError} from './ImportError'
8
+ import type {Rule} from 'eslint'
9
+ import type {TSESTree} from '@typescript-eslint/types'
9
10
 
10
11
  export const sortedImports: Rule.RuleModule = {
11
12
  meta: {
@@ -25,7 +26,8 @@ export const sortedImports: Rule.RuleModule = {
25
26
  create(context) {
26
27
  return {
27
28
  Program(node) {
28
- const declarations = getImportDeclarations(node.body)
29
+ const body = node.body as TSESTree.ProgramStatement[]
30
+ const declarations = getImportDeclarations(body)
29
31
  if (declarations.length === 0)
30
32
  return
31
33
 
@@ -42,7 +44,7 @@ export const sortedImports: Rule.RuleModule = {
42
44
  messageId: error.messageId,
43
45
  fix(fixer) {
44
46
  const sourceCode = context.sourceCode
45
- return createFix(fixer, declarations, sourceCode, node.body)
47
+ return createFix(fixer, declarations, sourceCode, body)
46
48
  },
47
49
  })
48
50
  }
@@ -1,5 +1,5 @@
1
- import type {ImportSpecifier} from 'estree'
2
1
  import {getSpecifierName} from './getSpecifierName'
2
+ import type {ImportSpecifier} from 'estree'
3
3
 
4
4
  export function sortSpecifiersText(
5
5
  specifiers: ImportSpecifier[],