@@ -23,8 +23,10 @@ import {
23
23
flatMap ,
24
24
formatting ,
25
25
getNewLineOrDefaultFromHost ,
26
+ getStringComparer ,
26
27
getUILocale ,
27
28
group ,
29
+ groupBy ,
28
30
Identifier ,
29
31
identity ,
30
32
ImportClause ,
@@ -94,7 +96,7 @@ export function organizeImports(
94
96
95
97
const processImportsOfSameModuleSpecifier = ( importGroup : readonly ImportDeclaration [ ] ) => {
96
98
if ( shouldRemove ) importGroup = removeUnusedImports ( importGroup , sourceFile , program ) ;
97
- if ( shouldCombine ) importGroup = coalesceImportsWorker ( importGroup , comparer , sourceFile ) ;
99
+ if ( shouldCombine ) importGroup = coalesceImportsWorker ( importGroup , comparer , sourceFile , preferences ) ;
98
100
if ( shouldSort ) importGroup = stableSort ( importGroup , ( s1 , s2 ) => compareImportsOrRequireStatements ( s1 , s2 , comparer ) ) ;
99
101
return importGroup ;
100
102
} ;
@@ -104,7 +106,7 @@ export function organizeImports(
104
106
// Exports are always used
105
107
if ( mode !== OrganizeImportsMode . RemoveUnused ) {
106
108
// All of the old ExportDeclarations in the file, in syntactic order.
107
- getTopLevelExportGroups ( sourceFile ) . forEach ( exportGroupDecl => organizeImportsWorker ( exportGroupDecl , group => coalesceExportsWorker ( group , comparer ) ) ) ;
109
+ getTopLevelExportGroups ( sourceFile ) . forEach ( exportGroupDecl => organizeImportsWorker ( exportGroupDecl , group => coalesceExportsWorker ( group , comparer , preferences ) ) ) ;
108
110
}
109
111
110
112
for ( const ambientModule of sourceFile . statements . filter ( isAmbientModule ) ) {
@@ -116,7 +118,7 @@ export function organizeImports(
116
118
// Exports are always used
117
119
if ( mode !== OrganizeImportsMode . RemoveUnused ) {
118
120
const ambientModuleExportDecls = ambientModule . body . statements . filter ( isExportDeclaration ) ;
119
- organizeImportsWorker ( ambientModuleExportDecls , group => coalesceExportsWorker ( group , comparer ) ) ;
121
+ organizeImportsWorker ( ambientModuleExportDecls , group => coalesceExportsWorker ( group , comparer , preferences ) ) ;
120
122
}
121
123
}
122
124
@@ -310,12 +312,12 @@ function getExternalModuleName(specifier: Expression | undefined) {
310
312
* @deprecated Only used for testing
311
313
* @internal
312
314
*/
313
- export function coalesceImports ( importGroup : readonly ImportDeclaration [ ] , ignoreCase : boolean , sourceFile ?: SourceFile ) : readonly ImportDeclaration [ ] {
315
+ export function coalesceImports ( importGroup : readonly ImportDeclaration [ ] , ignoreCase : boolean , sourceFile ?: SourceFile , preferences ?: UserPreferences ) : readonly ImportDeclaration [ ] {
314
316
const comparer = getOrganizeImportsOrdinalStringComparer ( ignoreCase ) ;
315
- return coalesceImportsWorker ( importGroup , comparer , sourceFile ) ;
317
+ return coalesceImportsWorker ( importGroup , comparer , sourceFile , preferences ) ;
316
318
}
317
319
318
- function coalesceImportsWorker ( importGroup : readonly ImportDeclaration [ ] , comparer : Comparer < string > , sourceFile ?: SourceFile ) : readonly ImportDeclaration [ ] {
320
+ function coalesceImportsWorker ( importGroup : readonly ImportDeclaration [ ] , comparer : Comparer < string > , sourceFile ?: SourceFile , preferences ?: UserPreferences ) : readonly ImportDeclaration [ ] {
319
321
if ( importGroup . length === 0 ) {
320
322
return importGroup ;
321
323
}
@@ -374,7 +376,7 @@ function coalesceImportsWorker(importGroup: readonly ImportDeclaration[], compar
374
376
newImportSpecifiers . push ( ...getNewImportSpecifiers ( namedImports ) ) ;
375
377
376
378
const sortedImportSpecifiers = factory . createNodeArray (
377
- sortSpecifiers ( newImportSpecifiers , comparer ) ,
379
+ sortSpecifiers ( newImportSpecifiers , comparer , preferences ) ,
378
380
firstNamedImport ?. importClause . namedBindings . elements . hasTrailingComma ,
379
381
) ;
380
382
@@ -491,18 +493,17 @@ function getCategorizedImports(importGroup: readonly ImportDeclaration[]) {
491
493
* @deprecated Only used for testing
492
494
* @internal
493
495
*/
494
- export function coalesceExports ( exportGroup : readonly ExportDeclaration [ ] , ignoreCase : boolean ) {
496
+ export function coalesceExports ( exportGroup : readonly ExportDeclaration [ ] , ignoreCase : boolean , preferences ?: UserPreferences ) {
495
497
const comparer = getOrganizeImportsOrdinalStringComparer ( ignoreCase ) ;
496
- return coalesceExportsWorker ( exportGroup , comparer ) ;
498
+ return coalesceExportsWorker ( exportGroup , comparer , preferences ) ;
497
499
}
498
500
499
- function coalesceExportsWorker ( exportGroup : readonly ExportDeclaration [ ] , comparer : Comparer < string > ) {
501
+ function coalesceExportsWorker ( exportGroup : readonly ExportDeclaration [ ] , comparer : Comparer < string > , preferences ?: UserPreferences ) {
500
502
if ( exportGroup . length === 0 ) {
501
503
return exportGroup ;
502
504
}
503
505
504
506
const { exportWithoutClause, namedExports, typeOnlyExports } = getCategorizedExports ( exportGroup ) ;
505
-
506
507
const coalescedExports : ExportDeclaration [ ] = [ ] ;
507
508
508
509
if ( exportWithoutClause ) {
@@ -516,7 +517,7 @@ function coalesceExportsWorker(exportGroup: readonly ExportDeclaration[], compar
516
517
const newExportSpecifiers : ExportSpecifier [ ] = [ ] ;
517
518
newExportSpecifiers . push ( ...flatMap ( exportGroup , i => i . exportClause && isNamedExports ( i . exportClause ) ? i . exportClause . elements : emptyArray ) ) ;
518
519
519
- const sortedExportSpecifiers = sortSpecifiers ( newExportSpecifiers , comparer ) ;
520
+ const sortedExportSpecifiers = sortSpecifiers ( newExportSpecifiers , comparer , preferences ) ;
520
521
521
522
const exportDecl = exportGroup [ 0 ] ;
522
523
coalescedExports . push (
@@ -583,13 +584,20 @@ function updateImportDeclarationAndClause(
583
584
) ;
584
585
}
585
586
586
- function sortSpecifiers < T extends ImportOrExportSpecifier > ( specifiers : readonly T [ ] , comparer : Comparer < string > ) {
587
- return stableSort ( specifiers , ( s1 , s2 ) => compareImportOrExportSpecifiers ( s1 , s2 , comparer ) ) ;
587
+ function sortSpecifiers < T extends ImportOrExportSpecifier > ( specifiers : readonly T [ ] , comparer : Comparer < string > , preferences ?: UserPreferences ) : readonly T [ ] {
588
+ return stableSort ( specifiers , ( s1 , s2 ) => compareImportOrExportSpecifiers ( s1 , s2 , comparer , preferences ) ) ;
588
589
}
589
590
590
591
/** @internal */
591
- export function compareImportOrExportSpecifiers < T extends ImportOrExportSpecifier > ( s1 : T , s2 : T , comparer : Comparer < string > ) : Comparison {
592
- return compareBooleans ( s1 . isTypeOnly , s2 . isTypeOnly ) || comparer ( s1 . name . text , s2 . name . text ) ;
592
+ export function compareImportOrExportSpecifiers < T extends ImportOrExportSpecifier > ( s1 : T , s2 : T , comparer : Comparer < string > , preferences ?: UserPreferences ) : Comparison {
593
+ switch ( preferences ?. organizeImportsTypeOrder ) {
594
+ case "first" :
595
+ return compareBooleans ( s2 . isTypeOnly , s1 . isTypeOnly ) || comparer ( s1 . name . text , s2 . name . text ) ;
596
+ case "inline" :
597
+ return comparer ( s1 . name . text , s2 . name . text ) ;
598
+ default :
599
+ return compareBooleans ( s1 . isTypeOnly , s2 . isTypeOnly ) || comparer ( s1 . name . text , s2 . name . text ) ;
600
+ }
593
601
}
594
602
595
603
/**
@@ -721,11 +729,51 @@ class ImportSpecifierSortingCache implements MemoizeCache<[readonly ImportSpecif
721
729
722
730
/** @internal */
723
731
export const detectImportSpecifierSorting = memoizeCached ( ( specifiers : readonly ImportSpecifier [ ] , preferences : UserPreferences ) : SortKind => {
724
- if ( ! arrayIsSorted ( specifiers , ( s1 , s2 ) => compareBooleans ( s1 . isTypeOnly , s2 . isTypeOnly ) ) ) {
725
- return SortKind . None ;
732
+ // If types are not sorted as specified, then imports are assumed to be unsorted.
733
+ // If there is no type sorting specification, we default to "last" and move on to case sensitivity detection.
734
+ switch ( preferences . organizeImportsTypeOrder ) {
735
+ case "first" :
736
+ if ( ! arrayIsSorted ( specifiers , ( s1 , s2 ) => compareBooleans ( s2 . isTypeOnly , s1 . isTypeOnly ) ) ) return SortKind . None ;
737
+ break ;
738
+ case "inline" :
739
+ if (
740
+ ! arrayIsSorted ( specifiers , ( s1 , s2 ) => {
741
+ const comparer = getStringComparer ( /*ignoreCase*/ true ) ;
742
+ return comparer ( s1 . name . text , s2 . name . text ) ;
743
+ } )
744
+ ) {
745
+ return SortKind . None ;
746
+ }
747
+ break ;
748
+ default :
749
+ if ( ! arrayIsSorted ( specifiers , ( s1 , s2 ) => compareBooleans ( s1 . isTypeOnly , s2 . isTypeOnly ) ) ) return SortKind . None ;
750
+ break ;
726
751
}
752
+
727
753
const collateCaseSensitive = getOrganizeImportsComparer ( preferences , /*ignoreCase*/ false ) ;
728
754
const collateCaseInsensitive = getOrganizeImportsComparer ( preferences , /*ignoreCase*/ true ) ;
755
+
756
+ if ( preferences . organizeImportsTypeOrder !== "inline" ) {
757
+ const { type : regularImports , regular : typeImports } = groupBy ( specifiers , s => s . isTypeOnly ? "type" : "regular" ) ;
758
+ const regularCaseSensitivity = regularImports ?. length
759
+ ? detectSortCaseSensitivity ( regularImports , specifier => specifier . name . text , collateCaseSensitive , collateCaseInsensitive )
760
+ : undefined ;
761
+ const typeCaseSensitivity = typeImports ?. length
762
+ ? detectSortCaseSensitivity ( typeImports , specifier => specifier . name . text ?? "" , collateCaseSensitive , collateCaseInsensitive )
763
+ : undefined ;
764
+ if ( regularCaseSensitivity === undefined ) {
765
+ return typeCaseSensitivity ?? SortKind . None ;
766
+ }
767
+ if ( typeCaseSensitivity === undefined ) {
768
+ return regularCaseSensitivity ;
769
+ }
770
+ if ( regularCaseSensitivity === SortKind . None || typeCaseSensitivity === SortKind . None ) {
771
+ return SortKind . None ;
772
+ }
773
+ return typeCaseSensitivity & regularCaseSensitivity ;
774
+ }
775
+
776
+ // else inline
729
777
return detectSortCaseSensitivity ( specifiers , specifier => specifier . name . text , collateCaseSensitive , collateCaseInsensitive ) ;
730
778
} , new ImportSpecifierSortingCache ( ) ) ;
731
779
@@ -736,8 +784,8 @@ export function getImportDeclarationInsertionIndex(sortedImports: readonly AnyIm
736
784
}
737
785
738
786
/** @internal */
739
- export function getImportSpecifierInsertionIndex ( sortedImports : readonly ImportSpecifier [ ] , newImport : ImportSpecifier , comparer : Comparer < string > ) {
740
- const index = binarySearch ( sortedImports , newImport , identity , ( s1 , s2 ) => compareImportOrExportSpecifiers ( s1 , s2 , comparer ) ) ;
787
+ export function getImportSpecifierInsertionIndex ( sortedImports : readonly ImportSpecifier [ ] , newImport : ImportSpecifier , comparer : Comparer < string > , preferences : UserPreferences ) {
788
+ const index = binarySearch ( sortedImports , newImport , identity , ( s1 , s2 ) => compareImportOrExportSpecifiers ( s1 , s2 , comparer , preferences ) ) ;
741
789
return index < 0 ? ~ index : index ;
742
790
}
743
791
0 commit comments