Skip to content

Commit c0d00b2

Browse files
committed
Allow customizing where type imports go
1 parent 9d89748 commit c0d00b2

11 files changed

+392
-4
lines changed

README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ Easy autofixable import sorting.
55
- ✅️ Runs via `eslint --fix` – no new tooling
66
- ✅️ Also sorts exports where possible
77
- ✅️ Handles comments
8-
- ✅️ Handles [Flow type imports] \(via [babel-eslint])
8+
- ✅️ Handles type imports/exports
99
- ✅️ [TypeScript] friendly \(via [@typescript-eslint/parser])
1010
- ✅️ [Prettier] friendly
1111
- ✅️ [eslint-plugin-import] friendly
@@ -16,9 +16,7 @@ Easy autofixable import sorting.
1616
This is for those who use `eslint --fix` (autofix) a lot and want to completely forget about sorting imports!
1717

1818
[@typescript-eslint/parser]: https://github.com/typescript-eslint/typescript-eslint/tree/master/packages/parser
19-
[babel-eslint]: https://github.com/babel/babel-eslint
2019
[eslint-plugin-import]: https://github.com/benmosher/eslint-plugin-import/
21-
[flow type imports]: https://flow.org/en/docs/types/modules/
2220
[no-require]: https://github.com/lydell/eslint-plugin-simple-import-sort/#does-it-support-require
2321
[prettier]: https://prettier.io/
2422
[typescript]: https://www.typescriptlang.org/
@@ -257,7 +255,7 @@ In other words, the imports/exports within groups are sorted alphabetically, cas
257255

258256
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).)
259257

260-
If both `import type` _and_ regular imports are used for the same source, the type imports come first. Same thing for `export type`.
258+
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].)
261259

262260
### Example
263261

@@ -397,6 +395,8 @@ Imports that don’t match any regex are grouped together last.
397395

398396
Side effect imports have `\u0000` prepended to their `from` string. You can match them with `"^\\u0000"`.
399397

398+
Type imports have `\u0000` appended to their `from` string. You can match them with `"\\u0000$"` – but you probably need more than that to avoid them also being matched by other groups.
399+
400400
The inner arrays are joined with one newline; the outer arrays are joined with two (creating a blank line).
401401

402402
Every group is sorted internally as mentioned in [Sort order]. Side effect imports are always placed first in the group and keep their internal order. It’s recommended to keep side effect imports in their own group.

examples/.eslintrc.js

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,108 @@ module.exports = {
118118
],
119119
},
120120
},
121+
{
122+
files: ["groups.type-imports-first.ts"],
123+
parser: "@typescript-eslint/parser",
124+
rules: {
125+
imports: [
126+
"error",
127+
{
128+
// The default grouping, but with type imports first as a separate group.
129+
groups: [["\u0000$"], ["^\\u0000"], ["^@?\\w"], ["^"], ["^\\."]],
130+
},
131+
],
132+
},
133+
},
134+
{
135+
files: ["groups.type-imports-last.ts"],
136+
parser: "@typescript-eslint/parser",
137+
rules: {
138+
imports: [
139+
"error",
140+
{
141+
// The default grouping, but with type imports last as a separate group.
142+
groups: [["^\\u0000"], ["^@?\\w"], ["^"], ["^\\."], ["^.+\u0000$"]],
143+
},
144+
],
145+
},
146+
},
147+
{
148+
files: ["groups.type-imports-first-sorted.ts"],
149+
parser: "@typescript-eslint/parser",
150+
rules: {
151+
imports: [
152+
"error",
153+
{
154+
// The default grouping, but with type imports first as a separate
155+
// group, sorting that group like non-type imports are grouped.
156+
groups: [
157+
["^@?\\w.*\\u0000$", "\\u0000$", "^\\..*\\u0000$"],
158+
["^\\u0000"],
159+
["^@?\\w"],
160+
["^"],
161+
["^\\."],
162+
],
163+
},
164+
],
165+
},
166+
},
167+
{
168+
files: ["groups.type-imports-last-sorted.ts"],
169+
parser: "@typescript-eslint/parser",
170+
rules: {
171+
imports: [
172+
"error",
173+
{
174+
// The default grouping, but with type imports last as a separate
175+
// group, sorting that group like non-type imports are grouped.
176+
groups: [
177+
["^\\u0000"],
178+
["^@?\\w"],
179+
["^"],
180+
["^\\."],
181+
["^@?\\w.*\\u0000$", "^[^.].*\\u0000$", "^\\..*\\u0000$"],
182+
],
183+
},
184+
],
185+
},
186+
},
187+
{
188+
files: ["groups.type-imports-first-in-each-group.ts"],
189+
parser: "@typescript-eslint/parser",
190+
rules: {
191+
imports: [
192+
"error",
193+
{
194+
// The default grouping, but with type imports first in each group.
195+
groups: [
196+
["^\\u0000"],
197+
["^@?\\w.*\\u0000$", "^@?\\w"],
198+
["(?<=\\u0000)$", "^"],
199+
["^\\..*\\u0000$", "^\\."],
200+
],
201+
},
202+
],
203+
},
204+
},
205+
{
206+
files: ["groups.type-imports-last-in-each-group.ts"],
207+
parser: "@typescript-eslint/parser",
208+
rules: {
209+
imports: [
210+
"error",
211+
{
212+
// The default grouping, but with type imports last in each group.
213+
groups: [
214+
["^\\u0000"],
215+
["^@?\\w", "^@?\\w.*\\u0000$"],
216+
["(?<!\\u0000)$", "(?<=\\u0000)$"],
217+
["^\\.", "^\\..*\\u0000$"],
218+
],
219+
},
220+
],
221+
},
222+
},
121223
{
122224
files: ["groups.none.js"],
123225
rules: {
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import "./polyfills";
2+
import react from "react";
3+
import type { Component } from "react";
4+
import type { Store } from "redux";
5+
import type { Story } from "@storybook/react";
6+
import { storiesOf } from "@storybook/react";
7+
import type { AppRouter } from "@/App";
8+
import App from "@/App";
9+
import type { Page } from "./page";
10+
import page from "./page";
11+
import type { Css } from "./styles";
12+
import styles from "./styles";
13+
import config from "/config";
14+
import type { Config } from "/config";
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import "./polyfills";
2+
import react from "react";
3+
import type { Component } from "react";
4+
import type { Store } from "redux";
5+
import type { Story } from "@storybook/react";
6+
import { storiesOf } from "@storybook/react";
7+
import type { AppRouter } from "@/App";
8+
import App from "@/App";
9+
import type { Page } from "./page";
10+
import page from "./page";
11+
import type { Css } from "./styles";
12+
import styles from "./styles";
13+
import config from "/config";
14+
import type { Config } from "/config";

examples/groups.type-imports-first.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import "./polyfills";
2+
import react from "react";
3+
import type { Component } from "react";
4+
import type { Store } from "redux";
5+
import type { Story } from "@storybook/react";
6+
import { storiesOf } from "@storybook/react";
7+
import type { AppRouter } from "@/App";
8+
import App from "@/App";
9+
import type { Page } from "./page";
10+
import page from "./page";
11+
import type { Css } from "./styles";
12+
import styles from "./styles";
13+
import config from "/config";
14+
import type { Config } from "/config";
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import "./polyfills";
2+
import react from "react";
3+
import type { Component } from "react";
4+
import type { Store } from "redux";
5+
import type { Story } from "@storybook/react";
6+
import { storiesOf } from "@storybook/react";
7+
import type { AppRouter } from "@/App";
8+
import App from "@/App";
9+
import type { Page } from "./page";
10+
import page from "./page";
11+
import type { Css } from "./styles";
12+
import styles from "./styles";
13+
import config from "/config";
14+
import type { Config } from "/config";
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import "./polyfills";
2+
import react from "react";
3+
import type { Component } from "react";
4+
import type { Store } from "redux";
5+
import type { Story } from "@storybook/react";
6+
import { storiesOf } from "@storybook/react";
7+
import type { AppRouter } from "@/App";
8+
import App from "@/App";
9+
import type { Page } from "./page";
10+
import page from "./page";
11+
import type { Css } from "./styles";
12+
import styles from "./styles";
13+
import config from "/config";
14+
import type { Config } from "/config";

examples/groups.type-imports-last.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import "./polyfills";
2+
import react from "react";
3+
import type { Component } from "react";
4+
import type { Store } from "redux";
5+
import type { Story } from "@storybook/react";
6+
import { storiesOf } from "@storybook/react";
7+
import type { AppRouter } from "@/App";
8+
import App from "@/App";
9+
import type { Page } from "./page";
10+
import page from "./page";
11+
import type { Css } from "./styles";
12+
import styles from "./styles";
13+
import config from "/config";
14+
import type { Config } from "/config";

src/imports.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,8 @@ function makeSortedItems(items, outerGroups) {
8787
const { originalSource } = item.source;
8888
const source = item.isSideEffectImport
8989
? `\0${originalSource}`
90+
: item.source.kind !== "value"
91+
? `${originalSource}\0`
9092
: originalSource;
9193
const [matchedGroup] = shared
9294
.flatMap(itemGroups, (groups) =>

test/__snapshots__/examples.test.js.snap

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,136 @@ import react from "react";
135135
136136
`;
137137

138+
exports[`examples groups.type-imports-first.ts 1`] = `
139+
import type { Page } from "./page";
140+
import type { Css } from "./styles";
141+
import type { Config } from "/config";
142+
import type { AppRouter } from "@/App";
143+
import type { Component } from "react";
144+
import type { Store } from "redux";
145+
146+
import "./polyfills";
147+
148+
import type { Story } from "@storybook/react";
149+
import { storiesOf } from "@storybook/react";
150+
import react from "react";
151+
152+
import config from "/config";
153+
import App from "@/App";
154+
155+
import page from "./page";
156+
import styles from "./styles";
157+
158+
`;
159+
160+
exports[`examples groups.type-imports-first-in-each-group.ts 1`] = `
161+
import "./polyfills";
162+
163+
import type { Story } from "@storybook/react";
164+
import type { Component } from "react";
165+
import type { Store } from "redux";
166+
import { storiesOf } from "@storybook/react";
167+
import react from "react";
168+
169+
import type { Config } from "/config";
170+
import type { AppRouter } from "@/App";
171+
import config from "/config";
172+
import App from "@/App";
173+
174+
import type { Page } from "./page";
175+
import type { Css } from "./styles";
176+
import page from "./page";
177+
import styles from "./styles";
178+
179+
`;
180+
181+
exports[`examples groups.type-imports-first-sorted.ts 1`] = `
182+
import type { Story } from "@storybook/react";
183+
import type { Component } from "react";
184+
import type { Store } from "redux";
185+
import type { Config } from "/config";
186+
import type { AppRouter } from "@/App";
187+
import type { Page } from "./page";
188+
import type { Css } from "./styles";
189+
190+
import "./polyfills";
191+
192+
import { storiesOf } from "@storybook/react";
193+
import react from "react";
194+
195+
import config from "/config";
196+
import App from "@/App";
197+
198+
import page from "./page";
199+
import styles from "./styles";
200+
201+
`;
202+
203+
exports[`examples groups.type-imports-last.ts 1`] = `
204+
import "./polyfills";
205+
206+
import { storiesOf } from "@storybook/react";
207+
import react from "react";
208+
209+
import config from "/config";
210+
import App from "@/App";
211+
212+
import page from "./page";
213+
import styles from "./styles";
214+
215+
import type { Page } from "./page";
216+
import type { Css } from "./styles";
217+
import type { Config } from "/config";
218+
import type { AppRouter } from "@/App";
219+
import type { Story } from "@storybook/react";
220+
import type { Component } from "react";
221+
import type { Store } from "redux";
222+
223+
`;
224+
225+
exports[`examples groups.type-imports-last-in-each-group.ts 1`] = `
226+
import "./polyfills";
227+
228+
import { storiesOf } from "@storybook/react";
229+
import react from "react";
230+
import type { Story } from "@storybook/react";
231+
import type { Component } from "react";
232+
import type { Store } from "redux";
233+
234+
import config from "/config";
235+
import App from "@/App";
236+
import type { Config } from "/config";
237+
import type { AppRouter } from "@/App";
238+
239+
import page from "./page";
240+
import styles from "./styles";
241+
import type { Page } from "./page";
242+
import type { Css } from "./styles";
243+
244+
`;
245+
246+
exports[`examples groups.type-imports-last-sorted.ts 1`] = `
247+
import "./polyfills";
248+
249+
import { storiesOf } from "@storybook/react";
250+
import react from "react";
251+
252+
import config from "/config";
253+
import App from "@/App";
254+
255+
import page from "./page";
256+
import styles from "./styles";
257+
258+
import type { Story } from "@storybook/react";
259+
import type { Component } from "react";
260+
import type { Store } from "redux";
261+
import type { Config } from "/config";
262+
import type { AppRouter } from "@/App";
263+
import type { Page } from "./page";
264+
import type { Css } from "./styles";
265+
266+
`;
267+
138268
exports[`examples ignore.js 1`] = `
139269
// First off, imports that are only used for side effects stay in the input
140270
// order. These won’t be sorted:

0 commit comments

Comments
 (0)