@@ -89,7 +89,7 @@ export function organizeImports(
8989 const shouldCombine = shouldSort ; // These are currently inseparable, but I draw a distinction for clarity and in case we add modes in the future.
9090 const shouldRemove = mode === OrganizeImportsMode . RemoveUnused || mode === OrganizeImportsMode . All ;
9191 // All of the old ImportDeclarations in the file, in syntactic order.
92- const topLevelImportGroupDecls = groupImportsByNewlineContiguous ( sourceFile , sourceFile . statements . filter ( isImportDeclaration ) ) ;
92+ const topLevelImportGroupDecls = groupByNewlineContiguous ( sourceFile , sourceFile . statements . filter ( isImportDeclaration ) ) ;
9393
9494 const comparer = getOrganizeImportsComparerWithDetection ( preferences , shouldSort ? ( ) => detectSortingWorker ( topLevelImportGroupDecls , preferences ) === SortKind . CaseInsensitive : undefined ) ;
9595
@@ -105,14 +105,14 @@ export function organizeImports(
105105 // Exports are always used
106106 if ( mode !== OrganizeImportsMode . RemoveUnused ) {
107107 // All of the old ExportDeclarations in the file, in syntactic order.
108- const topLevelExportDecls = sourceFile . statements . filter ( isExportDeclaration ) ;
109- organizeImportsWorker ( topLevelExportDecls , group => coalesceExportsWorker ( group , comparer ) ) ;
108+ getTopLevelExportGroups ( sourceFile ) . forEach ( exportGroupDecl =>
109+ organizeImportsWorker ( exportGroupDecl , group => coalesceExportsWorker ( group , comparer ) ) ) ;
110110 }
111111
112112 for ( const ambientModule of sourceFile . statements . filter ( isAmbientModule ) ) {
113113 if ( ! ambientModule . body ) continue ;
114114
115- const ambientModuleImportGroupDecls = groupImportsByNewlineContiguous ( sourceFile , ambientModule . body . statements . filter ( isImportDeclaration ) ) ;
115+ const ambientModuleImportGroupDecls = groupByNewlineContiguous ( sourceFile , ambientModule . body . statements . filter ( isImportDeclaration ) ) ;
116116 ambientModuleImportGroupDecls . forEach ( importGroupDecl => organizeImportsWorker ( importGroupDecl , processImportsOfSameModuleSpecifier ) ) ;
117117
118118 // Exports are always used
@@ -146,7 +146,7 @@ export function organizeImports(
146146 ? stableSort ( oldImportGroups , ( group1 , group2 ) => compareModuleSpecifiersWorker ( group1 [ 0 ] . moduleSpecifier , group2 [ 0 ] . moduleSpecifier , comparer ) )
147147 : oldImportGroups ;
148148 const newImportDecls = flatMap ( sortedImportGroups , importGroup =>
149- getExternalModuleName ( importGroup [ 0 ] . moduleSpecifier )
149+ getExternalModuleName ( importGroup [ 0 ] . moduleSpecifier ) || importGroup [ 0 ] . moduleSpecifier === undefined
150150 ? coalesce ( importGroup )
151151 : importGroup ) ;
152152
@@ -175,30 +175,30 @@ export function organizeImports(
175175 }
176176}
177177
178- function groupImportsByNewlineContiguous ( sourceFile : SourceFile , importDecls : ImportDeclaration [ ] ) : ImportDeclaration [ ] [ ] {
178+ function groupByNewlineContiguous < T extends ImportDeclaration | ExportDeclaration > ( sourceFile : SourceFile , decls : T [ ] ) : T [ ] [ ] {
179179 const scanner = createScanner ( sourceFile . languageVersion , /*skipTrivia*/ false , sourceFile . languageVariant ) ;
180- const groupImports : ImportDeclaration [ ] [ ] = [ ] ;
180+ const group : T [ ] [ ] = [ ] ;
181181 let groupIndex = 0 ;
182- for ( const topLevelImportDecl of importDecls ) {
183- if ( groupImports [ groupIndex ] && isNewGroup ( sourceFile , topLevelImportDecl , scanner ) ) {
182+ for ( const decl of decls ) {
183+ if ( group [ groupIndex ] && isNewGroup ( sourceFile , decl , scanner ) ) {
184184 groupIndex ++ ;
185185 }
186186
187- if ( ! groupImports [ groupIndex ] ) {
188- groupImports [ groupIndex ] = [ ] ;
187+ if ( ! group [ groupIndex ] ) {
188+ group [ groupIndex ] = [ ] ;
189189 }
190190
191- groupImports [ groupIndex ] . push ( topLevelImportDecl ) ;
191+ group [ groupIndex ] . push ( decl ) ;
192192 }
193193
194- return groupImports ;
194+ return group ;
195195}
196196
197- // a new group is created if an import includes at least two new line
197+ // a new group is created if an import/export includes at least two new line
198198// new line from multi-line comment doesn't count
199- function isNewGroup ( sourceFile : SourceFile , topLevelImportDecl : ImportDeclaration , scanner : Scanner ) {
200- const startPos = topLevelImportDecl . getFullStart ( ) ;
201- const endPos = topLevelImportDecl . getStart ( ) ;
199+ function isNewGroup ( sourceFile : SourceFile , decl : ImportDeclaration | ExportDeclaration , scanner : Scanner ) {
200+ const startPos = decl . getFullStart ( ) ;
201+ const endPos = decl . getStart ( ) ;
202202 scanner . setText ( sourceFile . text , startPos , endPos - startPos ) ;
203203
204204 let numberOfNewLines = 0 ;
@@ -617,7 +617,7 @@ function getModuleSpecifierExpression(declaration: AnyImportOrRequireStatement):
617617/** @internal */
618618export function detectSorting ( sourceFile : SourceFile , preferences : UserPreferences ) : SortKind {
619619 return detectSortingWorker (
620- groupImportsByNewlineContiguous ( sourceFile , sourceFile . statements . filter ( isImportDeclaration ) ) ,
620+ groupByNewlineContiguous ( sourceFile , sourceFile . statements . filter ( isImportDeclaration ) ) ,
621621 preferences ) ;
622622}
623623
@@ -824,3 +824,34 @@ function getOrganizeImportsComparerWithDetection(preferences: UserPreferences, d
824824 const ignoreCase = typeof preferences . organizeImportsIgnoreCase === "boolean" ? preferences . organizeImportsIgnoreCase : detectIgnoreCase ?.( ) ?? false ;
825825 return getOrganizeImportsComparer ( preferences , ignoreCase ) ;
826826}
827+
828+ function getTopLevelExportGroups ( sourceFile : SourceFile ) {
829+ const topLevelExportGroups : ExportDeclaration [ ] [ ] = [ ] ;
830+ const statements = sourceFile . statements ;
831+ const len = length ( statements ) ;
832+
833+ let i = 0 ;
834+ let groupIndex = 0 ;
835+ while ( i < len ) {
836+ if ( isExportDeclaration ( statements [ i ] ) ) {
837+ if ( topLevelExportGroups [ groupIndex ] === undefined ) {
838+ topLevelExportGroups [ groupIndex ] = [ ] ;
839+ }
840+ const exportDecl = statements [ i ] as ExportDeclaration ;
841+ if ( exportDecl . moduleSpecifier ) {
842+ topLevelExportGroups [ groupIndex ] . push ( exportDecl ) ;
843+ i ++ ;
844+ }
845+ else {
846+ while ( i < len && isExportDeclaration ( statements [ i ] ) ) {
847+ topLevelExportGroups [ groupIndex ] . push ( statements [ i ++ ] as ExportDeclaration ) ;
848+ }
849+ groupIndex ++ ;
850+ }
851+ }
852+ else {
853+ i ++ ;
854+ }
855+ }
856+ return flatMap ( topLevelExportGroups , exportGroupDecls => groupByNewlineContiguous ( sourceFile , exportGroupDecls ) ) ;
857+ }
0 commit comments