Skip to content

Commit c439402

Browse files
committed
Fix directory structure sort order
Closes #50
1 parent 52ee218 commit c439402

File tree

5 files changed

+75
-22
lines changed

5 files changed

+75
-22
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,7 @@ function compare(a, b) {
216216

217217
In other words, the imports within groups are sorted alphabetically, case-insensitively and treating numbers like a human would, falling back to good old character code sorting in case of ties. See [Intl.Collator] for more information.
218218

219-
Since “.” sorts before “/”, relative imports of files higher up in the directory structure come before closer ones – `"../../utils"` comes before `"../utils"`. Perhaps surprisingly though, `".."` would come before `"../../utils"` (since shorter substrings sort before longer strings). For that reason there’s one addition to the alphabetical rule: `"."` and `".."` are treated as `"./"` and `"../"`.
219+
There’s one addition to the alphabetical rule: Directory structure. Relative imports of files higher up in the directory structure come before closer ones – `"../../utils"` comes before `"../utils"`, which comes before `".."`. (In short, `.` and `/` sort before any other (non-whitespace, non-control) character. `".."` and similar sort like `"../,"` (to avoid the “shorter prefix comes first” sorting concept).)
220220

221221
If both `import type` _and_ regular imports are used for the same source, the type imports come first.
222222

@@ -236,9 +236,9 @@ import fs from "fs";
236236
import b from "https://example.com/script.js";
237237

238238
// Absolute imports and other imports.
239-
import Error from "@/components/error.vue";
240239
import c from "/";
241240
import d from "/home/user/foo";
241+
import Error from "@/components/error.vue";
242242

243243
// Relative imports.
244244
import e from "../..";

examples/readme-order.prettier.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@ import fs from "fs";
1010
import b from "https://example.com/script.js";
1111

1212
// Absolute imports and other imports.
13-
import Error from "@/components/error.vue"
1413
import c from "/";
1514
import d from "/home/user/foo";
15+
import Error from "@/components/error.vue"
1616

1717
// Relative imports.
1818
import e from "../..";

src/sort.js

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -874,15 +874,28 @@ function getSource(importNode) {
874874
const source = importNode.source.value;
875875

876876
return {
877-
source:
878-
// Due to "." sorting before "/" by default, relative imports are
879-
// automatically sorted in a logical manner for us: Imports from files
880-
// further up come first, with deeper imports last. There’s one
881-
// exception, though: When the `from` part ends with one or two dots:
882-
// "." and "..". Those are supposed to sort just like "./", "../". So
883-
// add in the slash for them. (No special handling is done for cases
884-
// like "./a/.." because nobody writes that anyway.)
885-
source === "." || source === ".." ? `${source}/` : source,
877+
// Sort by directory level rather than by string length.
878+
source: source
879+
// Treat `.` as `./`, `..` as `../`, `../..` as `../../` etc.
880+
.replace(/^[./]*\.$/, "$&/")
881+
// Make `../` sort after `../../` but before `../a` etc.
882+
// Why a comma? See the next comment.
883+
.replace(/^[./]*\/$/, "$&,")
884+
// Make `.` and `/` sort before any other punctation.
885+
// The default order is: _ - , x x x . x x x / x x x
886+
// We’re changing it to: . / , x x x _ x x x - x x x
887+
.replace(/[./_-]/g, (char) => {
888+
switch (char) {
889+
case ".":
890+
return "_";
891+
case "/":
892+
return "-";
893+
case "_":
894+
return ".";
895+
case "-":
896+
return "/";
897+
}
898+
}),
886899
originalSource: source,
887900
importKind: getImportKind(importNode),
888901
};

test/__snapshots__/examples.test.js.snap

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -104,8 +104,8 @@ import styles from "./styles.scss";
104104
exports[`examples groups.default-reverse.js 1`] = `
105105
import styles from "./styles";
106106
107-
import App from "@/App";
108107
import config from "/config";
108+
import App from "@/App";
109109
110110
import { storiesOf } from "@storybook/react";
111111
import react from "react";
@@ -128,9 +128,9 @@ import styles from "./styles.css";
128128

129129
exports[`examples groups.none.js 1`] = `
130130
import styles from "./styles";
131+
import config from "/config";
131132
import App from "@/App";
132133
import { storiesOf } from "@storybook/react";
133-
import config from "/config";
134134
import react from "react";
135135
136136
`;
@@ -293,9 +293,9 @@ import fs from "fs";
293293
import b from "https://example.com/script.js";
294294
295295
// Absolute imports and other imports.
296-
import Error from "@/components/error.vue";
297296
import c from "/";
298297
import d from "/home/user/foo";
298+
import Error from "@/components/error.vue";
299299
300300
// Relative imports.
301301
import e from "../..";

test/sort.test.js

Lines changed: 47 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -865,18 +865,38 @@ const baseTests = (expect) => ({
865865
|import {} from "./B"; // B2
866866
|import {} from "./A";
867867
|import {} from "./a";
868+
|import {} from "./_a";
869+
|import {} from "./-a";
870+
|import {} from "./[id]";
871+
|import {} from "./,";
868872
|import {} from "./ä";
869873
|import {} from "./ä"; // “a” followed by “\u0308̈” (COMBINING DIAERESIS).
870874
|import {} from "..";
871875
|import {} from "../";
872876
|import {} from "../a";
877+
|import {} from "../_a";
878+
|import {} from "../-a";
879+
|import {} from "../[id]";
880+
|import {} from "../,";
873881
|import {} from "../a/..";
874882
|import {} from "../a/../";
875883
|import {} from "../a/...";
876884
|import {} from "../a/../b";
877885
|import {} from "../../";
878886
|import {} from "../..";
879887
|import {} from "../../a";
888+
|import {} from "../../_a";
889+
|import {} from "../../-a";
890+
|import {} from "../../[id]";
891+
|import {} from "../../,";
892+
|import {} from "../../utils";
893+
|import {} from "../../..";
894+
|import {} from "../../../";
895+
|import {} from "../../../a";
896+
|import {} from "../../../_a";
897+
|import {} from "../../../[id]";
898+
|import {} from "../../../,";
899+
|import {} from "../../../utils";
880900
|import {} from "...";
881901
|import {} from ".../";
882902
|import {} from ".a";
@@ -924,35 +944,55 @@ const baseTests = (expect) => ({
924944
|import {} from "react";
925945
|
926946
|import {} from "";
927-
|import {} from "@/components/Alert"
928-
|import {} from "@/components/error.vue"
929947
|import {} from "/";
930948
|import {} from "/a";
931949
|import {} from "/a/b";
950+
|import {} from "@/components/Alert"
951+
|import {} from "@/components/error.vue"
932952
|import {} from "#/test"
933953
|import {} from "~/test"
934954
|
935955
|import {} from "...";
936956
|import {} from ".../";
937-
|import {} from "..";
938-
|import {} from "../";
957+
|import {} from "../../..";
958+
|import {} from "../../../";
959+
|import {} from "../../../,";
960+
|import {} from "../../../_a";
961+
|import {} from "../../../[id]";
962+
|import {} from "../../../a";
963+
|import {} from "../../../utils";
939964
|import {} from "../..";
940965
|import {} from "../../";
966+
|import {} from "../../,";
967+
|import {} from "../../_a";
968+
|import {} from "../../[id]";
969+
|import {} from "../../-a";
941970
|import {} from "../../a";
971+
|import {} from "../../utils";
972+
|import {} from "..";
973+
|import {} from "../";
974+
|import {} from "../,";
975+
|import {} from "../_a";
976+
|import {} from "../[id]";
977+
|import {} from "../-a";
942978
|import {} from "../a";
943979
|import {} from "../a/..";
944980
|import {} from "../a/...";
945981
|import {} from "../a/../";
946982
|import {} from "../a/../b";
983+
|import {} from ".//";
947984
|import {} from ".";
948985
|import {} from "./";
949-
|import {} from ".//";
986+
|import {} from "./,";
987+
|import {} from "./_a";
988+
|import {} from "./[id]";
989+
|import {} from "./-a";
950990
|import {} from "./A";
951991
|import {} from "./a";
952992
|import {} from "./ä"; // “a” followed by “̈̈” (COMBINING DIAERESIS).
953993
|import {} from "./ä";
954-
|import {} from "./a/-";
955994
|import {} from "./a/.";
995+
|import {} from "./a/-";
956996
|import {} from "./a/0";
957997
|import {} from "./B"; // B1
958998
|import {} from "./B"; // B2
@@ -1662,8 +1702,8 @@ const flowTests = {
16621702
|import type {X} from "X";
16631703
|import type {Z} from "Z";
16641704
|
1665-
|import type E from "@/B";
16661705
|import type C from "/B";
1706+
|import type E from "@/B";
16671707
|
16681708
|import type B from "./B";
16691709
|import typeof D from "./D";

0 commit comments

Comments
 (0)