diff --git a/src/index.ts b/src/index.ts index 5a6eafba..31925463 100644 --- a/src/index.ts +++ b/src/index.ts @@ -48,6 +48,17 @@ const options = { default: false, description: 'Should specifiers be sorted?', }, + importOrderSortByLength: { + type: 'choice', + category: 'Global', + default: null, + choices: [ + {value: 'asc', description: 'will sort from shortest to longest'}, + {value: 'desc', description: 'will sort from longest to shortest'}, + {value: null, description: 'will disable sorting based on length'} + ], + description: 'Should imports be sorted by their string length' + } }; module.exports = { diff --git a/src/preprocessors/preprocessor.ts b/src/preprocessors/preprocessor.ts index bf5da668..c5ce6a6b 100644 --- a/src/preprocessors/preprocessor.ts +++ b/src/preprocessors/preprocessor.ts @@ -16,6 +16,7 @@ export function preprocessor(code: string, options: PrettierOptions) { importOrderSeparation, importOrderGroupNamespaceSpecifiers, importOrderSortSpecifiers, + importOrderSortByLength } = options; const parserOptions: ParserOptions = { @@ -42,6 +43,7 @@ export function preprocessor(code: string, options: PrettierOptions) { importOrderSeparation, importOrderGroupNamespaceSpecifiers, importOrderSortSpecifiers, + importOrderSortByLength }); return getCodeFromAst(allImports, directives, code, interpreter); diff --git a/src/types.ts b/src/types.ts index 89eda919..48502116 100644 --- a/src/types.ts +++ b/src/types.ts @@ -19,5 +19,6 @@ export type GetSortedNodes = ( | 'importOrderSeparation' | 'importOrderGroupNamespaceSpecifiers' | 'importOrderSortSpecifiers' + | 'importOrderSortByLength' >, ) => ImportOrLine[]; diff --git a/src/utils/__tests__/get-all-comments-from-nodes.spec.ts b/src/utils/__tests__/get-all-comments-from-nodes.spec.ts index ff8f156b..06b2d895 100644 --- a/src/utils/__tests__/get-all-comments-from-nodes.spec.ts +++ b/src/utils/__tests__/get-all-comments-from-nodes.spec.ts @@ -14,6 +14,7 @@ const getSortedImportNodes = (code: string, options?: ParserOptions) => { importOrderSeparation: false, importOrderGroupNamespaceSpecifiers: false, importOrderSortSpecifiers: false, + importOrderSortByLength: null }); }; diff --git a/src/utils/__tests__/get-code-from-ast.spec.ts b/src/utils/__tests__/get-code-from-ast.spec.ts index da58c041..f746ffd4 100644 --- a/src/utils/__tests__/get-code-from-ast.spec.ts +++ b/src/utils/__tests__/get-code-from-ast.spec.ts @@ -25,6 +25,7 @@ import a from 'a'; importOrderSeparation: false, importOrderGroupNamespaceSpecifiers: false, importOrderSortSpecifiers: false, + importOrderSortByLength: null }); const formatted = getCodeFromAst(sortedNodes, [], code, null); expect(format(formatted, { parser: 'babel' })).toEqual( diff --git a/src/utils/__tests__/get-sorted-nodes.spec.ts b/src/utils/__tests__/get-sorted-nodes.spec.ts index d488c92a..b8b164a8 100644 --- a/src/utils/__tests__/get-sorted-nodes.spec.ts +++ b/src/utils/__tests__/get-sorted-nodes.spec.ts @@ -28,6 +28,7 @@ test('it returns all sorted nodes', () => { importOrderSeparation: false, importOrderGroupNamespaceSpecifiers: false, importOrderSortSpecifiers: false, + importOrderSortByLength: null }) as ImportDeclaration[]; expect(getSortedNodesNames(sorted)).toEqual([ @@ -72,6 +73,7 @@ test('it returns all sorted nodes case-insensitive', () => { importOrderSeparation: false, importOrderGroupNamespaceSpecifiers: false, importOrderSortSpecifiers: false, + importOrderSortByLength: null }) as ImportDeclaration[]; expect(getSortedNodesNames(sorted)).toEqual([ @@ -116,6 +118,7 @@ test('it returns all sorted nodes with sort order', () => { importOrderSeparation: false, importOrderGroupNamespaceSpecifiers: false, importOrderSortSpecifiers: false, + importOrderSortByLength: null }) as ImportDeclaration[]; expect(getSortedNodesNames(sorted)).toEqual([ @@ -160,6 +163,7 @@ test('it returns all sorted nodes with sort order case-insensitive', () => { importOrderSeparation: false, importOrderGroupNamespaceSpecifiers: false, importOrderSortSpecifiers: false, + importOrderSortByLength: null }) as ImportDeclaration[]; expect(getSortedNodesNames(sorted)).toEqual([ 'c', @@ -203,6 +207,7 @@ test('it returns all sorted import nodes with sorted import specifiers', () => { importOrderSeparation: false, importOrderGroupNamespaceSpecifiers: false, importOrderSortSpecifiers: true, + importOrderSortByLength: null }) as ImportDeclaration[]; expect(getSortedNodesNames(sorted)).toEqual([ 'XY', @@ -246,6 +251,7 @@ test('it returns all sorted import nodes with sorted import specifiers with case importOrderSeparation: false, importOrderGroupNamespaceSpecifiers: false, importOrderSortSpecifiers: true, + importOrderSortByLength: null }) as ImportDeclaration[]; expect(getSortedNodesNames(sorted)).toEqual([ 'c', @@ -289,6 +295,7 @@ test('it returns all sorted nodes with custom third party modules', () => { importOrderCaseInsensitive: true, importOrderGroupNamespaceSpecifiers: false, importOrderSortSpecifiers: false, + importOrderSortByLength: null }) as ImportDeclaration[]; expect(getSortedNodesNames(sorted)).toEqual([ 'a', @@ -313,6 +320,7 @@ test('it returns all sorted nodes with namespace specifiers at the top', () => { importOrderSeparation: false, importOrderGroupNamespaceSpecifiers: true, importOrderSortSpecifiers: false, + importOrderSortByLength: null }) as ImportDeclaration[]; expect(getSortedNodesNames(sorted)).toEqual([ @@ -329,3 +337,53 @@ test('it returns all sorted nodes with namespace specifiers at the top', () => { 'z', ]); }); + +test('it returns all sorted nodes, sorted shortest to longest', () => { + const result = getImportNodes(code) + const sorted = getSortedNodes(result, { + importOrder: [], + importOrderCaseInsensitive: false, + importOrderSeparation: false, + importOrderGroupNamespaceSpecifiers: false, + importOrderSortSpecifiers: false, + importOrderSortByLength: 'asc' + }) as ImportDeclaration[]; + expect(getSortedNodesNames(sorted)).toEqual([ + 'g', + 'z', + 'Ba', + 'BY', + 'Xa', + 'XY', + 'a', + 'x', + 'c', + 'k', + 't', + ]); +}) + +test('it returns all sorted nodes, sorted longest to shortest', () => { + const result = getImportNodes(code) + const sorted = getSortedNodes(result, { + importOrder: [], + importOrderCaseInsensitive: false, + importOrderSeparation: false, + importOrderGroupNamespaceSpecifiers: false, + importOrderSortSpecifiers: false, + importOrderSortByLength: 'desc' + }) as ImportDeclaration[]; + expect(getSortedNodesNames(sorted)).toEqual([ + 't', + 'k', + 'c', + 'a', + 'x', + 'Ba', + 'BY', + 'Xa', + 'XY', + 'g', + 'z' + ]); +}) diff --git a/src/utils/__tests__/remove-nodes-from-original-code.spec.ts b/src/utils/__tests__/remove-nodes-from-original-code.spec.ts index f214cf55..90717315 100644 --- a/src/utils/__tests__/remove-nodes-from-original-code.spec.ts +++ b/src/utils/__tests__/remove-nodes-from-original-code.spec.ts @@ -25,6 +25,7 @@ test('it should remove nodes from the original code', () => { importOrderSeparation: false, importOrderGroupNamespaceSpecifiers: false, importOrderSortSpecifiers: false, + importOrderSortByLength: null }); const allCommentsFromImports = getAllCommentsFromNodes(sortedNodes); diff --git a/src/utils/get-sorted-nodes-group.ts b/src/utils/get-sorted-nodes-group.ts index 89951336..beb4776a 100644 --- a/src/utils/get-sorted-nodes-group.ts +++ b/src/utils/get-sorted-nodes-group.ts @@ -1,18 +1,27 @@ -import { Import, ImportDeclaration } from '@babel/types'; +import { ImportDeclaration } from '@babel/types'; import { naturalSort } from '../natural-sort'; import { PrettierOptions } from '../types'; export const getSortedNodesGroup = ( imports: ImportDeclaration[], - options: Pick, + options: Pick, ) => { return imports.sort((a, b) => { + const aLength = (a.end || 0) - (a.start || 0) + const bLength = (b.end || 0) - (b.start || 0) + if (options.importOrderGroupNamespaceSpecifiers) { const diff = namespaceSpecifierSort(a, b); if (diff !== 0) return diff; } + if (options.importOrderSortByLength === 'asc') + return aLength - bLength || a.source.value.localeCompare(b.source.value) + + if (options.importOrderSortByLength === 'desc') + return bLength - aLength || a.source.value.localeCompare(b.source.value) + return naturalSort(a.source.value, b.source.value); }); }; diff --git a/src/utils/get-sorted-nodes.ts b/src/utils/get-sorted-nodes.ts index d2c81278..ca43921a 100644 --- a/src/utils/get-sorted-nodes.ts +++ b/src/utils/get-sorted-nodes.ts @@ -21,7 +21,7 @@ export const getSortedNodes: GetSortedNodes = (nodes, options) => { const { importOrderSeparation, importOrderSortSpecifiers, - importOrderGroupNamespaceSpecifiers, + importOrderGroupNamespaceSpecifiers,importOrderSortByLength } = options; const originalNodes = nodes.map(clone); @@ -58,6 +58,7 @@ export const getSortedNodes: GetSortedNodes = (nodes, options) => { const sortedInsideGroup = getSortedNodesGroup(groupNodes, { importOrderGroupNamespaceSpecifiers, + importOrderSortByLength }); // Sort the import specifiers diff --git a/types/index.d.ts b/types/index.d.ts index 4c5f02f3..21231277 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -47,21 +47,21 @@ export interface PluginConfig { /** * A boolean value to enable case-insensitivity in the sorting algorithm used to order imports within each match group. - * + * * For example, when false (or not specified): - * + * * ```js * import ExampleView from './ExampleView'; * import ExamplesList from './ExamplesList'; * ``` - * + * * compared with `"importOrderCaseInsensitive": true`: - * + * * ```js * import ExamplesList from './ExamplesList'; * import ExampleView from './ExampleView'; * ``` - * + * * @default false */ importOrderCaseInsensitive?: boolean; @@ -92,6 +92,14 @@ used to order imports within each match group. * @default ["typescript", "jsx"] */ importOrderParserPlugins?: ImportOrderParserPlugin[]; + + /** + * A choice value to enable sorting imports within their groups based on their string lengths, the two options being ascending and descending. + * Leaving the value blank or setting it to null will result in length being ignored + * + * @default null + */ + importOrderSortByLength?: 'asc' | 'desc' | null } export type PrettierConfig = PluginConfig & Config;