Skip to content

Commit 3e6ec46

Browse files
authored
fix: improve comma appending logic to handle nested template literals correctly (#1630)
1 parent 1b68ee6 commit 3e6ec46

2 files changed

Lines changed: 43 additions & 4 deletions

File tree

packages/ts-morph/src/manipulation/helpers/appendCommaToText.ts

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,44 @@ export function getAppendCommaPos(text: string) {
2121
scanner.setText(text);
2222

2323
try {
24-
if (scanner.scan() === ts.SyntaxKind.EndOfFileToken)
24+
let token = scanner.scan();
25+
26+
if (token === ts.SyntaxKind.EndOfFileToken)
2527
return -1;
2628

27-
while (scanner.scan() !== ts.SyntaxKind.EndOfFileToken) {
28-
// just keep scanning...
29+
// A stack to track nested template literals and their inner brace expressions
30+
// otherwise, the scanner will interpret text in template literals as actual tokens,
31+
// e.g. the /* in `${p}/*` will be interpreted as a comment, not inside the template literal
32+
const templateStack: ts.SyntaxKind[] = [];
33+
34+
while (token !== ts.SyntaxKind.EndOfFileToken) {
35+
switch (token) {
36+
case ts.SyntaxKind.TemplateHead:
37+
templateStack.push(token);
38+
break;
39+
case ts.SyntaxKind.OpenBraceToken:
40+
if (templateStack.length > 0)
41+
templateStack.push(token);
42+
break;
43+
case ts.SyntaxKind.CloseBraceToken: {
44+
if (templateStack.length > 0) {
45+
const lastTemplateStackToken = templateStack.at(-1);
46+
if (lastTemplateStackToken === ts.SyntaxKind.TemplateHead) {
47+
// Re-scan the template token to skip the inner text
48+
token = scanner.reScanTemplateToken(false);
49+
// Only pop for TemplateTail, not for TemplateMiddle
50+
if (token === ts.SyntaxKind.TemplateTail)
51+
templateStack.pop();
52+
} else
53+
templateStack.pop();
54+
}
55+
break;
56+
}
57+
}
58+
token = scanner.scan();
2959
}
3060

31-
const pos = scanner.getStartPos();
61+
const pos = scanner.getTokenFullStart();
3262
return text[pos - 1] === "," ? -1 : pos;
3363
} finally {
3464
// ensure the scanner doesn't hold onto the text so the string

packages/ts-morph/src/tests/manipulation/manipulations/insertIntoCommaSeparatedNodesTests.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,5 +229,14 @@ describe("insertIntoCommaSeparatedNodes", () => {
229229
"const t = {\n p,\n prop2: 4\n // test\n};",
230230
);
231231
});
232+
233+
it("should insert a template string with /* before a preceeding node correctly", () => {
234+
doTest(
235+
"const t = {\n prop1\n};",
236+
0,
237+
[{ name: "prop2", initializer: "`${p}/*`" }],
238+
"const t = {\n prop2: `${p}/*`,\n prop1\n};",
239+
);
240+
});
232241
});
233242
});

0 commit comments

Comments
 (0)