Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP new approach for sorting by ../ #129

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
8 changes: 4 additions & 4 deletions README.md
Expand Up @@ -218,7 +218,7 @@ function compare(a, b) {

In other words, the imports/exports 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. Note: `Intl.Collator` sorts punctuation in _some_ defined order. I have no idea what order punctuation sorts in, and I don’t care. There’s no ordered “alphabet” for punctuation that I know of.

There’s one addition to the alphabetical rule: Directory structure. Relative imports/exports 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).)
There’s one addition to the alphabetical rule: Directory structure. Relative imports/exports of files higher up in the directory structure come before closer ones – `"../../utils"` comes before `"../utils"`, which comes before `"."`. Note: No path or URL normalization is performed – the plugin simply looks for how many times `../` is repeated at the start of the `from` string. If you put a `./` in there (like `.././../` or `./../../`) then the directory structure sorting won’t work. Simply don’t do that. Also, you _have_ to use forward slashes – backslashes are not supported.

If both `import type` _and_ regular imports are used for the same source, the type imports come first. Same thing for `export type`. (You can move type imports to their own group, as mentioned in [custom grouping].)

Expand All @@ -241,9 +241,9 @@ import fs2 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 All @@ -255,8 +255,8 @@ import i from "./styles";

// Different types of exports:
export { a } from "../..";
export { b } from "/";
export { Error } from "@/components/error.vue";
export { b } from "/";
export * from "an-npm-package";
export { readFile } from "fs";
export * as ns from "https://example.com/script.js";
Expand Down Expand Up @@ -655,7 +655,7 @@ Some other differences:

The first question to ask yourself is if dprint is good enough. If so, you’ve got one tool less to worry about!

If you’d like to enforce grouping, though, you could still use `eslint-plugin-simple-import-sort`. However, the two might disagree slightly on some sorting edge cases. So it’s better to turn off sorting in your dprint config file:
If you’d like to enforce grouping, though, you could still use `eslint-plugin-simple-import-sort`. There is a risk that the two might disagree slightly on some sorting edge cases. So might want to turn off sorting in your dprint config file:

```json
{
Expand Down
4 changes: 2 additions & 2 deletions examples/readme-order.prettier.ts
Expand Up @@ -13,9 +13,9 @@ import fs2 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 All @@ -27,8 +27,8 @@ import i from "./styles";

// Different types of exports:
export { a } from "../..";
export { b } from "/";
export { Error } from "@/components/error.vue";
export { b } from "/";
export * from "an-npm-package";
export { readFile } from "fs";
export * as ns from "https://example.com/script.js";
Expand Down
29 changes: 5 additions & 24 deletions src/shared.js
Expand Up @@ -798,32 +798,13 @@ function isNewline(node) {
function getSource(node) {
const source = node.source.value;

const match = /^(?:\.\.\/)*\.\.(?=\/|$)/.exec(source);
const level =
Number.MAX_SAFE_INTEGER - (match === null ? 0 : match[0].length);

return {
// 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 "/";
// istanbul ignore next
default:
throw new Error(`Unknown source substitution character: ${char}`);
}
}),
source: `${level}:${source}`,
originalSource: source,
kind: getImportExportKind(node),
};
Expand Down
32 changes: 16 additions & 16 deletions test/__snapshots__/examples.test.js.snap
Expand Up @@ -104,8 +104,8 @@ import styles from "./styles.scss";
exports[`examples groups.default-reverse.js 1`] = `
import styles from "./styles";

import config from "/config";
import App from "@/App";
import config from "/config";

import { storiesOf } from "@storybook/react";
import fs2 from "fs";
Expand All @@ -132,9 +132,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 * as fs from "node:fs";
import react from "react";

Expand All @@ -143,9 +143,9 @@ import react from "react";
exports[`examples groups.type-imports-first.ts 1`] = `
import type { Page } from "./page";
import type { Css } from "./styles";
import type { Config } from "/config";
import type { AppRouter } from "@/App";
import type { Story } from "@storybook/react";
import type { Config } from "/config";
import type { Dirent } from "node:fs";
import type { ParsedPath } from "node:path";
import type { Component } from "react";
Expand All @@ -159,8 +159,8 @@ import * as path from "node:path";
import { storiesOf } from "@storybook/react";
import react from "react";

import config from "/config";
import App from "@/App";
import config from "/config";

import page from "./page";
import styles from "./styles";
Expand All @@ -181,10 +181,10 @@ import type { Store } from "redux";
import { storiesOf } from "@storybook/react";
import react from "react";

import type { Config } from "/config";
import type { AppRouter } from "@/App";
import config from "/config";
import type { Config } from "/config";
import App from "@/App";
import config from "/config";

import type { Page } from "./page";
import type { Css } from "./styles";
Expand All @@ -199,8 +199,8 @@ import type { ParsedPath } from "node:path";
import type { Story } from "@storybook/react";
import type { Component } from "react";
import type { Store } from "redux";
import type { Config } from "/config";
import type { AppRouter } from "@/App";
import type { Config } from "/config";
import type { Page } from "./page";
import type { Css } from "./styles";

Expand All @@ -212,8 +212,8 @@ import * as path from "node:path";
import { storiesOf } from "@storybook/react";
import react from "react";

import config from "/config";
import App from "@/App";
import config from "/config";

import page from "./page";
import styles from "./styles";
Expand All @@ -229,17 +229,17 @@ import * as path from "node:path";
import { storiesOf } from "@storybook/react";
import react from "react";

import config from "/config";
import App from "@/App";
import config from "/config";

import page from "./page";
import styles from "./styles";

import type { Page } from "./page";
import type { Css } from "./styles";
import type { Config } from "/config";
import type { AppRouter } from "@/App";
import type { Story } from "@storybook/react";
import type { Config } from "/config";
import type { Dirent } from "node:fs";
import type { ParsedPath } from "node:path";
import type { Component } from "react";
Expand All @@ -261,10 +261,10 @@ import type { Story } from "@storybook/react";
import type { Component } from "react";
import type { Store } from "redux";

import config from "/config";
import App from "@/App";
import type { Config } from "/config";
import config from "/config";
import type { AppRouter } from "@/App";
import type { Config } from "/config";

import page from "./page";
import styles from "./styles";
Expand All @@ -282,8 +282,8 @@ import * as path from "node:path";
import { storiesOf } from "@storybook/react";
import react from "react";

import config from "/config";
import App from "@/App";
import config from "/config";

import page from "./page";
import styles from "./styles";
Expand All @@ -293,8 +293,8 @@ import type { ParsedPath } from "node:path";
import type { Story } from "@storybook/react";
import type { Component } from "react";
import type { Store } from "redux";
import type { Config } from "/config";
import type { AppRouter } from "@/App";
import type { Config } from "/config";
import type { Page } from "./page";
import type { Css } from "./styles";

Expand Down Expand Up @@ -484,9 +484,9 @@ import fs2 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 All @@ -498,8 +498,8 @@ import i from "./styles";

// Different types of exports:
export { a } from "../..";
export { b } from "/";
export { Error } from "@/components/error.vue";
export { b } from "/";
export * from "an-npm-package";
export { readFile } from "fs";
export * as ns from "https://example.com/script.js";
Expand Down
4 changes: 2 additions & 2 deletions test/exports.test.js
Expand Up @@ -815,8 +815,8 @@ const flowTests = {
|export type {Z} from "Z";
|export type Y = 5;
|export type {B} from "./B";
|export type {C} from "/B";
|export type {E} from "@/B";
|export type {C} from "/B";
|export type {X} from "X";
`);
},
Expand Down Expand Up @@ -1099,8 +1099,8 @@ const typescriptTests = {
|export type Y = 5;
|export {a, type type as type, z} from "../type";
|export type {B} from "./B";
|export type {C} from "/B";
|export type {E} from "@/B";
|export type {C} from "/B";
|export type {X} from "X";
`);
},
Expand Down
52 changes: 38 additions & 14 deletions test/imports.test.js
Expand Up @@ -874,55 +874,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 "../../../_a";
|import {} from "../../../,";
|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 "../../,";
|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 "../,";
|import {} from "../[id]";
|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 "./,";
|import {} from "./_a";
|import {} from "./[id]";
|import {} from "./-a";
|import {} from "./,";
|import {} from "./[id]";
|import {} from ".//";
|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 @@ -1570,6 +1570,30 @@ const baseTests = (expect) => ({
},
errors: 1,
},

// groups – all in one group.
{
options: [{ groups: [] }],
code: input`
|import styles from "./styles";
|import App from "@/App";
|import { storiesOf } from "@storybook/react";
|import config from "/config";
|import react from "react";
|import * as fs from "node:fs";
`,
output: (actual) => {
expect(actual).toMatchInlineSnapshot(`
|import styles from "./styles";
|import App from "@/App";
|import { storiesOf } from "@storybook/react";
|import config from "/config";
|import * as fs from "node:fs";
|import react from "react";
`);
},
errors: 1,
},
],
});

Expand Down Expand Up @@ -1632,8 +1656,8 @@ const flowTests = {
|import type {X} from "X";
|import type {Z} from "Z";
|
|import type C from "/B";
|import type E from "@/B";
|import type C from "/B";
|
|import type B from "./B";
|import typeof D from "./D";
Expand Down