diff --git a/internal/printer/printer_test.go b/internal/printer/printer_test.go index 1bd17cc6c6..2e2c2f4600 100644 --- a/internal/printer/printer_test.go +++ b/internal/printer/printer_test.go @@ -28,6 +28,8 @@ func TestEmit(t *testing.T) { {title: "BooleanLiteral#1", input: `true`, output: `true;`}, {title: "BooleanLiteral#2", input: `false`, output: `false;`}, {title: "NoSubstitutionTemplateLiteral", input: "``", output: "``;"}, + {title: "NoSubstitutionTemplateLiteral#2", input: "`\n`", output: "`\n`;"}, + {title: "NoSubstitutionTemplateLiteral#3", input: "`\u001f`", output: "`\\u001F`;"}, {title: "RegularExpressionLiteral#1", input: `/a/`, output: `/a/;`}, {title: "RegularExpressionLiteral#2", input: `/a/g`, output: `/a/g;`}, {title: "NullLiteral", input: `null`, output: `null;`}, diff --git a/internal/printer/utilities.go b/internal/printer/utilities.go index 6561a5a95a..2e8838c1f6 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 } } @@ -205,6 +205,21 @@ func canUseOriginalText(node *ast.LiteralLikeNode, flags getLiteralTextFlags) bo } } + // For template literals, check if they contain characters that need escaping + if node.Kind == ast.KindNoSubstitutionTemplateLiteral || + node.Kind == ast.KindTemplateHead || + node.Kind == ast.KindTemplateMiddle || + node.Kind == ast.KindTemplateTail { + text := node.TemplateLiteralLikeData().Text + for _, ch := range text { + // Check if this character needs escaping according to the TypeScript PR #60303 fix + // Characters in range \u0000-\u001f (excluding \u000a which is handled separately) should be escaped + if ch <= '\u001f' && ch != '\n' { + return false // Force escaping path + } + } + } + // Finally, we do not use the original text of a BigInt literal // TODO(rbuckton): The reason as to why we do not use the original text for bigints is not mentioned in the // original compiler source. It could be that this is no longer necessary, in which case bigint literals should