@@ -181,6 +181,85 @@ namespace ts {
181
181
} ) ;
182
182
} ) ;
183
183
184
+ describe ( "Coalesce exports" , ( ) => {
185
+ it ( "No exports" , ( ) => {
186
+ assert . isEmpty ( OrganizeImports . coalesceExports ( [ ] ) ) ;
187
+ } ) ;
188
+
189
+ it ( "Sort specifiers" , ( ) => {
190
+ const sortedExports = parseExports ( `export { default as m, a as n, b, y, z as o } from "lib";` ) ;
191
+ const actualCoalescedExports = OrganizeImports . coalesceExports ( sortedExports ) ;
192
+ const expectedCoalescedExports = parseExports ( `export { a as n, b, default as m, y, z as o } from "lib";` ) ;
193
+ assertListEqual ( actualCoalescedExports , expectedCoalescedExports ) ;
194
+ } ) ;
195
+
196
+ it ( "Sort specifiers - case-insensitive" , ( ) => {
197
+ const sortedExports = parseExports ( `export { default as M, a as n, B, y, Z as O } from "lib";` ) ;
198
+ const actualCoalescedExports = OrganizeImports . coalesceExports ( sortedExports ) ;
199
+ const expectedCoalescedExports = parseExports ( `export { a as n, B, default as M, y, Z as O } from "lib";` ) ;
200
+ assertListEqual ( actualCoalescedExports , expectedCoalescedExports ) ;
201
+ } ) ;
202
+
203
+ it ( "Combine namespace re-exports" , ( ) => {
204
+ const sortedExports = parseExports (
205
+ `export * from "lib";` ,
206
+ `export * from "lib";` ) ;
207
+ const actualCoalescedExports = OrganizeImports . coalesceExports ( sortedExports ) ;
208
+ const expectedCoalescedExports = parseExports ( `export * from "lib";` ) ;
209
+ assertListEqual ( actualCoalescedExports , expectedCoalescedExports ) ;
210
+ } ) ;
211
+
212
+ it ( "Combine property exports" , ( ) => {
213
+ const sortedExports = parseExports (
214
+ `export { x };` ,
215
+ `export { y as z };` ) ;
216
+ const actualCoalescedExports = OrganizeImports . coalesceExports ( sortedExports ) ;
217
+ const expectedCoalescedExports = parseExports ( `export { x, y as z };` ) ;
218
+ assertListEqual ( actualCoalescedExports , expectedCoalescedExports ) ;
219
+ } ) ;
220
+
221
+ it ( "Combine property re-exports" , ( ) => {
222
+ const sortedExports = parseExports (
223
+ `export { x } from "lib";` ,
224
+ `export { y as z } from "lib";` ) ;
225
+ const actualCoalescedExports = OrganizeImports . coalesceExports ( sortedExports ) ;
226
+ const expectedCoalescedExports = parseExports ( `export { x, y as z } from "lib";` ) ;
227
+ assertListEqual ( actualCoalescedExports , expectedCoalescedExports ) ;
228
+ } ) ;
229
+
230
+ it ( "Combine namespace re-export with property re-export" , ( ) => {
231
+ const sortedExports = parseExports (
232
+ `export * from "lib";` ,
233
+ `export { y } from "lib";` ) ;
234
+ const actualCoalescedExports = OrganizeImports . coalesceExports ( sortedExports ) ;
235
+ const expectedCoalescedExports = sortedExports ;
236
+ assertListEqual ( actualCoalescedExports , expectedCoalescedExports ) ;
237
+ } ) ;
238
+
239
+ it ( "Combine many exports" , ( ) => {
240
+ const sortedExports = parseExports (
241
+ `export { x };` ,
242
+ `export { y as w, z as default };` ,
243
+ `export { w as q };` ) ;
244
+ const actualCoalescedExports = OrganizeImports . coalesceExports ( sortedExports ) ;
245
+ const expectedCoalescedExports = parseExports (
246
+ `export { w as q, x, y as w, z as default };` ) ;
247
+ assertListEqual ( actualCoalescedExports , expectedCoalescedExports ) ;
248
+ } ) ;
249
+
250
+ it ( "Combine many re-exports" , ( ) => {
251
+ const sortedExports = parseExports (
252
+ `export { x as a, y } from "lib";` ,
253
+ `export * from "lib";` ,
254
+ `export { z as b } from "lib";` ) ;
255
+ const actualCoalescedExports = OrganizeImports . coalesceExports ( sortedExports ) ;
256
+ const expectedCoalescedExports = parseExports (
257
+ `export * from "lib";` ,
258
+ `export { x as a, y, z as b } from "lib";` ) ;
259
+ assertListEqual ( actualCoalescedExports , expectedCoalescedExports ) ;
260
+ } ) ;
261
+ } ) ;
262
+
184
263
describe ( "Baselines" , ( ) => {
185
264
186
265
const libFile = {
@@ -471,6 +550,154 @@ import { React, Other } from "react";
471
550
} ,
472
551
reactLibFile ) ;
473
552
553
+ describe ( "Exports" , ( ) => {
554
+
555
+ testOrganizeExports ( "MoveToTop" ,
556
+ {
557
+ path : "/test.ts" ,
558
+ content : `
559
+ export { F1, F2 } from "lib";
560
+ 1;
561
+ export * from "lib";
562
+ 2;
563
+ ` ,
564
+ } ,
565
+ libFile ) ;
566
+
567
+ // tslint:disable no-invalid-template-strings
568
+ testOrganizeExports ( "MoveToTop_Invalid" ,
569
+ {
570
+ path : "/test.ts" ,
571
+ content : `
572
+ export { F1, F2 } from "lib";
573
+ 1;
574
+ export * from "lib";
575
+ 2;
576
+ export { b } from ${ "`${'lib'}`" } ;
577
+ export { a } from ${ "`${'lib'}`" } ;
578
+ export { D } from "lib";
579
+ 3;
580
+ ` ,
581
+ } ,
582
+ libFile ) ;
583
+ // tslint:enable no-invalid-template-strings
584
+
585
+ testOrganizeExports ( "MoveToTop_WithImportsFirst" ,
586
+ {
587
+ path : "/test.ts" ,
588
+ content : `
589
+ import { F1, F2 } from "lib";
590
+ 1;
591
+ export { F1, F2 } from "lib";
592
+ 2;
593
+ import * as NS from "lib";
594
+ 3;
595
+ export * from "lib";
596
+ 4;
597
+ F1(); F2(); NS.F1();
598
+ ` ,
599
+ } ,
600
+ libFile ) ;
601
+
602
+ testOrganizeExports ( "MoveToTop_WithExportsFirst" ,
603
+ {
604
+ path : "/test.ts" ,
605
+ content : `
606
+ export { F1, F2 } from "lib";
607
+ 1;
608
+ import { F1, F2 } from "lib";
609
+ 2;
610
+ export * from "lib";
611
+ 3;
612
+ import * as NS from "lib";
613
+ 4;
614
+ F1(); F2(); NS.F1();
615
+ ` ,
616
+ } ,
617
+ libFile ) ;
618
+
619
+ testOrganizeExports ( "CoalesceMultipleModules" ,
620
+ {
621
+ path : "/test.ts" ,
622
+ content : `
623
+ export { d } from "lib1";
624
+ export { b } from "lib1";
625
+ export { c } from "lib2";
626
+ export { a } from "lib2";
627
+ ` ,
628
+ } ,
629
+ { path : "/lib1.ts" , content : "export const b = 1, d = 2;" } ,
630
+ { path : "/lib2.ts" , content : "export const a = 3, c = 4;" } ) ;
631
+
632
+ testOrganizeExports ( "CoalesceTrivia" ,
633
+ {
634
+ path : "/test.ts" ,
635
+ content : `
636
+ /*A*/export /*B*/ { /*C*/ F2 /*D*/ } /*E*/ from /*F*/ "lib" /*G*/;/*H*/ //I
637
+ /*J*/export /*K*/ { /*L*/ F1 /*M*/ } /*N*/ from /*O*/ "lib" /*P*/;/*Q*/ //R
638
+ ` ,
639
+ } ,
640
+ libFile ) ;
641
+
642
+ testOrganizeExports ( "SortTrivia" ,
643
+ {
644
+ path : "/test.ts" ,
645
+ content : `
646
+ /*A*/export /*B*/ * /*C*/ from /*D*/ "lib2" /*E*/;/*F*/ //G
647
+ /*H*/export /*I*/ * /*J*/ from /*K*/ "lib1" /*L*/;/*M*/ //N
648
+ ` ,
649
+ } ,
650
+ { path : "/lib1.ts" , content : "" } ,
651
+ { path : "/lib2.ts" , content : "" } ) ;
652
+
653
+ testOrganizeExports ( "SortHeaderComment" ,
654
+ {
655
+ path : "/test.ts" ,
656
+ content : `
657
+ // Header
658
+ export * from "lib2";
659
+ export * from "lib1";
660
+ ` ,
661
+ } ,
662
+ { path : "/lib1.ts" , content : "" } ,
663
+ { path : "/lib2.ts" , content : "" } ) ;
664
+
665
+ testOrganizeExports ( "AmbientModule" ,
666
+ {
667
+ path : "/test.ts" ,
668
+ content : `
669
+ declare module "mod" {
670
+ export { F1 } from "lib";
671
+ export * from "lib";
672
+ export { F2 } from "lib";
673
+ }
674
+ ` ,
675
+ } ,
676
+ libFile ) ;
677
+
678
+ testOrganizeExports ( "TopLevelAndAmbientModule" ,
679
+ {
680
+ path : "/test.ts" ,
681
+ content : `
682
+ export { D } from "lib";
683
+
684
+ declare module "mod" {
685
+ export { F1 } from "lib";
686
+ export * from "lib";
687
+ export { F2 } from "lib";
688
+ }
689
+
690
+ export { E } from "lib";
691
+ export * from "lib";
692
+ ` ,
693
+ } ,
694
+ libFile ) ;
695
+ } ) ;
696
+
697
+ function testOrganizeExports ( testName : string , testFile : TestFSWithWatch . File , ...otherFiles : TestFSWithWatch . File [ ] ) {
698
+ testOrganizeImports ( `${ testName } .exports` , testFile , ...otherFiles ) ;
699
+ }
700
+
474
701
function testOrganizeImports ( testName : string , testFile : TestFSWithWatch . File , ...otherFiles : TestFSWithWatch . File [ ] ) {
475
702
it ( testName , ( ) => runBaseline ( `organizeImports/${ testName } .ts` , testFile , ...otherFiles ) ) ;
476
703
}
@@ -509,6 +736,13 @@ import { React, Other } from "react";
509
736
return imports ;
510
737
}
511
738
739
+ function parseExports ( ...exportStrings : string [ ] ) : ReadonlyArray < ExportDeclaration > {
740
+ const sourceFile = createSourceFile ( "a.ts" , exportStrings . join ( "\n" ) , ScriptTarget . ES2015 , /*setParentNodes*/ true , ScriptKind . TS ) ;
741
+ const exports = filter ( sourceFile . statements , isExportDeclaration ) ;
742
+ assert . equal ( exports . length , exportStrings . length ) ;
743
+ return exports ;
744
+ }
745
+
512
746
function assertEqual ( node1 ?: Node , node2 ?: Node ) {
513
747
if ( node1 === undefined ) {
514
748
assert . isUndefined ( node2 ) ;
@@ -550,6 +784,23 @@ import { React, Other } from "react";
550
784
assertEqual ( is1 . name , is2 . name ) ;
551
785
assertEqual ( is1 . propertyName , is2 . propertyName ) ;
552
786
break ;
787
+ case SyntaxKind . ExportDeclaration :
788
+ const ed1 = node1 as ExportDeclaration ;
789
+ const ed2 = node2 as ExportDeclaration ;
790
+ assertEqual ( ed1 . exportClause , ed2 . exportClause ) ;
791
+ assertEqual ( ed1 . moduleSpecifier , ed2 . moduleSpecifier ) ;
792
+ break ;
793
+ case SyntaxKind . NamedExports :
794
+ const ne1 = node1 as NamedExports ;
795
+ const ne2 = node2 as NamedExports ;
796
+ assertListEqual ( ne1 . elements , ne2 . elements ) ;
797
+ break ;
798
+ case SyntaxKind . ExportSpecifier :
799
+ const es1 = node1 as ExportSpecifier ;
800
+ const es2 = node2 as ExportSpecifier ;
801
+ assertEqual ( es1 . name , es2 . name ) ;
802
+ assertEqual ( es1 . propertyName , es2 . propertyName ) ;
803
+ break ;
553
804
case SyntaxKind . Identifier :
554
805
const id1 = node1 as Identifier ;
555
806
const id2 = node2 as Identifier ;
0 commit comments