@@ -114,8 +114,8 @@ namespace ts.codefix {
114
114
return false ;
115
115
}
116
116
case SyntaxKind . BinaryExpression : {
117
- const { left , operatorToken, right } = expression as BinaryExpression ;
118
- return operatorToken . kind === SyntaxKind . EqualsToken && convertAssignment ( sourceFile , checker , statement as ExpressionStatement , left , right , changes , exports ) ;
117
+ const { operatorToken } = expression as BinaryExpression ;
118
+ return operatorToken . kind === SyntaxKind . EqualsToken && convertAssignment ( sourceFile , checker , expression as BinaryExpression , changes , exports ) ;
119
119
}
120
120
}
121
121
}
@@ -144,7 +144,7 @@ namespace ts.codefix {
144
144
return convertPropertyAccessImport ( name , initializer . name . text , initializer . expression . arguments [ 0 ] , identifiers ) ;
145
145
}
146
146
else {
147
- // Move it out to its own variable statement.
147
+ // Move it out to its own variable statement. (This will not be used if `!foundImport`)
148
148
return createVariableStatement ( /*modifiers*/ undefined , createVariableDeclarationList ( [ decl ] , declarationList . flags ) ) ;
149
149
}
150
150
} ) ;
@@ -177,33 +177,32 @@ namespace ts.codefix {
177
177
function convertAssignment (
178
178
sourceFile : SourceFile ,
179
179
checker : TypeChecker ,
180
- statement : ExpressionStatement ,
181
- left : Expression ,
182
- right : Expression ,
180
+ assignment : BinaryExpression ,
183
181
changes : textChanges . ChangeTracker ,
184
182
exports : ExportRenames ,
185
183
) : ModuleExportsChanged {
184
+ const { left, right } = assignment ;
186
185
if ( ! isPropertyAccessExpression ( left ) ) {
187
186
return false ;
188
187
}
189
188
190
189
if ( isExportsOrModuleExportsOrAlias ( sourceFile , left ) ) {
191
190
if ( isExportsOrModuleExportsOrAlias ( sourceFile , right ) ) {
192
191
// `const alias = module.exports;` or `module.exports = alias;` can be removed.
193
- changes . deleteNode ( sourceFile , statement ) ;
192
+ changes . deleteNode ( sourceFile , assignment . parent ) ;
194
193
}
195
194
else {
196
195
let newNodes = isObjectLiteralExpression ( right ) ? tryChangeModuleExportsObject ( right ) : undefined ;
197
196
let changedToDefaultExport = false ;
198
197
if ( ! newNodes ) {
199
198
( [ newNodes , changedToDefaultExport ] = convertModuleExportsToExportDefault ( right , checker ) ) ;
200
199
}
201
- changes . replaceNodeWithNodes ( sourceFile , statement , newNodes ) ;
200
+ changes . replaceNodeWithNodes ( sourceFile , assignment . parent , newNodes ) ;
202
201
return changedToDefaultExport ;
203
202
}
204
203
}
205
204
else if ( isExportsOrModuleExportsOrAlias ( sourceFile , left . expression ) ) {
206
- convertNamedExport ( sourceFile , statement , left . name , right , changes , exports ) ;
205
+ convertNamedExport ( sourceFile , assignment as BinaryExpression & { left : PropertyAccessExpression } , changes , exports ) ;
207
206
}
208
207
209
208
return false ;
@@ -223,7 +222,7 @@ namespace ts.codefix {
223
222
case SyntaxKind . SpreadAssignment :
224
223
return undefined ;
225
224
case SyntaxKind . PropertyAssignment :
226
- return ! isIdentifier ( prop . name ) ? undefined : convertExportsDotXEquals ( prop . name . text , prop . initializer ) ;
225
+ return ! isIdentifier ( prop . name ) ? undefined : convertExportsDotXEquals_replaceNode ( prop . name . text , prop . initializer ) ;
227
226
case SyntaxKind . MethodDeclaration :
228
227
return ! isIdentifier ( prop . name ) ? undefined : functionExpressionToDeclaration ( prop . name . text , [ createToken ( SyntaxKind . ExportKeyword ) ] , prop ) ;
229
228
default :
@@ -234,28 +233,26 @@ namespace ts.codefix {
234
233
235
234
function convertNamedExport (
236
235
sourceFile : SourceFile ,
237
- statement : Statement ,
238
- propertyName : Identifier ,
239
- right : Expression ,
236
+ assignment : BinaryExpression & { left : PropertyAccessExpression } ,
240
237
changes : textChanges . ChangeTracker ,
241
238
exports : ExportRenames ,
242
239
) : void {
243
240
// If "originalKeywordKind" was set, this is e.g. `exports.
244
- const { text } = propertyName ;
241
+ const { text } = assignment . left . name ;
245
242
const rename = exports . get ( text ) ;
246
243
if ( rename !== undefined ) {
247
244
/*
248
245
const _class = 0;
249
246
export { _class as class };
250
247
*/
251
248
const newNodes = [
252
- makeConst ( /*modifiers*/ undefined , rename , right ) ,
249
+ makeConst ( /*modifiers*/ undefined , rename , assignment . right ) ,
253
250
makeExportDeclaration ( [ createExportSpecifier ( rename , text ) ] ) ,
254
251
] ;
255
- changes . replaceNodeWithNodes ( sourceFile , statement , newNodes ) ;
252
+ changes . replaceNodeWithNodes ( sourceFile , assignment . parent , newNodes ) ;
256
253
}
257
254
else {
258
- changes . replaceNode ( sourceFile , statement , convertExportsDotXEquals ( text , right ) ) ;
255
+ convertExportsPropertyAssignment ( assignment , sourceFile , changes ) ;
259
256
}
260
257
}
261
258
@@ -303,7 +300,27 @@ namespace ts.codefix {
303
300
return makeExportDeclaration ( [ createExportSpecifier ( /*propertyName*/ undefined , "default" ) ] , moduleSpecifier ) ;
304
301
}
305
302
306
- function convertExportsDotXEquals ( name : string | undefined , exported : Expression ) : Statement {
303
+ function convertExportsPropertyAssignment ( { left, right, parent } : BinaryExpression & { left : PropertyAccessExpression } , sourceFile : SourceFile , changes : textChanges . ChangeTracker ) : void {
304
+ const name = left . name . text ;
305
+ if ( ( isFunctionExpression ( right ) || isArrowFunction ( right ) || isClassExpression ( right ) ) && ( ! right . name || right . name . text === name ) ) {
306
+ // `exports.f = function() {}` -> `export function f() {}` -- Replace `exports.f = ` with `export `, and insert the name after `function`.
307
+ changes . replaceRange ( sourceFile , { pos : left . getStart ( sourceFile ) , end : right . getStart ( sourceFile ) } , createToken ( SyntaxKind . ExportKeyword ) , { suffix : " " } ) ;
308
+
309
+ if ( ! right . name ) changes . insertName ( sourceFile , right , name ) ;
310
+
311
+ const semi = findChildOfKind ( parent , SyntaxKind . SemicolonToken , sourceFile ) ;
312
+ if ( semi ) changes . deleteNode ( sourceFile , semi , { useNonAdjustedEndPosition : true } ) ;
313
+ }
314
+ else {
315
+ // `exports.f = function g() {}` -> `export const f = function g() {}` -- just replace `exports.` with `export const `
316
+ changes . replaceNodeRangeWithNodes ( sourceFile , left . expression , findChildOfKind ( left , SyntaxKind . DotToken , sourceFile ) ! ,
317
+ [ createToken ( SyntaxKind . ExportKeyword ) , createToken ( SyntaxKind . ConstKeyword ) ] ,
318
+ { joiner : " " , suffix : " " } ) ;
319
+ }
320
+ }
321
+
322
+ // TODO: GH#22492 this will cause an error if a change has been made inside the body of the node.
323
+ function convertExportsDotXEquals_replaceNode ( name : string | undefined , exported : Expression ) : Statement {
307
324
const modifiers = [ createToken ( SyntaxKind . ExportKeyword ) ] ;
308
325
switch ( exported . kind ) {
309
326
case SyntaxKind . FunctionExpression : {
0 commit comments