Skip to content

Commit f5d95e8

Browse files
neurolagljharb
authored andcommitted
[Fix] order/TypeScript: properly support import = object expressions
Just like ordinary `import x =` expressions, `export import x =` expressions can come with a number of different module-references. Either a require-expression such as `export import fs = require("fs")`, a literal such as `export import Console = console;` or an object-path `export import log = console.log`. This means, that the `isExport` property merely says whether the `TSImportEqualsDeclaration` has a leading `export`, but not what the `moduleReference` looks like. ---- This arguably is a semver-minor, but since it should have been included in import-js#1785, I'm calling this a bugfix. Fixes import-js#1821. Fixes import-js#1808.
1 parent b22a183 commit f5d95e8

File tree

6 files changed

+104
-19
lines changed

6 files changed

+104
-19
lines changed

.babelrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
{
22
"presets": [ "es2015-argon" ],
33
"sourceMaps": "inline",
4+
"retainLines": true,
45
"env": {
56
"test": {
67
"plugins": [

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ This change log adheres to standards from [Keep a CHANGELOG](http://keepachangel
99
- [`no-unused-modules`]: consider exported TypeScript interfaces, types and enums ([#1819], thanks [@nicolashenry])
1010

1111
### Fixed
12+
- [`order`]/TypeScript: properly support `import = object` expressions ([#1823], thanks [@manuth])
1213
- [`no-extraneous-dependencies`]/TypeScript: do not error when importing type from dev dependencies ([#1820], thanks [@fernandopasik])
1314
- [`default`]: avoid crash with `export =` ([#1822], thanks [@AndrewLeedham])
1415

@@ -712,6 +713,7 @@ for info on changes for earlier releases.
712713
[`memo-parser`]: ./memo-parser/README.md
713714

714715
[#1824]: https://github.com/benmosher/eslint-plugin-import/pull/1824
716+
[#1823]: https://github.com/benmosher/eslint-plugin-import/pull/1823
715717
[#1822]: https://github.com/benmosher/eslint-plugin-import/pull/1822
716718
[#1820]: https://github.com/benmosher/eslint-plugin-import/pull/1820
717719
[#1819]: https://github.com/benmosher/eslint-plugin-import/pull/1819

docs/rules/order.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ import bar from './bar';
2222
import baz from './bar/baz';
2323
// 6. "index" of the current directory
2424
import main from './';
25+
// 7. "object"-imports (only available in TypeScript)
26+
import log = console.log;
2527
```
2628

2729
Unassigned imports are ignored, as the order they are imported in may be important.
@@ -77,12 +79,15 @@ This rule supports the following options:
7779

7880
### `groups: [array]`:
7981

80-
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"`. 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:
82+
How groups are defined, and the order to respect. `groups` must be an array of `string` or [`string`]. The only allowed `string`s are:
83+
`"builtin"`, `"external"`, `"internal"`, `"unknown"`, `"parent"`, `"sibling"`, `"index"`, `"object"`.
84+
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:
8185
```js
8286
[
8387
'builtin', // Built-in types are first
8488
['sibling', 'parent'], // Then sibling and parent types. They can be mingled together
8589
'index', // Then the index file
90+
'object',
8691
// Then the rest: internal and external type
8792
]
8893
```
@@ -91,7 +96,7 @@ The default value is `["builtin", "external", "parent", "sibling", "index"]`.
9196
You can set the options like this:
9297

9398
```js
94-
"import/order": ["error", {"groups": ["index", "sibling", "parent", "internal", "external", "builtin"]}]
99+
"import/order": ["error", {"groups": ["index", "sibling", "parent", "internal", "external", "builtin", "object"]}]
95100
```
96101

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

src/rules/order.js

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -248,14 +248,14 @@ function makeOutOfOrderReport(context, imported) {
248248
}
249249

250250
function getSorter(ascending) {
251-
let multiplier = (ascending ? 1 : -1)
251+
const multiplier = ascending ? 1 : -1
252252

253253
return function importsSorter(importA, importB) {
254254
let result
255255

256-
if ((importA < importB) || importB === null) {
256+
if (importA < importB) {
257257
result = -1
258-
} else if ((importA > importB) || importA === null) {
258+
} else if (importA > importB) {
259259
result = 1
260260
} else {
261261
result = 0
@@ -310,24 +310,29 @@ function computePathRank(ranks, pathGroups, path, maxPosition) {
310310
}
311311
}
312312

313-
function computeRank(context, ranks, name, type, excludedImportTypes) {
314-
const impType = importType(name, context)
313+
function computeRank(context, node, ranks, name, type, excludedImportTypes) {
314+
let impType
315+
if (type === 'import:object') {
316+
impType = 'object'
317+
} else {
318+
impType = importType(name, context)
319+
}
315320
let rank
316321
if (!excludedImportTypes.has(impType)) {
317322
rank = computePathRank(ranks.groups, ranks.pathGroups, name, ranks.maxPosition)
318323
}
319324
if (typeof rank === 'undefined') {
320325
rank = ranks.groups[impType]
321326
}
322-
if (type !== 'import') {
327+
if (type !== 'import' && !type.startsWith('import:')) {
323328
rank += 100
324329
}
325330

326331
return rank
327332
}
328333

329334
function registerNode(context, node, name, type, ranks, imported, excludedImportTypes) {
330-
const rank = computeRank(context, ranks, name, type, excludedImportTypes)
335+
const rank = computeRank(context, node, ranks, name, type, excludedImportTypes)
331336
if (rank !== -1) {
332337
imported.push({name, rank, node})
333338
}
@@ -338,7 +343,7 @@ function isInVariableDeclarator(node) {
338343
(node.type === 'VariableDeclarator' || isInVariableDeclarator(node.parent))
339344
}
340345

341-
const types = ['builtin', 'external', 'internal', 'unknown', 'parent', 'sibling', 'index']
346+
const types = ['builtin', 'external', 'internal', 'unknown', 'parent', 'sibling', 'index', 'object']
342347

343348
// Creates an object with type-rank pairs.
344349
// Example: { index: 0, sibling: 1, parent: 1, external: 1, builtin: 2, internal: 2 }
@@ -563,7 +568,7 @@ module.exports = {
563568
create: function importOrderRule (context) {
564569
const options = context.options[0] || {}
565570
const newlinesBetweenImports = options['newlines-between'] || 'ignore'
566-
const pathGroupsExcludedImportTypes = new Set(options['pathGroupsExcludedImportTypes'] || ['builtin', 'external'])
571+
const pathGroupsExcludedImportTypes = new Set(options['pathGroupsExcludedImportTypes'] || ['builtin', 'external', 'object'])
567572
const alphabetize = getAlphabetizeConfig(options)
568573
let ranks
569574

@@ -609,18 +614,19 @@ module.exports = {
609614
},
610615
TSImportEqualsDeclaration: function handleImports(node) {
611616
let name
617+
let type
612618
if (node.moduleReference.type === 'TSExternalModuleReference') {
613619
name = node.moduleReference.expression.value
614-
} else if (node.isExport) {
615-
name = node.moduleReference.name
620+
type = 'import'
616621
} else {
617-
name = null
622+
name = context.getSourceCode().getText(node.moduleReference)
623+
type = 'import:object'
618624
}
619625
registerNode(
620626
context,
621627
node,
622628
name,
623-
'import',
629+
type,
624630
ranks,
625631
imported,
626632
pathGroupsExcludedImportTypes

tests/src/cli.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,14 +58,14 @@ describe('CLI regression tests', function () {
5858
nodeType: results.results[0].messages[0].nodeType, // we don't care about this one
5959
ruleId: 'json/*',
6060
severity: 2,
61-
source: '\n',
61+
source: results.results[0].messages[0].source, // NewLine-characters might differ depending on git-settings
6262
},
6363
],
6464
errorCount: 1,
6565
warningCount: 0,
6666
fixableErrorCount: 0,
6767
fixableWarningCount: 0,
68-
source: ',\n',
68+
source: results.results[0].source, // NewLine-characters might differ depending on git-settings
6969
},
7070
],
7171
errorCount: 1,

tests/src/rules/order.js

Lines changed: 73 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -711,6 +711,49 @@ ruleTester.run('order', rule, {
711711
},
712712
],
713713
}),
714+
...flatMap(getTSParsers, parser => [
715+
// Order of the `import ... = require(...)` syntax
716+
test({
717+
code: `
718+
import blah = require('./blah');
719+
import { hello } from './hello';`,
720+
parser,
721+
options: [
722+
{
723+
alphabetize: {
724+
order: 'asc',
725+
},
726+
},
727+
],
728+
}),
729+
// Order of object-imports
730+
test({
731+
code: `
732+
import blah = require('./blah');
733+
import log = console.log;`,
734+
parser,
735+
options: [
736+
{
737+
alphabetize: {
738+
order: 'asc',
739+
},
740+
},
741+
],
742+
}),
743+
test({
744+
code: `
745+
import debug = console.debug;
746+
import log = console.log;`,
747+
parser,
748+
options: [
749+
{
750+
alphabetize: {
751+
order: 'asc',
752+
},
753+
},
754+
],
755+
}),
756+
]),
714757
],
715758
invalid: [
716759
// builtin before external module (require)
@@ -1167,6 +1210,7 @@ ruleTester.run('order', rule, {
11671210
}],
11681211
}),
11691212
...flatMap(getTSParsers(), parser => [
1213+
// Order of the `import ... = require(...)` syntax
11701214
test({
11711215
code: `
11721216
var fs = require('fs');
@@ -1183,7 +1227,7 @@ ruleTester.run('order', rule, {
11831227
message: '`fs` import should occur after import of `../foo/bar`',
11841228
}],
11851229
}),
1186-
{
1230+
test({
11871231
code: `
11881232
var async = require('async');
11891233
var fs = require('fs');
@@ -1196,7 +1240,7 @@ ruleTester.run('order', rule, {
11961240
errors: [{
11971241
message: '`fs` import should occur before import of `async`',
11981242
}],
1199-
},
1243+
}),
12001244
test({
12011245
code: `
12021246
import sync = require('sync');
@@ -1219,6 +1263,33 @@ ruleTester.run('order', rule, {
12191263
message: '`async` import should occur before import of `sync`',
12201264
}],
12211265
}),
1266+
// Order of object-imports
1267+
test({
1268+
code: `
1269+
import log = console.log;
1270+
import blah = require('./blah');`,
1271+
parser,
1272+
errors: [{
1273+
message: '`./blah` import should occur before import of `console.log`',
1274+
}],
1275+
}),
1276+
// Alphabetization of object-imports
1277+
test({
1278+
code: `
1279+
import log = console.log;
1280+
import debug = console.debug;`,
1281+
parser,
1282+
errors: [{
1283+
message: '`console.debug` import should occur before import of `console.log`',
1284+
}],
1285+
options: [
1286+
{
1287+
alphabetize: {
1288+
order: 'asc',
1289+
},
1290+
},
1291+
],
1292+
}),
12221293
]),
12231294
// Default order using import with custom import alias
12241295
test({

0 commit comments

Comments
 (0)