Enforce a convention in the order of require()
/ import
statements. The default order is as shown in the following example:
// 1. "absolute" path modules
import abs from '/absolute-module'; // uncommon
// 2. all non-relative and non-absolute "modules"
import fs from 'fs';
import path from 'path';
import _ from 'lodash';
import chalk from 'chalk';
import foo from 'src/foo';
// 3. modules from a "parent" directory
import foo from '../foo';
import qux from '../../foo/qux';
// 4. "sibling" modules from the same or a sibling's directory
import bar from './bar';
import baz from './bar/baz';
// 5. "index" of the current directory
import main from './';
Notes:
- Unassigned imports are ignored (ex:
import 'polyfill'
), as the order they are imported in may be important. - Statements using the ES6
import
syntax must appear before anyrequire()
statements.
To use the rule, update your eslint
config.
{
// .eslintrc.js
plugins: ['eslint-plugin-import-helpers'],
rules: {
'import-helpers/order-imports': [
'warn',
{ // example configuration
newlinesBetween: 'always',
groups: [
'module',
'/^@shared/',
['parent', 'sibling', 'index'],
],
alphabetize: { order: 'asc', ignoreCase: true },
},
],
}
}
This rule supports the following options:
The default value is
["absolute", "module", "parent", "sibling", "index"]
.
Groups dictates how the imports should be grouped and it what order. groups
is an array. Each value in the array must be a valid string or an array of valid strings. The valid strings are:
'module'
|'absolute'
|'parent'
|'sibling'
|'index'
|'type'
- or a regular expression like string, ex:
/^shared/
- the wrapping
/
is essential - in this example, it would match any import paths starting with
'shared'
- note: files are first categorized as matching a regular expression group before going into another group
- the wrapping
The enforced order is the same as the order of each element in a group. Omitted groups are implicitly grouped together as the last element. Example:
[
'absolute', // any absolute path modules are first (ex: `/path/to/code.ts`)
'module', // then normal modules (ex: `lodash/pull`)
['sibling', 'parent'], // Then sibling and parent types. They can be mingled together
'/^shared/', // any import paths starting with 'shared'
'index', // Then the index file
];
You can set the options like this:
"import-helpers/order-imports": [
"error",
{"groups": [ 'module', '/^@shared/', ['parent', 'sibling', 'index'] ]}
]
TypeScript has what are called type imports, e.g.,
import type { ImportantType } from './thing';
If you would like to treat these type imports as a completely separate group (instead of sorted according to the file it was imported from), add a type
group to your groups
list.
With the type
group:
/* eslint import-helpers/order-imports: ["error", {"groups": ['sibling', 'module', 'type']}] */
import foo from './foo';
import fs from 'fs';
import path from 'path';
import type { ImportantType } from './sibling';
Without the type
group:
/* eslint import-helpers/order-imports: ["error", {"groups": ['sibling', 'module']}] */
import foo from './foo';
import type { ImportantType } from './sibling';
import fs from 'fs';
import path from 'path';
Enforces or forbids new lines between import groups:
- If set to
ignore
, no errors related to new lines between import groups will be reported (default). - If set to
always
, at least one new line between each group will be enforced, and new lines inside a group will be forbidden. To prevent multiple lines between imports, coreno-multiple-empty-lines
rule can be used. - If set to
always-and-inside-groups
, at least one new line between each import statement will be enforced. - If set to
never
, no new lines are allowed in the entire import section.
With the default group setting, the following will be valid:
/* eslint import-helpers/order-imports: ["error", {"newlinesBetween": "always"}] */
import fs from 'fs';
import path from 'path';
import sibling from './foo';
import index from './';
/* eslint import-helpers/order-imports: ["error", {"newlinesBetween": "never"}] */
import fs from 'fs';
import path from 'path';
import sibling from './foo';
import index from './';
Sort the order within each group in alphabetical manner:
order
: useasc
to sort in ascending order, anddesc
to sort in descending order (default:ignore
).ignoreCase
[boolean]: whentrue
, the rule ignores case-sensitivity of the import name (default:false
).
Example setting:
alphabetize: {
order: 'asc', /* sort in ascending order. Options: ['ignore', 'asc', 'desc'] */
ignoreCase: false, /* case-sensitive. This property does not have any effect if 'order' is set to 'ignore' */
}
This will pass:
import Baz from 'Baz';
import bar from 'bar';
import foo from 'foo';
This will fail the rule check:
import foo from 'foo';
import bar from 'bar';
import Baz from 'Baz';
In v1, builtin
, external
, internal
have all been combined into one group, module
. This simplifies the logic for this rule and makes it so it ONLY looks at the import strings and doesn't attempt any module resolution itself. The same functionality can be accomplished using regular expression groups.
If you want to keep the same builtin
functionality, create a custom regular expression group to replace it, like so.
// v0.14
groups: ['builtin', 'sibling'];
// v1
groups: [
'/^(assert|async_hooks|buffer|child_process|cluster|console|constants|crypto|dgram|dns|domain|events|fs|http|http2|https|inspector|module|net|os|path|perf_hooks|process|punycode|querystring|readline|repl|stream|string_decoder|timers|tls|trace_events|tty|url|util|v8|vm|zli)/',
'sibling',
];
If you want to keep the same internal
/external
functionality, create a custom regular expression group with your modules names.
In v1, the newLinesBetween
configuration option is now in camel case.