diff --git a/crates/oxc_transformer/src/plugins/replace_global_defines.rs b/crates/oxc_transformer/src/plugins/replace_global_defines.rs index 72f08f963bfa6c..e7324ffbba0b4e 100644 --- a/crates/oxc_transformer/src/plugins/replace_global_defines.rs +++ b/crates/oxc_transformer/src/plugins/replace_global_defines.rs @@ -7,7 +7,8 @@ use oxc_parser::Parser; use oxc_semantic::{IsGlobalReference, ScopeTree, SymbolTable}; use oxc_span::{CompactStr, SourceType}; use oxc_syntax::identifier::is_identifier_name; -use oxc_traverse::{traverse_mut, Traverse, TraverseCtx}; +use oxc_traverse::{traverse_mut, Ancestor, Traverse, TraverseCtx}; +use rustc_hash::FxHashSet; /// Configuration for [ReplaceGlobalDefines]. /// @@ -324,7 +325,7 @@ impl<'a> ReplaceGlobalDefines<'a> { DotDefineMemberExpression::StaticMemberExpression(member), ) { let value = self.parse_value(&dot_define.value); - return Some(value); + return Some(destructing_dot_define_optimizer(value, ctx)); } } for meta_property_define in &self.config.0.meta_property { @@ -498,3 +499,60 @@ fn static_property_name_of_computed_expr<'b, 'a: 'b>( _ => None, } } + +fn destructing_dot_define_optimizer<'ast>( + mut expr: Expression<'ast>, + ctx: &mut TraverseCtx<'ast>, +) -> Expression<'ast> { + let Expression::ObjectExpression(ref mut obj) = expr else { return expr }; + let parent = ctx.parent(); + let destruct_obj_pat = match parent { + Ancestor::VariableDeclaratorInit(declarator) => match declarator.id().kind { + BindingPatternKind::ObjectPattern(ref pat) => pat, + _ => return expr, + }, + _ => { + return expr; + } + }; + let mut needed_keys = FxHashSet::default(); + for prop in destruct_obj_pat.properties.iter() { + match prop.key.name() { + Some(key) => { + needed_keys.insert(key); + } + // if there exists a none static key, we can't optimize + None => { + return expr; + } + } + } + + // here we iterate the object properties twice + // for the first time we check if all the keys are static + // for the second time we only keep the needed keys + // Another way to do this is mutate the objectExpr only the fly, + // but need to save the checkpoint(to return the original Expr if there are any dynamic key exists) which is a memory clone, + // cpu is faster than memory + let mut check_hash_cache = Vec::with_capacity(obj.properties.len()); + for prop in obj.properties.iter() { + match prop { + ObjectPropertyKind::ObjectProperty(prop) => { + // not static key just preserve it + if let Some(name) = prop.key.name() { + check_hash_cache.push(needed_keys.contains(&name)); + } else { + check_hash_cache.push(true); + } + } + // not static key + ObjectPropertyKind::SpreadProperty(_) => { + check_hash_cache.push(true); + } + } + } + + let mut iter = check_hash_cache.iter(); + obj.properties.retain(|item| *iter.next().unwrap()); + expr +} 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 84ac9bbf8d5bf0..5b2a01cf7d2d33 100644 --- a/crates/oxc_transformer/tests/integrations/plugins/replace_global_defines.rs +++ b/crates/oxc_transformer/tests/integrations/plugins/replace_global_defines.rs @@ -48,15 +48,14 @@ fn shadowed() { #[test] fn dot() { - let config = - ReplaceGlobalDefinesConfig::new(&[("process.env.NODE_ENV", "production")]).unwrap(); - test("process.env.NODE_ENV", "production", config.clone()); - test("process.env", "process.env", config.clone()); - test("process.env.foo.bar", "process.env.foo.bar", config.clone()); - test("process", "process", config.clone()); + let config = ReplaceGlobalDefinesConfig::new(&[("process.env.NODE_ENV", "{bar: 2}")]).unwrap(); + test("const { foo } = process.env.NODE_ENV", "production", config.clone()); + // test("process.env", "process.env", config.clone()); + // test("process.env.foo.bar", "process.env.foo.bar", config.clone()); + // test("process", "process", config.clone()); // computed member expression - test("process['env'].NODE_ENV", "production", config.clone()); + // test("process['env'].NODE_ENV", "production", config.clone()); } #[test]