Skip to content

Commit

Permalink
Update: Add sort-imports ignoreDeclarationSort (fixes #11019) (#11040)
Browse files Browse the repository at this point in the history
* Update: Add sort-imports ignoreDeclarationSort (fixes #11019)

This allows users to enforce sorting import members, without having to enforce
sorting of import declarations.

* Docs: fix typo in documentation for sort-imports
  • Loading branch information
remcohaszing authored and btmills committed Jan 4, 2019
1 parent f92d6f0 commit 0d91e7d
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 37 deletions.
30 changes: 30 additions & 0 deletions docs/rules/sort-imports.md
Expand Up @@ -34,6 +34,7 @@ The `--fix` option on the command line automatically fixes some problems reporte
This rule accepts an object with its properties as

* `ignoreCase` (default: `false`)
* `ignoreDeclarationSort` (default: `false`)
* `ignoreMemberSort` (default: `false`)
* `memberSyntaxSortOrder` (default: `["none", "all", "multiple", "single"]`); all 4 items must be present in the array, but you can change the order:
* `none` = import module without exported bindings.
Expand All @@ -47,6 +48,7 @@ Default option settings are:
{
"sort-imports": ["error", {
"ignoreCase": false,
"ignoreDeclarationSort": false,
"ignoreMemberSort": false,
"memberSyntaxSortOrder": ["none", "all", "multiple", "single"]
}]
Expand Down Expand Up @@ -136,6 +138,34 @@ import c from 'baz.js';

Default is `false`.

### `ignoreDeclarationSort`

Ignores the sorting of import declaration statements.

Examples of **incorrect** code for this rule with the default `{ "ignoreDeclarationSort": false }` option:

```js
/*eslint sort-imports: ["error", { "ignoreDeclarationSort": false }]*/
import 'foo.js'
import 'bar.js'
```

Examples of **correct** code for this rule with the `{ "ignoreDeclarationSort": true }` option:

```js
/*eslint sort-imports: ["error", { "ignoreDeclarationSort": true }]*/
import 'foo.js'
import 'bar.js'
```

```js
/*eslint sort-imports: ["error", { "ignoreDeclarationSort": true }]*/
import 'bar.js'
import 'foo.js'
```

Default is `false`.

### `ignoreMemberSort`

Ignores the member sorting within a `multiple` member import declaration.
Expand Down
80 changes: 43 additions & 37 deletions lib/rules/sort-imports.js
Expand Up @@ -36,6 +36,9 @@ module.exports = {
minItems: 4,
maxItems: 4
},
ignoreDeclarationSort: {
type: "boolean"
},
ignoreMemberSort: {
type: "boolean"
}
Expand All @@ -51,6 +54,7 @@ module.exports = {

const configuration = context.options[0] || {},
ignoreCase = configuration.ignoreCase || false,
ignoreDeclarationSort = configuration.ignoreDeclarationSort || false,
ignoreMemberSort = configuration.ignoreMemberSort || false,
memberSyntaxSortOrder = configuration.memberSyntaxSortOrder || ["none", "all", "multiple", "single"],
sourceCode = context.getSourceCode();
Expand Down Expand Up @@ -105,44 +109,48 @@ module.exports = {

return {
ImportDeclaration(node) {
if (previousDeclaration) {
const currentMemberSyntaxGroupIndex = getMemberParameterGroupIndex(node),
previousMemberSyntaxGroupIndex = getMemberParameterGroupIndex(previousDeclaration);
let currentLocalMemberName = getFirstLocalMemberName(node),
previousLocalMemberName = getFirstLocalMemberName(previousDeclaration);

if (ignoreCase) {
previousLocalMemberName = previousLocalMemberName && previousLocalMemberName.toLowerCase();
currentLocalMemberName = currentLocalMemberName && currentLocalMemberName.toLowerCase();
}

/*
* When the current declaration uses a different member syntax,
* then check if the ordering is correct.
* Otherwise, make a default string compare (like rule sort-vars to be consistent) of the first used local member name.
*/
if (currentMemberSyntaxGroupIndex !== previousMemberSyntaxGroupIndex) {
if (currentMemberSyntaxGroupIndex < previousMemberSyntaxGroupIndex) {
context.report({
node,
message: "Expected '{{syntaxA}}' syntax before '{{syntaxB}}' syntax.",
data: {
syntaxA: memberSyntaxSortOrder[currentMemberSyntaxGroupIndex],
syntaxB: memberSyntaxSortOrder[previousMemberSyntaxGroupIndex]
}
});
if (!ignoreDeclarationSort) {
if (previousDeclaration) {
const currentMemberSyntaxGroupIndex = getMemberParameterGroupIndex(node),
previousMemberSyntaxGroupIndex = getMemberParameterGroupIndex(previousDeclaration);
let currentLocalMemberName = getFirstLocalMemberName(node),
previousLocalMemberName = getFirstLocalMemberName(previousDeclaration);

if (ignoreCase) {
previousLocalMemberName = previousLocalMemberName && previousLocalMemberName.toLowerCase();
currentLocalMemberName = currentLocalMemberName && currentLocalMemberName.toLowerCase();
}
} else {
if (previousLocalMemberName &&
currentLocalMemberName &&
currentLocalMemberName < previousLocalMemberName
) {
context.report({
node,
message: "Imports should be sorted alphabetically."
});

/*
* When the current declaration uses a different member syntax,
* then check if the ordering is correct.
* Otherwise, make a default string compare (like rule sort-vars to be consistent) of the first used local member name.
*/
if (currentMemberSyntaxGroupIndex !== previousMemberSyntaxGroupIndex) {
if (currentMemberSyntaxGroupIndex < previousMemberSyntaxGroupIndex) {
context.report({
node,
message: "Expected '{{syntaxA}}' syntax before '{{syntaxB}}' syntax.",
data: {
syntaxA: memberSyntaxSortOrder[currentMemberSyntaxGroupIndex],
syntaxB: memberSyntaxSortOrder[previousMemberSyntaxGroupIndex]
}
});
}
} else {
if (previousLocalMemberName &&
currentLocalMemberName &&
currentLocalMemberName < previousLocalMemberName
) {
context.report({
node,
message: "Imports should be sorted alphabetically."
});
}
}
}

previousDeclaration = node;
}

if (!ignoreMemberSort) {
Expand Down Expand Up @@ -191,8 +199,6 @@ module.exports = {
});
}
}

previousDeclaration = node;
}
};
}
Expand Down
23 changes: 23 additions & 0 deletions tests/lib/rules/sort-imports.js
Expand Up @@ -61,6 +61,14 @@ ruleTester.run("sort-imports", rule, {
options: ignoreCaseArgs
},
"import {a, b, c, d} from 'foo.js';",
{
code:
"import a from 'foo.js';\n" +
"import B from 'bar.js';",
options: [{
ignoreDeclarationSort: true
}]
},
{
code: "import {b, A, C, d} from 'foo.js';",
options: [{
Expand Down Expand Up @@ -175,6 +183,21 @@ ruleTester.run("sort-imports", rule, {
type: "ImportSpecifier"
}]
},
{
code:
"import {b, a, d, c} from 'foo.js';\n" +
"import {e, f, g, h} from 'bar.js';",
output:
"import {a, b, c, d} from 'foo.js';\n" +
"import {e, f, g, h} from 'bar.js';",
options: [{
ignoreDeclarationSort: true
}],
errors: [{
message: "Member 'a' of the import declaration should be sorted alphabetically.",
type: "ImportSpecifier"
}]
},
{
code: "import {a, B, c, D} from 'foo.js';",
output: "import {B, D, a, c} from 'foo.js';",
Expand Down

0 comments on commit 0d91e7d

Please sign in to comment.