mirror of
https://github.com/danbulant/oxc
synced 2026-05-25 04:42:10 +00:00
feat(oxc_transformer): replace_global_define destructuring assignment optimization (#7449)
1. Closed #7382 Co-authored-by: Hiroshi Ogawa <hi.ogawa.zz@gmail.com>
This commit is contained in:
parent
d5df615264
commit
87978499a3
2 changed files with 95 additions and 3 deletions
|
|
@ -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,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
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue