diff --git a/CHANGELOG.md b/CHANGELOG.md index edcde8533..fb347894f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ This change log adheres to standards from [Keep a CHANGELOG](http://keepachangel - add [`no-relative-packages`] ([#1860], [#966], thanks [@tapayne88] [@panrafal]) - add [`no-import-module-exports`] rule: report import declarations with CommonJS exports ([#804], thanks [@kentcdodds] and [@ttmarek]) - [`no-unused-modules`]: Support destructuring assignment for `export`. ([#1997], thanks [@s-h-a-d-o-w]) +- [`order`]: support type imports ([#2021], thanks [@grit96]) ### Fixed - [`export`]/TypeScript: properly detect export specifiers as children of a TS module block ([#1889], thanks [@andreubotella]) @@ -761,6 +762,7 @@ for info on changes for earlier releases. [`memo-parser`]: ./memo-parser/README.md +[#2021]: https://github.com/benmosher/eslint-plugin-import/pull/2021 [#1997]: https://github.com/benmosher/eslint-plugin-import/pull/1997 [#1993]: https://github.com/benmosher/eslint-plugin-import/pull/1993 [#1983]: https://github.com/benmosher/eslint-plugin-import/pull/1983 @@ -1347,3 +1349,4 @@ for info on changes for earlier releases. [@devongovett]: https://github.com/devongovett [@dwardu]: https://github.com/dwardu [@s-h-a-d-o-w]: https://github.com/s-h-a-d-o-w +[@grit96]: https://github.com/grit96 diff --git a/docs/rules/order.md b/docs/rules/order.md index 7d91efd6f..d760c2c0f 100644 --- a/docs/rules/order.md +++ b/docs/rules/order.md @@ -24,6 +24,8 @@ import baz from './bar/baz'; import main from './'; // 7. "object"-imports (only available in TypeScript) import log = console.log; +// 8. "type" imports (only available in Flow and TypeScript) +import type { Foo } from 'foo'; ``` Unassigned imports are ignored, as the order they are imported in may be important. @@ -80,7 +82,7 @@ This rule supports the following options: ### `groups: [array]`: How groups are defined, and the order to respect. `groups` must be an array of `string` or [`string`]. The only allowed `string`s are: -`"builtin"`, `"external"`, `"internal"`, `"unknown"`, `"parent"`, `"sibling"`, `"index"`, `"object"`. +`"builtin"`, `"external"`, `"internal"`, `"unknown"`, `"parent"`, `"sibling"`, `"index"`, `"object"`, `"type"`. The enforced order is the same as the order of each element in a group. Omitted types are implicitly grouped together as the last element. Example: ```js [ @@ -96,7 +98,7 @@ The default value is `["builtin", "external", "parent", "sibling", "index"]`. You can set the options like this: ```js -"import/order": ["error", {"groups": ["index", "sibling", "parent", "internal", "external", "builtin", "object"]}] +"import/order": ["error", {"groups": ["index", "sibling", "parent", "internal", "external", "builtin", "object", "type"]}] ``` ### `pathGroups: [array of objects]`: diff --git a/src/rules/order.js b/src/rules/order.js index bad382945..3aabbc74c 100644 --- a/src/rules/order.js +++ b/src/rules/order.js @@ -265,7 +265,7 @@ function mutateRanksToAlphabetize(imported, alphabetizeOptions) { if (!Array.isArray(acc[importedItem.rank])) { acc[importedItem.rank] = []; } - acc[importedItem.rank].push(importedItem.value); + acc[importedItem.rank].push(`${importedItem.value}-${importedItem.node.importKind}`); return acc; }, {}); @@ -290,7 +290,7 @@ function mutateRanksToAlphabetize(imported, alphabetizeOptions) { // mutate the original group-rank with alphabetized-rank imported.forEach(function(importedItem) { - importedItem.rank = alphabetizedRanks[importedItem.value]; + importedItem.rank = alphabetizedRanks[`${importedItem.value}-${importedItem.node.importKind}`]; }); } @@ -310,6 +310,8 @@ function computeRank(context, ranks, importEntry, excludedImportTypes) { let rank; if (importEntry.type === 'import:object') { impType = 'object'; + } else if (importEntry.node.importKind === 'type') { + impType = 'type'; } else { impType = importType(importEntry.value, context); } @@ -350,7 +352,7 @@ function isModuleLevelRequire(node) { ); } -const types = ['builtin', 'external', 'internal', 'unknown', 'parent', 'sibling', 'index', 'object']; +const types = ['builtin', 'external', 'internal', 'unknown', 'parent', 'sibling', 'index', 'object', 'type']; // Creates an object with type-rank pairs. // Example: { index: 0, sibling: 1, parent: 1, external: 1, builtin: 2, internal: 2 } diff --git a/tests/src/rules/order.js b/tests/src/rules/order.js index 42f99c2db..4fcbb29d7 100644 --- a/tests/src/rules/order.js +++ b/tests/src/rules/order.js @@ -2186,12 +2186,13 @@ context('TypeScript', function () { { code: ` import c from 'Bar'; - import type { C } from 'Bar'; import b from 'bar'; import a from 'foo'; - import type { A } from 'foo'; import index from './'; + + import type { C } from 'Bar'; + import type { A } from 'foo'; `, parser, options: [ @@ -2208,12 +2209,13 @@ context('TypeScript', function () { { code: ` import a from 'foo'; - import type { A } from 'foo'; import b from 'bar'; import c from 'Bar'; - import type { C } from 'Bar'; import index from './'; + + import type { A } from 'foo'; + import type { C } from 'Bar'; `, parser, options: [ @@ -2233,20 +2235,22 @@ context('TypeScript', function () { code: ` import b from 'bar'; import c from 'Bar'; - import type { C } from 'Bar'; import a from 'foo'; - import type { A } from 'foo'; import index from './'; + + import type { A } from 'foo'; + import type { C } from 'Bar'; `, output: ` import c from 'Bar'; - import type { C } from 'Bar'; import b from 'bar'; import a from 'foo'; - import type { A } from 'foo'; import index from './'; + + import type { C } from 'Bar'; + import type { A } from 'foo'; `, parser, options: [ @@ -2255,12 +2259,12 @@ context('TypeScript', function () { alphabetize: { order: 'asc' }, }, ], - errors: [ - { - message: semver.satisfies(eslintPkg.version, '< 3') - ? '`bar` import should occur after import of `Bar`' - : /(`bar` import should occur after import of `Bar`)|(`Bar` import should occur before import of `bar`)/, - }, + errors: semver.satisfies(eslintPkg.version, '< 3') ? [ + { message: '`Bar` import should occur before import of `bar`' }, + { message: '`Bar` import should occur before import of `foo`' }, + ] : [ + { message: /(`Bar` import should occur before import of `bar`)|(`bar` import should occur after import of `Bar`)/ }, + { message: /(`Bar` import should occur before import of `foo`)|(`foo` import should occur after import of `Bar`)/ }, ], }, parserConfig, @@ -2270,21 +2274,23 @@ context('TypeScript', function () { { code: ` import a from 'foo'; - import type { A } from 'foo'; import c from 'Bar'; - import type { C } from 'Bar'; import b from 'bar'; import index from './'; + + import type { C } from 'Bar'; + import type { A } from 'foo'; `, output: ` import a from 'foo'; - import type { A } from 'foo'; import b from 'bar'; import c from 'Bar'; - import type { C } from 'Bar'; import index from './'; + + import type { A } from 'foo'; + import type { C } from 'Bar'; `, parser, options: [ @@ -2293,12 +2299,12 @@ context('TypeScript', function () { alphabetize: { order: 'desc' }, }, ], - errors: [ - { - message: semver.satisfies(eslintPkg.version, '< 3') - ? '`bar` import should occur before import of `Bar`' - : /(`bar` import should occur before import of `Bar`)|(`Bar` import should occur after import of `bar`)/, - }, + errors: semver.satisfies(eslintPkg.version, '< 3') ? [ + { message: '`bar` import should occur before import of `Bar`' }, + { message: '`foo` import should occur before import of `Bar`' }, + ] : [ + { message: /(`bar` import should occur before import of `Bar`)|(`Bar` import should occur after import of `bar`)/ }, + { message: /(`foo` import should occur before import of `Bar`)|(`Bar` import should occur after import of `foo`)/ }, ], }, parserConfig,