From fa8de6e1d4497a68a0dc01e4fa4d856ce8669b55 Mon Sep 17 00:00:00 2001 From: Mark Skelton Date: Fri, 22 Mar 2024 17:24:39 -0500 Subject: [PATCH] fix(imports): Fix `side-effect` sort group with `import = require()` syntax --- bun.lockb | Bin 363573 -> 363573 bytes src/__tests__/imports.spec.ts | 32 ++++++++++++++++++++++++++++++++ src/rules/imports.ts | 21 ++++++++++++--------- src/ts-utils.ts | 3 +++ 4 files changed, 47 insertions(+), 9 deletions(-) diff --git a/bun.lockb b/bun.lockb index 2f59a50000c71bdbe1565c8454e1565d5371d30a..752c9d9fe8d1aeeb294769a72231ff8b90c15234 100755 GIT binary patch delta 6410 zcmXBX_Zzlk9ftAaeKp!on>KCQv?&|3X;Ycu#*G^{Zrr?1v`n-`wrD?PDK?*M(WWVF z$_8!PG%U)l^N0KUIj-|RxUadwyygn?^sBdAA8y%lc^_`s`6is5MGgbFyC@bgg14(; z2^09cDONB;u)AUn3xsJs*PBDQNtW+_D z4(#Iwc(1UZ9Vh#hiXDb#kf_IK$2^09aVg)k<+Z1b9AUs!b4aRv|5j3zm zUonOam=`D}(1LZLVhSDD7b#}YgHtHxFo1ipVgVy~mnfDnf&VJS3T6mityseX;iZae zFkYh-K?AGH6l2(cSt=&bf_1rK3LV&2C}z-u^IF9m25?`eSilJ0>lI6wz<+~c1v3P1 zRIFivuu@!u@g}VZ8dzPa7{dn4H!CL4g7p@~6gsfqs+d6!&f64o7{GnIVgVy~wPFbq z`0r4xV20qGiZv_{zDscp#=Es5Xkhgo#TYhVzE?4U7OeLvrqF@CR?MIW=lzN~4B&o1 zv49c0s}xI^!2h6P1v3O6QmkQt@M^_17}sb;(7>uuj9~-jwTcO}V0~CIg%0eGC}z-u z^HIed25>*7SilJ0#}!MM!2g6|1v3O2#Tpg}KdHC|<2tPf8d!ZwF@_D8pH@tu1?w}4 zDRf|eRxyJfoX;udFo4@C7BGVMdBqYY@V}r~!3@C{6>C@^{F34tj4x|N(7@^|iZN`! z{HkICEm)mm3LV&AQ_P?T=j)0&4B&o4v49c0Zz`5Bf&VSV3T6nttyseX;q{7ZFnX;B z8d!ZtF@_D8-&IVY1?zi?DRf|eUonFooF6FWFo63*#R5j~exz8!1pc5{!3@EV6>C@^ z{E6Zkj2pBfXkhhI#TYhV{!B4}7ObBurqF@?3&jk2a7M)(25^6=SilJ0uM|s|!2h*k z1v3P{QLJHs@VAO+MgtOurjK6C|(7@^+iZN`!{HJ09Em;3jOrZn&M#T(zaQ>~B z!vOAo6bl%^TNF!}!2hpe1+%y;*m~2?>#|yR`TxB<+#=RsY}JaOfz@`3F>JuxUNM0d zteYvO(1C3zX3&FkbHyA6aBrblzzE(g6-$`Fzm;MIGX%F*tYLxhHi~O7ZmShR1FMx{ z3>z?Srj25?`fSilJ0ixf+kz<;q~1v3OMQLJHsFi>2B@lve_8d$wdF@_D8 zFIP;U1#3se6gse9p_oAr&Q6Ls4B);}v49c0P_cvw{GAmmm?79jv4#c0T@}}0?4}h# z1FPK?W7vSXhhhRPSbHj_(19H(X3&GPmtqbBxO*!WFoL&_VhI!Y`zls2L$IG>4GV<( zE3UyfKr4a>cQKnvC}iYatpAFG%_56*FlISk;YiUo||9j{oz z1pWz%70eKvs93`S;Yo^XFizHrpn=sXiZN`!JXJA)7OYG$g%0e~6f@|-IbAV_0o*eb z3mCyWQ?Y~z{Ie7*m?1b@v4#c0a}?KL5{ECz3r~u>5}blxbc56 C;rL7d delta 6408 zcmXBX^&hro8HRD+Z9X} z0~;G-ioTx55BK%`oX3A~Uvq_d%@yY9S8usK+;WqxeYjsGK?BAG ziZQfcUZ|MB2CRz|Q|Q3HSTTbhoI){&0o+Rz3mCz>RI!8!{8uSfFhlTa#Tpg}FH>B@ z>NQ#sG+{-)=)iufVg@}pZ&S=+0Qc>R1&rX;iX}|o zzeBNt8G?5z*04bMF2yyh-mMiu1IBw4V`#yAuVMllu->PbLI?I*F@qkQ_bcWwfcpW( z0!HwzQY>Kt|AUGZ%n*D?v4#c0s}2Ah8E0g6%*Ki^$Dm?8MKVhsy~*DJ1J)oVr2 zfbkv07+NsDtC+wBtnVqN(1HDZ#SD6IexR7c0PYVJ3mC!skzxrG_=92vGXy_YtYLxh zCyHxW-Jlgg1IAAkV`#zrnPLJPuzs$XLI?IQ6f@|-85MIF!2P9S0V8<7QY>Kt|JRBY z%nJ%q zv|!#=F@X(Ow^K}^1N-)h8T8=XK{1B`+&d~3FoJg{#S$j)@2ptC41uXw!vf)56xXo2 zt5yUJ7iX}|o zKTNTL8G?r^*04bM2*ow59IXf%FdnHGLks4k6cgBh^=QQuI9Ny=)iueVg@}p zo?;FIxKC3oUgj*04bMEX6gfo~;!@1IBX{V`#zj6%*Ki^<2di zI^Vg@}pJ1gcefcpx?0!Hvc#S$j)cTuchhG1948Wsq5Q(VJpcdZB- zF!oT4p#^hK#RN8B?WLGP2X>^GK@ZN}ia8A6?xR@12;RPmB~0M&r&z%Z!TyRhED#=` zxQ5k%S`jp0#ELPrU>>BHzy_>?6;tTIK14Bt9-KoJa~QxqOtF9wyu%esn7}_mv4RFhg*XVhsy~Co8UDb&6I54H%~?#?XTKO2q^=U}cIabYP#Rm_ZND>54fF;GUsa zzzE)%iX}|opQTvA48hroH7pRGqqv4ut`$K8#<_|yv|yg6n7{_C^A%I*z`j5+gC3j< z6>}KCy-2Zu5xk2POPIhf6f2k^xJ0qu#@4H@+-|4KuG?{kb9cUUyKQg#Yj?hMhZ}DE EAFD0&yZ`_I 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