Skip to content

Commit

Permalink
[New] order: add pathGroups option to add support to order by paths
Browse files Browse the repository at this point in the history
Co-Authored-By: Matt Seccafien <matt@studiocartogram.com>
  • Loading branch information
2 people authored and ljharb committed Jun 20, 2019
1 parent 99b3fbf commit 0426f16
Show file tree
Hide file tree
Showing 4 changed files with 349 additions and 7 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ This change log adheres to standards from [Keep a CHANGELOG](http://keepachangel
- support `parseForESLint` from custom parser ([#1435], thanks [@JounQin])
- [`no-extraneous-dependencies`]: Implement support for [bundledDependencies](https://npm.github.io/using-pkgs-docs/package-json/types/bundleddependencies.html) ([#1436], thanks [@schmidsi]))
- [`no-unused-modules`]: add flow type support ([#1542], thanks [@rfermann])
- [`order`]: Adds support for pathGroups to allow ordering by defined patterns ([#795], [#1386], thanks [@Mairu])

### Fixed
- [`default`]: make error message less confusing ([#1470], thanks [@golopot])
Expand Down Expand Up @@ -644,6 +645,7 @@ for info on changes for earlier releases.
[#1401]: https://github.com/benmosher/eslint-plugin-import/pull/1401
[#1393]: https://github.com/benmosher/eslint-plugin-import/pull/1393
[#1389]: https://github.com/benmosher/eslint-plugin-import/pull/1389
[#1386]: https://github.com/benmosher/eslint-plugin-import/pull/1386
[#1377]: https://github.com/benmosher/eslint-plugin-import/pull/1377
[#1375]: https://github.com/benmosher/eslint-plugin-import/pull/1375
[#1372]: https://github.com/benmosher/eslint-plugin-import/pull/1372
Expand Down Expand Up @@ -788,6 +790,7 @@ for info on changes for earlier releases.
[#863]: https://github.com/benmosher/eslint-plugin-import/issues/863
[#842]: https://github.com/benmosher/eslint-plugin-import/issues/842
[#839]: https://github.com/benmosher/eslint-plugin-import/issues/839
[#795]: https://github.com/benmosher/eslint-plugin-import/issues/795
[#793]: https://github.com/benmosher/eslint-plugin-import/issues/793
[#720]: https://github.com/benmosher/eslint-plugin-import/issues/720
[#717]: https://github.com/benmosher/eslint-plugin-import/issues/717
Expand Down Expand Up @@ -1025,3 +1028,4 @@ for info on changes for earlier releases.
[@Taranys]: https://github.com/Taranys
[@maxmalov]: https://github.com/maxmalov
[@marcusdarmstrong]: https://github.com/marcusdarmstrong
[@Mairu]: https://github.com/Mairu
26 changes: 26 additions & 0 deletions docs/rules/order.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,32 @@ 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) |
| patternOptions | | object | options for minimatch, default: { nocomment: true } |
| group | x | string | one of the allowed groups, the pathGroup will be positioned relative to this group |
| position | | string | defines where around the group the pathGroup will be positioned, can be 'after' or 'before', if not provided pathGroup will be positioned like the group |

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

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


Expand Down
98 changes: 95 additions & 3 deletions src/rules/order.js
Original file line number Diff line number Diff line change
@@ -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 @@ -244,9 +245,29 @@ function makeOutOfOrderReport(context, imported) {

// DETECTING

function computePathRank(ranks, pathGroups, path, maxPosition) {
for (let i = 0, l = pathGroups.length; i < l; i++) {
const { pattern, patternOptions, group, position = 1 } = pathGroups[i]
if (minimatch(path, pattern, patternOptions || { nocomment: true })) {
return ranks[group] + (position / maxPosition)
}
}
}

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, ranks.maxPosition)
}
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 @@ -294,6 +315,49 @@ function convertGroupsToRanks(groups) {
}, rankObject)
}

function convertPathGroupsForRanks(pathGroups) {
const after = {}
const before = {}

const transformed = pathGroups.map((pathGroup, index) => {
const { group, position: positionString } = pathGroup
let position = 0
if (positionString === 'after') {
if (!after[group]) {
after[group] = 1
}
position = after[group]++
} else if (positionString === 'before') {
if (!before[group]) {
before[group] = []
}
before[group].push(index)
}

return Object.assign({}, pathGroup, { position })
})

let maxPosition = 1

Object.keys(before).forEach((group) => {
const groupLength = before[group].length
before[group].forEach((groupIndex, index) => {
transformed[groupIndex].position = -1 * (groupLength - index)
})
maxPosition = Math.max(maxPosition, groupLength)
})

Object.keys(after).forEach((key) => {
const groupNextPosition = after[key]
maxPosition = Math.max(maxPosition, groupNextPosition - 1)
})

return {
pathGroups: transformed,
maxPosition: maxPosition > 10 ? Math.pow(10, Math.ceil(Math.log10(maxPosition))) : 10,
}
}

function fixNewLineAfterImport(context, previousImport) {
const prevRoot = findRootNode(previousImport.node)
const tokensToEndOfLine = takeTokensAfterWhile(
Expand Down Expand Up @@ -378,6 +442,29 @@ 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: 'string',
enum: ['after', 'before'],
},
},
required: ['pattern', 'group'],
},
},
'newlines-between': {
enum: [
'ignore',
Expand All @@ -398,7 +485,12 @@ module.exports = {
let ranks

try {
ranks = convertGroupsToRanks(options.groups || defaultGroups)
const { pathGroups, maxPosition } = convertPathGroupsForRanks(options.pathGroups || [])
ranks = {
groups: convertGroupsToRanks(options.groups || defaultGroups),
pathGroups,
maxPosition,
}
} catch (error) {
// Malformed configuration
return {
Expand Down
Loading

0 comments on commit 0426f16

Please sign in to comment.