eslint-plugin-simple-import-sort
Easy autofixable import sorting.
This is for those who use eslint --fix
(autofix) a lot and want to completely
forget about sorting imports!
Contents
Example
import React from "react";
import Button from "../Button";
import styles from "./styles.css";
import type { User } from "../../types";
import { getUser } from "../../api";
import PropTypes from "prop-types";
import classnames from "classnames";
import { truncate, formatNumber } from "../../utils";
⬇️
import classnames from "classnames";
import PropTypes from "prop-types";
import React from "react";
import { getUser } from "../../api";
import type { User } from "../../types";
import { formatNumber, truncate } from "../../utils";
import Button from "../Button";
import styles from "./styles.css";
More examples
Installation
First you need to install ESLint:
npm install --save-dev eslint
Next, install eslint-plugin-simple-import-sort
:
npm install --save-dev eslint-plugin-simple-import-sort
Note: If you installed ESLint globally (using the -g
flag) then you must
also install eslint-plugin-simple-import-sort
globally.
Usage
Add simple-import-sort
to the plugins section of your .eslintrc
configuration file. You can omit the eslint-plugin-
prefix:
{
"plugins": ["simple-import-sort"]
}
Then add the import sort rule:
{
"rules": {
"simple-import-sort/sort": "error"
}
}
Make sure to remove or disable other sorting rules, such as sort-imports and
import/order.
{
"rules": {
"sort-imports": "off",
"import/order": "off"
}
}
Since this plugin does not support sorting require
, you might
want to enable some other sorting rule only for files that use require
:
{
"overrides": [
{
"files": "server/**/*.js",
"rules": {
"simple-import-sort/sort": "off",
"import/order": ["error", { "newlines-between": "always" }]
}
}
]
}
Example configuration
This example uses the following extra (optional) plugins:
{
"parserOptions": {
"sourceType": "module"
},
"env": { "es6": true },
"plugins": ["simple-import-sort", "prettier", "import"],
"rules": {
"simple-import-sort/sort": "error",
"sort-imports": "off",
"prettier/prettier": "error",
"import/first": "error",
"import/newline-after-import": "error",
"import/no-duplicates": "error"
},
"overrides": [
{
"files": "server/**/*.js",
"env": { "node": true },
"rules": {
"simple-import-sort/sort": "off",
"import/order": ["error", { "newlines-between": "always" }]
}
}
]
}
simple-import-sort/sort
is turned on by default.- The standard sort-imports rule is turned off, in case you extend a config
that includes it.
- [prettier/prettier] runs Prettier inside ESLint and helps formatting your
imports (and all other code) nicely. (autofixable)
- import/first makes sure all imports are at the top of the file.
(autofixable)
- import/newline-after-import makes sure there’s a newline after the imports.
(autofixable)
- import/no-duplicates merges import statements of the same file.
(autofixable, mostly)
- For Node.js code,
simple-import-sort/sort
is turned off and replaced with
import/order for sorting of require
calls.
With the above configuration, you don’t need to scroll to the top of the file to
add another import. Just put it above your function! ESLint will then snap it
into place (at the top of the file, in order, and without duplicates).
Sort order
This plugin is supposed to be used with autofix, ideally directly in your editor
via an ESLint extension, or with eslint --fix
otherwise.
This section is for learning how the sorting works, not for how to manually fix
errors. Use autofix!
TL;DR: First group, then sort alphabetically.
First, the plugin finds all chunks of imports. A “chunk” is a sequence of
import statements with only comments and whitespace between. Each chunk is
sorted separately. Use import/first if you want to make sure that all imports
end up in the same chunk.
Then, each chunk is grouped into sections with a blank line between each.
import "./setup"
: Side effect imports. (These are not sorted internally.)import react from "react"
: Packages (npm packages and Node.js builtins).import a from "/a"
: Absolute imports, full URLs and other imports (such as
Vue-style @/foo
ones).import a from "./a"
: Relative imports.
Within each section, the imports are sorted alphabetically on the from
string
(see also “Why sort on from
?”). Keep it simple! It helps looking
at the code here:
const collator = new Intl.Collator("en", {
sensitivity: "base",
numeric: true,
});
function compare(a, b) {
return collator.compare(a, b) || (a < b ? -1 : a > b ? 1 : 0);
}
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
"../"
. Also, within the absolute imports group, imports starting with an ASCII
letter or digit come first, separating them from those starting with symbols.
webpack loader syntax is stripped before sorting, so "loader!a"
sorts before
"b"
. If two sources are equal after stripping the loader syntax, the one with
loader syntax comes last. Similarly, if both import type
and regular imports
are used for the same source, the Flow type imports come first.
Example:
import "./setup";
import "some-polyfill";
import "./global.css";
import type A from "an-npm-package";
import a from "an-npm-package";
import fs from "fs";
import b from "https://example.com/script.js";
import Error from "@/components/error.vue";
import c from "/";
import d from "/home/user/foo";
import e from "../..";
import f from "../../Utils";
import type { B } from "../types";
import typeof C from "../types";
import g from ".";
import h from "./constants";
import i from "./styles";
import j from "html-loader!./text.html";
import {
type x,
typeof y,
img1,
img2,
img10,
k,
L,
m as anotherName,
m as tie,
n,
} from "./x";
Comment and whitespace handling
When an import is moved through sorting, it’s comments are moved with it.
Comments can be placed above an import (except the first one – more on that
later), or at the start or end of its line.
Example:
import c from "c";
import b from "b";
import a from "a";
⬇️
import a from "a";
import b from "b";
import c from "c";
Now compare these two examples:
import b from "b";
import a from "a";
import b from "b";
import a from "a";
The // @flow
comment is supposed to be at the top of the file (it enables
Flow type checking for the file), and isn’t related to the "b"
import. On
the other hand, the // eslint-disable-next-line
comment is related to the
"b"
import. Even a documentation comment could be either for the whole file,
or the first import. So this plugin can’t know if it should move comments above
the first import or not (but it knows that the //a
comment belongs to the
"a"
import).
For this reason, comments above and below chunks of imports are never moved. You
need to do so yourself, if needed.
Comments around imported items follow similar rules – they can be placed above
an item, or at the start or end of its line. Comments before the first item or
newline stay at the start, and comments after the last item stay at the end.
import {
c ,
b as renamed
, a
} from "wherever";
import {
e,
d,
} from "wherever2";
import { g, f } from "wherever3";
⬇️
import {
a,
b as renamed
,
c
} from "wherever";
import {
d, e,
} from "wherever2";
import { f, g } from "wherever3";
If you wonder what’s up with the strange whitespace – see “The sorting autofix
causes some odd whitespace!”
Speaking of whitespace – what about blank lines? Just like comments, it’s
difficult to know where blank lines should go after sorting. This plugin went
with a simple approach – all blank lines in chunks of imports are removed,
except in /**/
comments and the blank lines added between the groups mentioned
in Sort order.
(Since blank lines are removed, you might get slight incompatibilities with the
lines-around-comment and padding-line-between-statements rules – I don’t use
those myself, but I think there should be workarounds.)
The final whitespace rule is that this plugin puts one import per line, with no
indentation. I’ve never seen imports written any other way.
FAQ
Does it support require
?
No. This is intentional to keep things simple. Use some other sorting rule, such
as import/order, for sorting require
.
Why sort on from
?
Some other import sorting rules sort based on the first name after import
,
rather than the string after from
. This plugin intentionally sorts on the
from
string to be git diff
friendly.
Have a look at this example:
import { productType } from "./constants";
import { truncate } from "./utils";
Now let’s say you need the arraySplit
util as well:
import { productType } from "./constants";
import { arraySplit, truncate } from "./utils";
If the imports were sorted based on the first name after import
(“productType”
and “arraySplit” in this case), the two imports would now swap order:
import { arraySplit, truncate } from "./utils";
import { productType } from "./constants";
On the other hand, if sorting based on the from
string (like this plugin
does), the imports stay in the same order. This prevents the imports from
jumping around as you add and remove things, keeping your git history clean and
reducing the risk of merge conflicts.
Is sorting imports safe?
Mostly.
Imports can have side effects in JavaScript, so changing the order of the
imports can change the order that those side effects execute in. It is best
practice to either import a module for its side effects or for the things it
exports.
import "some-polyfill";
import { someUtil } from "some-library";
Imports that are only used for side effects stay in the input order. These won’t
be sorted:
import "b";
import "a";
Imports that both export stuff and run side effects are rare. If you run
into such a situation – try to fix it, since it will confuse everyone working
with the code. If that’s not possible, it’s possible to ignore (parts of)
sorting.
Another small caveat is that you sometimes need to move comments manually – see
Comment and whitespace handling.
For completeness, sorting the imported items of an import is always safe:
import { c, b, a } from "wherever";
import { a, b, c } from "wherever";
Note: import {} from "wherever"
is not treated as a side effect import.
The sorting autofix causes some odd whitespace!
You might end up with slightly weird spacing, for example a missing space after
a comma:
import {bar, baz,foo} from "example";
Sorting is the easy part of this plugin. Handling whitespace and comments is the
hard part. The autofix might end up with a little odd spacing around an import
sometimes. Rather than fixing those spaces by hand, I recommend using Prettier
or enabling other autofixable ESLint whitespace rules. See examples for more
information.
The reason the whitespace can end up weird is because this plugin re-uses and
moves around already existing whitespace rather than removing and adding new
whitespace. This is to stay compatible with other ESLint rules that deal with
whitespace.
Can I use this without autofix?
Not really. The error message for this rule is literally “Run autofix to sort
these imports!” Why? To actively encourage you to use autofix, and not waste
time on manually doing something that the computer does a lot better. I’ve seen
people painstakingly fixing cryptic (and annoying!) sorting errors from other
rules one by one, not realizing they could have been autofixed. Finally, not
trying to make more detailed messages makes the code of this plugin much
easier to work with.
Development
You can need Node.js 10 and npm 6.
npm scripts
npm run eslint
: Run ESLint (including Prettier).npm run eslint:fix
: Autofix ESLint errors.npm run eslint:examples
: Used by test/examples.test.js
.npm run prettier
: Run Prettier for files other than JS.npm run doctoc
: Run doctoc on README.md.npm run jest
: Run unit tests. During development, npm run jest -- --watch
is nice.npm run coverage
: Run unit tests with code coverage.npm test
: Check that everything works.npm publish
: Publish to npm, but only if npm test
passes.
Directories
src/
: Source code.examples/
: Examples, tested in test/examples.test.js
.test/
: Jest tests.
License
MIT