Difficulty converting babel to oxc #11855
-
Hi everyone, I'm trying to convert babel-based js code to oxc-based rust code. The purpose of this js function is to test whether an expression is completely static. For example, a static number, a string. I don't know if my conversion is accurate. I just found some APIs to restore this intent. I have run into some problems now. I want to write some unit tests for this function, but when impl Traverse, I don’t know how to get the Node data of the current expression. I'm wondering, should I be getting the reference_id of the Expression::Identifier to do this test? The original js code is export function getStaticExpression(path) {
const node = path.node;
let value, type;
return (
t.isJSXExpressionContainer(node) &&
t.isJSXElement(path.parent) &&
!isComponent(getTagName(path.parent)) &&
!t.isSequenceExpression(node.expression) &&
(value = path.get("expression").evaluate().value) !== undefined &&
((type = typeof value) === "string" || type === "number") &&
value
);
} The converted code is use indextree::Arena;
use indextree::Node;
use oxc_ast::AstKind;
use oxc_ast::ast::*;
use crate::shared::is_component;
pub fn get_static_expression<'a>(node: &Node<AstKind<'a>>, arena: &Arena<AstKind<'a>>) -> bool {
let node_data = node.get();
let parent = Node::parent(&node);
if let Some(parent_node_id) = parent {
if let Some(parent_node) = arena.get(parent_node_id) {
if let (
AstKind::JSXExpressionContainer(container),
AstKind::JSXElement(parent_element),
) = (node_data, parent_node.get())
{
// Check if parent is not a component
if let JSXElementName::Identifier(ident) = &parent_element.opening_element.name {
if !is_component(&ident.name) {
// Check not sequence expression
if let JSXExpression::SequenceExpression(_) = container.expression {
return false;
}
// For now we consider literals as static
return match container.expression {
JSXExpression::StringLiteral(_) | JSXExpression::NullLiteral(_) => true,
JSXExpression::NumericLiteral(_) => true,
JSXExpression::BooleanLiteral(_) => true,
JSXExpression::BigIntLiteral(_) => true,
_ => return false,
};
}
}
}
}
}
false
} The test code is #[cfg(test)]
mod tests {
use super::*;
use oxc_allocator::Allocator;
use oxc_codegen::Codegen;
use oxc_parser::Parser;
use oxc_semantic::SemanticBuilder;
use oxc_span::SourceType;
use oxc_traverse::{Ancestor, Traverse, TraverseCtx, traverse_mut};
struct TestVisitor<'a> {
allocator: &'a Allocator,
arena: Arena<AstKind<'a>>,
}
impl<'a> TestVisitor<'a> {
fn new(allocator: &'a Allocator) -> Self {
Self {
allocator,
arena: Arena::new(),
}
}
}
impl<'a> Traverse<'a> for TestVisitor<'a> {
fn enter_program(&mut self, node: &mut Program<'a>, _ctx: &mut TraverseCtx<'a>) {
println!("Entering program");
println!("Arena contents at program start:");
for (i, node) in self.arena.iter().enumerate() {
println!("Node {}: {:?}", i, node.get());
}
}
fn enter_call_expression(
&mut self,
node: &mut CallExpression<'a>,
ctx: &mut TraverseCtx<'a>,
) {
match &node.callee {
Expression::Identifier(identifier) => {
if identifier.name == "getType" {
if let Some(reference_id) = identifier.reference_id.get() {
let symbol_table = ctx.scoping.scoping();
let node_id = symbol_table.get_reference(reference_id).node_id();
}
}
}
_ => {}
}
}
fn enter_jsx_element(&mut self, node: &mut JSXElement<'a>, ctx: &mut TraverseCtx<'a>) {
if let Some(child) = node.children.first() {
if let JSXChild::ExpressionContainer(expr) = child {
let symbol_table = ctx.scoping.scoping();
let node_id = symbol_table.get_reference(reference_id).node_id();
for node in self.arena.iter() {
println!("Node kind: {:?}", node.get());
if let AstKind::JSXExpressionContainer(_) = node.get() {
println!("Node kind: {:?}", node.get());
assert!(get_static_expression(node, &self.arena));
break;
}
}
}
}
}
}
#[test]
fn test_static_expression() {
let source = r#"
function Test() {
return <div>{123}</div>;
}
"#;
let allocator = Allocator::default();
let source_type = SourceType::default().with_typescript(true).with_jsx(true);
let ret = Parser::new(&allocator, source, source_type).parse();
let mut program = ret.program;
let semantic_ret = SemanticBuilder::new().build(&program);
let scoping = semantic_ret.semantic.into_scoping();
let mut visitor = TestVisitor::new(&allocator);
traverse_mut(&mut visitor, &allocator, &mut program, scoping);
let result = Codegen::new().build(&program);
insta::assert_snapshot!(result.code);
}
} |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment
-
Since you need to get the parent-child relationship. Assume there is a macro and trait that can be used to create indextree directly. Later, you can test it in visit_jsx_expression_container use std::{fs, path::Path};
use indextree::{Arena, NodeId};
use oxc_allocator::Allocator;
use oxc_ast::{
AstKind,
ast::{JSXExpression, JSXExpressionContainer},
};
use oxc_ast_visit::{Visit, walk};
use oxc_codegen::Codegen;
use oxc_parser::Parser;
use oxc_span::SourceType;
use your_crate::tree_builder;
use your_crate2::{TreeBuilder, get_static_expression};
#[tree_builder]
struct TestVisitor<'a> {}
impl<'a> TreeBuilder<'a> for TestVisitor<'a> {
}
impl<'a> Visit<'a> for TestVisitor<'a> {
fn enter_node(&mut self, kind: AstKind<'a>) {
<Self as TreeBuilder>::enter_node(self, kind);
}
fn leave_node(&mut self, kind: AstKind<'a>) {
<Self as TreeBuilder>::leave_node(self, kind);
}
fn visit_jsx_expression_container(&mut self, it: &JSXExpressionContainer<'a>) {
walk::walk_jsx_expression_container(self, it);
let current_node = self.current_node().unwrap();
// get_static_expression
let is_static = get_static_expression(current_node, self.arena());
let mut codegen = Codegen::new();
codegen.print_expression(it.expression.to_expression());
println!("Expression source text: {}", codegen.into_source_text(),);
println!("Expression type is: {:?}", it.expression);
println!("Expression is static: {}", is_static);
match &it.expression {
JSXExpression::StringLiteral(_)
| JSXExpression::NumericLiteral(_)
| JSXExpression::BooleanLiteral(_) => {
assert!(is_static, "should be recognized as a static expression");
}
_ => {
assert!(!is_static, "should be recognized as a dynamic expression");
}
}
}
}
#[test]
fn test_get_static_expression() {
let path = Path::new("tests/fixtures/Test.tsx");
let source_text = fs::read_to_string(path).unwrap();
let allocator = Allocator::default();
let source_type = SourceType::default().with_typescript(true).with_jsx(true);
let ret = Parser::new(&allocator, &source_text, source_type).parse();
let mut program = ret.program;
let mut visitor = TestVisitor {
arena: Arena::new(),
node_stack: vec![],
};
visitor.visit_program(&mut program);
insta::assert_snapshot!(format!("{:?}", visitor.arena));
} |
Beta Was this translation helpful? Give feedback.
Since you need to get the parent-child relationship.
Assume there is a macro and trait that can be used to create indextree directly.
Later, you can test it in visit_jsx_expression_container