@@ -18,9 +18,11 @@ namespace ts.refactor {
18
18
} ,
19
19
} ) ;
20
20
21
- type ExportToConvert = ( FunctionDeclaration | ClassDeclaration | InterfaceDeclaration | EnumDeclaration | NamespaceDeclaration ) & { readonly name : Identifier } ;
21
+ // If a VariableStatement, will have exactly one VariableDeclaration, with an Identifier for a name.
22
+ type ExportToConvert = FunctionDeclaration | ClassDeclaration | InterfaceDeclaration | EnumDeclaration | NamespaceDeclaration | TypeAliasDeclaration | VariableStatement ;
22
23
interface Info {
23
24
readonly exportNode : ExportToConvert ;
25
+ readonly exportName : Identifier ; // This is exportNode.name except for VariableStatement_s.
24
26
readonly wasDefault : boolean ;
25
27
readonly exportingModuleSymbol : Symbol ;
26
28
}
@@ -48,35 +50,72 @@ namespace ts.refactor {
48
50
case SyntaxKind . ClassDeclaration :
49
51
case SyntaxKind . InterfaceDeclaration :
50
52
case SyntaxKind . EnumDeclaration :
51
- case SyntaxKind . ModuleDeclaration :
52
- return isIdentifier ( ( exportNode as ExportToConvert ) . name ) ? { exportNode : exportNode as ExportToConvert , wasDefault, exportingModuleSymbol } : undefined ;
53
+ case SyntaxKind . TypeAliasDeclaration :
54
+ case SyntaxKind . ModuleDeclaration : {
55
+ const node = exportNode as FunctionDeclaration | ClassDeclaration | InterfaceDeclaration | EnumDeclaration | TypeAliasDeclaration | NamespaceDeclaration ;
56
+ return node . name && isIdentifier ( node . name ) ? { exportNode : node , exportName : node . name , wasDefault, exportingModuleSymbol } : undefined ;
57
+ }
58
+ case SyntaxKind . VariableStatement : {
59
+ const vs = exportNode as VariableStatement ;
60
+ // Must be `export const x = something;`.
61
+ if ( ! ( vs . declarationList . flags & NodeFlags . Const ) || vs . declarationList . declarations . length !== 1 ) {
62
+ return undefined ;
63
+ }
64
+ const decl = first ( vs . declarationList . declarations ) ;
65
+ if ( ! decl . initializer ) return undefined ;
66
+ Debug . assert ( ! wasDefault ) ;
67
+ return isIdentifier ( decl . name ) ? { exportNode : vs , exportName : decl . name , wasDefault, exportingModuleSymbol } : undefined ;
68
+ }
53
69
default :
54
70
return undefined ;
55
71
}
56
72
}
57
73
58
74
function doChange ( exportingSourceFile : SourceFile , program : Program , info : Info , changes : textChanges . ChangeTracker , cancellationToken : CancellationToken | undefined ) : void {
59
- changeExport ( exportingSourceFile , info , changes ) ;
75
+ changeExport ( exportingSourceFile , info , changes , program . getTypeChecker ( ) ) ;
60
76
changeImports ( program , info , changes , cancellationToken ) ;
61
77
}
62
78
63
- function changeExport ( exportingSourceFile : SourceFile , { wasDefault, exportNode } : Info , changes : textChanges . ChangeTracker ) : void {
79
+ function changeExport ( exportingSourceFile : SourceFile , { wasDefault, exportNode, exportName } : Info , changes : textChanges . ChangeTracker , checker : TypeChecker ) : void {
64
80
if ( wasDefault ) {
65
81
changes . deleteNode ( exportingSourceFile , Debug . assertDefined ( findModifier ( exportNode , SyntaxKind . DefaultKeyword ) ) ) ;
66
82
}
67
83
else {
68
- changes . insertNodeAfter ( exportingSourceFile , Debug . assertDefined ( findModifier ( exportNode , SyntaxKind . ExportKeyword ) ) , createToken ( SyntaxKind . DefaultKeyword ) ) ;
84
+ const exportKeyword = Debug . assertDefined ( findModifier ( exportNode , SyntaxKind . ExportKeyword ) ) ;
85
+ switch ( exportNode . kind ) {
86
+ case SyntaxKind . FunctionDeclaration :
87
+ case SyntaxKind . ClassDeclaration :
88
+ case SyntaxKind . InterfaceDeclaration :
89
+ changes . insertNodeAfter ( exportingSourceFile , exportKeyword , createToken ( SyntaxKind . DefaultKeyword ) ) ;
90
+ break ;
91
+ case SyntaxKind . VariableStatement :
92
+ // If 'x' isn't used in this file, `export const x = 0;` --> `export default 0;`
93
+ if ( ! FindAllReferences . Core . isSymbolReferencedInFile ( exportName , checker , exportingSourceFile ) ) {
94
+ // We checked in `getInfo` that an initializer exists.
95
+ changes . replaceNode ( exportingSourceFile , exportNode , createExportDefault ( Debug . assertDefined ( first ( exportNode . declarationList . declarations ) . initializer ) ) ) ;
96
+ break ;
97
+ }
98
+ // falls through
99
+ case SyntaxKind . EnumDeclaration :
100
+ case SyntaxKind . TypeAliasDeclaration :
101
+ case SyntaxKind . ModuleDeclaration :
102
+ // `export type T = number;` -> `type T = number; export default T;`
103
+ changes . deleteModifier ( exportingSourceFile , exportKeyword ) ;
104
+ changes . insertNodeAfter ( exportingSourceFile , exportNode , createExportDefault ( createIdentifier ( exportName . text ) ) ) ;
105
+ break ;
106
+ default :
107
+ Debug . assertNever ( exportNode ) ;
108
+ }
69
109
}
70
110
}
71
111
72
- function changeImports ( program : Program , { wasDefault, exportNode , exportingModuleSymbol } : Info , changes : textChanges . ChangeTracker , cancellationToken : CancellationToken | undefined ) : void {
112
+ function changeImports ( program : Program , { wasDefault, exportName , exportingModuleSymbol } : Info , changes : textChanges . ChangeTracker , cancellationToken : CancellationToken | undefined ) : void {
73
113
const checker = program . getTypeChecker ( ) ;
74
- const exportSymbol = Debug . assertDefined ( checker . getSymbolAtLocation ( exportNode . name ) ) ;
75
- const exportName = exportNode . name . text ;
76
- FindAllReferences . Core . eachExportReference ( program . getSourceFiles ( ) , checker , cancellationToken , exportSymbol , exportingModuleSymbol , exportName , wasDefault , ref => {
114
+ const exportSymbol = Debug . assertDefined ( checker . getSymbolAtLocation ( exportName ) ) ;
115
+ FindAllReferences . Core . eachExportReference ( program . getSourceFiles ( ) , checker , cancellationToken , exportSymbol , exportingModuleSymbol , exportName . text , wasDefault , ref => {
77
116
const importingSourceFile = ref . getSourceFile ( ) ;
78
117
if ( wasDefault ) {
79
- changeDefaultToNamedImport ( importingSourceFile , ref , changes , exportName ) ;
118
+ changeDefaultToNamedImport ( importingSourceFile , ref , changes , exportName . text ) ;
80
119
}
81
120
else {
82
121
changeNamedToDefaultImport ( importingSourceFile , ref , changes ) ;
0 commit comments