@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 +2 -1
- package/bin/typecheck +8 -0
- package/dist/index.js +18 -19
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/rules/__tests__/individualImports.test.ts +2 -2
- package/src/rules/__tests__/sortedImports.test.ts +35 -1
- package/src/rules/sortedImports/ImportGroup.ts +1 -1
- package/src/rules/sortedImports/areSpecifiersSorted.ts +1 -1
- package/src/rules/sortedImports/categorizeImport.ts +5 -2
- package/src/rules/sortedImports/categorizeImports.ts +3 -3
- package/src/rules/sortedImports/checkAlphabeticalSorting.ts +1 -1
- package/src/rules/sortedImports/checkGroupOrdering.ts +1 -1
- package/src/rules/sortedImports/checkSpecifiersSorting.ts +2 -2
- package/src/rules/sortedImports/createFix/buildSortedCode.ts +3 -3
- package/src/rules/sortedImports/createFix/findLastImportIndex.ts +2 -2
- package/src/rules/sortedImports/createFix/formatNamedImport.ts +1 -1
- package/src/rules/sortedImports/createFix/getReplacementRange.ts +6 -18
- package/src/rules/sortedImports/createFix/groupImportsByType.ts +1 -0
- package/src/rules/sortedImports/createFix/index.ts +5 -6
- package/src/rules/sortedImports/createFix/sortImportGroups.ts +2 -0
- package/src/rules/sortedImports/getImportDeclarations.ts +3 -4
- package/src/rules/sortedImports/getSortKey.ts +5 -5
- package/src/rules/sortedImports/index.ts +6 -4
- package/src/rules/sortedImports/sortSpecifiersText.ts +1 -1
package/bin/lint
CHANGED
package/bin/typecheck
ADDED
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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,
|
|
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":";AAAA,OAAO,YAAY;AACnB,OAAO,WAAW;AAClB,OAAO,gBAAgB;AACvB,OAAO,eAAe;AACtB,OAAO,gBAAgB;AACvB,SAAQ,oBAAmB;;;ACHpB,IAAM,oBAAqC;AAAA,EAChD,MAAM;AAAA,IACJ,MAAM;AAAA,MACJ,aAAa;AAAA,MACb,aAAa;AAAA,IACf;AAAA,IACA,SAAS;AAAA,IACT,UAAU;AAAA,MACR,mBAAmB;AAAA,IACrB;AAAA,IACA,QAAQ,CAAC;AAAA,IACT,MAAM;AAAA,EACR;AAAA,EACA,OAAO,SAAS;AACd,WAAO;AAAA,MACL,kBAAkB,MAAM;AACtB,YAAI,KAAK,WAAW,UAAU;AAC5B;AACF,gBAAQ,OAAO;AAAA,UACb;AAAA,UACA,WAAW;AAAA,UACX,IAAI,OAAO;AACT,kBAAM,SAAS,KAAK,OAAO;AAC3B,kBAAM,aAAa,KAAK,WACrB,IAAI,qBAAmB;AACtB,kBAAI,gBAAgB,SAAS;AAC3B,uBAAO,WAAW,gBAAgB,MAAM,IAAI,UAAU,MAAM;AAC9D,qBAAO;AAAA,YACT,CAAC,EACA,OAAO,OAAO;AAEjB,gBAAI,WAAW,WAAW,KAAK,WAAW;AACxC,qBAAO;AAET,mBAAO,MAAM;AAAA,cACX;AAAA,cACA,WAAW,KAAK,IAAI;AAAA,YACtB;AAAA,UACF;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AACF;;;AC1CO,SAAS,iBAAiB,aAAsD;AACrF,MAAI,YAAY,eAAe;AAC7B,WAAO;AAET,MAAI,YAAY,WAAW,WAAW;AACpC,WAAO;AAET,MAAI,YAAY,WAAW,KAAK,OAAK,EAAE,SAAS,wBAAwB;AACtE,WAAO;AAET,SAAO;AACT;;;ACXO,SAAS,WAAW,aAAiD;AAC1E,QAAM,QAAQ,iBAAiB,WAAW;AAE1C,MAAI,UAAU;AACZ,WAAQ,YAAY,OAAO,MAAiB,YAAY;AAE1D,MAAI,UAAU,WAAW;AACvB,UAAM,mBAAmB,YAAY,WAAW;AAAA,MAC9C,OAAK,EAAE,SAAS;AAAA,IAClB;AAEA,WAAO,kBAAkB,MAAM,KAAK,YAAY,KAAK;AAAA,EACvD;AAEA,QAAM,YAAY,YAAY,WAAW,CAAC;AAC1C,SAAO,UAAU,MAAM,KAAK,YAAY;AAC1C;;;ACdO,SAAS,kBAAkB,cAAiE;AACjG,SAAO,aAAa,IAAI,kBAAgB;AAAA,IACtC;AAAA,IACA,OAAO,iBAAiB,WAAW;AAAA,IACnC,SAAS,WAAW,WAAW;AAAA,EACjC,EAAE;AACJ;;;ACPO,SAAS,yBAAyB,aAAiD;AACxF,QAAM,SAAwB,CAAC;AAE/B,aAAW,SAAS,CAAC,eAAe,WAAW,SAAS,MAAM,GAAoB;AAChF,UAAM,eAAe,YAAY,OAAO,OAAK,EAAE,UAAU,KAAK;AAC9D,UAAM,SAAS,CAAC,GAAG,YAAY,EAAE,KAAK,CAAC,GAAG,MAAM,EAAE,QAAQ,cAAc,EAAE,OAAO,CAAC;AAClF,aAAS,IAAI,GAAG,IAAI,aAAa,QAAQ,KAAK;AAC5C,UAAI,aAAa,CAAC,MAAM,OAAO,CAAC,GAAG;AACjC,eAAO,KAAK;AAAA,UACV,MAAM,aAAa,CAAC,EAAE;AAAA,UACtB,WAAW;AAAA,QACb,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;;;ACjBO,SAAS,mBAAmB,aAAiD;AAClF,QAAM,aAA4B,CAAC,eAAe,WAAW,SAAS,MAAM;AAC5E,QAAM,SAAwB,CAAC;AAE/B,MAAI,oBAAoB;AACxB,aAAW,EAAC,aAAa,MAAK,KAAK,aAAa;AAC9C,UAAM,aAAa,WAAW,QAAQ,KAAK;AAC3C,QAAI,aAAa,mBAAmB;AAClC,aAAO,KAAK;AAAA,QACV,MAAM;AAAA,QACN,WAAW;AAAA,MACb,CAAC;AAAA,IACH;AACE,0BAAoB;AAAA,EACxB;AAEA,SAAO;AACT;;;ACnBO,SAAS,iBAAiB,WAAoC;AACnE,SAAO,UAAU,SAAS,SAAS,eAC/B,UAAU,SAAS,OACnB,OAAO,UAAU,SAAS,KAAK;AACrC;;;ACHO,SAAS,oBAAoB,YAAwC;AAC1E,QAAM,QAAQ,WAAW,IAAI,OAAK,iBAAiB,CAAC,CAAC;AACrD,QAAM,SAAS,CAAC,GAAG,KAAK,EAAE;AAAA,IAAK,CAAC,GAAG,MACjC,EAAE,YAAY,EAAE,cAAc,EAAE,YAAY,CAAC;AAAA,EAC/C;AACA,SAAO,MAAM,MAAM,CAAC,MAAM,MAAM,SAAS,OAAO,CAAC,CAAC;AACpD;;;ACNO,SAAS,mBAAmB,aAAmD;AACpF,SAAO,YAAY,WAAW;AAAA,IAC5B,CAAC,MAA4B,EAAE,SAAS;AAAA,EAC1C;AACF;;;ACFO,SAAS,uBAAuB,aAAiD;AACtF,QAAM,SAAwB,CAAC;AAC/B,QAAM,eAAe,YAAY,OAAO,OAAK,EAAE,UAAU,OAAO;AAEhE,aAAW,EAAC,YAAW,KAAK,cAAc;AACxC,UAAM,aAAa,mBAAmB,WAAW;AACjD,QAAI,WAAW,SAAS,KAAK,CAAC,oBAAoB,UAAU,GAAG;AAC7D,aAAO,KAAK;AAAA,QACV,MAAM;AAAA,QACN,WAAW;AAAA,MACb,CAAC;AAAA,IACH;AAAA,EACF;AAEA,SAAO;AACT;;;ACjBO,SAAS,mBACd,YACA,YACQ;AACR,QAAM,SAAS,CAAC,GAAG,UAAU,EAAE,KAAK,CAAC,GAAG,MAAM;AAC5C,UAAM,SAAS,iBAAiB,CAAC,EAAE,YAAY;AAC/C,UAAM,SAAS,iBAAiB,CAAC,EAAE,YAAY;AAC/C,WAAO,OAAO,cAAc,MAAM;AAAA,EACpC,CAAC;AACD,SAAO,OAAO,IAAI,OAAK,WAAW,QAAQ,CAAC,CAAC,EAAE,KAAK,IAAI;AACzD;;;ACRO,SAAS,kBACd,aACA,YACQ;AACR,QAAM,aAAa,mBAAmB,WAAW;AAEjD,MAAI,WAAW,SAAS,KAAK,CAAC,oBAAoB,UAAU,GAAG;AAC7D,UAAM,aAAa,WAAW,QAAQ,WAAW;AACjD,UAAM,kBAAkB,WAAW,QAAQ,GAAG;AAC9C,UAAM,gBAAgB,WAAW,YAAY,GAAG;AAChD,UAAM,SAAS,WAAW,UAAU,GAAG,kBAAkB,CAAC;AAC1D,UAAM,QAAQ,WAAW,UAAU,aAAa;AAChD,UAAM,mBAAmB,mBAAmB,YAAY,UAAU;AAClE,WAAO,SAAS,MAAM,mBAAmB,MAAM;AAAA,EACjD;AAEA,SAAO,WAAW,QAAQ,WAAW;AACvC;;;AClBO,SAAS,gBACd,SACA,YACU;AACV,QAAM,aAA4B,CAAC,eAAe,WAAW,SAAS,MAAM;AAC5E,QAAM,aAAuB,CAAC;AAE9B,aAAW,SAAS,YAAY;AAC9B,eAAW,EAAC,YAAW,KAAK,QAAQ,KAAK,GAAG;AAC1C,UAAI,UAAU,WAAW,UAAU;AACjC,mBAAW,KAAK,kBAAkB,aAAa,UAAU,CAAC;AAAA;AAE1D,mBAAW,KAAK,WAAW,QAAQ,WAAW,CAAC;AAAA,IACnD;AAAA,EACF;AAEA,SAAO;AACT;;;ACnBO,SAAS,oBAAoB,aAAkD;AACpF,MAAI,YAAY;AAChB,WAAS,IAAI,GAAG,IAAI,YAAY,QAAQ,KAAK;AAC3C,QAAI,YAAY,CAAC,EAAE,SAAS;AAC1B,kBAAY;AAAA;AAEZ;AAAA,EACJ;AACA,SAAO;AACT;;;ACPO,SAAS,oBACd,aACkB;AAClB,QAAM,YAAY,oBAAoB,WAAW;AACjD,QAAM,cAAc,YAAY,CAAC;AACjC,QAAM,aAAa,YAAY,SAAS;AACxC,QAAM,QAAQ,YAAY,MAAO,CAAC;AAClC,QAAM,MAAM,WAAW,MAAO,CAAC;AAC/B,SAAO,EAAC,OAAO,IAAG;AACpB;;;ACVO,SAAS,mBACd,aAC0C;AAC1C,QAAM,UAAoD;AAAA,IACxD,eAAe,CAAC;AAAA,IAChB,SAAS,CAAC;AAAA,IACV,OAAO,CAAC;AAAA,IACR,MAAM,CAAC;AAAA,EACT;AAEA,aAAW,QAAQ;AACjB,YAAQ,KAAK,KAAK,EAAE,KAAK,IAAI;AAE/B,SAAO;AACT;;;ACdO,SAAS,iBACd,SACM;AACN,UAAQ,aAAa,EAAE,KAAK,CAAC,GAAG,MAAM,EAAE,QAAQ,cAAc,EAAE,OAAO,CAAC;AACxE,UAAQ,SAAS,EAAE,KAAK,CAAC,GAAG,MAAM,EAAE,QAAQ,cAAc,EAAE,OAAO,CAAC;AACpE,UAAQ,OAAO,EAAE,KAAK,CAAC,GAAG,MAAM,EAAE,QAAQ,cAAc,EAAE,OAAO,CAAC;AAClE,UAAQ,MAAM,EAAE,KAAK,CAAC,GAAG,MAAM,EAAE,QAAQ,cAAc,EAAE,OAAO,CAAC;AACnE;;;ACFO,SAAS,UACd,OACA,oBACA,YACA,aACA;AACA,QAAM,QAAQ,oBAAoB,WAAW;AAC7C,QAAM,cAAc,kBAAkB,kBAAkB;AACxD,QAAM,UAAU,mBAAmB,WAAW;AAE9C,mBAAiB,OAAO;AAExB,QAAM,aAAa,gBAAgB,SAAS,UAAU,EACnD,KAAK,IAAI;AAEZ,SAAO,MAAM;AAAA,IACX,CAAC,MAAM,OAAO,MAAM,GAAG;AAAA,IACvB;AAAA,EACF;AACF;;;ACzBO,SAAS,sBAAsB,aAAwE;AAC5G,SAAO,YAAY;AAAA,IACjB,CAAC,cACC,UAAU,SAAS;AAAA,EACvB;AACF;;;ACGO,IAAM,gBAAiC;AAAA,EAC5C,MAAM;AAAA,IACJ,MAAM;AAAA,MACJ,aAAa;AAAA,MACb,aAAa;AAAA,IACf;AAAA,IACA,SAAS;AAAA,IACT,UAAU;AAAA,MACR,eAAe;AAAA,MACf,aAAa;AAAA,MACb,YAAY;AAAA,IACd;AAAA,IACA,QAAQ,CAAC;AAAA,IACT,MAAM;AAAA,EACR;AAAA,EACA,OAAO,SAAS;AACd,WAAO;AAAA,MACL,QAAQ,MAAM;AACZ,cAAM,OAAO,KAAK;AAClB,cAAM,eAAe,sBAAsB,IAAI;AAC/C,YAAI,aAAa,WAAW;AAC1B;AAEF,cAAM,cAAc,kBAAkB,YAAY;AAClD,cAAM,SAAwB;AAAA,UAC5B,GAAG,mBAAmB,WAAW;AAAA,UACjC,GAAG,yBAAyB,WAAW;AAAA,UACvC,GAAG,uBAAuB,WAAW;AAAA,QACvC;AAEA,mBAAW,SAAS,QAAQ;AAC1B,kBAAQ,OAAO;AAAA,YACb,MAAM,MAAM;AAAA,YACZ,WAAW,MAAM;AAAA,YACjB,IAAI,OAAO;AACT,oBAAM,aAAa,QAAQ;AAC3B,qBAAO,UAAU,OAAO,cAAc,YAAY,IAAI;AAAA,YACxD;AAAA,UACF,CAAC;AAAA,QACH;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;;;ApB5CO,IAAM,SAAS,aAAa;AAAA,EACjC;AAAA,IACE,SAAS;AAAA,MACP;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA,EACA;AAAA,IACE,UAAU;AAAA,MACR,OAAO;AAAA,QACL,SAAS;AAAA,MACX;AAAA,IACF;AAAA,EACF;AAAA,EACA,OAAO,QAAQ;AAAA,EACf,MAAM,QAAQ,KAAK;AAAA,EACnB,UAAU,QAAQ;AAAA,EAClB,WAAW,QAAQ;AAAA,EACnB,WAAW,QAAQ;AAAA,EACnB;AAAA,IACE,SAAS;AAAA,MACP,eAAe;AAAA,IACjB;AAAA,IACA,OAAO,WAAW,QAAQ,YAAY;AAAA,EACxC;AAAA,EACA;AAAA,IACE,SAAS;AAAA,MACP,gBAAgB;AAAA,QACd,OAAO;AAAA,UACL,sBAAsB;AAAA,UACtB,kBAAkB;AAAA,QACpB;AAAA,MACF;AAAA,IACF;AAAA,IACA,OAAO;AAAA,MACL,mCAAmC;AAAA,MACnC,+BAA+B;AAAA,IACjC;AAAA,EACF;AAAA,EACA;AAAA,IACE,OAAO;AAAA,MACL,wBAAwB;AAAA,QACtB;AAAA,QACA;AAAA,QACA,EAAC,2BAA2B,KAAI;AAAA,MAClC;AAAA,MACA,4BAA4B;AAAA,MAC5B,2BAA2B;AAAA,QACzB;AAAA,QACA;AAAA,MACF;AAAA,MACA,oCAAoC;AAAA,QAClC;AAAA,QACA;AAAA,MACF;AAAA,MACA,oCAAoC;AAAA,QAClC;AAAA,QACA;AAAA,MACF;AAAA,MACA,oCAAoC;AAAA,QAClC;AAAA,QACA;AAAA,MACF;AAAA,MACA,4BAA4B;AAAA,MAC5B,0BAA0B;AAAA,QACxB;AAAA,QACA;AAAA,QACA,EAAC,iBAAiB,KAAI;AAAA,MACxB;AAAA,MACA,qBAAqB;AAAA,QACnB;AAAA,QACA;AAAA,QACA,EAAC,cAAc,CAAC,kBAAkB,EAAC;AAAA,MACrC;AAAA,MACA,8BAA8B;AAAA,QAC5B;AAAA,QACA;AAAA,UACE,cAAc;AAAA,UACd,eAAe;AAAA,UACf,mBAAmB;AAAA,UACnB,cAAc;AAAA,QAChB;AAAA,MACF;AAAA,MACA,kCAAkC;AAAA,MAClC,0CAA0C;AAAA,MAC1C,mCAAmC;AAAA,QACjC;AAAA,QACA,EAAC,YAAY,KAAI;AAAA,MACnB;AAAA,MACA,mCAAmC;AAAA,QACjC;AAAA,QACA;AAAA,MACF;AAAA,MACA,iCAAiC;AAAA,QAC/B;AAAA,QACA;AAAA,QACA,EAAC,WAAW,EAAC,KAAK,QAAO,EAAC;AAAA,MAC5B;AAAA,MACA,qBAAqB;AAAA,QACnB;AAAA,QACA;AAAA,QACA,EAAC,aAAa,KAAI;AAAA,MACpB;AAAA,MACA,0BAA0B;AAAA,QACxB;AAAA,QACA;AAAA,MACF;AAAA,MACA,mBAAmB;AAAA,QACjB;AAAA,QACA;AAAA,QACA,EAAC,kCAAkC,SAAQ;AAAA,MAC7C;AAAA,MACA,wCAAwC;AAAA,MACxC,sDAAsD;AAAA,IACxD;AAAA,EACF;AACF,CAAC;","names":[]}
|
package/package.json
CHANGED
|
@@ -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 {
|
|
1
|
+
import type {TSESTree} from '@typescript-eslint/types'
|
|
2
2
|
|
|
3
|
-
export function findLastImportIndex(programBody:
|
|
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:
|
|
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
|
-
|
|
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
|
}
|
|
@@ -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:
|
|
13
|
+
programBody: TSESTree.ProgramStatement[],
|
|
15
14
|
) {
|
|
16
|
-
const range = getReplacementRange(programBody
|
|
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 {
|
|
2
|
-
import type {Program} from 'estree'
|
|
1
|
+
import type {TSESTree} from '@typescript-eslint/types'
|
|
3
2
|
|
|
4
|
-
export function getImportDeclarations(programBody:
|
|
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
|
-
|
|
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
|
|
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,
|
|
47
|
+
return createFix(fixer, declarations, sourceCode, body)
|
|
46
48
|
},
|
|
47
49
|
})
|
|
48
50
|
}
|