diff --git a/crates/oxc_codegen/examples/codegen.rs b/crates/oxc_codegen/examples/codegen.rs index 5fcbd44269856..806c9a6984e3b 100644 --- a/crates/oxc_codegen/examples/codegen.rs +++ b/crates/oxc_codegen/examples/codegen.rs @@ -30,14 +30,16 @@ fn main() -> std::io::Result<()> { let options = CodegenOptions { enable_source_map: false, enable_typescript: true, ..Default::default() }; - let printed = - Codegen::::new("", &source_text, options, None).build(&ret.program).source_text; + let printed = Codegen::::new("", &source_text, ret.trivias, options) + .build(&ret.program) + .source_text; println!("Printed:"); println!("{printed}"); let ret = Parser::new(&allocator, &printed, source_type).parse(); - let minified = - Codegen::::new("", &source_text, options, None).build(&ret.program).source_text; + let minified = Codegen::::new("", &source_text, ret.trivias, options) + .build(&ret.program) + .source_text; println!("Minified:"); println!("{minified}"); diff --git a/crates/oxc_codegen/examples/sourcemap.rs b/crates/oxc_codegen/examples/sourcemap.rs index 807422626454d..40c559b17240d 100644 --- a/crates/oxc_codegen/examples/sourcemap.rs +++ b/crates/oxc_codegen/examples/sourcemap.rs @@ -29,9 +29,13 @@ fn main() -> std::io::Result<()> { let codegen_options = CodegenOptions { enable_source_map: true, enable_typescript: true, ..Default::default() }; - let CodegenReturn { source_text, source_map } = - Codegen::::new(path.to_string_lossy().as_ref(), &source_text, codegen_options, None) - .build(&ret.program); + let CodegenReturn { source_text, source_map } = Codegen::::new( + path.to_string_lossy().as_ref(), + &source_text, + ret.trivias, + codegen_options, + ) + .build(&ret.program); if let Some(source_map) = source_map { let result = source_map.to_json_string().unwrap(); diff --git a/crates/oxc_codegen/src/lib.rs b/crates/oxc_codegen/src/lib.rs index f33a469f23ff5..687cd888e7264 100644 --- a/crates/oxc_codegen/src/lib.rs +++ b/crates/oxc_codegen/src/lib.rs @@ -37,6 +37,8 @@ pub use crate::{ }; // use crate::mangler::Mangler; +pub type MoveCommentMap = FxHashMap; + #[derive(Debug, Default, Clone, Copy)] pub struct CodegenOptions { /// Pass in the filename to enable source map support. @@ -66,9 +68,12 @@ pub struct CodegenReturn { } pub struct Codegen<'a, const MINIFY: bool> { - #[allow(unused)] options: CodegenOptions, + source_code: &'a str, + + trivias: Trivias, + // mangler: Option, /// Output Code code: Vec, @@ -90,17 +95,13 @@ pub struct Codegen<'a, const MINIFY: bool> { /// Track the current indentation level indentation: u8, + // Builders sourcemap_builder: Option, - comment_gen_related: Option, - source_code: &'a str, -} -pub struct CommentGenRelated { - pub trivials: Trivias, /// The key of map is the node start position, /// the first element of value is the start of the comment /// the second element of value includes the end of the comment and comment kind. - pub move_comment_map: FxHashMap, + move_comment_map: MoveCommentMap, } #[derive(Debug, Clone, Copy)] @@ -114,8 +115,8 @@ impl<'a, const MINIFY: bool> Codegen<'a, MINIFY> { pub fn new( source_name: &str, source_code: &'a str, + trivias: Trivias, options: CodegenOptions, - comment_gen_related: Option, ) -> Self { // Initialize the output code buffer to reduce memory reallocation. // Minification will reduce by at least half of the original size. @@ -130,6 +131,8 @@ impl<'a, const MINIFY: bool> Codegen<'a, MINIFY> { Self { options, + source_code, + trivias, // mangler: None, code: Vec::with_capacity(capacity), needs_semicolon: false, @@ -142,8 +145,7 @@ impl<'a, const MINIFY: bool> Codegen<'a, MINIFY> { start_of_default_export: 0, indentation: 0, sourcemap_builder, - comment_gen_related, - source_code, + move_comment_map: MoveCommentMap::default(), } } @@ -205,27 +207,19 @@ impl<'a, const MINIFY: bool> Codegen<'a, MINIFY> { /// }, b = 10000; /// ``` pub fn move_comment(&mut self, position: u32, full_comment_info: (u32, Comment)) { - if let Some(comment_gen_related) = &mut self.comment_gen_related { - comment_gen_related.move_comment_map.insert(position, full_comment_info); - } + self.move_comment_map.insert(position, full_comment_info); } pub fn try_get_leading_comment(&self, start: u32) -> Option<(&u32, &Comment)> { - self.comment_gen_related.as_ref().and_then(|comment_gen_related| { - comment_gen_related.trivials.comments_range(0..start).next_back() - }) + self.trivias.comments_range(0..start).next_back() } pub fn try_take_moved_comment(&mut self, node_start: u32) -> Option<(u32, Comment)> { - self.comment_gen_related.as_mut().and_then(|comment_gen_related| { - comment_gen_related.move_comment_map.remove(&node_start) - }) + self.move_comment_map.remove(&node_start) } pub fn try_get_leading_comment_from_move_map(&self, start: u32) -> Option<&(u32, Comment)> { - self.comment_gen_related - .as_ref() - .and_then(|comment_gen_related| comment_gen_related.move_comment_map.get(&start)) + self.move_comment_map.get(&start) } fn print_soft_space(&mut self) { diff --git a/crates/oxc_codegen/tests/mod.rs b/crates/oxc_codegen/tests/mod.rs index 4f42e22f5e540..89e5bce2ab309 100644 --- a/crates/oxc_codegen/tests/mod.rs +++ b/crates/oxc_codegen/tests/mod.rs @@ -2,26 +2,15 @@ use oxc_allocator::Allocator; use oxc_codegen::{Codegen, CodegenOptions}; use oxc_parser::Parser; use oxc_span::SourceType; -use rustc_hash::FxHashMap; fn test(source_text: &str, expected: &str, options: Option) { let allocator = Allocator::default(); let source_type = SourceType::default().with_module(true); - let parse_return = Parser::new(&allocator, source_text, source_type).parse(); - let program = parse_return.program; - let program = allocator.alloc(program); + let ret = Parser::new(&allocator, source_text, source_type).parse(); + let program = allocator.alloc(ret.program); let options = options.unwrap_or_default(); - let result = Codegen::::new( - "", - source_text, - options, - Some(oxc_codegen::CommentGenRelated { - trivials: parse_return.trivias, - move_comment_map: FxHashMap::default(), - }), - ) - .build(program) - .source_text; + let result = + Codegen::::new("", source_text, ret.trivias, options).build(program).source_text; assert_eq!(expected, result, "for source {source_text}, expect {expected}, got {result}"); } @@ -31,11 +20,12 @@ fn test_ts(source_text: &str, expected: &str, is_typescript_definition: bool) { .with_typescript(true) .with_typescript_definition(is_typescript_definition) .with_module(true); - let program = Parser::new(&allocator, source_text, source_type).parse().program; - let program = allocator.alloc(program); + let ret = Parser::new(&allocator, source_text, source_type).parse(); + let program = allocator.alloc(ret.program); let codegen_options = CodegenOptions { enable_typescript: true, ..CodegenOptions::default() }; - let result = - Codegen::::new("", source_text, codegen_options, None).build(program).source_text; + let result = Codegen::::new("", source_text, ret.trivias, codegen_options) + .build(program) + .source_text; assert_eq!(expected, result, "for source {source_text}, expect {expected}, got {result}"); } diff --git a/crates/oxc_linter/src/fixer.rs b/crates/oxc_linter/src/fixer.rs index 6a18fcef1ac5d..be35a6aff4106 100644 --- a/crates/oxc_linter/src/fixer.rs +++ b/crates/oxc_linter/src/fixer.rs @@ -1,5 +1,6 @@ use std::borrow::Cow; +use oxc_ast::Trivias; use oxc_codegen::{Codegen, CodegenOptions}; use oxc_diagnostics::OxcDiagnostic; use oxc_span::{GetSpan, Span}; @@ -64,7 +65,7 @@ impl<'c, 'a: 'c> RuleFixer<'c, 'a> { #[allow(clippy::unused_self)] pub fn codegen(self) -> Codegen<'a, false> { - Codegen::::new("", "", CodegenOptions::default(), None) + Codegen::::new("", "", Trivias::default(), CodegenOptions::default()) } } diff --git a/crates/oxc_minifier/examples/minifier.rs b/crates/oxc_minifier/examples/minifier.rs index 7e976e221bd10..45ae3ab61d38f 100644 --- a/crates/oxc_minifier/examples/minifier.rs +++ b/crates/oxc_minifier/examples/minifier.rs @@ -38,14 +38,15 @@ fn main() -> std::io::Result<()> { fn minify(source_text: &str, source_type: SourceType, mangle: bool, whitespace: bool) -> String { let allocator = Allocator::default(); - let program = Parser::new(&allocator, source_text, source_type).parse().program; - let program = allocator.alloc(program); + let ret = Parser::new(&allocator, source_text, source_type).parse(); + let program = allocator.alloc(ret.program); let options = MinifierOptions { mangle, ..MinifierOptions::default() }; Minifier::new(options).build(&allocator, program); if whitespace { - Codegen::::new("", source_text, CodegenOptions::default(), None).build(program) + Codegen::::new("", source_text, ret.trivias, CodegenOptions::default()).build(program) } else { - Codegen::::new("", source_text, CodegenOptions::default(), None).build(program) + Codegen::::new("", source_text, ret.trivias, CodegenOptions::default()) + .build(program) } .source_text } diff --git a/crates/oxc_minifier/tests/mod.rs b/crates/oxc_minifier/tests/mod.rs index 1bd653139f73a..572d3e0a991dc 100644 --- a/crates/oxc_minifier/tests/mod.rs +++ b/crates/oxc_minifier/tests/mod.rs @@ -16,10 +16,10 @@ pub(crate) fn minify( options: MinifierOptions, ) -> String { let allocator = Allocator::default(); - let program = Parser::new(&allocator, source_text, source_type).parse().program; - let program = allocator.alloc(program); + let ret = Parser::new(&allocator, source_text, source_type).parse(); + let program = allocator.alloc(ret.program); Minifier::new(options).build(&allocator, program); - Codegen::::new("", source_text, CodegenOptions::default(), None) + Codegen::::new("", source_text, ret.trivias, CodegenOptions::default()) .build(program) .source_text } diff --git a/crates/oxc_transformer/examples/transformer.rs b/crates/oxc_transformer/examples/transformer.rs index 6c50f9950b85a..0412ff0260498 100644 --- a/crates/oxc_transformer/examples/transformer.rs +++ b/crates/oxc_transformer/examples/transformer.rs @@ -46,11 +46,18 @@ fn main() { }, ..Default::default() }; - Transformer::new(&allocator, path, source_type, &source_text, ret.trivias, transform_options) - .build(&mut program) - .unwrap(); + Transformer::new( + &allocator, + path, + source_type, + &source_text, + ret.trivias.clone(), + transform_options, + ) + .build(&mut program) + .unwrap(); - let printed = Codegen::::new("", &source_text, CodegenOptions::default(), None) + let printed = Codegen::::new("", &source_text, ret.trivias, CodegenOptions::default()) .build(&program) .source_text; println!("Transformed:\n"); diff --git a/crates/oxc_wasm/src/lib.rs b/crates/oxc_wasm/src/lib.rs index 558f43a4a2ba8..245f1a65f04d0 100644 --- a/crates/oxc_wasm/src/lib.rs +++ b/crates/oxc_wasm/src/lib.rs @@ -234,9 +234,15 @@ impl Oxc { if run_options.transform() { let options = TransformOptions::default(); - let result = - Transformer::new(&allocator, &path, source_type, source_text, ret.trivias, options) - .build(program); + let result = Transformer::new( + &allocator, + &path, + source_type, + source_text, + ret.trivias.clone(), + options, + ) + .build(program); if let Err(errs) = result { self.save_diagnostics(errs); } @@ -273,9 +279,13 @@ impl Oxc { ..CodegenOptions::default() }; self.codegen_text = if minifier_options.whitespace() { - Codegen::::new("", source_text, codegen_options, None).build(program).source_text + Codegen::::new("", source_text, ret.trivias, codegen_options) + .build(program) + .source_text } else { - Codegen::::new("", source_text, codegen_options, None).build(program).source_text + Codegen::::new("", source_text, ret.trivias, codegen_options) + .build(program) + .source_text }; Ok(()) diff --git a/tasks/benchmark/benches/codegen_sourcemap.rs b/tasks/benchmark/benches/codegen_sourcemap.rs index 3765df1af48ca..f7f5c7bf68365 100644 --- a/tasks/benchmark/benches/codegen_sourcemap.rs +++ b/tasks/benchmark/benches/codegen_sourcemap.rs @@ -13,13 +13,18 @@ fn bench_codegen_sourcemap(criterion: &mut Criterion) { let source_type = SourceType::from_path(&file.file_name).unwrap(); group.bench_with_input(id, &file.source_text, |b, source_text| { let allocator = Allocator::default(); - let program = Parser::new(&allocator, source_text, source_type).parse().program; + let ret = Parser::new(&allocator, source_text, source_type).parse(); let codegen_options = CodegenOptions { enable_source_map: true, ..CodegenOptions::default() }; b.iter_with_large_drop(|| { - Codegen::::new(file.file_name.as_str(), source_text, codegen_options, None) - .build(&program) - .source_map + Codegen::::new( + file.file_name.as_str(), + source_text, + ret.trivias.clone(), + codegen_options, + ) + .build(&ret.program) + .source_map }); }); } diff --git a/tasks/benchmark/benches/sourcemap.rs b/tasks/benchmark/benches/sourcemap.rs index 812629411716e..810023a6ad564 100644 --- a/tasks/benchmark/benches/sourcemap.rs +++ b/tasks/benchmark/benches/sourcemap.rs @@ -15,17 +15,17 @@ fn bench_sourcemap(criterion: &mut Criterion) { let source_type = SourceType::from_path(&file.file_name).unwrap(); group.bench_with_input(id, &file.source_text, |b, source_text| { let allocator = Allocator::default(); - let program = Parser::new(&allocator, source_text, source_type).parse().program; + let ret = Parser::new(&allocator, source_text, source_type).parse(); let codegen_options = CodegenOptions { enable_source_map: true, ..CodegenOptions::default() }; b.iter(|| { let CodegenReturn { source_map, source_text } = Codegen::::new( file.file_name.as_str(), source_text, + ret.trivias.clone(), codegen_options, - None, ) - .build(&program); + .build(&ret.program); let line = source_text.matches('\n').count() as u32; if let Some(sourcemap) = source_map { let mut concat_sourcemap_builder = ConcatSourceMapBuilder::default(); diff --git a/tasks/coverage/src/codegen.rs b/tasks/coverage/src/codegen.rs index 7e9af5efc69f2..3421c24dbe758 100644 --- a/tasks/coverage/src/codegen.rs +++ b/tasks/coverage/src/codegen.rs @@ -68,11 +68,11 @@ fn get_normal_result( let options = CodegenOptions::default().with_typescript(source_type.is_typescript()); let allocator = Allocator::default(); let parse_result1 = Parser::new(&allocator, source_text, source_type).parse(); - let source_text1 = Codegen::::new("", source_text, options, None) + let source_text1 = Codegen::::new("", source_text, parse_result1.trivias, options) .build(&parse_result1.program) .source_text; let parse_result2 = Parser::new(&allocator, &source_text1, source_type).parse(); - let source_text2 = Codegen::::new("", &source_text1, options, None) + let source_text2 = Codegen::::new("", &source_text1, parse_result2.trivias, options) .build(&parse_result2.program) .source_text; let result = source_text1 == source_text2; @@ -111,11 +111,12 @@ fn get_minify_result( let options = CodegenOptions::default().with_typescript(source_type.is_typescript()); let allocator = Allocator::default(); let parse_result1 = Parser::new(&allocator, source_text, source_type).parse(); - let source_text1 = Codegen::::new("", source_text, options, None) - .build(&parse_result1.program) - .source_text; + let source_text1 = + Codegen::::new("", source_text, parse_result1.trivias.clone(), options) + .build(&parse_result1.program) + .source_text; let parse_result2 = Parser::new(&allocator, source_text1.as_str(), source_type).parse(); - let source_text2 = Codegen::::new("", &source_text1, options, None) + let source_text2 = Codegen::::new("", &source_text1, parse_result2.trivias, options) .build(&parse_result2.program) .source_text; let result = source_text1 == source_text2; diff --git a/tasks/coverage/src/minifier.rs b/tasks/coverage/src/minifier.rs index 34269ed9fd801..b968922d95b0b 100644 --- a/tasks/coverage/src/minifier.rs +++ b/tasks/coverage/src/minifier.rs @@ -97,10 +97,10 @@ fn get_result(source_text: &str, source_type: SourceType) -> TestResult { fn minify(source_text: &str, source_type: SourceType, options: MinifierOptions) -> String { let allocator = Allocator::default(); - let program = Parser::new(&allocator, source_text, source_type).parse().program; - let program = allocator.alloc(program); + let ret = Parser::new(&allocator, source_text, source_type).parse(); + let program = allocator.alloc(ret.program); Minifier::new(options).build(&allocator, program); - Codegen::::new("", source_text, CodegenOptions::default(), None) + Codegen::::new("", source_text, ret.trivias, CodegenOptions::default()) .build(program) .source_text } diff --git a/tasks/coverage/src/runtime/mod.rs b/tasks/coverage/src/runtime/mod.rs index 5e19b041eac79..bbd9880ff3cda 100644 --- a/tasks/coverage/src/runtime/mod.rs +++ b/tasks/coverage/src/runtime/mod.rs @@ -140,10 +140,10 @@ impl Case for CodegenRuntimeTest262Case { let is_only_strict = self.base.meta().flags.contains(&TestFlag::OnlyStrict); let source_type = SourceType::default().with_module(is_module); let allocator = Allocator::default(); - let program = Parser::new(&allocator, source_text, source_type).parse().program; + let ret = Parser::new(&allocator, source_text, source_type).parse(); let mut text = - Codegen::::new("", source_text, CodegenOptions::default(), None) - .build(&program) + Codegen::::new("", source_text, ret.trivias, CodegenOptions::default()) + .build(&ret.program) .source_text; if is_only_strict { text = format!("\"use strict\";\n{text}"); diff --git a/tasks/coverage/src/sourcemap.rs b/tasks/coverage/src/sourcemap.rs index 7b47e5d778567..d3348d869eafd 100644 --- a/tasks/coverage/src/sourcemap.rs +++ b/tasks/coverage/src/sourcemap.rs @@ -132,8 +132,8 @@ impl Case for SourcemapCase { let codegen_ret = Codegen::::new( self.path.to_string_lossy().as_ref(), source_text, + ret.trivias, codegen_options, - None, ) .build(&ret.program); diff --git a/tasks/coverage/src/transformer.rs b/tasks/coverage/src/transformer.rs index 2eb049184fe82..096c433c4b770 100644 --- a/tasks/coverage/src/transformer.rs +++ b/tasks/coverage/src/transformer.rs @@ -48,7 +48,7 @@ fn get_result( source_path, source_type, source_text, - parse_result1.trivias, + parse_result1.trivias.clone(), options.clone(), ) .build(&mut program); @@ -56,16 +56,20 @@ fn get_result( let ts_source_text1 = Codegen::::new( &filename, source_text, + parse_result1.trivias.clone(), CodegenOptions::default().with_typescript(true), - None, ) .build(&program) .source_text; - let source_text1 = - Codegen::::new(&filename, source_text, CodegenOptions::default(), None) - .build(&program) - .source_text; + let source_text1 = Codegen::::new( + &filename, + source_text, + parse_result1.trivias.clone(), + CodegenOptions::default(), + ) + .build(&program) + .source_text; if transform_result1.is_ok() && ts_source_text1 != source_text1 { return TestResult::Mismatch(ts_source_text1.clone(), source_text1.clone()); @@ -79,27 +83,32 @@ fn get_result( source_path, source_type, &source_text1, - parse_result2.trivias, + parse_result2.trivias.clone(), options, ) .build(&mut program); - let source_text2 = - Codegen::::new(&filename, &source_text1, CodegenOptions::default(), None) - .build(&program) - .source_text; + let source_text2 = Codegen::::new( + &filename, + &source_text1, + parse_result2.trivias, + CodegenOptions::default(), + ) + .build(&program) + .source_text; - if source_text1 == source_text2 { + if source_text1 == source_text2 + || transform_result1.is_err_and(|err| { + // If error messages are the same, we consider it as a pass. + transform_result2 + .map_err(|err| err.iter().map(ToString::to_string).collect::>().join("\n")) + .is_err_and(|err_message| { + err.iter().map(ToString::to_string).collect::>().join("\n") + == err_message + }) + }) + { TestResult::Passed - } else if transform_result1.is_err_and(|err| { - // If error messages are the same, we consider it as a pass. - transform_result2 - .map_err(|err| err.iter().map(ToString::to_string).collect::>().join("\n")) - .is_err_and(|err_message| { - err.iter().map(ToString::to_string).collect::>().join("\n") == err_message - }) - }) { - return TestResult::Passed; } else { TestResult::Mismatch(source_text1.clone(), source_text2) } diff --git a/tasks/minsize/src/lib.rs b/tasks/minsize/src/lib.rs index 8ba5ef17dc467..e242b7fb60732 100644 --- a/tasks/minsize/src/lib.rs +++ b/tasks/minsize/src/lib.rs @@ -70,10 +70,10 @@ fn minify_twice(file: &TestFile) -> String { fn minify(source_text: &str, source_type: SourceType, options: MinifierOptions) -> String { let allocator = Allocator::default(); - let program = Parser::new(&allocator, source_text, source_type).parse().program; - let program = allocator.alloc(program); + let ret = Parser::new(&allocator, source_text, source_type).parse(); + let program = allocator.alloc(ret.program); Minifier::new(options).build(&allocator, program); - Codegen::::new("", source_text, CodegenOptions::default(), None) + Codegen::::new("", source_text, ret.trivias, CodegenOptions::default()) .build(program) .source_text } diff --git a/tasks/transform_conformance/src/test_case.rs b/tasks/transform_conformance/src/test_case.rs index 1f4be30b72432..dae0aa8bcb11e 100644 --- a/tasks/transform_conformance/src/test_case.rs +++ b/tasks/transform_conformance/src/test_case.rs @@ -176,7 +176,7 @@ pub trait TestCase { path, source_type, &source_text, - ret.trivias, + ret.trivias.clone(), transform_options.clone(), ) .build(&mut program); @@ -185,8 +185,8 @@ pub trait TestCase { Codegen::::new( "", &source_text, + ret.trivias, CodegenOptions::default().with_typescript(true), - None, ) .build(&program) .source_text @@ -270,14 +270,15 @@ impl TestCase for ConformanceTestCase { &self.path, source_type, &input, - ret.trivias, + ret.trivias.clone(), transform_options.clone(), ); let result = transformer.build(&mut program); if result.is_ok() { - transformed_code = Codegen::::new("", &input, codegen_options, None) - .build(&program) - .source_text; + transformed_code = + Codegen::::new("", &input, ret.trivias, codegen_options) + .build(&program) + .source_text; } else { let error = result .err() @@ -318,9 +319,9 @@ impl TestCase for ConformanceTestCase { }, |output| { // Get expected code by parsing the source text, so we can get the same code generated result. - let program = Parser::new(&allocator, &output, source_type).parse().program; - Codegen::::new("", &output, codegen_options, None) - .build(&program) + let ret = Parser::new(&allocator, &output, source_type).parse(); + Codegen::::new("", &output, ret.trivias, codegen_options) + .build(&ret.program) .source_text }, ); @@ -385,15 +386,14 @@ impl ExecTestCase { fs::write(&target_path, content).unwrap(); let source_text = fs::read_to_string(&target_path).unwrap(); let source_type = SourceType::from_path(&target_path).unwrap(); - let transformed_program = - Parser::new(&allocator, &source_text, source_type).parse().program; + let transformed_ret = Parser::new(&allocator, &source_text, source_type).parse(); let result = Codegen::::new( "", &source_text, + transformed_ret.trivias, CodegenOptions::default().with_typescript(true), - None, ) - .build(&transformed_program) + .build(&transformed_ret.program) .source_text; fs::write(&target_path, result).unwrap();