diff --git a/CHANGELOG.md b/CHANGELOG.md index 2e82be4d0fe..06e50a92d6a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,8 @@ This mirrors how the JS code generator similarly avoids the character sequence `= 1 && text[i-1] == '<' && strings.HasPrefix(text[i+1:], "style") { + if i >= 1 && text[i-1] == '<' && i+6 <= len(text) && strings.EqualFold(text[i+1:i+6], "style") { escape = escapeBackslash } diff --git a/internal/css_printer/css_printer_test.go b/internal/css_printer/css_printer_test.go index d1964e890a8..7fde4dc06f3 100644 --- a/internal/css_printer/css_printer_test.go +++ b/internal/css_printer/css_printer_test.go @@ -90,7 +90,11 @@ func TestStringQuote(t *testing.T) { expectPrintedString(t, "", "\"\"") expectPrintedString(t, "", "\"<\\/style>\"") expectPrintedString(t, "/style", "\">/style\"") + expectPrintedString(t, ">/STYLE", "\">/STYLE\"") + expectPrintedString(t, ">/StYlE", "\">/StYlE\"") } func TestURLQuote(t *testing.T) { diff --git a/internal/js_lexer/js_lexer.go b/internal/js_lexer/js_lexer.go index 9257ff7aae1..ed161e26aab 100644 --- a/internal/js_lexer/js_lexer.go +++ b/internal/js_lexer/js_lexer.go @@ -2840,7 +2840,7 @@ func StringToUTF16(text string) []uint16 { } func UTF16ToString(text []uint16) string { - temp := make([]byte, utf8.UTFMax) + var temp [utf8.UTFMax]byte b := strings.Builder{} n := len(text) for i := 0; i < n; i++ { @@ -2851,14 +2851,14 @@ func UTF16ToString(text []uint16) string { i++ } } - width := encodeWTF8Rune(temp, r1) + width := encodeWTF8Rune(temp[:], r1) b.Write(temp[:width]) } return b.String() } func UTF16ToStringWithValidation(text []uint16) (string, uint16, bool) { - temp := make([]byte, utf8.UTFMax) + var temp [utf8.UTFMax]byte b := strings.Builder{} n := len(text) for i := 0; i < n; i++ { @@ -2877,7 +2877,7 @@ func UTF16ToStringWithValidation(text []uint16) (string, uint16, bool) { } else if r1 >= 0xDC00 && r1 <= 0xDFFF { return "", uint16(r1), false } - width := encodeWTF8Rune(temp, r1) + width := encodeWTF8Rune(temp[:], r1) b.Write(temp[:width]) } return b.String(), 0, true @@ -2889,7 +2889,7 @@ func UTF16EqualsString(text []uint16, str string) bool { // Strings can't be equal if UTF-16 encoding is longer than UTF-8 encoding return false } - temp := [utf8.UTFMax]byte{} + var temp [utf8.UTFMax]byte n := len(text) j := 0 for i := 0; i < n; i++ { diff --git a/internal/js_parser/js_parser.go b/internal/js_parser/js_parser.go index fa061324232..c1a3fe815ef 100644 --- a/internal/js_parser/js_parser.go +++ b/internal/js_parser/js_parser.go @@ -10515,6 +10515,20 @@ func (p *parser) mangleTemplate(loc logger.Loc, e *js_ast.ETemplate) js_ast.Expr return js_ast.Expr{Loc: loc, Data: e} } +func containsClosingScriptTag(text string) bool { + for { + i := strings.Index(text, "= 6 && strings.EqualFold(text[:6], "script") { + return true + } + } + return false +} + // This function takes "exprIn" as input from the caller and produces "exprOut" // for the caller to pass along extra data. This is mostly for optional chaining. func (p *parser) visitExprInOut(expr js_ast.Expr, in exprIn) (js_ast.Expr, exprOut) { @@ -10785,11 +10799,11 @@ func (p *parser) visitExprInOut(expr js_ast.Expr, in exprIn) (js_ast.Expr, exprO // Lower tagged template literals that include "= 2 && text[i-2] == '<' && i+6 <= len(text) && js_lexer.UTF16EqualsString(text[i:i+6], "script") { - js = append(js, '\\') + if i >= 2 && text[i-2] == '<' && i+6 <= len(text) { + script := "script" + matches := true + for j := 0; j < 6; j++ { + a := text[i+j] + b := uint16(script[j]) + if a >= 'A' && a <= 'Z' { + a += 'a' - 'A' + } + if a != b { + matches = false + break + } + } + if matches { + js = append(js, '\\') + } } js = append(js, '/') @@ -1781,7 +1796,7 @@ func (p *printer) printExpr(expr js_ast.Expr, level js_ast.L, flags printExprFla if n > 0 { // Avoid forming a single-line comment or "= 7 && strings.EqualFold(e.Value[:7], "/script")) { p.print(" ") } } @@ -2405,9 +2420,30 @@ func (p *printer) printIf(s *js_ast.SIf) { } } +func escapeClosingScriptTag(text string) string { + i := strings.Index(text, "= 7 && strings.EqualFold(text[:7], "/script") { + b.WriteByte('\\') + } + i = strings.Index(text, "/script\n//! /script", "//! <\\/script\n//! >/script\n//! /script\n") + expectPrinted(t, "//! /SCRIPT\n//! /SCRIPT", "//! <\\/SCRIPT\n//! >/SCRIPT\n//! /SCRIPT\n") + expectPrinted(t, "//! /ScRiPt\n//! /ScRiPt", "//! <\\/ScRiPt\n//! >/ScRiPt\n//! /ScRiPt\n") + expectPrinted(t, "/*! \";\n") expectPrinted(t, "String.raw`\";\n") expectPrinted(t, "String.raw`${a}\";\n") + expectPrinted(t, "String.raw`\";\n") + expectPrinted(t, "String.raw`\";\n") // Negative cases expectPrinted(t, "x = '