Skip to content

Commit 0426f16

Browse files
MairuMatt Seccafien
authored andcommitted
[New] order: add pathGroups option to add support to order by paths
Co-Authored-By: Matt Seccafien <matt@studiocartogram.com>
1 parent 99b3fbf commit 0426f16

File tree

4 files changed

+349
-7
lines changed

4 files changed

+349
-7
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ This change log adheres to standards from [Keep a CHANGELOG](http://keepachangel
1313
- support `parseForESLint` from custom parser ([#1435], thanks [@JounQin])
1414
- [`no-extraneous-dependencies`]: Implement support for [bundledDependencies](https://npm.github.io/using-pkgs-docs/package-json/types/bundleddependencies.html) ([#1436], thanks [@schmidsi]))
1515
- [`no-unused-modules`]: add flow type support ([#1542], thanks [@rfermann])
16+
- [`order`]: Adds support for pathGroups to allow ordering by defined patterns ([#795], [#1386], thanks [@Mairu])
1617

1718
### Fixed
1819
- [`default`]: make error message less confusing ([#1470], thanks [@golopot])
@@ -644,6 +645,7 @@ for info on changes for earlier releases.
644645
[#1401]: https://github.com/benmosher/eslint-plugin-import/pull/1401
645646
[#1393]: https://github.com/benmosher/eslint-plugin-import/pull/1393
646647
[#1389]: https://github.com/benmosher/eslint-plugin-import/pull/1389
648+
[#1386]: https://github.com/benmosher/eslint-plugin-import/pull/1386
647649
[#1377]: https://github.com/benmosher/eslint-plugin-import/pull/1377
648650
[#1375]: https://github.com/benmosher/eslint-plugin-import/pull/1375
649651
[#1372]: https://github.com/benmosher/eslint-plugin-import/pull/1372
@@ -788,6 +790,7 @@ for info on changes for earlier releases.
788790
[#863]: https://github.com/benmosher/eslint-plugin-import/issues/863
789791
[#842]: https://github.com/benmosher/eslint-plugin-import/issues/842
790792
[#839]: https://github.com/benmosher/eslint-plugin-import/issues/839
793+
[#795]: https://github.com/benmosher/eslint-plugin-import/issues/795
791794
[#793]: https://github.com/benmosher/eslint-plugin-import/issues/793
792795
[#720]: https://github.com/benmosher/eslint-plugin-import/issues/720
793796
[#717]: https://github.com/benmosher/eslint-plugin-import/issues/717
@@ -1025,3 +1028,4 @@ for info on changes for earlier releases.
10251028
[@Taranys]: https://github.com/Taranys
10261029
[@maxmalov]: https://github.com/maxmalov
10271030
[@marcusdarmstrong]: https://github.com/marcusdarmstrong
1031+
[@Mairu]: https://github.com/Mairu

docs/rules/order.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,32 @@ You can set the options like this:
9494
"import/order": ["error", {"groups": ["index", "sibling", "parent", "internal", "external", "builtin"]}]
9595
```
9696

97+
### `pathGroups: [array of objects]`:
98+
99+
To be able so group by paths mostly needed with aliases pathGroups can be defined.
100+
101+
Properties of the objects
102+
103+
| property | required | type | description |
104+
|----------------|:--------:|--------|---------------|
105+
| pattern | x | string | minimatch pattern for the paths to be in this group (will not be used for builtins or externals) |
106+
| patternOptions | | object | options for minimatch, default: { nocomment: true } |
107+
| group | x | string | one of the allowed groups, the pathGroup will be positioned relative to this group |
108+
| 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 |
109+
110+
```json
111+
{
112+
"import/order": ["error", {
113+
"pathGroups": [
114+
{
115+
"pattern": "~/**",
116+
"group": "external"
117+
}
118+
]
119+
}]
120+
}
121+
```
122+
97123
### `newlines-between: [ignore|always|always-and-inside-groups|never]`:
98124

99125

src/rules/order.js

Lines changed: 95 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
'use strict'
22

3+
import minimatch from 'minimatch'
34
import importType from '../core/importType'
45
import isStaticRequire from '../core/staticRequire'
56
import docsUrl from '../docsUrl'
@@ -244,9 +245,29 @@ function makeOutOfOrderReport(context, imported) {
244245

245246
// DETECTING
246247

248+
function computePathRank(ranks, pathGroups, path, maxPosition) {
249+
for (let i = 0, l = pathGroups.length; i < l; i++) {
250+
const { pattern, patternOptions, group, position = 1 } = pathGroups[i]
251+
if (minimatch(path, pattern, patternOptions || { nocomment: true })) {
252+
return ranks[group] + (position / maxPosition)
253+
}
254+
}
255+
}
256+
247257
function computeRank(context, ranks, name, type) {
248-
return ranks[importType(name, context)] +
249-
(type === 'import' ? 0 : 100)
258+
const impType = importType(name, context)
259+
let rank
260+
if (impType !== 'builtin' && impType !== 'external') {
261+
rank = computePathRank(ranks.groups, ranks.pathGroups, name, ranks.maxPosition)
262+
}
263+
if (!rank) {
264+
rank = ranks.groups[impType]
265+
}
266+
if (type !== 'import') {
267+
rank += 100
268+
}
269+
270+
return rank
250271
}
251272

252273
function registerNode(context, node, name, type, ranks, imported) {
@@ -294,6 +315,49 @@ function convertGroupsToRanks(groups) {
294315
}, rankObject)
295316
}
296317

318+
function convertPathGroupsForRanks(pathGroups) {
319+
const after = {}
320+
const before = {}
321+
322+
const transformed = pathGroups.map((pathGroup, index) => {
323+
const { group, position: positionString } = pathGroup
324+
let position = 0
325+
if (positionString === 'after') {
326+
if (!after[group]) {
327+
after[group] = 1
328+
}
329+
position = after[group]++
330+
} else if (positionString === 'before') {
331+
if (!before[group]) {
332+
before[group] = []
333+
}
334+
before[group].push(index)
335+
}
336+
337+
return Object.assign({}, pathGroup, { position })
338+
})
339+
340+
let maxPosition = 1
341+
342+
Object.keys(before).forEach((group) => {
343+
const groupLength = before[group].length
344+
before[group].forEach((groupIndex, index) => {
345+
transformed[groupIndex].position = -1 * (groupLength - index)
346+
})
347+
maxPosition = Math.max(maxPosition, groupLength)
348+
})
349+
350+
Object.keys(after).forEach((key) => {
351+
const groupNextPosition = after[key]
352+
maxPosition = Math.max(maxPosition, groupNextPosition - 1)
353+
})
354+
355+
return {
356+
pathGroups: transformed,
357+
maxPosition: maxPosition > 10 ? Math.pow(10, Math.ceil(Math.log10(maxPosition))) : 10,
358+
}
359+
}
360+
297361
function fixNewLineAfterImport(context, previousImport) {
298362
const prevRoot = findRootNode(previousImport.node)
299363
const tokensToEndOfLine = takeTokensAfterWhile(
@@ -378,6 +442,29 @@ module.exports = {
378442
groups: {
379443
type: 'array',
380444
},
445+
pathGroups: {
446+
type: 'array',
447+
items: {
448+
type: 'object',
449+
properties: {
450+
pattern: {
451+
type: 'string',
452+
},
453+
patternOptions: {
454+
type: 'object',
455+
},
456+
group: {
457+
type: 'string',
458+
enum: types,
459+
},
460+
position: {
461+
type: 'string',
462+
enum: ['after', 'before'],
463+
},
464+
},
465+
required: ['pattern', 'group'],
466+
},
467+
},
381468
'newlines-between': {
382469
enum: [
383470
'ignore',
@@ -398,7 +485,12 @@ module.exports = {
398485
let ranks
399486

400487
try {
401-
ranks = convertGroupsToRanks(options.groups || defaultGroups)
488+
const { pathGroups, maxPosition } = convertPathGroupsForRanks(options.pathGroups || [])
489+
ranks = {
490+
groups: convertGroupsToRanks(options.groups || defaultGroups),
491+
pathGroups,
492+
maxPosition,
493+
}
402494
} catch (error) {
403495
// Malformed configuration
404496
return {

0 commit comments

Comments
 (0)