From 5b7e1adc9bff7c3e5cc26beec999ec24f0855504 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Fri, 13 Dec 2024 20:22:49 +0900 Subject: [PATCH] fix(transformer): remove span of define value (#7811) - Closes https://github.com/oxc-project/oxc/issues/7797 Removing span seems to work, but I'm not sure traversing all the time is okay. --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> --- Cargo.lock | 1 + crates/oxc_transformer/Cargo.toml | 1 + .../src/plugins/replace_global_defines.rs | 20 ++++++- .../plugins/replace_global_defines.rs | 55 +++++++++++++++++++ ...eplace_global_defines__test_sourcemap.snap | 23 ++++++++ 5 files changed, 97 insertions(+), 3 deletions(-) create mode 100644 crates/oxc_transformer/tests/integrations/plugins/snapshots/integrations__plugins__replace_global_defines__test_sourcemap.snap diff --git a/Cargo.lock b/Cargo.lock index bcf2d186b2847..d3cd6715dd986 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2057,6 +2057,7 @@ dependencies = [ "oxc_parser", "oxc_regular_expression", "oxc_semantic", + "oxc_sourcemap", "oxc_span", "oxc_syntax", "oxc_traverse", diff --git a/crates/oxc_transformer/Cargo.toml b/crates/oxc_transformer/Cargo.toml index af00bfd7a98d9..2c0904f4253f8 100644 --- a/crates/oxc_transformer/Cargo.toml +++ b/crates/oxc_transformer/Cargo.toml @@ -50,6 +50,7 @@ sha1 = { workspace = true } insta = { workspace = true } oxc_codegen = { workspace = true } oxc_parser = { workspace = true } +oxc_sourcemap = { workspace = true } pico-args = { workspace = true } [features] diff --git a/crates/oxc_transformer/src/plugins/replace_global_defines.rs b/crates/oxc_transformer/src/plugins/replace_global_defines.rs index bb41d4cb2420c..932fca5b7ca71 100644 --- a/crates/oxc_transformer/src/plugins/replace_global_defines.rs +++ b/crates/oxc_transformer/src/plugins/replace_global_defines.rs @@ -2,11 +2,11 @@ use std::{cmp::Ordering, sync::Arc}; use lazy_static::lazy_static; use oxc_allocator::{Address, Allocator, GetAddress}; -use oxc_ast::ast::*; +use oxc_ast::{ast::*, VisitMut}; use oxc_diagnostics::OxcDiagnostic; use oxc_parser::Parser; use oxc_semantic::{IsGlobalReference, ScopeFlags, ScopeTree, SymbolTable}; -use oxc_span::{CompactStr, SourceType}; +use oxc_span::{CompactStr, SourceType, SPAN}; use oxc_syntax::identifier::is_identifier_name; use oxc_traverse::{traverse_mut, Ancestor, Traverse, TraverseCtx}; use rustc_hash::FxHashSet; @@ -293,7 +293,13 @@ impl<'a> ReplaceGlobalDefines<'a> { // Allocate the string lazily because replacement happens rarely. let source_text = self.allocator.alloc_str(source_text); // Unwrapping here, it should already be checked by [ReplaceGlobalDefinesConfig::new]. - Parser::new(self.allocator, source_text, SourceType::default()).parse_expression().unwrap() + let mut expr = Parser::new(self.allocator, source_text, SourceType::default()) + .parse_expression() + .unwrap(); + + RemoveSpans.visit_expression(&mut expr); + + expr } fn replace_identifier_defines( @@ -706,3 +712,11 @@ fn assignment_target_from_expr(expr: Expression) -> Option { _ => None, } } + +struct RemoveSpans; + +impl<'ast> VisitMut<'ast> for RemoveSpans { + fn visit_span(&mut self, span: &mut Span) { + *span = SPAN; + } +} diff --git a/crates/oxc_transformer/tests/integrations/plugins/replace_global_defines.rs b/crates/oxc_transformer/tests/integrations/plugins/replace_global_defines.rs index 1dd021a0bb897..828c9c9adb413 100644 --- a/crates/oxc_transformer/tests/integrations/plugins/replace_global_defines.rs +++ b/crates/oxc_transformer/tests/integrations/plugins/replace_global_defines.rs @@ -2,6 +2,7 @@ use oxc_allocator::Allocator; use oxc_codegen::{CodeGenerator, CodegenOptions}; use oxc_parser::Parser; use oxc_semantic::SemanticBuilder; +use oxc_sourcemap::SourcemapVisualizer; use oxc_span::SourceType; use oxc_transformer::{ReplaceGlobalDefines, ReplaceGlobalDefinesConfig}; @@ -235,3 +236,57 @@ console.log( config.clone(), ); } + +#[test] +fn test_sourcemap() { + let config = ReplaceGlobalDefinesConfig::new(&[ + ("__OBJECT__", r#"{"hello": "test"}"#), + ("__STRING__", r#""development""#), + ("__MEMBER__", r"xx.yy.zz"), + ]) + .unwrap(); + let source_text = r" +1; +__OBJECT__; +2; +__STRING__; +3; +log(__OBJECT__); +4; +log(__STRING__); +5; +__OBJECT__.hello; +6; +log(__MEMBER__); +7; +" + .trim_start(); + + let source_type = SourceType::default(); + let allocator = Allocator::default(); + let ret = Parser::new(&allocator, source_text, source_type).parse(); + let mut program = ret.program; + let (symbols, scopes) = + SemanticBuilder::new().build(&program).semantic.into_symbol_table_and_scope_tree(); + let _ = ReplaceGlobalDefines::new(&allocator, config).build(symbols, scopes, &mut program); + let result = CodeGenerator::new() + .with_options(CodegenOptions { + single_quote: true, + source_map_path: Some(std::path::Path::new(&"test.js.map").to_path_buf()), + ..CodegenOptions::default() + }) + .build(&program); + + let output = result.code; + let output_map = result.map.unwrap(); + let visualizer = SourcemapVisualizer::new(&output, &output_map); + let snapshot = visualizer.into_visualizer_text(); + insta::assert_snapshot!("test_sourcemap", snapshot); +} + +#[test] +#[should_panic(expected = "")] +fn test_complex() { + let config = ReplaceGlobalDefinesConfig::new(&[("__DEF__", "((() => {})())")]).unwrap(); + test("__DEF__", "1", config.clone()); +} diff --git a/crates/oxc_transformer/tests/integrations/plugins/snapshots/integrations__plugins__replace_global_defines__test_sourcemap.snap b/crates/oxc_transformer/tests/integrations/plugins/snapshots/integrations__plugins__replace_global_defines__test_sourcemap.snap new file mode 100644 index 0000000000000..bbe59a12ff6e0 --- /dev/null +++ b/crates/oxc_transformer/tests/integrations/plugins/snapshots/integrations__plugins__replace_global_defines__test_sourcemap.snap @@ -0,0 +1,23 @@ +--- +source: crates/oxc_transformer/tests/integrations/plugins/replace_global_defines.rs +expression: snapshot +snapshot_kind: text +--- +- test.js.map +(0:0) "1;\n" --> (0:0) "1;\n" +(1:0) "__OBJECT__;\n" --> (1:0) "({ 'hello': 'test' });\n" +(2:0) "2;\n" --> (2:0) "2;\n" +(3:0) "__STRING__;\n" --> (3:0) "'development';\n" +(4:0) "3;\n" --> (4:0) "3;\n" +(5:0) "log(__OBJECT__)" --> (5:0) "log({ 'hello': 'test' })" +(5:15) ";\n" --> (5:24) ";\n" +(6:0) "4;\n" --> (6:0) "4;\n" +(7:0) "log(__STRING__)" --> (7:0) "log('development')" +(7:15) ";\n" --> (7:18) ";\n" +(8:0) "5;\n" --> (8:0) "5;\n" +(9:0) "__OBJECT__." --> (9:0) "({ 'hello': 'test' })." +(9:11) "hello;\n" --> (9:22) "hello;\n" +(10:0) "6;\n" --> (10:0) "6;\n" +(11:0) "log(__MEMBER__)" --> (11:0) "log(xx.yy.zz)" +(11:15) ";\n" --> (11:13) ";\n" +(12:0) "7;\n" --> (12:0) "7;\n"