Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 47 additions & 19 deletions crates/oxc_transformer_plugins/src/inject_global_variables.rs
Original file line number Diff line number Diff line change
Expand Up @@ -261,27 +261,55 @@ impl<'a> InjectGlobalVariables<'a> {
}

fn replace_dot_defines(&mut self, expr: &mut Expression<'a>, ctx: &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,
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(|| {
self.replaced_dot_defines
.push((dot_define.parts[0].clone(), dot_define.value.clone()));
self.ast.atom(dot_define.value.as_str())
});

let value = self.ast.expression_identifier(SPAN, value_atom);
*expr = value;
self.mark_as_changed();
break;
match expr {
Expression::StaticMemberExpression(member) => {
for DotDefineState { dot_define, value_atom } in &mut self.dot_defines {
if ReplaceGlobalDefines::is_dot_define(
ctx,
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(|| {
self.replaced_dot_defines
.push((dot_define.parts[0].clone(), dot_define.value.clone()));
self.ast.atom(dot_define.value.as_str())
});

let value = self.ast.expression_identifier(SPAN, value_atom);
*expr = value;
self.mark_as_changed();
break;
}
}
}
Expression::MetaProperty(meta_property) => {
// Check if this is import.meta and if it should be replaced
if meta_property.meta.name == "import" && meta_property.property.name == "meta" {
for DotDefineState { dot_define, value_atom } in &mut self.dot_defines {
// Check if dot_define is exactly ["import", "meta"]
if dot_define.parts.len() == 2
&& dot_define.parts[0].as_str() == "import"
&& dot_define.parts[1].as_str() == "meta"
{
// 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(|| {
self.replaced_dot_defines
.push((dot_define.parts[0].clone(), dot_define.value.clone()));
self.ast.atom(dot_define.value.as_str())
});

let value = self.ast.expression_identifier(SPAN, value_atom);
*expr = value;
self.mark_as_changed();
break;
}
}
}
}
_ => {}
}
}
}
19 changes: 19 additions & 0 deletions crates/oxc_transformer_plugins/src/replace_global_defines.rs
Original file line number Diff line number Diff line change
Expand Up @@ -616,6 +616,25 @@ impl<'a> ReplaceGlobalDefines<'a> {
cur_part_name = &THIS_ATOM;
None
}
Expression::MetaProperty(meta) => {
// Handle import.meta
// When we encounter a MetaProperty, we need to verify that the remaining
// parts match ["import", "meta"]
if meta.meta.name == "import" && meta.property.name == "meta" {
// At this point, i is the current position we're checking
// We need the next two parts (going backwards) to be "meta" then "import"
// i.e., parts[i-1] == "meta" and parts[i-2] == "import"
if i >= 2
&& dot_define.parts[i - 1].as_str() == "meta"
&& dot_define.parts[i - 2].as_str() == "import"
{
// Successfully matched import.meta at the expected position
// Return true if we've consumed all parts (i == 2)
return i == 2;
}
}
None
}
_ => None,
}
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -424,3 +424,76 @@ fn escape_source_with_newline() {
config,
);
}

#[test]
fn import_meta() {
// handles import.meta
let config = InjectGlobalVariablesConfig::new(vec![InjectImport::named_specifier(
"foo",
None,
"import.meta",
)]);
test(
"console.log(import.meta)",
"
import { default as $inject_import_meta } from 'foo';
console.log($inject_import_meta);
",
config,
);
}

#[test]
fn import_meta_property() {
// handles import.meta.foo
let config = InjectGlobalVariablesConfig::new(vec![InjectImport::named_specifier(
"bar",
None,
"import.meta.foo",
)]);
test(
"console.log(import.meta.foo)",
"
import { default as $inject_import_meta_foo } from 'bar';
console.log($inject_import_meta_foo);
",
config,
);
}

#[test]
fn import_meta_nested_property() {
// handles import.meta.env.MODE
let config = InjectGlobalVariablesConfig::new(vec![InjectImport::named_specifier(
"baz",
None,
"import.meta.env.MODE",
)]);
test(
"console.log(import.meta.env.MODE)",
"
import { default as $inject_import_meta_env_MODE } from 'baz';
console.log($inject_import_meta_env_MODE);
",
config,
);
}

#[test]
fn import_meta_combined() {
// handles both import.meta and import.meta.foo together
let config = InjectGlobalVariablesConfig::new(vec![
InjectImport::named_specifier("foo", None, "import.meta"),
InjectImport::named_specifier("bar", None, "import.meta.foo"),
]);
test(
"console.log(import.meta); console.log(import.meta.foo)",
"
import { default as $inject_import_meta } from 'foo';
import { default as $inject_import_meta_foo } from 'bar';
console.log($inject_import_meta);
console.log($inject_import_meta_foo);
",
config,
);
}
Loading