diff --git a/CHANGELOG.md b/CHANGELOG.md index b3061594a..c05d4a045 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,9 @@ This change log adheres to standards from [Keep a CHANGELOG](http://keepachangel - [`no-extraneous-dependencies`]: ensure `node.source` is truthy ([#1589], thanks [@ljharb]) - [`extensions`]: Ignore query strings when checking for extensions ([#1572], thanks [@pcorpet]) +### Added +- [`order`]: add option pathGroupsExcludedImportTypes to allow ordering of external import types ([#1565], thanks [@Mairu]) + ## [2.19.1] - 2019-12-08 ### Fixed - [`no-extraneous-dependencies`]: ensure `node.source` exists @@ -789,6 +792,7 @@ for info on changes for earlier releases. [#211]: https://github.com/benmosher/eslint-plugin-import/pull/211 [#164]: https://github.com/benmosher/eslint-plugin-import/pull/164 [#157]: https://github.com/benmosher/eslint-plugin-import/pull/157 +[#1565]: https://github.com/benmosher/eslint-plugin-import/issues/1565 [#1366]: https://github.com/benmosher/eslint-plugin-import/issues/1366 [#1334]: https://github.com/benmosher/eslint-plugin-import/issues/1334 [#1323]: https://github.com/benmosher/eslint-plugin-import/issues/1323 diff --git a/docs/rules/order.md b/docs/rules/order.md index 8012e637f..667b63374 100644 --- a/docs/rules/order.md +++ b/docs/rules/order.md @@ -96,7 +96,7 @@ You can set the options like this: ### `pathGroups: [array of objects]`: -To be able so group by paths mostly needed with aliases pathGroups can be defined. +To be able to group by paths mostly needed with aliases pathGroups can be defined. Properties of the objects @@ -120,6 +120,28 @@ Properties of the objects } ``` +### `pathGroupsExcludedImportTypes: [array]`: + +This defines import types that are not handled by configured pathGroups. +This is mostly needed when you want to handle path groups that look like external imports. + +Example: +```json +{ + "import/order": ["error", { + "pathGroups": [ + { + "pattern": "@app/**", + "group": "external", + "position": "after" + } + ], + "pathGroupsExcludedImportTypes": ["builtin"] + }] +} +``` +The default value is `["builtin", "external"]`. + ### `newlines-between: [ignore|always|always-and-inside-groups|never]`: Enforces or forbids new lines between import groups: diff --git a/src/rules/order.js b/src/rules/order.js index a842f60af..fcdf12bda 100644 --- a/src/rules/order.js +++ b/src/rules/order.js @@ -269,7 +269,7 @@ function importsSorterDesc(importA, importB) { function mutateRanksToAlphabetize(imported, alphabetizeOptions) { const groupedByRanks = imported.reduce(function(acc, importedItem) { - if (!Array.isArray(acc[importedItem.rank])) { + if (!Array.isArray(acc[importedItem.rank])) { acc[importedItem.rank] = [] } acc[importedItem.rank].push(importedItem.name) @@ -312,10 +312,10 @@ function computePathRank(ranks, pathGroups, path, maxPosition) { } } -function computeRank(context, ranks, name, type) { +function computeRank(context, ranks, name, type, excludedImportTypes) { const impType = importType(name, context) let rank - if (impType !== 'builtin' && impType !== 'external') { + if (!excludedImportTypes.has(impType)) { rank = computePathRank(ranks.groups, ranks.pathGroups, name, ranks.maxPosition) } if (!rank) { @@ -328,8 +328,8 @@ function computeRank(context, ranks, name, type) { return rank } -function registerNode(context, node, name, type, ranks, imported) { - const rank = computeRank(context, ranks, name, type) +function registerNode(context, node, name, type, ranks, imported, excludedImportTypes) { + const rank = computeRank(context, ranks, name, type, excludedImportTypes) if (rank !== -1) { imported.push({name, rank, node}) } @@ -508,6 +508,9 @@ module.exports = { groups: { type: 'array', }, + pathGroupsExcludedImportTypes: { + type: 'array', + }, pathGroups: { type: 'array', items: { @@ -562,6 +565,7 @@ module.exports = { create: function importOrderRule (context) { const options = context.options[0] || {} const newlinesBetweenImports = options['newlines-between'] || 'ignore' + const pathGroupsExcludedImportTypes = new Set(options['pathGroupsExcludedImportTypes'] || ['builtin', 'external']) const alphabetize = getAlphabetizeConfig(options) let ranks @@ -594,7 +598,15 @@ module.exports = { ImportDeclaration: function handleImports(node) { if (node.specifiers.length) { // Ignoring unassigned imports const name = node.source.value - registerNode(context, node, name, 'import', ranks, imported) + registerNode( + context, + node, + name, + 'import', + ranks, + imported, + pathGroupsExcludedImportTypes + ) } }, CallExpression: function handleRequires(node) { @@ -602,7 +614,15 @@ module.exports = { return } const name = node.arguments[0].value - registerNode(context, node, name, 'require', ranks, imported) + registerNode( + context, + node, + name, + 'require', + ranks, + imported, + pathGroupsExcludedImportTypes + ) }, 'Program:exit': function reportAndReset() { if (newlinesBetweenImports !== 'ignore') { diff --git a/tests/src/rules/order.js b/tests/src/rules/order.js index 11309f933..8ba8b9f1e 100644 --- a/tests/src/rules/order.js +++ b/tests/src/rules/order.js @@ -276,6 +276,28 @@ ruleTester.run('order', rule, { ], }], }), + // Using pathGroups to customize ordering for imports that are recognized as 'external' + // by setting pathGroupsExcludedImportTypes without 'external' + test({ + code: ` + import fs from 'fs'; + + import { Input } from '@app/components/Input'; + + import { Button } from '@app2/components/Button'; + + import _ from 'lodash'; + + import { add } from './helper';`, + options: [{ + 'newlines-between': 'always', + pathGroupsExcludedImportTypes: ['builtin'], + pathGroups: [ + { pattern: '@app/**', group: 'external', position: 'before' }, + { pattern: '@app2/**', group: 'external', position: 'before' }, + ], + }], + }), // Option: newlines-between: 'always' test({