Skip to content

Commit

Permalink
Fix directory structure sort order
Browse files Browse the repository at this point in the history
Closes #50
  • Loading branch information
lydell committed Nov 7, 2020
1 parent 52ee218 commit c439402
Show file tree
Hide file tree
Showing 5 changed files with 75 additions and 22 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ function compare(a, b) {

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.

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 `"../"`.
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).)

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

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

// Absolute imports and other imports.
import Error from "@/components/error.vue";
import c from "/";
import d from "/home/user/foo";
import Error from "@/components/error.vue";

// Relative imports.
import e from "../..";
Expand Down
2 changes: 1 addition & 1 deletion examples/readme-order.prettier.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ import fs from "fs";
import b from "https://example.com/script.js";

// Absolute imports and other imports.
import Error from "@/components/error.vue"
import c from "/";
import d from "/home/user/foo";
import Error from "@/components/error.vue"

// Relative imports.
import e from "../..";
Expand Down
31 changes: 22 additions & 9 deletions src/sort.js
Original file line number Diff line number Diff line change
Expand Up @@ -874,15 +874,28 @@ function getSource(importNode) {
const source = importNode.source.value;

return {
source:
// Due to "." sorting before "/" by default, relative imports are
// automatically sorted in a logical manner for us: Imports from files
// further up come first, with deeper imports last. There’s one
// exception, though: When the `from` part ends with one or two dots:
// "." and "..". Those are supposed to sort just like "./", "../". So
// add in the slash for them. (No special handling is done for cases
// like "./a/.." because nobody writes that anyway.)
source === "." || source === ".." ? `${source}/` : source,
// Sort by directory level rather than by string length.
source: source
// Treat `.` as `./`, `..` as `../`, `../..` as `../../` etc.
.replace(/^[./]*\.$/, "$&/")
// Make `../` sort after `../../` but before `../a` etc.
// Why a comma? See the next comment.
.replace(/^[./]*\/$/, "$&,")
// Make `.` and `/` sort before any other punctation.
// The default order is: _ - , x x x . x x x / x x x
// We’re changing it to: . / , x x x _ x x x - x x x
.replace(/[./_-]/g, (char) => {
switch (char) {
case ".":
return "_";
case "/":
return "-";
case "_":
return ".";
case "-":
return "/";
}
}),
originalSource: source,
importKind: getImportKind(importNode),
};
Expand Down
6 changes: 3 additions & 3 deletions test/__snapshots__/examples.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -104,8 +104,8 @@ import styles from "./styles.scss";
exports[`examples groups.default-reverse.js 1`] = `
import styles from "./styles";
import App from "@/App";
import config from "/config";
import App from "@/App";
import { storiesOf } from "@storybook/react";
import react from "react";
Expand All @@ -128,9 +128,9 @@ import styles from "./styles.css";

exports[`examples groups.none.js 1`] = `
import styles from "./styles";
import config from "/config";
import App from "@/App";
import { storiesOf } from "@storybook/react";
import config from "/config";
import react from "react";
`;
Expand Down Expand Up @@ -293,9 +293,9 @@ import fs from "fs";
import b from "https://example.com/script.js";
// Absolute imports and other imports.
import Error from "@/components/error.vue";
import c from "/";
import d from "/home/user/foo";
import Error from "@/components/error.vue";
// Relative imports.
import e from "../..";
Expand Down
54 changes: 47 additions & 7 deletions test/sort.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -865,18 +865,38 @@ const baseTests = (expect) => ({
|import {} from "./B"; // B2
|import {} from "./A";
|import {} from "./a";
|import {} from "./_a";
|import {} from "./-a";
|import {} from "./[id]";
|import {} from "./,";
|import {} from "./ä";
|import {} from "./ä"; // “a” followed by “\u0308̈” (COMBINING DIAERESIS).
|import {} from "..";
|import {} from "../";
|import {} from "../a";
|import {} from "../_a";
|import {} from "../-a";
|import {} from "../[id]";
|import {} from "../,";
|import {} from "../a/..";
|import {} from "../a/../";
|import {} from "../a/...";
|import {} from "../a/../b";
|import {} from "../../";
|import {} from "../..";
|import {} from "../../a";
|import {} from "../../_a";
|import {} from "../../-a";
|import {} from "../../[id]";
|import {} from "../../,";
|import {} from "../../utils";
|import {} from "../../..";
|import {} from "../../../";
|import {} from "../../../a";
|import {} from "../../../_a";
|import {} from "../../../[id]";
|import {} from "../../../,";
|import {} from "../../../utils";
|import {} from "...";
|import {} from ".../";
|import {} from ".a";
Expand Down Expand Up @@ -924,35 +944,55 @@ const baseTests = (expect) => ({
|import {} from "react";
|
|import {} from "";
|import {} from "@/components/Alert"
|import {} from "@/components/error.vue"
|import {} from "/";
|import {} from "/a";
|import {} from "/a/b";
|import {} from "@/components/Alert"
|import {} from "@/components/error.vue"
|import {} from "#/test"
|import {} from "~/test"
|
|import {} from "...";
|import {} from ".../";
|import {} from "..";
|import {} from "../";
|import {} from "../../..";
|import {} from "../../../";
|import {} from "../../../,";
|import {} from "../../../_a";
|import {} from "../../../[id]";
|import {} from "../../../a";
|import {} from "../../../utils";
|import {} from "../..";
|import {} from "../../";
|import {} from "../../,";
|import {} from "../../_a";
|import {} from "../../[id]";
|import {} from "../../-a";
|import {} from "../../a";
|import {} from "../../utils";
|import {} from "..";
|import {} from "../";
|import {} from "../,";
|import {} from "../_a";
|import {} from "../[id]";
|import {} from "../-a";
|import {} from "../a";
|import {} from "../a/..";
|import {} from "../a/...";
|import {} from "../a/../";
|import {} from "../a/../b";
|import {} from ".//";
|import {} from ".";
|import {} from "./";
|import {} from ".//";
|import {} from "./,";
|import {} from "./_a";
|import {} from "./[id]";
|import {} from "./-a";
|import {} from "./A";
|import {} from "./a";
|import {} from "./ä"; // “a” followed by “̈̈” (COMBINING DIAERESIS).
|import {} from "./ä";
|import {} from "./a/-";
|import {} from "./a/.";
|import {} from "./a/-";
|import {} from "./a/0";
|import {} from "./B"; // B1
|import {} from "./B"; // B2
Expand Down Expand Up @@ -1662,8 +1702,8 @@ const flowTests = {
|import type {X} from "X";
|import type {Z} from "Z";
|
|import type E from "@/B";
|import type C from "/B";
|import type E from "@/B";
|
|import type B from "./B";
|import typeof D from "./D";
Expand Down

0 comments on commit c439402

Please sign in to comment.