@@ -17,7 +17,9 @@ namespace ts.OrganizeImports {
17
17
18
18
const changeTracker = textChanges . ChangeTracker . fromContext ( { host, formatContext, preferences } ) ;
19
19
20
- const coalesceAndOrganizeImports = ( importGroup : readonly ImportDeclaration [ ] ) => coalesceImports ( removeUnusedImports ( importGroup , sourceFile , program ) ) ;
20
+ const coalesceAndOrganizeImports = ( importGroup : readonly ImportDeclaration [ ] ) => stableSort (
21
+ coalesceImports ( removeUnusedImports ( importGroup , sourceFile , program ) ) ,
22
+ ( s1 , s2 ) => compareImportsOrRequireStatements ( s1 , s2 ) ) ;
21
23
22
24
// All of the old ImportDeclarations in the file, in syntactic order.
23
25
const topLevelImportDecls = sourceFile . statements . filter ( isImportDeclaration ) ;
@@ -55,7 +57,7 @@ namespace ts.OrganizeImports {
55
57
suppressLeadingTrivia ( oldImportDecls [ 0 ] ) ;
56
58
57
59
const oldImportGroups = group ( oldImportDecls , importDecl => getExternalModuleName ( importDecl . moduleSpecifier ! ) ! ) ;
58
- const sortedImportGroups = stableSort ( oldImportGroups , ( group1 , group2 ) => compareModuleSpecifiers ( group1 [ 0 ] . moduleSpecifier ! , group2 [ 0 ] . moduleSpecifier ! ) ) ;
60
+ const sortedImportGroups = stableSort ( oldImportGroups , ( group1 , group2 ) => compareModuleSpecifiers ( group1 [ 0 ] . moduleSpecifier , group2 [ 0 ] . moduleSpecifier ) ) ;
59
61
const newImportDecls = flatMap ( sortedImportGroups , importGroup =>
60
62
getExternalModuleName ( importGroup [ 0 ] . moduleSpecifier ! )
61
63
? coalesce ( importGroup )
@@ -395,15 +397,18 @@ namespace ts.OrganizeImports {
395
397
}
396
398
397
399
function sortSpecifiers < T extends ImportOrExportSpecifier > ( specifiers : readonly T [ ] ) {
398
- return stableSort ( specifiers , ( s1 , s2 ) =>
399
- compareIdentifiers ( s1 . propertyName || s1 . name , s2 . propertyName || s2 . name ) ||
400
- compareIdentifiers ( s1 . name , s2 . name ) ) ;
400
+ return stableSort ( specifiers , compareImportOrExportSpecifiers ) ;
401
+ }
402
+
403
+ export function compareImportOrExportSpecifiers < T extends ImportOrExportSpecifier > ( s1 : T , s2 : T ) {
404
+ return compareIdentifiers ( s1 . propertyName || s1 . name , s2 . propertyName || s2 . name )
405
+ || compareIdentifiers ( s1 . name , s2 . name ) ;
401
406
}
402
407
403
408
/* internal */ // Exported for testing
404
- export function compareModuleSpecifiers ( m1 : Expression , m2 : Expression ) {
405
- const name1 = getExternalModuleName ( m1 ) ;
406
- const name2 = getExternalModuleName ( m2 ) ;
409
+ export function compareModuleSpecifiers ( m1 : Expression | undefined , m2 : Expression | undefined ) {
410
+ const name1 = m1 === undefined ? undefined : getExternalModuleName ( m1 ) ;
411
+ const name2 = m2 === undefined ? undefined : getExternalModuleName ( m2 ) ;
407
412
return compareBooleans ( name1 === undefined , name2 === undefined ) ||
408
413
compareBooleans ( isExternalModuleNameRelative ( name1 ! ) , isExternalModuleNameRelative ( name2 ! ) ) ||
409
414
compareStringsCaseInsensitive ( name1 ! , name2 ! ) ;
@@ -412,4 +417,63 @@ namespace ts.OrganizeImports {
412
417
function compareIdentifiers ( s1 : Identifier , s2 : Identifier ) {
413
418
return compareStringsCaseInsensitive ( s1 . text , s2 . text ) ;
414
419
}
420
+
421
+ function getModuleSpecifierExpression ( declaration : AnyImportOrRequireStatement ) : Expression | undefined {
422
+ switch ( declaration . kind ) {
423
+ case SyntaxKind . ImportEqualsDeclaration :
424
+ return tryCast ( declaration . moduleReference , isExternalModuleReference ) ?. expression ;
425
+ case SyntaxKind . ImportDeclaration :
426
+ return declaration . moduleSpecifier ;
427
+ case SyntaxKind . VariableStatement :
428
+ return declaration . declarationList . declarations [ 0 ] . initializer . arguments [ 0 ] ;
429
+ }
430
+ }
431
+
432
+ export function importsAreSorted ( imports : readonly AnyImportOrRequireStatement [ ] ) : imports is SortedReadonlyArray < AnyImportOrRequireStatement > {
433
+ return arrayIsSorted ( imports , compareImportsOrRequireStatements ) ;
434
+ }
435
+
436
+ export function importSpecifiersAreSorted ( imports : readonly ImportSpecifier [ ] ) : imports is SortedReadonlyArray < ImportSpecifier > {
437
+ return arrayIsSorted ( imports , compareImportOrExportSpecifiers ) ;
438
+ }
439
+
440
+ export function getImportDeclarationInsertionIndex ( sortedImports : SortedReadonlyArray < AnyImportOrRequireStatement > , newImport : AnyImportOrRequireStatement ) {
441
+ const index = binarySearch ( sortedImports , newImport , identity , compareImportsOrRequireStatements ) ;
442
+ return index < 0 ? ~ index : index ;
443
+ }
444
+
445
+ export function getImportSpecifierInsertionIndex ( sortedImports : SortedReadonlyArray < ImportSpecifier > , newImport : ImportSpecifier ) {
446
+ const index = binarySearch ( sortedImports , newImport , identity , compareImportOrExportSpecifiers ) ;
447
+ return index < 0 ? ~ index : index ;
448
+ }
449
+
450
+ export function compareImportsOrRequireStatements ( s1 : AnyImportOrRequireStatement , s2 : AnyImportOrRequireStatement ) {
451
+ return compareModuleSpecifiers ( getModuleSpecifierExpression ( s1 ) , getModuleSpecifierExpression ( s2 ) ) || compareImportKind ( s1 , s2 ) ;
452
+ }
453
+
454
+ function compareImportKind ( s1 : AnyImportOrRequireStatement , s2 : AnyImportOrRequireStatement ) {
455
+ return compareValues ( getImportKindOrder ( s1 ) , getImportKindOrder ( s2 ) ) ;
456
+ }
457
+
458
+ // 1. Side-effect imports
459
+ // 2. Type-only imports
460
+ // 3. Namespace imports
461
+ // 4. Default imports
462
+ // 5. Named imports
463
+ // 6. ImportEqualsDeclarations
464
+ // 7. Require variable statements
465
+ function getImportKindOrder ( s1 : AnyImportOrRequireStatement ) {
466
+ switch ( s1 . kind ) {
467
+ case SyntaxKind . ImportDeclaration :
468
+ if ( ! s1 . importClause ) return 0 ;
469
+ if ( s1 . importClause . isTypeOnly ) return 1 ;
470
+ if ( s1 . importClause . namedBindings ?. kind === SyntaxKind . NamespaceImport ) return 2 ;
471
+ if ( s1 . importClause . name ) return 3 ;
472
+ return 4 ;
473
+ case SyntaxKind . ImportEqualsDeclaration :
474
+ return 5 ;
475
+ case SyntaxKind . VariableStatement :
476
+ return 6 ;
477
+ }
478
+ }
415
479
}
0 commit comments