diff --git a/bun.lockb b/bun.lockb index 2f59a50..752c9d9 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/src/__tests__/imports.spec.ts b/src/__tests__/imports.spec.ts index 7ad5c92..ecc5c87 100644 --- a/src/__tests__/imports.spec.ts +++ b/src/__tests__/imports.spec.ts @@ -917,6 +917,35 @@ createRuleTester({ }, ], }, + { + name: "Sort groups - import = require()", + code: dedent` + import 'index.css' + import 'side-effect' + import a from "dependency-b" + import b from "dependency-c" + import type { A } from "dependency-a" + import c from "a.png" + import d from "b.jpg" + import e = require("a") + import f from "b" + import g from "c" + import h from "../b" + import i from "./b" + `, + options: [ + { + groups: [ + { type: "side-effect", order: 10 }, + { type: "type", order: 30 }, + { regex: "\\.(png|jpg)$", order: 40 }, + { regex: "^\\.+\\/", order: 60 }, + { type: "dependency", order: 20 }, + { type: "other", order: 50 }, + ], + }, + ], + }, ], invalid: [ { @@ -1051,6 +1080,7 @@ createRuleTester({ code: dedent` import c from "a.png" import h = require("../b") + import "./styles.css" import b from "dependency-c" import type { A } from "dependency-a" import d = require("b.jpg") @@ -1065,11 +1095,13 @@ createRuleTester({ import d = require("b.jpg") import h = require("../b") import i from "./b" + import "./styles.css" `, errors: [{ messageId: "unsorted" }], options: [ { groups: [ + { type: "side-effect", order: 60 }, { type: "type", order: 30 }, { regex: "\\.(png|jpg)$", order: 40 }, { regex: "^\\.+\\/", order: 60 }, diff --git a/src/rules/imports.ts b/src/rules/imports.ts index f4d1117..84c1498 100644 --- a/src/rules/imports.ts +++ b/src/rules/imports.ts @@ -1,6 +1,8 @@ import { Rule, AST } from "eslint" +import { TSESTree } from "@typescript-eslint/experimental-utils" import * as ESTree from "estree" import { isResolved } from "../resolver.js" +import * as tsUtils from "../ts-utils.js" import { docsURL, enumerate, @@ -20,6 +22,8 @@ import { const sortGroupsTypes = ["side-effect", "dependency", "type", "other"] as const +type Import = ESTree.ImportDeclaration | TSESTree.TSImportEqualsDeclaration + interface SortGroup { order: number type?: (typeof sortGroupsTypes)[number] @@ -32,23 +36,25 @@ interface Options extends SorterOptions { typeOrder?: TypeOrder } -const getSortValue = (node: ESTree.ImportDeclaration) => +const getSortValue = (node: Import) => node.type === "ImportDeclaration" ? getName(node.source) - : getName((node as any).moduleReference.expression) + : tsUtils.getName(node.moduleReference) /** * Returns the order of a given node based on the sort groups configured in the * rule options. If no sort groups are configured (default), the order returned * is always 0. */ -function getSortGroup(sortGroups: SortGroup[], node: ESTree.ImportDeclaration) { +function getSortGroup(sortGroups: SortGroup[], node: Import) { const source = getSortValue(node) for (const { regex, type, order } of sortGroups) { switch (type) { case "side-effect": - if (!node.specifiers.length) return order + if (node.type === "ImportDeclaration" && !node.specifiers.length) { + return order + } break case "type": { @@ -73,10 +79,7 @@ function getSortGroup(sortGroups: SortGroup[], node: ESTree.ImportDeclaration) { return 0 } -function getImportKindWeight( - options: Options | undefined, - node: ESTree.ImportDeclaration -) { +function getImportKindWeight(options: Options | undefined, node: Import) { const typeOrder = options?.typeOrder ?? "preserve" const kind = (node as { importKind?: ImportOrExportKind }).importKind @@ -136,7 +139,7 @@ export default { // When sorting, the comments for the first node are not copied as // we cannot determine if they are comments for the entire file or // just the first import. - const isFirst = (node: ESTree.ImportDeclaration) => node === nodes[0] + const isFirst = (node: ESTree.Node) => node === nodes[0] context.report({ node: firstUnsortedNode, diff --git a/src/ts-utils.ts b/src/ts-utils.ts index 0548ad5..78418bc 100644 --- a/src/ts-utils.ts +++ b/src/ts-utils.ts @@ -10,6 +10,9 @@ import { getTextRange } from "./utils.js" */ export function getName(node?: TSESTree.Node): string { switch (node?.type) { + case AST_NODE_TYPES.TSExternalModuleReference: + return getName(node.expression) + case AST_NODE_TYPES.Identifier: return node.name