use oxc::{
allocator::{Allocator, Box, Dummy as _, IntoIn as _, TakeIn as _},
ast::ast::{
ArrayAssignmentTarget, ArrayExpressionElement, AssignmentTarget, AssignmentTargetMaybeDefault,
AssignmentTargetRest, AssignmentTargetWithDefault, BindingPattern, Expression,
ObjectAssignmentTarget, ObjectPropertyKind, PropertyKind,
},
span::SPAN,
};
use crate::AstSnippet;
use super::binding_property_ext::BindingPropertyExt as _;
pub trait BindingPatternExt<'ast> {
fn into_assignment_target(self, alloc: &'ast Allocator) -> AssignmentTarget<'ast>;
fn into_expression(self, snippet: &AstSnippet<'ast>) -> Expression<'ast>;
}
impl<'ast> BindingPatternExt<'ast> for BindingPattern<'ast> {
fn into_assignment_target(self, alloc: &'ast Allocator) -> AssignmentTarget<'ast> {
match self {
BindingPattern::BindingIdentifier(id) => {
AstSnippet::new(alloc).simple_id_assignment_target(&id.name, id.span)
}
BindingPattern::ObjectPattern(mut obj_pat) => {
let mut obj_target = ObjectAssignmentTarget {
rest: obj_pat.rest.take().map(|rest| {
Box::new_in(
AssignmentTargetRest {
span: rest.span,
target: rest.unbox().argument.into_assignment_target(alloc),
..AssignmentTargetRest::dummy(alloc)
},
alloc,
)
}),
..ObjectAssignmentTarget::dummy(alloc)
};
obj_pat.properties.take_in(alloc).into_iter().for_each(|binding_prop| {
obj_target.properties.push(binding_prop.into_assignment_target_property(alloc));
});
AssignmentTarget::ObjectAssignmentTarget(obj_target.into_in(alloc))
}
BindingPattern::ArrayPattern(mut arr_pat) => {
let mut arr_target = ArrayAssignmentTarget {
span: arr_pat.span,
rest: arr_pat.rest.take().map(|rest| {
Box::new_in(
AssignmentTargetRest {
span: rest.span,
target: rest.unbox().argument.into_assignment_target(alloc),
..AssignmentTargetRest::dummy(alloc)
},
alloc,
)
}),
elements: oxc::allocator::Vec::with_capacity_in(arr_pat.elements.len(), alloc),
..ArrayAssignmentTarget::dummy(alloc)
};
arr_pat.elements.take_in(alloc).into_iter().for_each(|binding_pat| {
arr_target.elements.push(binding_pat.map(|binding_pat| match binding_pat {
BindingPattern::AssignmentPattern(assign_pat) => {
let assign_pat = assign_pat.unbox();
AssignmentTargetMaybeDefault::AssignmentTargetWithDefault(
AssignmentTargetWithDefault {
span: assign_pat.span,
init: assign_pat.right,
binding: assign_pat.left.into_assignment_target(alloc),
..AssignmentTargetWithDefault::dummy(alloc)
}
.into_in(alloc),
)
}
_ => AssignmentTargetMaybeDefault::from(binding_pat.into_assignment_target(alloc)),
}));
});
AssignmentTarget::ArrayAssignmentTarget(arr_target.into_in(alloc))
}
BindingPattern::AssignmentPattern(_) => {
unreachable!("`BindingPattern::AssignmentPattern` should be pre-handled in above")
}
}
}
fn into_expression(self, snippet: &AstSnippet<'ast>) -> Expression<'ast> {
match self {
BindingPattern::BindingIdentifier(id) => snippet.builder.expression_identifier(SPAN, id.name),
BindingPattern::ObjectPattern(mut obj_pat) => {
let capacity = obj_pat.properties.len() + usize::from(obj_pat.rest.is_some());
let mut properties = snippet.builder.vec_with_capacity(capacity);
obj_pat.properties.take_in(snippet.alloc()).into_iter().for_each(|binding_prop| {
properties.push(ObjectPropertyKind::ObjectProperty(
snippet.builder.alloc_object_property(
SPAN,
PropertyKind::Init,
binding_prop.key,
binding_prop.value.into_expression(snippet),
false,
binding_prop.shorthand,
binding_prop.computed,
),
));
});
if let Some(rest) = obj_pat.rest.take() {
let BindingPattern::BindingIdentifier(ref id) = rest.argument else {
unreachable!("The rest element should be `BindingIdentifier`")
};
properties.push(ObjectPropertyKind::ObjectProperty(
snippet.builder.alloc_object_property(
SPAN,
PropertyKind::Init,
snippet.builder.property_key_static_identifier(SPAN, id.name),
snippet.builder.expression_identifier(SPAN, id.name),
false,
true,
false,
),
));
}
Expression::ObjectExpression(snippet.builder.alloc_object_expression(SPAN, properties))
}
BindingPattern::ArrayPattern(mut arg_pat) => {
let capacity = arg_pat.elements.len() + usize::from(arg_pat.rest.is_some());
let mut elements = snippet.builder.vec_with_capacity(capacity);
arg_pat.elements.take_in(snippet.alloc()).into_iter().for_each(|binding_pat| {
elements.push(binding_pat.map_or(
ArrayExpressionElement::Elision(snippet.builder.alloc_elision(SPAN)),
|binding_pat| ArrayExpressionElement::from(binding_pat.into_expression(snippet)),
));
});
if let Some(rest) = arg_pat.rest.take() {
let BindingPattern::BindingIdentifier(ref id) = rest.argument else {
unreachable!("The rest element should be `BindingIdentifier`")
};
elements.push(ArrayExpressionElement::Identifier(
snippet.builder.alloc_identifier_reference(SPAN, id.name),
));
}
Expression::ArrayExpression(snippet.builder.alloc_array_expression(SPAN, elements))
}
BindingPattern::AssignmentPattern(mut assign_pat) => {
assign_pat.left.take_in(snippet.alloc()).into_expression(snippet)
}
}
}
}