From 21614f23b687420d623e414f77ca98f5f4030a7e Mon Sep 17 00:00:00 2001 From: IWANABETHATGUY <17974631+IWANABETHATGUY@users.noreply.github.com> Date: Sat, 23 Nov 2024 10:26:43 +0000 Subject: [PATCH] feat(oxc_transformer): ReplaceGlobalDefinesPlugin for ComputedMemberExpr (#7431) support replace `test['foo']` like `ComputedMemberExpr`, since they are static either, e.g.: https://hyrious.me/esbuild-repl/?version=0.23.0&b=e%00entry.js%00console.log%28a%5B%27b%27%5D.c%29&b=%00file.js%00&b=%00file2.js%00&o=%7B%0A++treeShaking%3A+true%2C%0A%0AglobalName%3A+%22global%22%2C%0A%22bundle%22%3A+false%2C%0Aformat%3A+%22esm%22%2C%0Adefine%3A+%7B%0A++%22a.b.c%22%3A+%22foo%22%0A%7D%0A%7D --- .../src/plugins/inject_global_variables.rs | 11 ++- .../src/plugins/replace_global_defines.rs | 75 +++++++++++++++++-- .../plugins/replace_global_defines.rs | 9 ++- 3 files changed, 84 insertions(+), 11 deletions(-) diff --git a/crates/oxc_transformer/src/plugins/inject_global_variables.rs b/crates/oxc_transformer/src/plugins/inject_global_variables.rs index 0b3a4b2dbd834..7fe01c47ec324 100644 --- a/crates/oxc_transformer/src/plugins/inject_global_variables.rs +++ b/crates/oxc_transformer/src/plugins/inject_global_variables.rs @@ -8,7 +8,10 @@ use oxc_semantic::{ScopeTree, SymbolTable}; use oxc_span::{CompactStr, SPAN}; use oxc_traverse::{traverse_mut, Traverse, TraverseCtx}; -use super::replace_global_defines::{DotDefine, ReplaceGlobalDefines}; +use super::{ + replace_global_defines::{DotDefine, ReplaceGlobalDefines}, + DotDefineMemberExpression, +}; #[derive(Debug, Clone)] pub struct InjectGlobalVariablesConfig { @@ -233,7 +236,11 @@ impl<'a> InjectGlobalVariables<'a> { fn replace_dot_defines(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) { if let Expression::StaticMemberExpression(member) = expr { for DotDefineState { dot_define, value_atom } in &mut self.dot_defines { - if ReplaceGlobalDefines::is_dot_define(ctx.symbols(), dot_define, member) { + if ReplaceGlobalDefines::is_dot_define( + ctx.symbols(), + dot_define, + DotDefineMemberExpression::StaticMemberExpression(member), + ) { // If this is first replacement made for this dot define, // create `Atom` for replacement, and record in `replaced_dot_defines` let value_atom = value_atom.get_or_insert_with(|| { diff --git a/crates/oxc_transformer/src/plugins/replace_global_defines.rs b/crates/oxc_transformer/src/plugins/replace_global_defines.rs index 333bfb10ce094..6bd29ce8ed9aa 100644 --- a/crates/oxc_transformer/src/plugins/replace_global_defines.rs +++ b/crates/oxc_transformer/src/plugins/replace_global_defines.rs @@ -252,7 +252,11 @@ impl<'a> ReplaceGlobalDefines<'a> { match expr { Expression::StaticMemberExpression(member) => { for dot_define in &self.config.0.dot { - if Self::is_dot_define(ctx.symbols(), dot_define, member) { + if Self::is_dot_define( + ctx.symbols(), + dot_define, + DotDefineMemberExpression::StaticMemberExpression(member), + ) { let value = self.parse_value(&dot_define.value); *expr = value; return; @@ -266,6 +270,20 @@ impl<'a> ReplaceGlobalDefines<'a> { } } } + Expression::ComputedMemberExpression(member) => { + for dot_define in &self.config.0.dot { + if Self::is_dot_define( + ctx.symbols(), + dot_define, + DotDefineMemberExpression::ComputedMemberExpression(member), + ) { + let value = self.parse_value(&dot_define.value); + *expr = value; + return; + } + } + // TODO: meta_property_define + } Expression::MetaProperty(meta_property) => { if let Some(ref replacement) = self.config.0.import_meta { if meta_property.meta.name == "import" && meta_property.property.name == "meta" @@ -356,30 +374,37 @@ impl<'a> ReplaceGlobalDefines<'a> { false } - pub fn is_dot_define( + pub fn is_dot_define<'b>( symbols: &SymbolTable, dot_define: &DotDefine, - member: &StaticMemberExpression<'a>, + member: DotDefineMemberExpression<'b, 'a>, ) -> bool { debug_assert!(dot_define.parts.len() > 1); + let Some(mut cur_part_name) = member.name() else { + return false; + }; let mut current_part_member_expression = Some(member); - let mut cur_part_name = &member.property.name; for (i, part) in dot_define.parts.iter().enumerate().rev() { if cur_part_name.as_str() != part { return false; } - if i == 0 { break; } current_part_member_expression = if let Some(member) = current_part_member_expression { - match &member.object { + match &member.object() { Expression::StaticMemberExpression(member) => { cur_part_name = &member.property.name; - Some(member) + Some(DotDefineMemberExpression::StaticMemberExpression(member)) + } + Expression::ComputedMemberExpression(computed_member) => { + static_property_name_of_computed_expr(computed_member).map(|name| { + cur_part_name = name; + DotDefineMemberExpression::ComputedMemberExpression(computed_member) + }) } Expression::Identifier(ident) => { if !ident.is_global_reference(symbols) { @@ -398,3 +423,39 @@ impl<'a> ReplaceGlobalDefines<'a> { true } } + +#[derive(Debug, Clone, Copy)] +pub enum DotDefineMemberExpression<'b, 'ast: 'b> { + StaticMemberExpression(&'b StaticMemberExpression<'ast>), + ComputedMemberExpression(&'b ComputedMemberExpression<'ast>), +} + +impl<'b, 'a> DotDefineMemberExpression<'b, 'a> { + fn name(&self) -> Option<&'b Atom<'a>> { + match self { + DotDefineMemberExpression::StaticMemberExpression(expr) => Some(&expr.property.name), + DotDefineMemberExpression::ComputedMemberExpression(expr) => { + static_property_name_of_computed_expr(expr) + } + } + } + + fn object(&self) -> &'b Expression<'a> { + match self { + DotDefineMemberExpression::StaticMemberExpression(expr) => &expr.object, + DotDefineMemberExpression::ComputedMemberExpression(expr) => &expr.object, + } + } +} + +fn static_property_name_of_computed_expr<'b, 'a: 'b>( + expr: &'b ComputedMemberExpression<'a>, +) -> Option<&'b Atom<'a>> { + match &expr.expression { + Expression::StringLiteral(lit) => Some(&lit.value), + Expression::TemplateLiteral(lit) if lit.expressions.is_empty() && lit.quasis.len() == 1 => { + Some(&lit.quasis[0].value.raw) + } + _ => None, + } +} 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 87850340e99a3..ae610853fc666 100644 --- a/crates/oxc_transformer/tests/integrations/plugins/replace_global_defines.rs +++ b/crates/oxc_transformer/tests/integrations/plugins/replace_global_defines.rs @@ -53,13 +53,18 @@ fn dot() { 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); + test("process", "process", config.clone()); + + // computed member expression + test("process['env'].NODE_ENV", "production", config.clone()); } #[test] fn dot_nested() { let config = ReplaceGlobalDefinesConfig::new(&[("process", "production")]).unwrap(); - test("foo.process.NODE_ENV", "foo.process.NODE_ENV", config); + test("foo.process.NODE_ENV", "foo.process.NODE_ENV", config.clone()); + // computed member expression + test("foo['process'].NODE_ENV", "foo['process'].NODE_ENV", config); } #[test]