@@ -67,6 +67,7 @@ impl<'a> SortableImport<'a> {
6767 has_namespace_specifier : * has_namespace_specifier,
6868 has_named_specifier : * has_named_specifier,
6969 path_kind : to_path_kind ( source) ,
70+ source,
7071 } ;
7172 self . group_idx = matcher. match_group ( & options. groups ) ;
7273
@@ -112,17 +113,18 @@ fn has_side_effect_style_group(groups: &[Vec<String>]) -> bool {
112113/// Contains all characteristics of an import needed to determine which group it belongs to,
113114/// such as whether it's a type import, side-effect import, style import, and what kind of path it uses.
114115#[ derive( Debug , Clone ) ]
115- struct ImportGroupMatcher {
116+ struct ImportGroupMatcher < ' a > {
116117 is_side_effect : bool ,
117118 is_type_import : bool ,
118119 is_style_import : bool ,
119120 has_default_specifier : bool ,
120121 has_namespace_specifier : bool ,
121122 has_named_specifier : bool ,
122123 path_kind : ImportPathKind ,
124+ source : & ' a str ,
123125}
124126
125- impl ImportGroupMatcher {
127+ impl < ' a > ImportGroupMatcher < ' a > {
126128 /// Match this import against the configured groups and return the group index.
127129 /// Returns the index of the first matching group, or the index of "unknown" group if present,
128130 /// or the last index + 1 if no match found.
@@ -195,6 +197,7 @@ impl ImportGroupMatcher {
195197 | ImportSelector :: Parent
196198 | ImportSelector :: Sibling
197199 | ImportSelector :: Index
200+ | ImportSelector :: Subpath
198201 ) ;
199202
200203 if is_path_type_selector {
@@ -274,15 +277,30 @@ impl ImportGroupMatcher {
274277 }
275278
276279 // Path-based selectors
280+ // Order matches perfectionist: index, sibling, parent, subpath, internal, builtin, external
277281 match self . path_kind {
278282 ImportPathKind :: Index => selectors. push ( ImportSelector :: Index ) ,
279283 ImportPathKind :: Sibling => selectors. push ( ImportSelector :: Sibling ) ,
280284 ImportPathKind :: Parent => selectors. push ( ImportSelector :: Parent ) ,
285+ ImportPathKind :: Internal => { }
286+ ImportPathKind :: Builtin => { }
287+ ImportPathKind :: External => { }
288+ ImportPathKind :: Unknown => { }
289+ }
290+
291+ // Subpath selector (independent of path kind, comes after parent)
292+ if is_subpath ( self . source ) {
293+ selectors. push ( ImportSelector :: Subpath ) ;
294+ }
295+
296+ // Continue with remaining path-based selectors
297+ match self . path_kind {
281298 ImportPathKind :: Internal => selectors. push ( ImportSelector :: Internal ) ,
282299 ImportPathKind :: Builtin => selectors. push ( ImportSelector :: Builtin ) ,
283300 ImportPathKind :: External => selectors. push ( ImportSelector :: External ) ,
284- ImportPathKind :: Unknown => { }
301+ _ => { }
285302 }
303+
286304 // Catch-all selector
287305 selectors. push ( ImportSelector :: Import ) ;
288306
@@ -345,6 +363,8 @@ enum ImportSelector {
345363 Sibling ,
346364 /// Parent module imports (`../foo`)
347365 Parent ,
366+ /// Subpath imports (package.json imports field, e.g., `#foo`)
367+ Subpath ,
348368 /// Internal module imports (matching internal patterns like `~/`, `@/`)
349369 Internal ,
350370 /// Built-in module imports (`node:fs`, `fs`)
@@ -372,6 +392,7 @@ impl ImportSelector {
372392 Self :: Index => "index" ,
373393 Self :: Sibling => "sibling" ,
374394 Self :: Parent => "parent" ,
395+ Self :: Subpath => "subpath" ,
375396 Self :: Internal => "internal" ,
376397 Self :: Builtin => "builtin" ,
377398 Self :: External => "external" ,
@@ -520,3 +541,9 @@ fn is_index_import(source: &str) -> bool {
520541 | "./index.d.js"
521542 ) || source. ends_with ( '/' )
522543}
544+
545+ /// Check if an import is a Node.js subpath import.
546+ /// Subpath imports start with '#' and are defined in package.json imports field.
547+ fn is_subpath ( source : & str ) -> bool {
548+ source. starts_with ( '#' )
549+ }
0 commit comments