IWANABETHATGUY 2024-11-23 10:26:43 +00:00
parent 6fd0fcb4ff
commit 21614f23b6
3 changed files with 84 additions and 11 deletions

View file

@ -8,7 +8,10 @@ use oxc_semantic::{ScopeTree, SymbolTable};
use oxc_span::{CompactStr, SPAN}; use oxc_span::{CompactStr, SPAN};
use oxc_traverse::{traverse_mut, Traverse, TraverseCtx}; 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)] #[derive(Debug, Clone)]
pub struct InjectGlobalVariablesConfig { 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>) { fn replace_dot_defines(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) {
if let Expression::StaticMemberExpression(member) = expr { if let Expression::StaticMemberExpression(member) = expr {
for DotDefineState { dot_define, value_atom } in &mut self.dot_defines { 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, // If this is first replacement made for this dot define,
// create `Atom` for replacement, and record in `replaced_dot_defines` // create `Atom` for replacement, and record in `replaced_dot_defines`
let value_atom = value_atom.get_or_insert_with(|| { let value_atom = value_atom.get_or_insert_with(|| {

View file

@ -252,7 +252,11 @@ impl<'a> ReplaceGlobalDefines<'a> {
match expr { match expr {
Expression::StaticMemberExpression(member) => { Expression::StaticMemberExpression(member) => {
for dot_define in &self.config.0.dot { 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); let value = self.parse_value(&dot_define.value);
*expr = value; *expr = value;
return; 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) => { Expression::MetaProperty(meta_property) => {
if let Some(ref replacement) = self.config.0.import_meta { if let Some(ref replacement) = self.config.0.import_meta {
if meta_property.meta.name == "import" && meta_property.property.name == "meta" if meta_property.meta.name == "import" && meta_property.property.name == "meta"
@ -356,30 +374,37 @@ impl<'a> ReplaceGlobalDefines<'a> {
false false
} }
pub fn is_dot_define( pub fn is_dot_define<'b>(
symbols: &SymbolTable, symbols: &SymbolTable,
dot_define: &DotDefine, dot_define: &DotDefine,
member: &StaticMemberExpression<'a>, member: DotDefineMemberExpression<'b, 'a>,
) -> bool { ) -> bool {
debug_assert!(dot_define.parts.len() > 1); 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 current_part_member_expression = Some(member);
let mut cur_part_name = &member.property.name;
for (i, part) in dot_define.parts.iter().enumerate().rev() { for (i, part) in dot_define.parts.iter().enumerate().rev() {
if cur_part_name.as_str() != part { if cur_part_name.as_str() != part {
return false; return false;
} }
if i == 0 { if i == 0 {
break; break;
} }
current_part_member_expression = if let Some(member) = current_part_member_expression { current_part_member_expression = if let Some(member) = current_part_member_expression {
match &member.object { match &member.object() {
Expression::StaticMemberExpression(member) => { Expression::StaticMemberExpression(member) => {
cur_part_name = &member.property.name; 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) => { Expression::Identifier(ident) => {
if !ident.is_global_reference(symbols) { if !ident.is_global_reference(symbols) {
@ -398,3 +423,39 @@ impl<'a> ReplaceGlobalDefines<'a> {
true 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,
}
}

View file

@ -53,13 +53,18 @@ fn dot() {
test("process.env.NODE_ENV", "production", config.clone()); test("process.env.NODE_ENV", "production", config.clone());
test("process.env", "process.env", config.clone()); test("process.env", "process.env", config.clone());
test("process.env.foo.bar", "process.env.foo.bar", 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] #[test]
fn dot_nested() { fn dot_nested() {
let config = ReplaceGlobalDefinesConfig::new(&[("process", "production")]).unwrap(); 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] #[test]