Skip to content

Commit

Permalink
feat: replace_global_define destruct
Browse files Browse the repository at this point in the history
  • Loading branch information
IWANABETHATGUY committed Nov 24, 2024
1 parent b04041d commit 0ec3330
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 9 deletions.
62 changes: 60 additions & 2 deletions crates/oxc_transformer/src/plugins/replace_global_defines.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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].
///
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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
}
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down

0 comments on commit 0ec3330

Please sign in to comment.