Skip to content

Commit

Permalink
order rule: add pathGroups option to add support to order by paths
Browse files Browse the repository at this point in the history
  • Loading branch information
Mairu committed Jun 20, 2019
1 parent 45bfe47 commit 5c46fae
Show file tree
Hide file tree
Showing 4 changed files with 279 additions and 8 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -6,6 +6,7 @@ This change log adheres to standards from [Keep a CHANGELOG](http://keepachangel
## [Unreleased]

- [`order`]: Adds support for correctly sorting unknown types into a single group (thanks [@swernerx])
- [`order`]: Adds support for pathGroups to allow ordering by defined patterns ([#795], thanks [@Mairu])

## [2.17.3] - 2019-05-23

Expand Down
25 changes: 25 additions & 0 deletions docs/rules/order.md
Expand Up @@ -94,6 +94,31 @@ You can set the options like this:
"import/order": ["error", {"groups": ["index", "sibling", "parent", "internal", "external", "builtin"]}]
```

### `pathGroups: [array of objects]`:

To be able so group by paths mostly needed with aliases pathGroups can be defined.

Properties of the objects

| property | required | type | description |
|----------|:--------:|---------|---------------|
| pattern | x | string | minimatch pattern for the paths to be in this group (will not be used for builtins or externals) |
| group | x | string | one of the allowed groups, the pathGroup will be positioned relative to this group |
| position | | integer | defines where around the group the pathGroup will be positioned <br> negative -> before, 0 = like the group, positive -> after <br> default: 1 <br> maximal magnitude: 1000 |

```json
{
"import/order": ["error", {
"pathGroups": [
{
"pattern": "~/**",
"external": "external"
}
]
}]
}
```

### `newlines-between: [ignore|always|always-and-inside-groups|never]`:


Expand Down
53 changes: 50 additions & 3 deletions src/rules/order.js
@@ -1,5 +1,6 @@
'use strict'

import minimatch from 'minimatch'
import importType from '../core/importType'
import isStaticRequire from '../core/staticRequire'
import docsUrl from '../docsUrl'
Expand Down Expand Up @@ -242,9 +243,28 @@ function makeOutOfOrderReport(context, imported) {

// DETECTING

function computePathRank(ranks, pathGroups, path) {
for (const { pattern, patternOptions, group, position = 1 } of pathGroups) {
if (minimatch(path, pattern, patternOptions ||{ nocomment: true })) {
return ranks[group] + (position / 1000)
}
}
}

function computeRank(context, ranks, name, type) {
return ranks[importType(name, context)] +
(type === 'import' ? 0 : 100)
const impType = importType(name, context)
let rank
if (impType !== 'builtin' && impType !== 'external') {
rank = computePathRank(ranks.groups, ranks.pathGroups, name)
}
if (!rank) {
rank = ranks.groups[impType]
}
if (type !== 'import') {
rank += 100
}

return rank
}

function registerNode(context, node, name, type, ranks, imported) {
Expand Down Expand Up @@ -376,6 +396,30 @@ module.exports = {
groups: {
type: 'array',
},
pathGroups: {
type: 'array',
items: {
type: 'object',
properties: {
pattern: {
type: 'string',
},
patternOptions: {
type: 'object',
},
group: {
type: 'string',
enum: types,
},
position: {
type: 'integer',
minimum: -1000,
maximum: 1000,
},
},
required: ['pattern', 'group'],
},
},
'newlines-between': {
enum: [
'ignore',
Expand All @@ -396,7 +440,10 @@ module.exports = {
let ranks

try {
ranks = convertGroupsToRanks(options.groups || defaultGroups)
ranks = {
groups: convertGroupsToRanks(options.groups || defaultGroups),
pathGroups: options.pathGroups || [],
}
} catch (error) {
// Malformed configuration
return {
Expand Down
208 changes: 203 additions & 5 deletions tests/src/rules/order.js
Expand Up @@ -164,7 +164,7 @@ ruleTester.run('order', rule, {
var index = require('./');
`,
}),
// Addijg unknown import types (e.g. using an resolver alias via babel) to the groups.
// Add unknown import types (e.g. using an resolver alias via babel) to the groups.
test({
code: `
import fs from 'fs';
Expand Down Expand Up @@ -204,6 +204,59 @@ ruleTester.run('order', rule, {
},
],
}),

// Using pathGroups to customize ordering, positive position means after
test({
code: `
import fs from 'fs';
import _ from 'lodash';
import { Input } from '~/components/Input';
import { Button } from '#/components/Button';
import { add } from './helper';`,
options: [{
pathGroups: [
{ pattern: '~/**', group: 'external', position: 1 },
{ pattern: '#/**', group: 'external', position: 2 },
],
}],
}),
// pathGroup with position 0 means "equal" with group
test({
code: `
import fs from 'fs';
import { Input } from '~/components/Input';
import async from 'async';
import { Button } from '#/components/Button';
import _ from 'lodash';
import { add } from './helper';`,
options: [{
pathGroups: [
{ pattern: '~/**', group: 'external', position: 0 },
{ pattern: '#/**', group: 'external', position: 0 },
],
}],
}),
// Using pathGroups to customize ordering, negative position means before
test({
code: `
import fs from 'fs';
import { Input } from '~/components/Input';
import { Button } from '#/components/Button';
import _ from 'lodash';
import { add } from './helper';`,
options: [{
'newlines-between': 'always',
pathGroups: [
{ pattern: '~/**', group: 'external', position: -2 },
{ pattern: '#/**', group: 'external', position: -1 },
],
}],
}),

// Option: newlines-between: 'always'
test({
code: `
Expand Down Expand Up @@ -573,7 +626,7 @@ ruleTester.run('order', rule, {
message: '`fs` import should occur before import of `async`',
}],
}),
// fix order of multile import
// fix order of multiline import
test({
code: `
var async = require('async');
Expand Down Expand Up @@ -1266,6 +1319,151 @@ ruleTester.run('order', rule, {
],
}),

// pathGroup with positive position
test({
code: `
import fs from 'fs';
import _ from 'lodash';
import { add } from './helper';
import { Input } from '~/components/Input';
`,
output: `
import fs from 'fs';
import _ from 'lodash';
import { Input } from '~/components/Input';
import { add } from './helper';
`,
options: [{
pathGroups: [
{ pattern: '~/**', group: 'external', position: 1 },
],
}],
errors: [{
ruleId: 'order',
message: '`~/components/Input` import should occur before import of `./helper`',
}],
}),
// pathGroup with zero position
test({
code: `
import fs from 'fs';
import _ from 'lodash';
import { add } from './helper';
import { Input } from '~/components/Input';
import async from 'async';
`,
output: `
import fs from 'fs';
import _ from 'lodash';
import { Input } from '~/components/Input';
import async from 'async';
import { add } from './helper';
`,
options: [{
pathGroups: [
{ pattern: '~/**', group: 'external', position: 0 },
],
}],
errors: [{
ruleId: 'order',
message: '`./helper` import should occur after import of `async`',
}],
}),
// pathGroup with negative position
test({
code: `
import fs from 'fs';
import _ from 'lodash';
import { add } from './helper';
import { Input } from '~/components/Input';
`,
output: `
import fs from 'fs';
import { Input } from '~/components/Input';
import _ from 'lodash';
import { add } from './helper';
`,
options: [{
pathGroups: [
{ pattern: '~/**', group: 'external', position: -1 },
],
}],
errors: [{
ruleId: 'order',
message: '`~/components/Input` import should occur before import of `lodash`',
}],
}),
// multiple pathGroup with different positions for same group (positive fix)
test({
code: `
import fs from 'fs';
import { Import } from '$/components/Import';
import _ from 'lodash';
import { Output } from '~/components/Output';
import { Input } from '#/components/Input';
import { add } from './helper';
import { Export } from '-/components/Export';
`,
output: `
import fs from 'fs';
import { Export } from '-/components/Export';
import { Import } from '$/components/Import';
import _ from 'lodash';
import { Output } from '~/components/Output';
import { Input } from '#/components/Input';
import { add } from './helper';
`,
options: [{
pathGroups: [
{ pattern: '~/**', group: 'external', position: 1 },
{ pattern: '#/**', group: 'external', position: 2 },
{ pattern: '$/**', group: 'external', position: -1 },
{ pattern: '-/**', group: 'external', position: -2 },
],
}],
errors: [
{
ruleId: 'order',
message: '`-/components/Export` import should occur before import of `$/components/Import`',
},
],
}),
// multiple pathGroup with different positions for same group (negative fix)
test({
code: `
import fs from 'fs';
import { Export } from '-/components/Export';
import { Import } from '$/components/Import';
import _ from 'lodash';
import { Input } from '#/components/Input';
import { add } from './helper';
import { Output } from '~/components/Output';
`,
output: `
import fs from 'fs';
import { Export } from '-/components/Export';
import { Import } from '$/components/Import';
import _ from 'lodash';
import { Output } from '~/components/Output';
import { Input } from '#/components/Input';
import { add } from './helper';
`,
options: [{
pathGroups: [
{ pattern: '~/**', group: 'external', position: 1 },
{ pattern: '#/**', group: 'external', position: 2 },
{ pattern: '$/**', group: 'external', position: -1 },
{ pattern: '-/**', group: 'external', position: -2 },
],
}],
errors: [
{
ruleId: 'order',
message: '`~/components/Output` import should occur before import of `#/components/Input`',
},
],
}),

// reorder fix cannot cross non import or require
test(withoutAutofixOutput({
code: `
Expand Down Expand Up @@ -1312,7 +1510,7 @@ ruleTester.run('order', rule, {
message: '`fs` import should occur before import of `async`',
}],
})),
// cannot require in case of not assignement require
// cannot require in case of not assignment require
test(withoutAutofixOutput({
code: `
var async = require('async');
Expand All @@ -1336,7 +1534,7 @@ ruleTester.run('order', rule, {
message: '`fs` import should occur before import of `async`',
}],
})),
// reorder cannot cross variable assignemet (import statement)
// reorder cannot cross variable assignment (import statement)
test(withoutAutofixOutput({
code: `
import async from 'async';
Expand All @@ -1360,7 +1558,7 @@ ruleTester.run('order', rule, {
message: '`fs` import should occur before import of `async`',
}],
})),
// cannot reorder in case of not assignement import
// cannot reorder in case of not assignment import
test(withoutAutofixOutput({
code: `
import async from 'async';
Expand Down

0 comments on commit 5c46fae

Please sign in to comment.