@@ -13,38 +13,51 @@ namespace ts.OrganizeImports {
13
13
host : LanguageServiceHost ,
14
14
program : Program ,
15
15
preferences : UserPreferences ,
16
- skipDestructiveCodeActions ?: boolean
16
+ mode : OrganizeImportsMode ,
17
17
) {
18
18
const changeTracker = textChanges . ChangeTracker . fromContext ( { host, formatContext, preferences } ) ;
19
-
20
- const coalesceAndOrganizeImports = ( importGroup : readonly ImportDeclaration [ ] ) => stableSort (
21
- coalesceImports ( removeUnusedImports ( importGroup , sourceFile , program , skipDestructiveCodeActions ) ) ,
22
- ( s1 , s2 ) => compareImportsOrRequireStatements ( s1 , s2 ) ) ;
19
+ const shouldSort = mode === OrganizeImportsMode . SortAndCombine || mode === OrganizeImportsMode . All ;
20
+ const shouldCombine = shouldSort ; // These are currently inseparable, but I draw a distinction for clarity and in case we add modes in the future.
21
+ const shouldRemove = mode === OrganizeImportsMode . RemoveUnused || mode === OrganizeImportsMode . All ;
22
+ const maybeRemove = shouldRemove ? removeUnusedImports : identity ;
23
+ const maybeCoalesce = shouldCombine ? coalesceImports : identity ;
24
+ const processImportsOfSameModuleSpecifier = ( importGroup : readonly ImportDeclaration [ ] ) => {
25
+ const processedDeclarations = maybeCoalesce ( maybeRemove ( importGroup , sourceFile , program ) ) ;
26
+ return shouldSort
27
+ ? stableSort ( processedDeclarations , ( s1 , s2 ) => compareImportsOrRequireStatements ( s1 , s2 ) )
28
+ : processedDeclarations ;
29
+ } ;
23
30
24
31
// All of the old ImportDeclarations in the file, in syntactic order.
25
32
const topLevelImportGroupDecls = groupImportsByNewlineContiguous ( sourceFile , sourceFile . statements . filter ( isImportDeclaration ) ) ;
26
- topLevelImportGroupDecls . forEach ( importGroupDecl => organizeImportsWorker ( importGroupDecl , coalesceAndOrganizeImports ) ) ;
33
+ topLevelImportGroupDecls . forEach ( importGroupDecl => organizeImportsWorker ( importGroupDecl , processImportsOfSameModuleSpecifier ) ) ;
27
34
28
- // All of the old ExportDeclarations in the file, in syntactic order.
29
- const topLevelExportDecls = sourceFile . statements . filter ( isExportDeclaration ) ;
30
- organizeImportsWorker ( topLevelExportDecls , coalesceExports ) ;
35
+ // Exports are always used
36
+ if ( mode !== OrganizeImportsMode . RemoveUnused ) {
37
+ // All of the old ExportDeclarations in the file, in syntactic order.
38
+ const topLevelExportDecls = sourceFile . statements . filter ( isExportDeclaration ) ;
39
+ organizeImportsWorker ( topLevelExportDecls , coalesceExports ) ;
40
+ }
31
41
32
42
for ( const ambientModule of sourceFile . statements . filter ( isAmbientModule ) ) {
33
43
if ( ! ambientModule . body ) continue ;
34
44
35
45
const ambientModuleImportGroupDecls = groupImportsByNewlineContiguous ( sourceFile , ambientModule . body . statements . filter ( isImportDeclaration ) ) ;
36
- ambientModuleImportGroupDecls . forEach ( importGroupDecl => organizeImportsWorker ( importGroupDecl , coalesceAndOrganizeImports ) ) ;
46
+ ambientModuleImportGroupDecls . forEach ( importGroupDecl => organizeImportsWorker ( importGroupDecl , processImportsOfSameModuleSpecifier ) ) ;
37
47
38
- const ambientModuleExportDecls = ambientModule . body . statements . filter ( isExportDeclaration ) ;
39
- organizeImportsWorker ( ambientModuleExportDecls , coalesceExports ) ;
48
+ // Exports are always used
49
+ if ( mode !== OrganizeImportsMode . RemoveUnused ) {
50
+ const ambientModuleExportDecls = ambientModule . body . statements . filter ( isExportDeclaration ) ;
51
+ organizeImportsWorker ( ambientModuleExportDecls , coalesceExports ) ;
52
+ }
40
53
}
41
54
42
55
return changeTracker . getChanges ( ) ;
43
56
44
57
function organizeImportsWorker < T extends ImportDeclaration | ExportDeclaration > (
45
58
oldImportDecls : readonly T [ ] ,
46
- coalesce : ( group : readonly T [ ] ) => readonly T [ ] ) {
47
-
59
+ coalesce : ( group : readonly T [ ] ) => readonly T [ ] ,
60
+ ) {
48
61
if ( length ( oldImportDecls ) === 0 ) {
49
62
return ;
50
63
}
@@ -56,8 +69,12 @@ namespace ts.OrganizeImports {
56
69
// but the consequences of being wrong are very minor.
57
70
suppressLeadingTrivia ( oldImportDecls [ 0 ] ) ;
58
71
59
- const oldImportGroups = group ( oldImportDecls , importDecl => getExternalModuleName ( importDecl . moduleSpecifier ! ) ! ) ;
60
- const sortedImportGroups = stableSort ( oldImportGroups , ( group1 , group2 ) => compareModuleSpecifiers ( group1 [ 0 ] . moduleSpecifier , group2 [ 0 ] . moduleSpecifier ) ) ;
72
+ const oldImportGroups = shouldCombine
73
+ ? group ( oldImportDecls , importDecl => getExternalModuleName ( importDecl . moduleSpecifier ! ) ! )
74
+ : [ oldImportDecls ] ;
75
+ const sortedImportGroups = shouldSort
76
+ ? stableSort ( oldImportGroups , ( group1 , group2 ) => compareModuleSpecifiers ( group1 [ 0 ] . moduleSpecifier , group2 [ 0 ] . moduleSpecifier ) )
77
+ : oldImportGroups ;
61
78
const newImportDecls = flatMap ( sortedImportGroups , importGroup =>
62
79
getExternalModuleName ( importGroup [ 0 ] . moduleSpecifier ! )
63
80
? coalesce ( importGroup )
@@ -129,12 +146,7 @@ namespace ts.OrganizeImports {
129
146
return false ;
130
147
}
131
148
132
- function removeUnusedImports ( oldImports : readonly ImportDeclaration [ ] , sourceFile : SourceFile , program : Program , skipDestructiveCodeActions : boolean | undefined ) {
133
- // As a precaution, consider unused import detection to be destructive (GH #43051)
134
- if ( skipDestructiveCodeActions ) {
135
- return oldImports ;
136
- }
137
-
149
+ function removeUnusedImports ( oldImports : readonly ImportDeclaration [ ] , sourceFile : SourceFile , program : Program ) {
138
150
const typeChecker = program . getTypeChecker ( ) ;
139
151
const compilerOptions = program . getCompilerOptions ( ) ;
140
152
const jsxNamespace = typeChecker . getJsxNamespace ( sourceFile ) ;
0 commit comments