@@ -17,7 +17,9 @@ namespace ts.OrganizeImports {
1717
1818 const changeTracker = textChanges . ChangeTracker . fromContext ( { host, formatContext, preferences } ) ;
1919
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 ) ) ;
2123
2224 // All of the old ImportDeclarations in the file, in syntactic order.
2325 const topLevelImportDecls = sourceFile . statements . filter ( isImportDeclaration ) ;
@@ -55,7 +57,7 @@ namespace ts.OrganizeImports {
5557 suppressLeadingTrivia ( oldImportDecls [ 0 ] ) ;
5658
5759 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 ) ) ;
5961 const newImportDecls = flatMap ( sortedImportGroups , importGroup =>
6062 getExternalModuleName ( importGroup [ 0 ] . moduleSpecifier ! )
6163 ? coalesce ( importGroup )
@@ -395,15 +397,18 @@ namespace ts.OrganizeImports {
395397 }
396398
397399 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 ) ;
401406 }
402407
403408 /* 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 ) ;
407412 return compareBooleans ( name1 === undefined , name2 === undefined ) ||
408413 compareBooleans ( isExternalModuleNameRelative ( name1 ! ) , isExternalModuleNameRelative ( name2 ! ) ) ||
409414 compareStringsCaseInsensitive ( name1 ! , name2 ! ) ;
@@ -412,4 +417,63 @@ namespace ts.OrganizeImports {
412417 function compareIdentifiers ( s1 : Identifier , s2 : Identifier ) {
413418 return compareStringsCaseInsensitive ( s1 . text , s2 . text ) ;
414419 }
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+ }
415479}
0 commit comments