From 62b554bc5f5024695b40d8701fbd8267fb4eb73f Mon Sep 17 00:00:00 2001 From: Manuel Thalmann Date: Fri, 29 May 2020 21:26:28 +0200 Subject: [PATCH] [New] `order`: Add support for TypeScript's "import equals"-expressions --- CHANGELOG.md | 3 +++ package.json | 4 +-- src/rules/order.js | 55 +++++++++++++++++++++++-------------- tests/src/rules/order.js | 58 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 98 insertions(+), 22 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a97d84aa2..e4d37cdd5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ This change log adheres to standards from [Keep a CHANGELOG](http://keepachangel ### Added - [`import/default`]: support default export in TSExportAssignment ([#1528], thanks [@joaovieira]) - [`no-cycle`]: add `ignoreExternal` option ([#1681], thanks [@sveyret]) +- [`order`]: Add support for TypeScript's "import equals"-expressions ([#1785], thanks [@manuth]) ### Fixed - [`group-exports`]: Flow type export awareness ([#1702], thanks [@ernestostifano]) @@ -678,6 +679,7 @@ for info on changes for earlier releases. [`memo-parser`]: ./memo-parser/README.md [#1788]: https://github.com/benmosher/eslint-plugin-import/pull/1788 +[#1785]: https://github.com/benmosher/eslint-plugin-import/pull/1785 [#1770]: https://github.com/benmosher/eslint-plugin-import/pull/1770 [#1726]: https://github.com/benmosher/eslint-plugin-import/pull/1726 [#1724]: https://github.com/benmosher/eslint-plugin-import/pull/1724 @@ -1162,3 +1164,4 @@ for info on changes for earlier releases. [@atos1990]: https://github.com/atos1990 [@Hypnosphi]: https://github.com/Hypnosphi [@nickofthyme]: https://github.com/nickofthyme +[@manuth]: https://github.com/manuth diff --git a/package.json b/package.json index acbf3dbbf..90596b292 100644 --- a/package.json +++ b/package.json @@ -25,8 +25,8 @@ "watch": "npm run tests-only -- -- --watch", "pretest": "linklocal", "posttest": "eslint .", - "mocha": "nyc -s mocha", - "tests-only": "cross-env BABEL_ENV=test npm run mocha tests/src", + "mocha": "cross-env BABEL_ENV=test nyc -s mocha", + "tests-only": "npm run mocha tests/src", "test": "npm run tests-only", "test-compiled": "npm run prepublish && BABEL_ENV=testCompiled mocha --compilers js:babel-register tests/src", "test-all": "npm test && for resolver in ./resolvers/*; do cd $resolver && npm test && cd ../..; done", diff --git a/src/rules/order.js b/src/rules/order.js index f17b6ad0c..9edac3af9 100644 --- a/src/rules/order.js +++ b/src/rules/order.js @@ -157,8 +157,12 @@ function isPlainImportModule(node) { return node.type === 'ImportDeclaration' && node.specifiers != null && node.specifiers.length > 0 } +function isPlainImportEquals(node) { + return node.type === 'TSImportEqualsDeclaration' && node.moduleReference.expression +} + function canCrossNodeWhileReorder(node) { - return isPlainRequireModule(node) || isPlainImportModule(node) + return isPlainRequireModule(node) || isPlainImportModule(node) || isPlainImportEquals(node) } function canReorderItems(firstNode, secondNode) { @@ -243,28 +247,22 @@ function makeOutOfOrderReport(context, imported) { reportOutOfOrder(context, imported, outOfOrder, 'before') } -function importsSorterAsc(importA, importB) { - if (importA < importB) { - return -1 - } - - if (importA > importB) { - return 1 - } +function getSorter(ascending) { + let multiplier = (ascending ? 1 : -1) - return 0 -} + return function importsSorter(importA, importB) { + let result -function importsSorterDesc(importA, importB) { - if (importA < importB) { - return 1 - } + if ((importA < importB) || importB === null) { + result = -1 + } else if ((importA > importB) || importA === null) { + result = 1 + } else { + result = 0 + } - if (importA > importB) { - return -1 + return result * multiplier } - - return 0 } function mutateRanksToAlphabetize(imported, alphabetizeOptions) { @@ -278,7 +276,7 @@ function mutateRanksToAlphabetize(imported, alphabetizeOptions) { const groupRanks = Object.keys(groupedByRanks) - const sorterFn = alphabetizeOptions.order === 'asc' ? importsSorterAsc : importsSorterDesc + const sorterFn = getSorter(alphabetizeOptions.order === 'asc') const comparator = alphabetizeOptions.caseInsensitive ? (a, b) => sorterFn(String(a).toLowerCase(), String(b).toLowerCase()) : (a, b) => sorterFn(a, b) // sort imports locally within their group groupRanks.forEach(function(groupRank) { @@ -609,6 +607,23 @@ module.exports = { ) } }, + TSImportEqualsDeclaration: function handleImports(node) { + let name + if (node.moduleReference.type === 'TSExternalModuleReference') { + name = node.moduleReference.expression.value + } else { + name = null + } + registerNode( + context, + node, + name, + 'import', + ranks, + imported, + pathGroupsExcludedImportTypes + ) + }, CallExpression: function handleRequires(node) { if (level !== 0 || !isStaticRequire(node) || !isInVariableDeclarator(node.parent)) { return diff --git a/tests/src/rules/order.js b/tests/src/rules/order.js index aee2b1124..529582e84 100644 --- a/tests/src/rules/order.js +++ b/tests/src/rules/order.js @@ -167,6 +167,22 @@ ruleTester.run('order', rule, { var index = require('./'); `, }), + // Export equals expressions should be on top alongside with ordinary import-statements. + ...getTSParsers().map(parser => ( + test({ + code: ` + import async, {foo1} from 'async'; + import relParent2, {foo2} from '../foo/bar'; + import sibling, {foo3} from './foo'; + var fs = require('fs'); + var util = require("util"); + var relParent1 = require('../foo'); + var relParent3 = require('../'); + var index = require('./'); + `, + parser, + }) + )), // Adding unknown import types (e.g. using a resolver alias via babel) to the groups. test({ code: ` @@ -1142,6 +1158,24 @@ ruleTester.run('order', rule, { message: '`fs` import should occur after import of `../foo/bar`', }], }), + ...getTSParsers().map(parser => ( + test({ + code: ` + var fs = require('fs'); + import async, {foo1} from 'async'; + import bar = require("../foo/bar"); + `, + output: ` + import async, {foo1} from 'async'; + import bar = require("../foo/bar"); + var fs = require('fs'); + `, + parser, + errors: [{ + message: '`fs` import should occur after import of `../foo/bar`', + }], + }) + )), // Default order using import with custom import alias test({ code: ` @@ -1913,6 +1947,30 @@ ruleTester.run('order', rule, { message: '`Bar` import should occur before import of `bar`', }], }), + ...getTSParsers().map(parser => ( + test({ + code: ` + import sync = require('sync'); + import async, {foo1} from 'async'; + + import index from './'; + `, + output: ` + import async, {foo1} from 'async'; + import sync = require('sync'); + + import index from './'; + `, + options: [{ + groups: ['external', 'index'], + alphabetize: {order: 'asc'}, + }], + parser, + errors: [{ + message: '`async` import should occur before import of `sync`', + }], + }) + )), // Option alphabetize: {order: 'desc'} test({ code: `