Skip to content

Commit 04d750f

Browse files
committed
Preserve method comments in JS->ES6 conversion. Fixes #16622
1 parent f1fb1b9 commit 04d750f

File tree

2 files changed

+64
-7
lines changed

2 files changed

+64
-7
lines changed

src/services/refactors/convertFunctionToEs6Class.ts

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -164,12 +164,15 @@ namespace ts.refactor {
164164
}
165165

166166
switch (assignmentBinaryExpression.right.kind) {
167-
case SyntaxKind.FunctionExpression:
167+
case SyntaxKind.FunctionExpression: {
168168
const functionExpression = assignmentBinaryExpression.right as FunctionExpression;
169-
return createMethod(/*decorators*/ undefined, modifiers, /*asteriskToken*/ undefined, memberDeclaration.name, /*questionToken*/ undefined,
169+
const method = createMethod(/*decorators*/ undefined, modifiers, /*asteriskToken*/ undefined, memberDeclaration.name, /*questionToken*/ undefined,
170170
/*typeParameters*/ undefined, functionExpression.parameters, /*type*/ undefined, functionExpression.body);
171+
copyComments(assignmentBinaryExpression, method);
172+
return method;
173+
}
171174

172-
case SyntaxKind.ArrowFunction:
175+
case SyntaxKind.ArrowFunction: {
173176
const arrowFunction = assignmentBinaryExpression.right as ArrowFunction;
174177
const arrowFunctionBody = arrowFunction.body;
175178
let bodyBlock: Block;
@@ -183,20 +186,40 @@ namespace ts.refactor {
183186
const expression = arrowFunctionBody as Expression;
184187
bodyBlock = createBlock([createReturn(expression)]);
185188
}
186-
return createMethod(/*decorators*/ undefined, modifiers, /*asteriskToken*/ undefined, memberDeclaration.name, /*questionToken*/ undefined,
189+
const method = createMethod(/*decorators*/ undefined, modifiers, /*asteriskToken*/ undefined, memberDeclaration.name, /*questionToken*/ undefined,
187190
/*typeParameters*/ undefined, arrowFunction.parameters, /*type*/ undefined, bodyBlock);
191+
copyComments(assignmentBinaryExpression, method);
192+
return method;
193+
}
188194

189-
default:
195+
default: {
190196
// Don't try to declare members in JavaScript files
191197
if (isSourceFileJavaScript(sourceFile)) {
192198
return;
193199
}
194200
return createProperty(/*decorators*/ undefined, modifiers, memberDeclaration.name, /*questionToken*/ undefined,
195201
/*type*/ undefined, assignmentBinaryExpression.right);
202+
}
196203
}
197204
}
198205
}
199206

207+
function copyComments(sourceNode: Node, targetNode: Node) {
208+
forEachLeadingCommentRange(sourceFile.text, sourceNode.pos, (pos, end, kind, htnl) => {
209+
if (kind === SyntaxKind.MultiLineCommentTrivia) {
210+
// Remove leading /*
211+
pos += 2;
212+
// Remove trailing */
213+
end -= 2;
214+
}
215+
else {
216+
// Remove leading //
217+
pos += 2;
218+
}
219+
addSyntheticLeadingComment(targetNode, kind, sourceFile.text.slice(pos, end), htnl);
220+
});
221+
}
222+
200223
function createClassFromVariableDeclaration(node: VariableDeclaration): ClassDeclaration {
201224
const initializer = node.initializer as FunctionExpression;
202225
if (!initializer || initializer.kind !== SyntaxKind.FunctionExpression) {
@@ -212,17 +235,22 @@ namespace ts.refactor {
212235
memberElements.unshift(createConstructor(/*decorators*/ undefined, /*modifiers*/ undefined, initializer.parameters, initializer.body));
213236
}
214237

215-
return createClassDeclaration(/*decorators*/ undefined, /*modifiers*/ undefined, node.name,
238+
const cls = createClassDeclaration(/*decorators*/ undefined, /*modifiers*/ undefined, node.name,
216239
/*typeParameters*/ undefined, /*heritageClauses*/ undefined, memberElements);
240+
// Don't call copyComments here because we'll already leave them in place
241+
return cls;
217242
}
218243

219244
function createClassFromFunctionDeclaration(node: FunctionDeclaration): ClassDeclaration {
220245
const memberElements = createClassElementsFromSymbol(ctorSymbol);
221246
if (node.body) {
222247
memberElements.unshift(createConstructor(/*decorators*/ undefined, /*modifiers*/ undefined, node.parameters, node.body));
223248
}
224-
return createClassDeclaration(/*decorators*/ undefined, /*modifiers*/ undefined, node.name,
249+
250+
const cls = createClassDeclaration(/*decorators*/ undefined, /*modifiers*/ undefined, node.name,
225251
/*typeParameters*/ undefined, /*heritageClauses*/ undefined, memberElements);
252+
// Don't call copyComments here because we'll already leave them in place
253+
return cls;
226254
}
227255
}
228256
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/// <reference path='fourslash.ts' />
2+
3+
// @allowNonTsExtensions: true
4+
// @Filename: test123.js
5+
//// function fn() {
6+
//// this.x = 100;
7+
//// }
8+
////
9+
//// /**
10+
//// * This is a cool function!
11+
//// */
12+
//// /*1*/fn.prototype.bar = function (x, y, z) {
13+
//// this.x = y;
14+
//// };
15+
16+
verify.fileAfterApplyingRefactorAtMarker('1',
17+
`class fn {
18+
constructor() {
19+
this.x = 100;
20+
}
21+
/**
22+
* This is a cool function!
23+
*/
24+
bar(x, y, z) {
25+
this.x = y;
26+
}
27+
}
28+
29+
`, 'Convert to ES2015 class', 'convert');

0 commit comments

Comments
 (0)