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

Allow customizing where type imports go #62

Merged
merged 3 commits into from Dec 8, 2020
Merged
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
10 changes: 5 additions & 5 deletions README.md
Expand Up @@ -5,7 +5,7 @@ Easy autofixable import sorting.
- ✅️ Runs via `eslint --fix` – no new tooling
- ✅️ Also sorts exports where possible
- ✅️ Handles comments
- ✅️ Handles [Flow type imports] \(via [babel-eslint])
- ✅️ Handles type imports/exports
- ✅️ [TypeScript] friendly \(via [@typescript-eslint/parser])
- ✅️ [Prettier] friendly
- ✅️ [eslint-plugin-import] friendly
Expand All @@ -16,9 +16,7 @@ Easy autofixable import sorting.
This is for those who use `eslint --fix` (autofix) a lot and want to completely forget about sorting imports!

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

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

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

### Example

Expand Down Expand Up @@ -395,7 +393,9 @@ Each `import` is matched against _all_ regexes on the `from` string. The import
Imports that don’t match any regex are grouped together last.

Side effect imports have `\u0000` prepended to their `from` string. You can match them with `"^\\u0000"`.
Side effect imports have `\u0000` _prepended_ to their `from` string (starts with `\u0000`). You can match them with `"^\\u0000"`.

Type imports have `\u0000` _appended_ to their `from` string (ends with `\u0000`). You can match them with `"\\u0000$"` – but you probably need more than that to avoid them also being matched by other groups.

The inner arrays are joined with one newline; the outer arrays are joined with two (creating a blank line).

Expand Down
102 changes: 102 additions & 0 deletions examples/.eslintrc.js
Expand Up @@ -118,6 +118,108 @@ module.exports = {
],
},
},
{
files: ["groups.type-imports-first.ts"],
parser: "@typescript-eslint/parser",
rules: {
imports: [
"error",
{
// The default grouping, but with type imports first as a separate group.
groups: [["^.*\\u0000$"], ["^\\u0000"], ["^@?\\w"], ["^"], ["^\\."]],
},
],
},
},
{
files: ["groups.type-imports-last.ts"],
parser: "@typescript-eslint/parser",
rules: {
imports: [
"error",
{
// The default grouping, but with type imports last as a separate group.
groups: [["^\\u0000"], ["^@?\\w"], ["^"], ["^\\."], ["^.+\\u0000$"]],
},
],
},
},
{
files: ["groups.type-imports-first-sorted.ts"],
parser: "@typescript-eslint/parser",
rules: {
imports: [
"error",
{
// The default grouping, but with type imports first as a separate
// group, sorting that group like non-type imports are grouped.
groups: [
["^@?\\w.*\\u0000$", "^[^.].*\\u0000$", "^\\..*\\u0000$"],
["^\\u0000"],
["^@?\\w"],
["^"],
["^\\."],
],
},
],
},
},
{
files: ["groups.type-imports-last-sorted.ts"],
parser: "@typescript-eslint/parser",
rules: {
imports: [
"error",
{
// The default grouping, but with type imports last as a separate
// group, sorting that group like non-type imports are grouped.
groups: [
["^\\u0000"],
["^@?\\w"],
["^"],
["^\\."],
["^@?\\w.*\\u0000$", "^[^.].*\\u0000$", "^\\..*\\u0000$"],
],
},
],
},
},
{
files: ["groups.type-imports-first-in-each-group.ts"],
parser: "@typescript-eslint/parser",
rules: {
imports: [
"error",
{
// The default grouping, but with type imports first in each group.
groups: [
["^\\u0000"],
["^@?\\w.*\\u0000$", "^@?\\w"],
["(?<=\\u0000)$", "^"],
["^\\..*\\u0000$", "^\\."],
],
},
],
},
},
{
files: ["groups.type-imports-last-in-each-group.ts"],
parser: "@typescript-eslint/parser",
rules: {
imports: [
"error",
{
// The default grouping, but with type imports last in each group.
groups: [
["^\\u0000"],
["^@?\\w", "^@?\\w.*\\u0000$"],
["(?<!\\u0000)$", "(?<=\\u0000)$"],
["^\\.", "^\\..*\\u0000$"],
],
},
],
},
},
{
files: ["groups.none.js"],
rules: {
Expand Down
14 changes: 14 additions & 0 deletions examples/groups.type-imports-first-in-each-group.ts
@@ -0,0 +1,14 @@
import "./polyfills";
import react from "react";
import type { Component } from "react";
import type { Store } from "redux";
import type { Story } from "@storybook/react";
import { storiesOf } from "@storybook/react";
import type { AppRouter } from "@/App";
import App from "@/App";
import type { Page } from "./page";
import page from "./page";
import type { Css } from "./styles";
import styles from "./styles";
import config from "/config";
import type { Config } from "/config";
14 changes: 14 additions & 0 deletions examples/groups.type-imports-first-sorted.ts
@@ -0,0 +1,14 @@
import "./polyfills";
import react from "react";
import type { Component } from "react";
import type { Store } from "redux";
import type { Story } from "@storybook/react";
import { storiesOf } from "@storybook/react";
import type { AppRouter } from "@/App";
import App from "@/App";
import type { Page } from "./page";
import page from "./page";
import type { Css } from "./styles";
import styles from "./styles";
import config from "/config";
import type { Config } from "/config";
14 changes: 14 additions & 0 deletions examples/groups.type-imports-first.ts
@@ -0,0 +1,14 @@
import "./polyfills";
import react from "react";
import type { Component } from "react";
import type { Store } from "redux";
import type { Story } from "@storybook/react";
import { storiesOf } from "@storybook/react";
import type { AppRouter } from "@/App";
import App from "@/App";
import type { Page } from "./page";
import page from "./page";
import type { Css } from "./styles";
import styles from "./styles";
import config from "/config";
import type { Config } from "/config";
14 changes: 14 additions & 0 deletions examples/groups.type-imports-last-in-each-group.ts
@@ -0,0 +1,14 @@
import "./polyfills";
import react from "react";
import type { Component } from "react";
import type { Store } from "redux";
import type { Story } from "@storybook/react";
import { storiesOf } from "@storybook/react";
import type { AppRouter } from "@/App";
import App from "@/App";
import type { Page } from "./page";
import page from "./page";
import type { Css } from "./styles";
import styles from "./styles";
import config from "/config";
import type { Config } from "/config";
14 changes: 14 additions & 0 deletions examples/groups.type-imports-last-sorted.ts
@@ -0,0 +1,14 @@
import "./polyfills";
import react from "react";
import type { Component } from "react";
import type { Store } from "redux";
import type { Story } from "@storybook/react";
import { storiesOf } from "@storybook/react";
import type { AppRouter } from "@/App";
import App from "@/App";
import type { Page } from "./page";
import page from "./page";
import type { Css } from "./styles";
import styles from "./styles";
import config from "/config";
import type { Config } from "/config";
14 changes: 14 additions & 0 deletions examples/groups.type-imports-last.ts
@@ -0,0 +1,14 @@
import "./polyfills";
import react from "react";
import type { Component } from "react";
import type { Store } from "redux";
import type { Story } from "@storybook/react";
import { storiesOf } from "@storybook/react";
import type { AppRouter } from "@/App";
import App from "@/App";
import type { Page } from "./page";
import page from "./page";
import type { Css } from "./styles";
import styles from "./styles";
import config from "/config";
import type { Config } from "/config";
2 changes: 2 additions & 0 deletions src/imports.js
Expand Up @@ -87,6 +87,8 @@ function makeSortedItems(items, outerGroups) {
const { originalSource } = item.source;
const source = item.isSideEffectImport
? `\0${originalSource}`
: item.source.kind !== "value"
? `${originalSource}\0`
: originalSource;
const [matchedGroup] = shared
.flatMap(itemGroups, (groups) =>
Expand Down
130 changes: 130 additions & 0 deletions test/__snapshots__/examples.test.js.snap
Expand Up @@ -135,6 +135,136 @@ 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 { Component } from "react";
import type { Store } from "redux";
import "./polyfills";
import { storiesOf } from "@storybook/react";
import react from "react";
import config from "/config";
import App from "@/App";
import page from "./page";
import styles from "./styles";
`;

exports[`examples groups.type-imports-first-in-each-group.ts 1`] = `
import "./polyfills";
import type { Story } from "@storybook/react";
import type { Component } from "react";
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 App from "@/App";
import type { Page } from "./page";
import type { Css } from "./styles";
import page from "./page";
import styles from "./styles";
`;

exports[`examples groups.type-imports-first-sorted.ts 1`] = `
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 { Page } from "./page";
import type { Css } from "./styles";
import "./polyfills";
import { storiesOf } from "@storybook/react";
import react from "react";
import config from "/config";
import App from "@/App";
import page from "./page";
import styles from "./styles";
`;

exports[`examples groups.type-imports-last.ts 1`] = `
import "./polyfills";
import { storiesOf } from "@storybook/react";
import react from "react";
import config from "/config";
import App from "@/App";
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 { Component } from "react";
import type { Store } from "redux";
`;

exports[`examples groups.type-imports-last-in-each-group.ts 1`] = `
import "./polyfills";
import { storiesOf } from "@storybook/react";
import react from "react";
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 type { AppRouter } from "@/App";
import page from "./page";
import styles from "./styles";
import type { Page } from "./page";
import type { Css } from "./styles";
`;

exports[`examples groups.type-imports-last-sorted.ts 1`] = `
import "./polyfills";
import { storiesOf } from "@storybook/react";
import react from "react";
import config from "/config";
import App from "@/App";
import page from "./page";
import styles from "./styles";
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 { Page } from "./page";
import type { Css } from "./styles";
`;

exports[`examples ignore.js 1`] = `
// First off, imports that are only used for side effects stay in the input
// order. These won’t be sorted:
Expand Down