Skip to content

Commit 1d3732b

Browse files
Copilotandrewbranch
andcommitted
Fix template string escaping per TypeScript PR #60303
Co-authored-by: andrewbranch <3277153+andrewbranch@users.noreply.github.com>
1 parent 3e616fb commit 1d3732b

File tree

4 files changed

+53
-80
lines changed

4 files changed

+53
-80
lines changed

internal/printer/printer_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ func TestEmit(t *testing.T) {
2828
{title: "BooleanLiteral#1", input: `true`, output: `true;`},
2929
{title: "BooleanLiteral#2", input: `false`, output: `false;`},
3030
{title: "NoSubstitutionTemplateLiteral", input: "``", output: "``;"},
31+
{title: "NoSubstitutionTemplateLiteral#2", input: "`\n`", output: "`\n`;"},
3132
{title: "RegularExpressionLiteral#1", input: `/a/`, output: `/a/;`},
3233
{title: "RegularExpressionLiteral#2", input: `/a/g`, output: `/a/g;`},
3334
{title: "NullLiteral", input: `null`, output: `null;`},

internal/printer/template_escaping_test.go

Lines changed: 0 additions & 78 deletions
This file was deleted.
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package printer_test
2+
3+
import (
4+
"testing"
5+
6+
"github.com/microsoft/typescript-go/internal/ast"
7+
"github.com/microsoft/typescript-go/internal/testutil/emittestutil"
8+
"github.com/microsoft/typescript-go/internal/testutil/parsetestutil"
9+
)
10+
11+
// TestTemplateStringEscapingFix tests the fix for TypeScript issue #59150
12+
// This test mirrors the test case added in TypeScript PR #60303
13+
func TestTemplateStringEscapingFix(t *testing.T) {
14+
t.Parallel()
15+
16+
// https://github.com/microsoft/TypeScript/issues/59150
17+
// Replicates: printsCorrectly("template string", {}, printer =>
18+
// printer.printNode(
19+
// ts.EmitHint.Unspecified,
20+
// ts.factory.createNoSubstitutionTemplateLiteral("\n"),
21+
// ts.createSourceFile("source.ts", "", ts.ScriptTarget.ESNext),
22+
// ));
23+
24+
var factory ast.NodeFactory
25+
26+
// Create a synthetic NoSubstitutionTemplateLiteral with just a newline character
27+
templateLiteral := factory.NewNoSubstitutionTemplateLiteral("\n")
28+
29+
// Create a synthetic source file
30+
file := factory.NewSourceFile("/source.ts", "/source.ts", "/source.ts", factory.NewNodeList([]*ast.Node{
31+
factory.NewExpressionStatement(templateLiteral),
32+
}))
33+
ast.SetParentInChildren(file)
34+
parsetestutil.MarkSyntheticRecursive(file)
35+
36+
// The fix ensures that LF newlines are NOT escaped in template literals
37+
// Expected: `\n` (with literal newline character, not escaped)
38+
// Bug would produce: `\\n` (with escaped newline)
39+
emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "`\n`;")
40+
}

internal/printer/utilities.go

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,8 +103,18 @@ func escapeStringWorker(s string, quoteChar QuoteChar, flags getLiteralTextFlags
103103
escape = true
104104
}
105105
default:
106-
if ch < '\u001f' || flags&getLiteralTextFlagsNeverAsciiEscape == 0 && ch > '\u007f' {
107-
escape = true
106+
// For template strings, exclude \u000a (LF/newline) from the character range that gets escaped
107+
// This matches the TypeScript fix in PR #60303 that changed the regex from \u0000-\u001f to \u0000-\u0009\u000b-\u001f
108+
if quoteChar == QuoteCharBacktick {
109+
// For template strings: escape \u0000-\u0009 and \u000b-\u001f (excluding \u000a which is LF)
110+
if (ch >= '\u0000' && ch <= '\u0009') || (ch >= '\u000b' && ch <= '\u001f') || flags&getLiteralTextFlagsNeverAsciiEscape == 0 && ch > '\u007f' {
111+
escape = true
112+
}
113+
} else {
114+
// For regular strings: escape \u0000-\u001f as before
115+
if ch < '\u001f' || flags&getLiteralTextFlagsNeverAsciiEscape == 0 && ch > '\u007f' {
116+
escape = true
117+
}
108118
}
109119
}
110120

0 commit comments

Comments
 (0)