diff --git a/internal/printer/utilities.go b/internal/printer/utilities.go index 6561a5a95a..9eac7f1b89 100644 --- a/internal/printer/utilities.go +++ b/internal/printer/utilities.go @@ -103,7 +103,7 @@ func escapeStringWorker(s string, quoteChar QuoteChar, flags getLiteralTextFlags escape = true } default: - if ch < '\u001f' || flags&getLiteralTextFlagsNeverAsciiEscape == 0 && ch > '\u007f' { + if ch <= '\u001f' || flags&getLiteralTextFlagsNeverAsciiEscape == 0 && ch > '\u007f' { escape = true } } diff --git a/internal/printer/utilities_test.go b/internal/printer/utilities_test.go index cbe034f22d..fcd9ab3412 100644 --- a/internal/printer/utilities_test.go +++ b/internal/printer/utilities_test.go @@ -25,6 +25,14 @@ func TestEscapeString(t *testing.T) { {s: "ab'c", quoteChar: QuoteCharSingleQuote, expected: `ab\'c`}, {s: "ab\"c", quoteChar: QuoteCharSingleQuote, expected: `ab"c`}, {s: "ab`c", quoteChar: QuoteCharBacktick, expected: "ab\\`c"}, + // Test case for issue #59150: newlines in template strings should NOT be escaped + {s: "\n", quoteChar: QuoteCharBacktick, expected: "\n"}, + {s: "ab\nc", quoteChar: QuoteCharBacktick, expected: "ab\nc"}, + // Test other control characters that SHOULD be escaped in template strings + {s: "\u0001", quoteChar: QuoteCharBacktick, expected: "\\u0001"}, // control character should be escaped + {s: "\u0009", quoteChar: QuoteCharBacktick, expected: "\\t"}, // tab should be escaped + {s: "\u000b", quoteChar: QuoteCharBacktick, expected: "\\v"}, // vertical tab should be escaped + {s: "\u001f", quoteChar: QuoteCharBacktick, expected: "\\u001F"}, // unit separator should be escaped } for i, rec := range data { t.Run(fmt.Sprintf("[%d] escapeString(%q, %v)", i, rec.s, rec.quoteChar), func(t *testing.T) { @@ -51,7 +59,9 @@ func TestEscapeNonAsciiString(t *testing.T) { {s: "ab'c", quoteChar: QuoteCharSingleQuote, expected: `ab\'c`}, {s: "ab\"c", quoteChar: QuoteCharSingleQuote, expected: `ab"c`}, {s: "ab`c", quoteChar: QuoteCharBacktick, expected: "ab\\`c"}, - {s: "ab\u008fc", quoteChar: QuoteCharDoubleQuote, expected: `ab\u008Fc`}, + // Test case for issue #59150: newlines in template strings should NOT be escaped + {s: "\n", quoteChar: QuoteCharBacktick, expected: "\n"}, + {s: "ab\nc", quoteChar: QuoteCharBacktick, expected: "ab\nc"}, {s: "𝟘𝟙", quoteChar: QuoteCharDoubleQuote, expected: `\uD835\uDFD8\uD835\uDFD9`}, } for i, rec := range data { @@ -150,3 +160,19 @@ func TestIsRecognizedTripleSlashComment(t *testing.T) { }) } } + +func TestSyntheticTemplateLiteral(t *testing.T) { + t.Parallel() + + // Test the specific issue: LF character should not be escaped in template strings + // This mimics the TypeScript test case: ts.factory.createNoSubstitutionTemplateLiteral("\n") + + result := EscapeString("\n", QuoteCharBacktick) + expected := "\n" // LF should NOT be escaped for backticks + assert.Equal(t, result, expected) + + // Also test that other control characters are still escaped + result2 := EscapeString("\u0001", QuoteCharBacktick) + expected2 := "\\u0001" // Other control characters should still be escaped + assert.Equal(t, result2, expected2) +} diff --git a/testdata/baselines/reference/submodule/conformance/templateStringControlCharacterEscapes03.types b/testdata/baselines/reference/submodule/conformance/templateStringControlCharacterEscapes03.types index cb40439a14..aea47c3578 100644 --- a/testdata/baselines/reference/submodule/conformance/templateStringControlCharacterEscapes03.types +++ b/testdata/baselines/reference/submodule/conformance/templateStringControlCharacterEscapes03.types @@ -3,5 +3,5 @@ === templateStringControlCharacterEscapes03.ts === var x = `\x1F\u001f 1F 1f`; >x : string ->`\x1F\u001f 1F 1f` : " 1F 1f" +>`\x1F\u001f 1F 1f` : "\u001F\u001F 1F 1f" diff --git a/testdata/baselines/reference/submodule/conformance/templateStringControlCharacterEscapes03.types.diff b/testdata/baselines/reference/submodule/conformance/templateStringControlCharacterEscapes03.types.diff deleted file mode 100644 index 5fcd94380c..0000000000 --- a/testdata/baselines/reference/submodule/conformance/templateStringControlCharacterEscapes03.types.diff +++ /dev/null @@ -1,8 +0,0 @@ ---- old.templateStringControlCharacterEscapes03.types -+++ new.templateStringControlCharacterEscapes03.types -@@= skipped -2, +2 lines =@@ - === templateStringControlCharacterEscapes03.ts === - var x = `\x1F\u001f 1F 1f`; - >x : string -->`\x1F\u001f 1F 1f` : "\u001F\u001F 1F 1f" -+>`\x1F\u001f 1F 1f` : " 1F 1f" diff --git a/testdata/baselines/reference/submodule/conformance/templateStringControlCharacterEscapes03_ES6.types b/testdata/baselines/reference/submodule/conformance/templateStringControlCharacterEscapes03_ES6.types index f1e600e06b..42501f67b6 100644 --- a/testdata/baselines/reference/submodule/conformance/templateStringControlCharacterEscapes03_ES6.types +++ b/testdata/baselines/reference/submodule/conformance/templateStringControlCharacterEscapes03_ES6.types @@ -3,5 +3,5 @@ === templateStringControlCharacterEscapes03_ES6.ts === var x = `\x1F\u001f 1F 1f`; >x : string ->`\x1F\u001f 1F 1f` : " 1F 1f" +>`\x1F\u001f 1F 1f` : "\u001F\u001F 1F 1f" diff --git a/testdata/baselines/reference/submodule/conformance/templateStringControlCharacterEscapes03_ES6.types.diff b/testdata/baselines/reference/submodule/conformance/templateStringControlCharacterEscapes03_ES6.types.diff deleted file mode 100644 index 30a3c9a4e6..0000000000 --- a/testdata/baselines/reference/submodule/conformance/templateStringControlCharacterEscapes03_ES6.types.diff +++ /dev/null @@ -1,8 +0,0 @@ ---- old.templateStringControlCharacterEscapes03_ES6.types -+++ new.templateStringControlCharacterEscapes03_ES6.types -@@= skipped -2, +2 lines =@@ - === templateStringControlCharacterEscapes03_ES6.ts === - var x = `\x1F\u001f 1F 1f`; - >x : string -->`\x1F\u001f 1F 1f` : "\u001F\u001F 1F 1f" -+>`\x1F\u001f 1F 1f` : " 1F 1f"