use std::cell::Cell;
use oxc::{
allocator::{Allocator, Box, Vec},
ast::ast::{
Argument, ArrowFunctionExpression, CallExpression, Expression, FormalParameterKind,
FormalParameters, FunctionBody, IdentifierReference, JSXExpressionContainer,
ObjectExpression, ParenthesizedExpression, Statement,
},
semantic::{ReferenceId, ScopeFlags, ScopeId, SymbolId},
span::{Atom, Span},
};
use oxc_traverse::TraverseCtx;
use crate::constants::S;
pub struct ElemIdent<'a> {
pub name: Atom<'a>,
pub sym: SymbolId,
pub ident: Expression<'a>,
}
pub enum SurplusChildType<'a> {
Normal(Expression<'a>),
Whitespace(Expression<'a>),
}
impl<'a> SurplusChildType<'a> {
pub fn is_whitespace(&self) -> bool {
matches!(self, SurplusChildType::Whitespace(_))
}
pub fn unwrap(self) -> Expression<'a> {
match self {
SurplusChildType::Normal(expr) | SurplusChildType::Whitespace(expr) => expr,
}
}
}
pub struct SurplusElement<'a> {
pub self_closing: bool,
pub statements: Vec<'a, Statement<'a>>,
pub elem: Option<ElemIdent<'a>>,
pub scope: ScopeId,
pub construction_obj: Option<ObjectExpression<'a>>,
pub fn_expressions: Vec<'a, Box<'a, JSXExpressionContainer<'a>>>,
pub ref_var: Option<(Expression<'a>, Span)>,
pub event_handlers: Vec<'a, (Atom<'a>, Expression<'a>, Span)>,
pub child_exprs: Vec<'a, SurplusChildType<'a>>,
}
impl<'a> SurplusElement<'a> {
pub fn new_in(ctx: &mut TraverseCtx<'a>, allocator: &'a Allocator) -> Self {
Self {
self_closing: false,
statements: Vec::new_in(allocator),
elem: None,
scope: ctx.create_child_scope_of_current(ScopeFlags::Arrow),
construction_obj: None,
fn_expressions: Vec::new_in(allocator),
ref_var: None,
event_handlers: Vec::new_in(allocator),
child_exprs: Vec::new_in(allocator),
}
}
pub fn into_surplus_comp(
self,
span: Span,
ctx: &mut TraverseCtx<'a>,
s_ref: ReferenceId,
allocator: &'a Allocator,
) -> Expression<'a> {
assert!(self.construction_obj.is_none());
assert!(self.ref_var.is_none());
let scope = ctx.create_child_scope_of_current(ScopeFlags::Arrow);
let function_body = ctx.alloc(FunctionBody {
directives: Vec::from_array_in([], allocator),
span,
statements: self.statements,
});
let computation = ctx.alloc(ArrowFunctionExpression {
r#async: false,
span,
expression: false,
type_parameters: None,
scope_id: Cell::new(Some(scope)),
params: ctx.alloc(FormalParameters {
span: Span::default(),
kind: FormalParameterKind::ArrowFormalParameters,
items: Vec::new_in(allocator),
rest: None,
}),
return_type: None,
pure: false,
body: function_body,
});
Expression::CallExpression(ctx.alloc(CallExpression {
arguments: Vec::from_array_in(
[Argument::ArrowFunctionExpression(computation)],
allocator,
),
optional: false,
pure: false,
span,
type_arguments: None,
callee: Expression::Identifier(ctx.alloc(IdentifierReference {
name: S,
span: Span::default(),
reference_id: Cell::new(Some(s_ref)),
})),
}))
}
pub fn into_surplus(
self,
span: Span,
ctx: &mut TraverseCtx<'a>,
s_ref: ReferenceId,
allocator: &'a Allocator,
) -> Expression<'a> {
let expression = self.into_surplus_comp(span, ctx, s_ref, allocator);
Expression::ParenthesizedExpression(ctx.alloc(ParenthesizedExpression {
span,
expression: Expression::CallExpression(ctx.alloc(CallExpression {
arguments: Vec::new_in(allocator),
span,
type_arguments: None,
pure: false,
optional: false,
callee: expression,
})),
}))
}
}