Skip to content

Commit

Permalink
feat(napi/parser): expose span positions of import.meta (#7677)
Browse files Browse the repository at this point in the history
  • Loading branch information
Boshen authored and IWANABETHATGUY committed Dec 6, 2024
1 parent 72b5d58 commit 67f9a0e
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 12 deletions.
74 changes: 64 additions & 10 deletions crates/oxc_transformer/src/plugins/replace_global_defines.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::{cmp::Ordering, sync::Arc};

use lazy_static::lazy_static;
use oxc_allocator::Allocator;
use oxc_allocator::{Address, Allocator, GetAddress};
use oxc_ast::ast::*;
use oxc_diagnostics::OxcDiagnostic;
use oxc_parser::Parser;
Expand Down Expand Up @@ -220,26 +220,62 @@ pub struct ReplaceGlobalDefinesReturn {
pub struct ReplaceGlobalDefines<'a> {
allocator: &'a Allocator,
config: ReplaceGlobalDefinesConfig,
/// Since `Traverse` did not provide a way to skipping visiting sub tree of the AstNode,
/// Use `Option<Address>` to lock the current node when it is `Some`.
/// during visiting sub tree, the `Lock` will always be `Some`, and we can early return, this
/// could acheieve same effect as skipping visiting sub tree.
/// When `exit` the node, reset the `Lock` to `None` to make sure not affect other
/// transformation.
ast_node_lock: Option<Address>,
}

impl<'a> Traverse<'a> for ReplaceGlobalDefines<'a> {
fn enter_expression(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) {
self.replace_identifier_defines(expr, ctx);
self.replace_dot_defines(expr, ctx);
if self.ast_node_lock.is_some() {
return;
}
let is_replaced =
self.replace_identifier_defines(expr, ctx) || self.replace_dot_defines(expr, ctx);
if is_replaced {
self.ast_node_lock = Some(expr.address());
}
}

fn exit_expression(&mut self, node: &mut Expression<'a>, _ctx: &mut TraverseCtx<'a>) {
if self.ast_node_lock == Some(node.address()) {
self.ast_node_lock = None;
}
}

fn enter_assignment_expression(
&mut self,
node: &mut AssignmentExpression<'a>,
ctx: &mut TraverseCtx<'a>,
) {
self.replace_define_with_assignment_expr(node, ctx);
if self.ast_node_lock.is_some() {
return;
}
if self.replace_define_with_assignment_expr(node, ctx) {
// `AssignmentExpression` is stored in a `Box`, so we can use `from_ptr` to get
// the stable address
self.ast_node_lock = Some(Address::from_ptr(node));
}
}

fn exit_assignment_expression(
&mut self,
node: &mut AssignmentExpression<'a>,
_: &mut TraverseCtx<'a>,
) {
if self.ast_node_lock == Some(Address::from_ptr(node)) {
self.ast_node_lock = None;
}
}
}

impl<'a> ReplaceGlobalDefines<'a> {
pub fn new(allocator: &'a Allocator, config: ReplaceGlobalDefinesConfig) -> Self {
Self { allocator, config }
Self { allocator, config, ast_node_lock: None }
}

pub fn build(
Expand All @@ -260,11 +296,16 @@ impl<'a> ReplaceGlobalDefines<'a> {
Parser::new(self.allocator, source_text, SourceType::default()).parse_expression().unwrap()
}

fn replace_identifier_defines(&self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) {
fn replace_identifier_defines(
&self,
expr: &mut Expression<'a>,
ctx: &mut TraverseCtx<'a>,
) -> bool {
match expr {
Expression::Identifier(ident) => {
if let Some(new_expr) = self.replace_identifier_define_impl(ident, ctx) {
*expr = new_expr;
return true;
}
}
Expression::ThisExpression(_)
Expand All @@ -275,12 +316,14 @@ impl<'a> ReplaceGlobalDefines<'a> {
if key.as_str() == "this" {
let value = self.parse_value(value);
*expr = value;
break;

return true;
}
}
}
_ => {}
}
false
}

fn replace_identifier_define_impl(
Expand All @@ -305,7 +348,7 @@ impl<'a> ReplaceGlobalDefines<'a> {
&mut self,
node: &mut AssignmentExpression<'a>,
ctx: &mut TraverseCtx<'a>,
) {
) -> bool {
let new_left = node
.left
.as_simple_assignment_target_mut()
Expand All @@ -324,10 +367,16 @@ impl<'a> ReplaceGlobalDefines<'a> {
.and_then(assignment_target_from_expr);
if let Some(new_left) = new_left {
node.left = new_left;
return true;
}
false
}

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>,
) -> bool {
match expr {
Expression::ChainExpression(chain) => {
let Some(new_expr) =
Expand All @@ -341,18 +390,21 @@ impl<'a> ReplaceGlobalDefines<'a> {
MemberExpression::PrivateFieldExpression(_) => None,
})
else {
return;
return false;
};
*expr = new_expr;
return true;
}
Expression::StaticMemberExpression(member) => {
if let Some(new_expr) = self.replace_dot_static_member_expr(ctx, member) {
*expr = new_expr;
return true;
}
}
Expression::ComputedMemberExpression(member) => {
if let Some(new_expr) = self.replace_dot_computed_member_expr(ctx, member) {
*expr = new_expr;
return true;
}
}
Expression::MetaProperty(meta_property) => {
Expand All @@ -361,11 +413,13 @@ impl<'a> ReplaceGlobalDefines<'a> {
{
let value = self.parse_value(replacement);
*expr = value;
return true;
}
}
}
_ => {}
}
false
}

fn replace_dot_computed_member_expr(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,6 @@ fn dot() {
test("process['env'].NODE_ENV", "production", config.clone());
}

#[ignore]
#[test]
fn dot_with_overlap() {
let config = ReplaceGlobalDefinesConfig::new(&[
Expand All @@ -68,7 +67,12 @@ fn dot_with_overlap() {
])
.unwrap();
test("import.meta.env", "__foo__", config.clone());
test("import.meta.env.NODE_ENV", "import.meta.env.NODE_ENV", config.clone());
test("import.meta.env.FOO", "import.meta.env.FOO", config.clone());
test("import.meta.env.NODE_ENV", "__foo__.NODE_ENV", config.clone());

test("import.meta.env = 0", "__foo__ = 0", config.clone());
test("import.meta.env.NODE_ENV = 0", "__foo__.NODE_ENV = 0", config.clone());
test("import.meta.env.FOO = 0", "import.meta.env.FOO = 0", config.clone());
}

#[test]
Expand Down

0 comments on commit 67f9a0e

Please sign in to comment.