Skip to content

Commit

Permalink
Feature: drop javascript-natural-sort dep
Browse files Browse the repository at this point in the history
Minor problem: localeCompare appears to always interleave capital & lowercase letters.

I don't know how to do both numeric-sort AND case-sensitive at the same time using pure locale-compare.
  • Loading branch information
fbartho committed May 29, 2022
1 parent 3b72a60 commit 5f07e19
Show file tree
Hide file tree
Showing 9 changed files with 109 additions and 32 deletions.
1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
},
Expand Down
54 changes: 54 additions & 0 deletions src/natural-sort/__tests__/natural-sort.spec.ts
Original file line number Diff line number Diff line change
@@ -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']);
});
27 changes: 25 additions & 2 deletions src/natural-sort/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,26 @@
import naturalSort from 'javascript-natural-sort';
import type { PrettierOptions } from '../types';

export { naturalSort };
export type NaturalSortOptions = Partial<
Pick<PrettierOptions, 'importOrderCaseInsensitive'>
>;

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);
}
9 changes: 0 additions & 9 deletions src/natural-sort/natural-sort.d.ts

This file was deleted.

18 changes: 14 additions & 4 deletions src/utils/__tests__/get-sorted-import-specifiers.spec.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,32 @@
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,
);

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,
);
Expand Down
9 changes: 6 additions & 3 deletions src/utils/get-sorted-import-specifiers.ts
Original file line number Diff line number Diff line change
@@ -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;
};
7 changes: 3 additions & 4 deletions src/utils/get-sorted-nodes-by-import-order.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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,
Expand Down Expand Up @@ -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),
);
}

Expand Down
11 changes: 7 additions & 4 deletions src/utils/get-sorted-nodes-group.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,22 @@
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<PrettierOptions, 'importOrderGroupNamespaceSpecifiers'>,
options: Pick<
PrettierOptions,
'importOrderGroupNamespaceSpecifiers' | 'importOrderCaseInsensitive'
>,
) => {
return imports.sort((a, b) => {
if (options.importOrderGroupNamespaceSpecifiers) {
const diff = namespaceSpecifierSort(a, b);
if (diff !== 0) return diff;
}

return naturalSort(a.source.value, b.source.value);
return naturalSort(a.source.value, b.source.value, options);
});
};

Expand Down
5 changes: 0 additions & 5 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down

0 comments on commit 5f07e19

Please sign in to comment.