@@ -50,7 +50,6 @@ import {
50
50
map ,
51
51
MemoizeCache ,
52
52
memoizeCached ,
53
- mergeAndDeduplicateSorted ,
54
53
NamedImportBindings ,
55
54
NamedImports ,
56
55
NamespaceImport ,
@@ -70,9 +69,6 @@ import {
70
69
tryCast ,
71
70
UserPreferences ,
72
71
} from "./_namespaces/ts" ;
73
- import {
74
- getDiffNum ,
75
- } from "./utilities" ;
76
72
77
73
/**
78
74
* Organize imports by:
@@ -100,46 +96,36 @@ export function organizeImports(
100
96
// All of the old ImportDeclarations in the file, in syntactic order.
101
97
const topLevelImportGroupDecls = groupByNewlineContiguous ( sourceFile , sourceFile . statements . filter ( isImportDeclaration ) ) ;
102
98
103
- let { moduleSpecifierComparer, namedImportComparer, typeOrder } = getDetectionByDiff ( topLevelImportGroupDecls , preferences ) ;
104
-
105
99
const DefaultComparer = getOrganizeImportsComparer ( preferences , /*ignoreCase*/ true ) ;
106
- const exportComparer = namedImportComparer ?? moduleSpecifierComparer ?? DefaultComparer ;
107
-
108
- moduleSpecifierComparer = moduleSpecifierComparer ?? DefaultComparer ;
109
- namedImportComparer = namedImportComparer ?? DefaultComparer ;
110
- typeOrder = typeOrder ?? preferences . organizeImportsTypeOrder ?? "last" ;
111
100
112
- const processImportsOfSameModuleSpecifier = ( importGroup : readonly ImportDeclaration [ ] ) => {
113
- if ( shouldRemove ) importGroup = removeUnusedImports ( importGroup , sourceFile , program ) ;
114
- if ( shouldCombine ) importGroup = coalesceImportsWorker ( importGroup , namedImportComparer ?? DefaultComparer , sourceFile , { organizeImportsTypeOrder : typeOrder } ) ;
115
- if ( shouldSort ) importGroup = stableSort ( importGroup , ( s1 , s2 ) => compareImportsOrRequireStatements ( s1 , s2 , moduleSpecifierComparer ?? DefaultComparer ) ) ;
116
- return importGroup ;
117
- } ;
101
+ let detectedModuleCaseComparer = DefaultComparer ;
102
+ let detectedNamedImportCaseComparer = ( s1 , s2 ) => compareImportOrExportSpecifiers ( s1 , s2 , DefaultComparer , { organizeImportsTypeOrder : "last" } ) ;
103
+ let detectedTypeOrder : typeof preferences . organizeImportsTypeOrder = "last" ;
118
104
119
- topLevelImportGroupDecls . forEach ( importGroupDecl => organizeImportsWorker ( importGroupDecl , processImportsOfSameModuleSpecifier ) ) ;
105
+ topLevelImportGroupDecls . forEach ( importGroupDecl => organizeImportsWorker ( importGroupDecl ) ) ;
120
106
121
107
// Exports are always used
122
108
if ( mode !== OrganizeImportsMode . RemoveUnused ) {
123
109
// All of the old ExportDeclarations in the file, in syntactic order.
124
- getTopLevelExportGroups ( sourceFile ) . forEach ( exportGroupDecl => organizeImportsWorker ( exportGroupDecl , group => coalesceExportsWorker ( group , exportComparer , preferences ) ) ) ;
110
+ getTopLevelExportGroups ( sourceFile ) . forEach ( exportGroupDecl => organizeExportsWorker ( exportGroupDecl ) ) ;
125
111
}
126
112
127
113
for ( const ambientModule of sourceFile . statements . filter ( isAmbientModule ) ) {
128
114
if ( ! ambientModule . body ) continue ;
129
115
130
116
const ambientModuleImportGroupDecls = groupByNewlineContiguous ( sourceFile , ambientModule . body . statements . filter ( isImportDeclaration ) ) ;
131
- ambientModuleImportGroupDecls . forEach ( importGroupDecl => organizeImportsWorker ( importGroupDecl , processImportsOfSameModuleSpecifier ) ) ;
117
+ ambientModuleImportGroupDecls . forEach ( importGroupDecl => organizeImportsWorker ( importGroupDecl ) ) ;
132
118
133
119
// Exports are always used
134
120
if ( mode !== OrganizeImportsMode . RemoveUnused ) {
135
121
const ambientModuleExportDecls = ambientModule . body . statements . filter ( isExportDeclaration ) ;
136
- organizeImportsWorker ( ambientModuleExportDecls , group => coalesceExportsWorker ( group , exportComparer , { organizeImportsTypeOrder : typeOrder } ) ) ;
122
+ organizeExportsWorker ( ambientModuleExportDecls ) ;
137
123
}
138
124
}
139
125
140
126
return changeTracker . getChanges ( ) ;
141
127
142
- function organizeImportsWorker < T extends ImportDeclaration | ExportDeclaration > (
128
+ function organizeDeclsWorker < T extends ImportDeclaration | ExportDeclaration > (
143
129
oldImportDecls : readonly T [ ] ,
144
130
coalesce : ( group : readonly T [ ] ) => readonly T [ ] ,
145
131
) {
@@ -158,7 +144,7 @@ export function organizeImports(
158
144
? group ( oldImportDecls , importDecl => getExternalModuleName ( importDecl . moduleSpecifier ) ! )
159
145
: [ oldImportDecls ] ;
160
146
const sortedImportGroups = shouldSort
161
- ? stableSort ( oldImportGroups , ( group1 , group2 ) => compareModuleSpecifiersWorker ( group1 [ 0 ] . moduleSpecifier , group2 [ 0 ] . moduleSpecifier , moduleSpecifierComparer ?? DefaultComparer ) )
147
+ ? stableSort ( oldImportGroups , ( group1 , group2 ) => compareModuleSpecifiersWorker ( group1 [ 0 ] . moduleSpecifier , group2 [ 0 ] . moduleSpecifier , detectedModuleCaseComparer ) )
162
148
: oldImportGroups ;
163
149
const newImportDecls = flatMap ( sortedImportGroups , importGroup =>
164
150
getExternalModuleName ( importGroup [ 0 ] . moduleSpecifier ) || importGroup [ 0 ] . moduleSpecifier === undefined
@@ -188,6 +174,32 @@ export function organizeImports(
188
174
} , hasTrailingComment ) ;
189
175
}
190
176
}
177
+
178
+ function organizeImportsWorker ( oldImportDecls : readonly ImportDeclaration [ ] , comparer ?: Comparer < string > ) {
179
+ if ( ! comparer ) {
180
+ const { moduleSpecifierComparer, namedImportComparer, typeOrder } = getDetectionByDiff ( topLevelImportGroupDecls , preferences )
181
+
182
+ detectedModuleCaseComparer = moduleSpecifierComparer ?? detectedModuleCaseComparer ;
183
+ detectedNamedImportCaseComparer = namedImportComparer ?? detectedNamedImportCaseComparer ;
184
+ detectedTypeOrder = typeOrder ?? detectedTypeOrder ;
185
+ // TODO return unset comparer
186
+ }
187
+
188
+ const processImportsOfSameModuleSpecifier = ( importGroup : readonly ImportDeclaration [ ] ) => {
189
+ if ( shouldRemove ) importGroup = removeUnusedImports ( importGroup , sourceFile , program ) ;
190
+ if ( shouldCombine ) importGroup = coalesceImportsWorker ( importGroup , detectedNamedImportCaseComparer , sourceFile , { organizeImportsTypeOrder : detectedTypeOrder } ) ;
191
+ if ( shouldSort ) importGroup = stableSort ( importGroup , ( s1 , s2 ) => compareImportsOrRequireStatements ( s1 , s2 , detectedModuleCaseComparer ) ) ;
192
+ return importGroup ;
193
+ } ;
194
+
195
+ organizeDeclsWorker ( oldImportDecls , processImportsOfSameModuleSpecifier ) ;
196
+ // return { moduleSpecifierComparer, namedImportComparer, typeOrder };
197
+ }
198
+
199
+ function organizeExportsWorker ( oldExportDecls : readonly ExportDeclaration [ ] , comparer ?: Comparer < string > ) {
200
+ const useComparer = comparer ?? DefaultComparer ;
201
+ organizeDeclsWorker ( oldExportDecls , group => coalesceExportsWorker ( group , useComparer , { organizeImportsTypeOrder : detectedTypeOrder ?? preferences . organizeImportsTypeOrder } ) )
202
+ }
191
203
}
192
204
193
205
function groupByNewlineContiguous < T extends ImportDeclaration | ExportDeclaration > ( sourceFile : SourceFile , decls : T [ ] ) : T [ ] [ ] {
@@ -967,19 +979,20 @@ export function getDetectionByDiff(importDeclsByGroup: ImportDeclaration[][], pr
967
979
const CASE_SENSITIVE_COMPARER = getOrganizeImportsComparer ( preferences , /*ignoreCase*/ false ) ;
968
980
comparers = [ CASE_INSENSITIVE_COMPARER , CASE_SENSITIVE_COMPARER ] ;
969
981
970
- const moduleSpecifiersByGroup : string [ ] [ ] = [ ] ;
971
- importDeclsByGroup . forEach ( importGroup => {
972
- // turns importdeclbygroup into string[][] of module specifiers by group to detect sorting on module specifiers
973
- moduleSpecifiersByGroup . push ( importGroup . map ( i => getExternalModuleName ( i . moduleSpecifier ) ! ) ) ;
974
- } ) ;
975
- comparer . moduleSpecifierComparer = detectCaseSensitivityByDiff ( moduleSpecifiersByGroup ) ;
982
+ getModuleSpecifierNames ( importDeclsByGroup , comparer , detectCaseSensitivityBySort ) ;
976
983
}
977
984
978
985
// filter for import declarations with named imports. Will be a flat array of import declarations without separations by group
986
+ let bothNamedImports = false ;
979
987
const importDeclsWithNamed = importDeclsByGroup . map ( importGroup =>
980
988
importGroup . filter ( i => {
981
- const namedImports = tryCast ( i . importClause ?. namedBindings , isNamedImports ) ?. elements . length ! > 1 ;
982
- return namedImports ;
989
+ const namedImports = tryCast ( i . importClause ?. namedBindings , isNamedImports ) ?. elements ;
990
+ if ( ! namedImports ?. length ) return false ;
991
+ if ( ! bothNamedImports && namedImports . some ( n => n . isTypeOnly ) && namedImports . some ( n => ! n . isTypeOnly ) ) {
992
+ //todo:improve check
993
+ bothNamedImports = true ;
994
+ }
995
+ return true ;
983
996
} )
984
997
) . flat ( ) ;
985
998
@@ -988,27 +1001,37 @@ export function getDetectionByDiff(importDeclsByGroup: ImportDeclaration[][], pr
988
1001
989
1002
// TODO combine imports with the same module specifier
990
1003
991
- let bothNamedImports = false ;
992
1004
// formats the code in order to detect type order
993
1005
const namedImportsByDecl = importDeclsWithNamed . map ( importDecl => {
994
- const originalNamedImports = tryCast ( importDecl . importClause ?. namedBindings , isNamedImports ) ?. elements . map ( n => n . name . text ) ;
995
- if ( ! originalNamedImports ) return { originalNamedImports : [ ] } ;
996
- const { regular, type } = groupBy ( ( importDecl . importClause ?. namedBindings as NamedImports ) . elements , s => s . isTypeOnly ? "type" : "regular" ) ;
997
- const regularImportNames = regular ?. map ( n => n . name . text ) ;
998
- const typeImportNames = type ?. map ( n => n . name . text ) ;
999
- if ( regularImportNames && typeImportNames ) {
1000
- bothNamedImports = true ;
1001
- }
1002
- return { regularImportNames, typeImportNames, originalNamedImports } ;
1003
- } ) ;
1004
-
1005
- const { namedImportComparer, typeOrder } = detectNamedImportOrganizationByDiff ( namedImportsByDecl , bothNamedImports ) ;
1006
+ return tryCast ( importDecl . importClause ?. namedBindings , isNamedImports ) ?. elements ;
1007
+ } ) . filter ( elements => elements !== undefined ) as any as ImportSpecifier [ ] [ ] ;
1008
+ // const originalNamedImports = tryCast(importDecl.importClause?.namedBindings, isNamedImports)?.elements.map(n => n.name.text);
1009
+ // if (!originalNamedImports) return { originalNamedImports: [] };
1010
+ // const { regular, type } = groupBy((importDecl.importClause?.namedBindings as NamedImports).elements, s => s.isTypeOnly ? "type" : "regular");
1011
+ // const regularImportNames = regular?.map(n => n.name.text);
1012
+ // const typeImportNames = type?.map(n => n.name.text);
1013
+ // if (regularImportNames && typeImportNames) {
1014
+ // bothNamedImports = true;
1015
+ // }
1016
+ // return { regularImportNames, typeImportNames, originalNamedImports };
1017
+ // });
1018
+
1019
+ const { namedImportComparer, typeOrder } = detectNamedImportOrganizationBySort ( namedImportsByDecl ) ;
1006
1020
comparer . namedImportComparer = namedImportComparer ;
1007
1021
comparer . typeOrder = typeOrder ;
1008
1022
1009
1023
return comparer ;
1010
1024
1011
- function detectCaseSensitivityByDiff ( originalGroups : string [ ] [ ] ) : Comparer < string > {
1025
+ function getSortedMeasure < T > ( arr : readonly T [ ] , comparer : Comparer < T > ) {
1026
+ let i = 0 ;
1027
+ for ( let j = 0 ; j < arr . length - 1 ; j ++ ) {
1028
+ if ( comparer ( arr [ j ] , arr [ j + 1 ] ) > 0 ) {
1029
+ i ++ ;
1030
+ }
1031
+ }
1032
+ return i ;
1033
+ }
1034
+ function detectCaseSensitivityBySort ( originalGroups : string [ ] [ ] ) : Comparer < string > {
1012
1035
// each entry in originalGroups will be sorted and compared against the original entry.
1013
1036
// the total diff of each comparison is the sum of the diffs of all groups
1014
1037
let bestComparer ;
@@ -1020,8 +1043,8 @@ export function getDetectionByDiff(importDeclsByGroup: ImportDeclaration[][], pr
1020
1043
for ( const listToSort of originalGroups ) {
1021
1044
if ( listToSort . length <= 1 ) continue ;
1022
1045
1023
- const sortedList = sort ( listToSort , curComparer ) as any as string [ ] ;
1024
- const diff = getDiffNum ( listToSort , sortedList ) ;
1046
+ // const sortedList = sort(listToSort, curComparer) as any as string[];
1047
+ const diff = getSortedMeasure ( listToSort , curComparer ) ;
1025
1048
diffOfCurrentComparer += diff ;
1026
1049
}
1027
1050
@@ -1033,26 +1056,26 @@ export function getDetectionByDiff(importDeclsByGroup: ImportDeclaration[][], pr
1033
1056
return bestComparer ?? comparers [ 0 ] ;
1034
1057
}
1035
1058
1036
- interface NamedImportByDecl {
1037
- originalNamedImports : string [ ] ;
1038
- regularImportNames ?: string [ ] ;
1039
- typeImportNames ?: string [ ] ;
1040
- }
1041
- function detectNamedImportOrganizationByDiff ( originalGroups : NamedImportByDecl [ ] , bothNamedImports : boolean ) : { namedImportComparer : Comparer < string > ; typeOrder ?: "first" | "last" | "inline" ; } {
1059
+ // interface NamedImportByDecl {
1060
+ // originalNamedImports: string[];
1061
+ // regularImportNames?: string[];
1062
+ // typeImportNames?: string[];
1063
+ // }
1064
+ function detectNamedImportOrganizationBySort ( originalGroups : ImportSpecifier [ ] [ ] ) : { namedImportComparer : Comparer < string > ; typeOrder ?: "first" | "last" | "inline" ; } {
1042
1065
// if we don't have any import statements with both named regular and type imports, we do not need to detect a type ordering
1043
1066
if ( ! bothNamedImports ) {
1044
- return { namedImportComparer : detectCaseSensitivityByDiff ( originalGroups . map ( i => i . originalNamedImports ) ) } ;
1045
- }
1046
- if ( preferences . organizeImportsTypeOrder !== undefined ) {
1047
- switch ( preferences . organizeImportsTypeOrder ) {
1048
- case "first" :
1049
- return { namedImportComparer : detectCaseSensitivityByDiff ( originalGroups . map ( i => [ i . typeImportNames ?? [ ] , i . regularImportNames ?? [ ] ] ) . flat ( ) ) , typeOrder : "first" } ;
1050
- case "last" :
1051
- return { namedImportComparer : detectCaseSensitivityByDiff ( originalGroups . map ( i => [ i . typeImportNames ?? [ ] , i . regularImportNames ?? [ ] ] ) . flat ( ) ) , typeOrder : "last" } ;
1052
- case "inline" :
1053
- return { namedImportComparer : detectCaseSensitivityByDiff ( originalGroups . map ( i => i . originalNamedImports ) ) , typeOrder : "inline" } ;
1054
- }
1067
+ return { namedImportComparer : detectCaseSensitivityBySort ( originalGroups . map ( i => i . map ( n => n . name . text ) ) ) } ;
1055
1068
}
1069
+ // if (preferences.organizeImportsTypeOrder !== undefined) {
1070
+ // switch (preferences.organizeImportsTypeOrder) {
1071
+ // case "first":
1072
+ // return { namedImportComparer: detectCaseSensitivityBySort(originalGroups.map(i => [i.typeImportNames ?? [], i.regularImportNames ?? []]).flat()), typeOrder: "first" };
1073
+ // case "last":
1074
+ // return { namedImportComparer: detectCaseSensitivityBySort(originalGroups.map(i => [i.typeImportNames ?? [], i.regularImportNames ?? []]).flat()), typeOrder: "last" };
1075
+ // case "inline":
1076
+ // return { namedImportComparer: detectCaseSensitivityBySort(originalGroups.map(i => i.originalNamedImports)), typeOrder: "inline" };
1077
+ // }
1078
+ // }
1056
1079
1057
1080
type TypeOrder = "first" | "last" | "inline" ;
1058
1081
@@ -1062,24 +1085,24 @@ export function getDetectionByDiff(importDeclsByGroup: ImportDeclaration[][], pr
1062
1085
for ( const curComparer of comparers ) {
1063
1086
const currDiff = { first : 0 , last : 0 , inline : 0 } ;
1064
1087
1065
- for ( const { regularImportNames , typeImportNames , originalNamedImports } of originalGroups ) {
1066
- if ( ! regularImportNames || ! typeImportNames ) {
1067
- const sortedList = sort ( originalNamedImports , curComparer ) as any as string [ ] ;
1068
- const diff = getDiffNum ( originalNamedImports , sortedList ) ;
1069
- for ( const typeOrder in currDiff ) {
1070
- currDiff [ typeOrder as TypeOrder ] += diff ;
1071
- }
1072
- continue ;
1073
- }
1088
+ for ( const importDecl of originalGroups ) {
1089
+ // if (!regularImportNames || !typeImportNames) {
1090
+ // const sortedList = sort(originalNamedImports, curComparer) as any as string[];
1091
+ // const diff = getSortedMeasure (originalNamedImports, comparer );
1092
+ // for (const typeOrder in currDiff) {
1093
+ // currDiff[typeOrder as TypeOrder] += diff;
1094
+ // }
1095
+ // continue;
1096
+ // }
1074
1097
1075
1098
// ordering
1076
- const sortedRegular = sort ( regularImportNames , curComparer ) ;
1077
- const sortedType = sort ( typeImportNames , curComparer ) ;
1078
- const sortedInline = mergeAndDeduplicateSorted ( sortedRegular , sortedType , curComparer ) ;
1099
+ // const sortedRegular = sort(regularImportNames, curComparer);
1100
+ // const sortedType = sort(typeImportNames, curComparer);
1101
+ // const sortedInline = mergeAndDeduplicateSorted(sortedRegular, sortedType, curComparer);
1079
1102
1080
- currDiff . inline += getDiffNum ( originalNamedImports , sortedInline as any as string [ ] ) ;
1081
- currDiff . first += getDiffNum ( originalNamedImports , sortedType . concat ( sortedRegular ) ) ;
1082
- currDiff . last += getDiffNum ( originalNamedImports , sortedRegular . concat ( sortedType ) ) ;
1103
+ currDiff . inline += getSortedMeasure ( importDecl , ( n1 , n2 ) => compareImportOrExportSpecifiers ( n1 , n2 , curComparer , { organizeImportsTypeOrder : "inline" } ) ) ;
1104
+ currDiff . first += getSortedMeasure ( importDecl , ( n1 , n2 ) => compareImportOrExportSpecifiers ( n1 , n2 , curComparer , { organizeImportsTypeOrder : "first" } ) ) ;
1105
+ currDiff . last += getSortedMeasure ( importDecl , ( n1 , n2 ) => compareImportOrExportSpecifiers ( n1 , n2 , curComparer , { organizeImportsTypeOrder : "last" } ) ) ;
1083
1106
}
1084
1107
for ( const key in currDiff ) {
1085
1108
const typeOrder = key as TypeOrder ;
@@ -1090,16 +1113,25 @@ export function getDetectionByDiff(importDeclsByGroup: ImportDeclaration[][], pr
1090
1113
}
1091
1114
}
1092
1115
1093
- if ( bestDiff . last <= bestDiff . first && bestDiff . last <= bestDiff . inline ) {
1094
- return { namedImportComparer : bestComparer . last , typeOrder : "last" } ;
1095
- }
1096
1116
if ( bestDiff . inline <= bestDiff . first && bestDiff . inline <= bestDiff . last ) {
1097
1117
return { namedImportComparer : bestComparer . inline , typeOrder : "inline" } ;
1098
1118
}
1119
+ if ( bestDiff . last <= bestDiff . first && bestDiff . last <= bestDiff . inline ) {
1120
+ return { namedImportComparer : bestComparer . last , typeOrder : "last" } ;
1121
+ }
1099
1122
if ( bestDiff . first <= bestDiff . inline && bestDiff . first <= bestDiff . last ) {
1100
1123
return { namedImportComparer : bestComparer . first , typeOrder : "first" } ;
1101
1124
}
1102
1125
// hopefully never hit.....
1103
1126
return { namedImportComparer : bestComparer . last , typeOrder : "last" } ;
1104
1127
}
1105
1128
}
1129
+ function getModuleSpecifierNames ( importDeclsByGroup : ImportDeclaration [ ] [ ] , comparer : { moduleSpecifierComparer : Comparer < string > ; namedImportComparer ?: Comparer < string > | undefined ; typeOrder ?: "first" | "last" | "inline" | undefined ; } , detectCaseSensitivityByDiff : ( originalGroups : string [ ] [ ] ) => Comparer < string > ) {
1130
+ const moduleSpecifiersByGroup : string [ ] [ ] = [ ] ;
1131
+ importDeclsByGroup . forEach ( importGroup => {
1132
+ // turns importdeclbygroup into string[][] of module specifiers by group to detect sorting on module specifiers
1133
+ moduleSpecifiersByGroup . push ( importGroup . map ( i => getExternalModuleName ( i . moduleSpecifier ) ! ) ) ;
1134
+ } ) ;
1135
+ comparer . moduleSpecifierComparer = detectCaseSensitivityByDiff ( moduleSpecifiersByGroup ) ;
1136
+ }
1137
+
0 commit comments