diff --git a/crates/oxc_codegen/src/gen.rs b/crates/oxc_codegen/src/gen.rs index 611754475..c8f9353fc 100644 --- a/crates/oxc_codegen/src/gen.rs +++ b/crates/oxc_codegen/src/gen.rs @@ -1098,9 +1098,7 @@ fn choose_quote(s: &str) -> char { } } -fn print_str(s: &str, p: &mut Codegen<{ MINIFY }>) { - let quote = choose_quote(s); - p.print(quote as u8); +fn print_unquoted_str(s: &str, quote: char, p: &mut Codegen<{ MINIFY }>) { let mut chars = s.chars(); while let Some(c) = chars.next() { @@ -1153,6 +1151,20 @@ fn print_str(s: &str, p: &mut Codegen<{ MINIFY }>) { p.print_str(b"\""); } } + '`' => { + if quote == '`' { + p.print_str(b"\\`"); + } else { + p.print_str(b"`"); + } + } + '$' => { + if chars.clone().next().is_some_and(|next| next == '{') { + p.print_str(b"\\$"); + } else { + p.print_str(b"$"); + } + } // Allow `U+2028` and `U+2029` in string literals // // @@ -1164,12 +1176,15 @@ fn print_str(s: &str, p: &mut Codegen<{ MINIFY }>) { _ => p.print_str(c.escape_default().to_string().as_bytes()), } } - p.print(quote as u8); } impl Gen for StringLiteral { fn gen(&self, p: &mut Codegen<{ MINIFY }>, _ctx: Context) { - print_str(self.value.as_str(), p); + let s = &self.value.as_str(); + let quote = choose_quote(s); + p.print(quote as u8); + print_unquoted_str(s, quote, p); + p.print(quote as u8); } } @@ -1725,7 +1740,9 @@ impl<'a, const MINIFY: bool> Gen for TemplateLiteral<'a> { let mut expressions = self.expressions.iter(); for quasi in &self.quasis { - p.print_str(quasi.value.raw.as_bytes()); + if let Some(cooked) = &quasi.value.cooked { + print_unquoted_str(cooked.as_str(), '`', p); + } if let Some(expr) = expressions.next() { p.print_str(b"${"); diff --git a/crates/oxc_codegen/tests/mod.rs b/crates/oxc_codegen/tests/mod.rs index 84cf15246..38530ce1d 100644 --- a/crates/oxc_codegen/tests/mod.rs +++ b/crates/oxc_codegen/tests/mod.rs @@ -5,10 +5,11 @@ use oxc_span::SourceType; fn test(source_text: &str, expected: &str) { let allocator = Allocator::default(); - let program = Parser::new(&allocator, source_text, SourceType::default()).parse().program; + let source_type = SourceType::default().with_module(true); + let program = Parser::new(&allocator, source_text, source_type).parse().program; let program = allocator.alloc(program); let result = Codegen::::new(source_text.len(), CodegenOptions).build(program); - assert_eq!(expected, result, "for source {source_text}"); + assert_eq!(expected, result, "for source {source_text}, expect {expected}, got {result}"); } #[test] @@ -55,25 +56,24 @@ fn string() { } #[test] -#[ignore] -fn template() { +fn test_template_1() { test("let x = `\\0`", "let x = `\\0`;\n"); - test("let x = `\\x01`", "let x = `\x01`;\n"); + // test("let x = `\\x01`", "let x = `\\x01`;\n"); test("let x = `\\0${0}`", "let x = `\\0${0}`;\n"); - test("let x = `\\x01${0}`", "let x = `\x01${0}`;\n"); + // test("let x = `\\x01${0}`", "let x = `\x01${0}`;\n"); test("let x = `${0}\\0`", "let x = `${0}\\0`;\n"); - test("let x = `${0}\\x01`", "let x = `${0}\x01`;\n"); + // test("let x = `${0}\\x01`", "let x = `${0}\x01`;\n"); test("let x = `${0}\\0${1}`", "let x = `${0}\\0${1}`;\n"); - test("let x = `${0}\\x01${1}`", "let x = `${0}\x01${1}`;\n"); + // test("let x = `${0}\\x01${1}`", "let x = `${0}\x01${1}`;\n"); - test("let x = String.raw`\\1`", "let x = String.raw`\\1`;\n"); - test("let x = String.raw`\\x01`", "let x = String.raw`\\x01`;\n"); - test("let x = String.raw`\\1${0}`", "let x = String.raw`\\1${0}`;\n"); - test("let x = String.raw`\\x01${0}`", "let x = String.raw`\\x01${0}`;\n"); - test("let x = String.raw`${0}\\1`", "let x = String.raw`${0}\\1`;\n"); - test("let x = String.raw`${0}\\x01`", "let x = String.raw`${0}\\x01`;\n"); - test("let x = String.raw`${0}\\1${1}`", "let x = String.raw`${0}\\1${1}`;\n"); - test("let x = String.raw`${0}\\x01${1}`", "let x = String.raw`${0}\\x01${1}`;\n"); + // test("let x = String.raw`\\1`", "let x = String.raw`\\1`;\n"); + // test("let x = String.raw`\\x01`", "let x = String.raw`\\x01`;\n"); + // test("let x = String.raw`\\1${0}`", "let x = String.raw`\\1${0}`;\n"); + // test("let x = String.raw`\\x01${0}`", "let x = String.raw`\\x01${0}`;\n"); + // test("let x = String.raw`${0}\\1`", "let x = String.raw`${0}\\1`;\n"); + // test("let x = String.raw`${0}\\x01`", "let x = String.raw`${0}\\x01`;\n"); + // test("let x = String.raw`${0}\\1${1}`", "let x = String.raw`${0}\\1${1}`;\n"); + // test("let x = String.raw`${0}\\x01${1}`", "let x = String.raw`${0}\\x01${1}`;\n"); test("let x = `${y}`", "let x = `${y}`;\n"); test("let x = `$(y)`", "let x = `$(y)`;\n"); @@ -84,7 +84,7 @@ fn template() { test("await tag`x`", "await tag`x`;\n"); test("await (tag`x`)", "await tag`x`;\n"); - test("(await tag)`x`", "(await tag)`x`;\n"); + // test("(await tag)`x`", "(await tag)`x`;\n"); test("await tag`${x}`", "await tag`${x}`;\n"); test("await (tag`${x}`)", "await tag`${x}`;\n"); @@ -92,13 +92,13 @@ fn template() { test("new tag`x`", "new tag`x`();\n"); test("new (tag`x`)", "new tag`x`();\n"); - test("new tag()`x`", "new tag()`x`;\n"); - test("(new tag)`x`", "new tag()`x`;\n"); + // test("new tag()`x`", "new tag()`x`;\n"); + // test("(new tag)`x`", "new tag()`x`;\n"); test("new tag`${x}`", "new tag`${x}`();\n"); test("new (tag`${x}`)", "new tag`${x}`();\n"); - test("new tag()`${x}`", "new tag()`${x}`;\n"); - test("(new tag)`${x}`", "new tag()`${x}`;\n"); + // test("new tag()`${x}`", "new tag()`${x}`;\n"); + // test("(new tag)`${x}`", "new tag()`${x}`;\n"); } #[test]