diff --git a/package.json b/package.json index 6ceb92f..d7e8554 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,6 @@ "@babel/parser": "^7.17.7", "@babel/traverse": "^7.17.3", "@babel/types": "^7.17.0", - "javascript-natural-sort": "0.7.1", "lodash.clone": "^4.5.0", "lodash.isequal": "^4.5.0" }, diff --git a/src/natural-sort/__tests__/natural-sort.spec.ts b/src/natural-sort/__tests__/natural-sort.spec.ts new file mode 100644 index 0000000..d81a6bc --- /dev/null +++ b/src/natural-sort/__tests__/natural-sort.spec.ts @@ -0,0 +1,54 @@ +import { naturalSort } from '..'; + +it('should sort normal things alphabetically', () => { + expect( + ['a', 'h', 'b', 'i', 'c', 'd', 'j', 'e', 'k', 'f', 'g'].sort((a, b) => + naturalSort(a, b, { importOrderCaseInsensitive: false }), + ), + ).toEqual(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k']); +}); + +it('should treat capital letters as alphabetically earlier than lowercase if case sensitive (or unspecified)', () => { + expect( + ['./ExampleView', './ExamplesList'].sort((a, b) => + naturalSort(a, b, { importOrderCaseInsensitive: false }), + ), + ).toMatchInlineSnapshot( + ['./ExampleView', './ExamplesList'], + ` + Array [ + "./ExampleView", + "./ExamplesList", + ] + `, + ); + expect( + ['./ExampleView', './ExamplesList'].sort((a, b) => + naturalSort(a, b, {}), + ), + ).toMatchInlineSnapshot( + ['./ExampleView', './ExamplesList'], + ` + Array [ + "./ExampleView", + "./ExamplesList", + ] + `, + ); +}); + +it('should ignore capitalization differences if case-insensitive', () => { + expect( + ['./ExampleView', './ExamplesList'].sort((a, b) => + naturalSort(a, b, { importOrderCaseInsensitive: true }), + ), + ).toEqual(['./ExamplesList', './ExampleView']); +}); + +it('should sort things numerically', () => { + expect( + ['a2', 'a3', 'a10', 'a1', 'a11', 'a9'].sort((a, b) => + naturalSort(a, b, { importOrderCaseInsensitive: true }), + ), + ).toEqual(['a1', 'a2', 'a3', 'a9', 'a10', 'a11']); +}); diff --git a/src/natural-sort/index.ts b/src/natural-sort/index.ts index a5c25ab..44706a5 100644 --- a/src/natural-sort/index.ts +++ b/src/natural-sort/index.ts @@ -1,3 +1,26 @@ -import naturalSort from 'javascript-natural-sort'; +import type { PrettierOptions } from '../types'; -export { naturalSort }; +export type NaturalSortOptions = Partial< + Pick +>; + +export function naturalSort( + a: string, + b: string, + { importOrderCaseInsensitive }: NaturalSortOptions, +): number { + const left = typeof a === 'string' ? a : String(a); + + if (!importOrderCaseInsensitive) { + return left < b ? -1 : left > b ? 1 : 0; + } + + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Collator/Collator#syntax + const sortOptions: Intl.CollatorOptions = { + sensitivity: 'base', + numeric: true, + caseFirst: 'lower', + }; + + return left.localeCompare(b, 'en', sortOptions); +} diff --git a/src/natural-sort/natural-sort.d.ts b/src/natural-sort/natural-sort.d.ts deleted file mode 100644 index e65f22d..0000000 --- a/src/natural-sort/natural-sort.d.ts +++ /dev/null @@ -1,9 +0,0 @@ -declare function f(a: string | number, b: string | number): number; - -declare module 'javascript-natural-sort' { - export default f; -} - -declare module f { - var insensitive: boolean; -} diff --git a/src/utils/__tests__/get-sorted-import-specifiers.spec.ts b/src/utils/__tests__/get-sorted-import-specifiers.spec.ts index 40675f2..d93b077 100644 --- a/src/utils/__tests__/get-sorted-import-specifiers.spec.ts +++ b/src/utils/__tests__/get-sorted-import-specifiers.spec.ts @@ -1,11 +1,18 @@ +import type { NaturalSortOptions } from '../../natural-sort'; import { getImportNodes } from '../get-import-nodes'; import { getSortedImportSpecifiers } from '../get-sorted-import-specifiers'; import { getSortedNodesModulesNames } from '../get-sorted-nodes-modules-names'; -test('should return correct sorted nodes', () => { +const defaultSortOptions: NaturalSortOptions = { + importOrderCaseInsensitive: false, +}; +it('should return correct sorted nodes', () => { const code = `import { filter, reduce, eventHandler } from '@server/z';`; const [importNode] = getImportNodes(code); - const sortedImportSpecifiers = getSortedImportSpecifiers(importNode); + const sortedImportSpecifiers = getSortedImportSpecifiers( + importNode, + defaultSortOptions, + ); const specifiersList = getSortedNodesModulesNames( sortedImportSpecifiers.specifiers, ); @@ -13,10 +20,13 @@ test('should return correct sorted nodes', () => { expect(specifiersList).toEqual(['eventHandler', 'filter', 'reduce']); }); -test('should return correct sorted nodes with default import', () => { +it('should return correct sorted nodes with default import', () => { const code = `import Component, { filter, reduce, eventHandler } from '@server/z';`; const [importNode] = getImportNodes(code); - const sortedImportSpecifiers = getSortedImportSpecifiers(importNode); + const sortedImportSpecifiers = getSortedImportSpecifiers( + importNode, + defaultSortOptions, + ); const specifiersList = getSortedNodesModulesNames( sortedImportSpecifiers.specifiers, ); diff --git a/src/utils/get-sorted-import-specifiers.ts b/src/utils/get-sorted-import-specifiers.ts index 3897b78..73fa875 100644 --- a/src/utils/get-sorted-import-specifiers.ts +++ b/src/utils/get-sorted-import-specifiers.ts @@ -1,19 +1,22 @@ import { ImportDeclaration } from '@babel/types'; -import { naturalSort } from '../natural-sort'; +import { NaturalSortOptions, naturalSort } from '../natural-sort'; /** * This function returns import nodes with alphabetically sorted module * specifiers * @param node Import declaration node */ -export const getSortedImportSpecifiers = (node: ImportDeclaration) => { +export const getSortedImportSpecifiers = ( + node: ImportDeclaration, + options: NaturalSortOptions, +) => { node.specifiers.sort((a, b) => { if (a.type !== b.type) { return a.type === 'ImportDefaultSpecifier' ? -1 : 1; } - return naturalSort(a.local.name, b.local.name); + return naturalSort(a.local.name, b.local.name, options); }); return node; }; diff --git a/src/utils/get-sorted-nodes-by-import-order.ts b/src/utils/get-sorted-nodes-by-import-order.ts index 30d2d5f..61c187a 100644 --- a/src/utils/get-sorted-nodes-by-import-order.ts +++ b/src/utils/get-sorted-nodes-by-import-order.ts @@ -5,7 +5,6 @@ import { THIRD_PARTY_MODULES_SPECIAL_WORD, newLineNode, } from '../constants'; -import { naturalSort } from '../natural-sort'; import { GetSortedNodes, ImportGroups, ImportOrLine } from '../types'; import { getImportNodesMatchedGroup } from './get-import-nodes-matched-group'; import { getSortedImportSpecifiers } from './get-sorted-import-specifiers'; @@ -19,10 +18,9 @@ import { getSortedNodesGroup } from './get-sorted-nodes-group'; * @param options Options to influence the behavior of the sorting algorithm. */ export const getSortedNodesByImportOrder: GetSortedNodes = (nodes, options) => { - naturalSort.insensitive = options.importOrderCaseInsensitive; - let { importOrder } = options; const { + importOrderCaseInsensitive, importOrderSeparation, importOrderSortSpecifiers, importOrderGroupNamespaceSpecifiers, @@ -84,12 +82,13 @@ export const getSortedNodesByImportOrder: GetSortedNodes = (nodes, options) => { const sortedInsideGroup = getSortedNodesGroup(groupNodes, { importOrderGroupNamespaceSpecifiers, + importOrderCaseInsensitive, }); // Sort the import specifiers if (importOrderSortSpecifiers) { sortedInsideGroup.forEach((node) => - getSortedImportSpecifiers(node), + getSortedImportSpecifiers(node, options), ); } diff --git a/src/utils/get-sorted-nodes-group.ts b/src/utils/get-sorted-nodes-group.ts index 8995133..3469e29 100644 --- a/src/utils/get-sorted-nodes-group.ts +++ b/src/utils/get-sorted-nodes-group.ts @@ -1,11 +1,14 @@ -import { Import, ImportDeclaration } from '@babel/types'; +import type { ImportDeclaration } from '@babel/types'; import { naturalSort } from '../natural-sort'; -import { PrettierOptions } from '../types'; +import type { PrettierOptions } from '../types'; export const getSortedNodesGroup = ( imports: ImportDeclaration[], - options: Pick, + options: Pick< + PrettierOptions, + 'importOrderGroupNamespaceSpecifiers' | 'importOrderCaseInsensitive' + >, ) => { return imports.sort((a, b) => { if (options.importOrderGroupNamespaceSpecifiers) { @@ -13,7 +16,7 @@ export const getSortedNodesGroup = ( if (diff !== 0) return diff; } - return naturalSort(a.source.value, b.source.value); + return naturalSort(a.source.value, b.source.value, options); }); }; diff --git a/yarn.lock b/yarn.lock index d3a4f30..a962d45 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1393,11 +1393,6 @@ istanbul-reports@^3.1.3: html-escaper "^2.0.0" istanbul-lib-report "^3.0.0" -javascript-natural-sort@0.7.1: - version "0.7.1" - resolved "https://registry.yarnpkg.com/javascript-natural-sort/-/javascript-natural-sort-0.7.1.tgz#f9e2303d4507f6d74355a73664d1440fb5a0ef59" - integrity sha1-+eIwPUUH9tdDVac2ZNFED7Wg71k= - jest-changed-files@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-27.5.1.tgz#a348aed00ec9bf671cc58a66fcbe7c3dfd6a68f5"