Skip to content

Commit

Permalink
fix(oxc_codegen): don't emit empty span mapping (#7448)
Browse files Browse the repository at this point in the history
  • Loading branch information
hi-ogawa authored and IWANABETHATGUY committed Nov 24, 2024
1 parent d5df615 commit 85ffed8
Show file tree
Hide file tree
Showing 2 changed files with 95 additions and 3 deletions.
65 changes: 62 additions & 3 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,13 +325,13 @@ 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 {
if Self::is_meta_property_define(meta_property_define, member) {
let value = self.parse_value(&meta_property_define.value);
return Some(value);
return Some(destructing_dot_define_optimizer(value, ctx));
}
}
None
Expand Down Expand Up @@ -498,3 +499,61 @@ 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 {
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 allocation
let mut should_preserved_keys = Vec::with_capacity(obj.properties.len());
for prop in &obj.properties {
let v = match prop {
ObjectPropertyKind::ObjectProperty(prop) => {
// not static key just preserve it
if let Some(name) = prop.key.name() {
needed_keys.contains(&name)
} else {
true
}
}
// not static key
ObjectPropertyKind::SpreadProperty(_) => true,
};
should_preserved_keys.push(v);
}

// we could ensure `should_preserved_keys` has the same length as `obj.properties`
// the method copy from std doc https://doc.rust-lang.org/std/vec/struct.Vec.html#examples-26
let mut iter = should_preserved_keys.iter();
obj.properties.retain(|_| *iter.next().unwrap());
expr
}
Original file line number Diff line number Diff line change
Expand Up @@ -104,3 +104,36 @@ fn optional_chain() {
test_same("a?.[b][c]", config.clone());
test_same("a[b]?.[c]", config.clone());
}

#[test]
fn dot_define_with_destruct() {
let config = ReplaceGlobalDefinesConfig::new(&[(
"process.env.NODE_ENV",
"{'a': 1, b: 2, c: true, d: {a: b}}",
)])
.unwrap();
test(
"const {a, c} = process.env.NODE_ENV",
"const { a, c } = {\n\t'a': 1,\n\tc: true};",
config.clone(),
);
// bailout
test(
"const {[any]: alias} = process.env.NODE_ENV",
"const { [any]: alias } = {\n\t'a': 1,\n\tb: 2,\n\tc: true,\n\td: { a: b }\n};",
config.clone(),
);

// should filterout unused key even rhs objectExpr has SpreadElement

let config = ReplaceGlobalDefinesConfig::new(&[(
"process.env.NODE_ENV",
"{'a': 1, b: 2, c: true, ...unknown}",
)])
.unwrap();
test(
"const {a} = process.env.NODE_ENV",
"const { a } = {\n\t'a': 1,\n\t...unknown\n};\n",
config.clone(),
);
}

0 comments on commit 85ffed8

Please sign in to comment.