mirror of
https://github.com/danbulant/oxc
synced 2026-05-25 04:42:10 +00:00
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
This commit is contained in:
parent
6fd0fcb4ff
commit
21614f23b6
3 changed files with 84 additions and 11 deletions
|
|
@ -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(|| {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
|
|
|
|||
Loading…
Reference in a new issue