use oxc::{
allocator::{self, GetAllocator, IntoIn},
ast::{
ast::{
Argument, ArrowFunctionExpression, AssignmentOperator, AssignmentTarget, BindingIdentifier,
BindingPattern, CallExpression, ClassElement, Declaration, ExportDefaultDeclarationKind,
ExportSpecifier, Expression, FormalParameter, FormalParameterKind, FormalParameters,
FunctionBody, FunctionType, IdentifierName, ImportDeclarationSpecifier, ImportOrExportKind,
MemberExpression, ModuleExportName, NumberBase, ObjectExpression, ObjectPropertyKind,
PropertyKey, PropertyKind, Statement, StringLiteral, VariableDeclarationKind,
VariableDeclarator,
},
builder::{GetAstBuilder, NONE},
},
span::{GetSpanMut, SPAN, Span},
};
use rolldown_common::{EcmaModuleAstUsage, Interop, MemberExprProp};
use rolldown_utils::ecmascript::is_validate_identifier_name;
pub struct EsmWrapperStmtOptions<'ast, 'data> {
pub binding_name: &'data str,
pub esm_fn_expr: Expression<'ast>,
pub statements: allocator::Vec<'ast, Statement<'ast>>,
pub profiler_name: Option<&'data str>,
pub call_kind: EsmWrapperCallKind,
pub body_kind: EsmWrapperBodyKind,
pub decl_kind: EsmWrapperDeclKind,
}
#[derive(Clone, Copy)]
pub enum EsmWrapperCallKind {
Plain,
Pife,
}
#[derive(Clone, Copy)]
pub enum EsmWrapperBodyKind {
Sync,
Async,
}
#[derive(Clone, Copy)]
pub enum EsmWrapperDeclKind {
Var,
HoistedFunction,
}
pub trait ExpressionFactoryExt<'ast> {
fn new_id_ref_expr<B: GetAstBuilder<'ast> + GetAllocator<'ast>>(
span: Span,
name: &str,
builder: &B,
) -> Expression<'ast> {
Expression::new_identifier(span, oxc::ast::ast::Str::from_str_in(name, builder), builder)
}
fn new_arrow_returning<B: GetAstBuilder<'ast> + GetAllocator<'ast>>(
expr: Expression<'ast>,
builder: &B,
) -> Expression<'ast> {
let statements = oxc::allocator::Vec::from_value_in(
Statement::new_expression_statement(SPAN, expr, builder),
builder,
);
Expression::new_arrow_function_expression(
SPAN,
true,
false,
NONE,
FormalParameters::new(SPAN, FormalParameterKind::Signature, [], NONE, builder),
NONE,
FunctionBody::new(SPAN, [], statements, builder),
builder,
)
}
fn new_member_access_expr<B: GetAstBuilder<'ast> + GetAllocator<'ast>>(
object: &str,
property: &str,
builder: &B,
) -> Expression<'ast> {
Expression::from(MemberExpression::new_member_access(object, property, builder))
}
fn new_call_with_arg<B: GetAstBuilder<'ast> + GetAllocator<'ast>>(
callee: Expression<'ast>,
arg: Expression<'ast>,
pure: bool,
builder: &B,
) -> Expression<'ast> {
let mut call_expr = CallExpression::new(SPAN, callee, NONE, [], false, builder);
call_expr.pure = pure;
call_expr.arguments.push(arg.into());
Expression::CallExpression(call_expr.into_in(builder.allocator()))
}
fn new_promise_resolve_then<B: GetAstBuilder<'ast> + GetAllocator<'ast>>(
expr: Expression<'ast>,
builder: &B,
) -> Expression<'ast> {
Expression::new_call_expression(
SPAN,
Expression::new_static_member_expression(
SPAN,
Expression::new_call_expression(
SPAN,
Expression::new_static_member_expression(
SPAN,
Expression::new_identifier(SPAN, "Promise", builder),
IdentifierName::new(SPAN, "resolve", builder),
false,
builder,
),
NONE,
[],
false,
builder,
),
IdentifierName::new(SPAN, "then", builder),
false,
builder,
),
NONE,
[Argument::from(Expression::new_arrow_returning(expr, builder))],
false,
builder,
)
}
fn new_to_esm_call_with_interop<B: GetAstBuilder<'ast> + GetAllocator<'ast>>(
to_esm_fn_name: &str,
call_expr: Expression<'ast>,
interop: Option<Interop>,
builder: &B,
) -> Expression<'ast> {
let arguments = match interop {
None => return call_expr,
Some(Interop::Babel) => {
oxc::allocator::Vec::from_value_in(Argument::from(call_expr), builder)
}
Some(Interop::Node) => oxc::allocator::Vec::from_iter_in(
[
Argument::from(call_expr),
Argument::new_numeric_literal(SPAN, 1.0, None, NumberBase::Decimal, builder),
],
builder,
),
};
Expression::new_call_expression(
SPAN,
Expression::new_identifier(
SPAN,
oxc::ast::ast::Str::from_str_in(to_esm_fn_name, builder),
builder,
),
NONE,
arguments,
false,
builder,
)
}
fn new_seq_in_parens<B: GetAstBuilder<'ast> + GetAllocator<'ast>>(
a: Expression<'ast>,
b: Expression<'ast>,
builder: &B,
) -> Expression<'ast> {
let mut expressions = oxc::allocator::Vec::with_capacity_in(2, builder);
expressions.push(a);
expressions.push(b);
Expression::new_parenthesized_expression(
SPAN,
Expression::new_sequence_expression(SPAN, expressions, builder),
builder,
)
}
fn new_member_expr_or_ident_ref<B: GetAstBuilder<'ast> + GetAllocator<'ast>>(
object: Expression<'ast>,
props: &[MemberExprProp],
span: Span,
builder: &B,
) -> Expression<'ast> {
let mut cur = object;
for prop in props {
cur = if oxc::syntax::identifier::is_identifier_name(&prop.name) {
Expression::new_static_member_expression(
SPAN,
cur,
IdentifierName::new(
prop.span,
oxc::ast::ast::Str::from_str_in(&prop.name, builder),
builder,
),
prop.optional,
builder,
)
} else {
Expression::new_computed_member_expression(
SPAN,
cur,
Expression::new_string_literal(
prop.span,
oxc::ast::ast::Str::from_str_in(&prop.name, builder),
None,
builder,
),
prop.optional,
builder,
)
};
}
*cur.span_mut() = span;
cur
}
#[inline]
fn new_member_expr_with_void_zero_object<B: GetAstBuilder<'ast> + GetAllocator<'ast>>(
props: &[MemberExprProp],
span: Span,
builder: &B,
) -> Expression<'ast> {
if props.is_empty() {
Expression::new_void_0(SPAN, builder)
} else {
Expression::new_member_expr_or_ident_ref(
Expression::new_void_0(SPAN, builder),
&props[1..],
span,
builder,
)
}
}
fn new_keep_name_call<B: GetAstBuilder<'ast> + GetAllocator<'ast>>(
original_name: &str,
target: Expression<'ast>,
callee: Expression<'ast>,
pure: bool,
builder: &B,
) -> Expression<'ast> {
Expression::new_call_expression_with_pure(
SPAN,
callee,
NONE,
{
let mut items = oxc::allocator::Vec::with_capacity_in(2, builder);
items.push(target.into());
items.push(
Expression::new_string_literal(
SPAN,
oxc::ast::ast::Str::from_str_in(original_name, builder),
None,
builder,
)
.into(),
);
items
},
false,
pure,
builder,
)
}
fn new_to_esm_wrapper<B: GetAstBuilder<'ast> + GetAllocator<'ast>>(
to_esm_fn_expr: Expression<'ast>,
expr: Expression<'ast>,
node_mode: bool,
builder: &B,
) -> Expression<'ast> {
let args = if node_mode {
oxc::allocator::Vec::from_iter_in(
[
Argument::from(expr),
Argument::new_numeric_literal(SPAN, 1.0, None, NumberBase::Decimal, builder),
],
builder,
)
} else {
oxc::allocator::Vec::from_value_in(Argument::from(expr), builder)
};
Expression::new_call_expression_with_pure(
SPAN,
to_esm_fn_expr,
NONE,
args,
false,
true,
builder,
)
}
fn new_callee_then_call<B: GetAstBuilder<'ast> + GetAllocator<'ast>>(
call_expr: Expression<'ast>,
return_expr: Expression<'ast>,
builder: &B,
) -> Expression<'ast> {
Expression::new_call_expression(
SPAN,
Expression::new_static_member_expression(
SPAN,
call_expr,
IdentifierName::new(SPAN, "then", builder),
false,
builder,
),
NONE,
[Argument::from(Expression::new_arrow_returning(return_expr, builder))],
false,
builder,
)
}
}
impl<'ast> ExpressionFactoryExt<'ast> for Expression<'ast> {}
pub trait BindingIdentifierFactoryExt<'ast> {
fn new_id<B: GetAstBuilder<'ast> + GetAllocator<'ast>>(
span: Span,
name: &str,
builder: &B,
) -> BindingIdentifier<'ast> {
BindingIdentifier::new(span, oxc::ast::ast::Str::from_str_in(name, builder), builder)
}
}
impl<'ast> BindingIdentifierFactoryExt<'ast> for BindingIdentifier<'ast> {}
pub trait IdentifierNameFactoryExt<'ast> {
fn new_id_name<B: GetAstBuilder<'ast> + GetAllocator<'ast>>(
span: Span,
name: &str,
builder: &B,
) -> IdentifierName<'ast> {
IdentifierName::new(span, oxc::ast::ast::Str::from_str_in(name, builder), builder)
}
}
impl<'ast> IdentifierNameFactoryExt<'ast> for IdentifierName<'ast> {}
pub trait MemberExpressionFactoryExt<'ast> {
fn new_member_access<B: GetAstBuilder<'ast> + GetAllocator<'ast>>(
object: &str,
property: &str,
builder: &B,
) -> MemberExpression<'ast> {
MemberExpression::new_static_member_expression(
SPAN,
Expression::new_id_ref_expr(SPAN, object, builder),
IdentifierName::new_id_name(SPAN, property, builder),
false,
builder,
)
}
}
impl<'ast> MemberExpressionFactoryExt<'ast> for MemberExpression<'ast> {}
pub trait ObjectPropertyKindFactoryExt<'ast> {
fn new_lazy_export_property<B: GetAstBuilder<'ast> + GetAllocator<'ast>>(
key: &str,
expr: Expression<'ast>,
computed: bool,
builder: &B,
) -> ObjectPropertyKind<'ast> {
ObjectPropertyKind::new_object_property(
SPAN,
PropertyKind::Init,
if computed {
PropertyKey::new_string_literal(
SPAN,
oxc::ast::ast::Str::from_str_in(key, builder),
None,
builder,
)
} else {
PropertyKey::new_static_identifier(
SPAN,
oxc::ast::ast::Str::from_str_in(key, builder),
builder,
)
},
Expression::new_arrow_returning(expr, builder),
true,
false,
computed,
builder,
)
}
}
impl<'ast> ObjectPropertyKindFactoryExt<'ast> for ObjectPropertyKind<'ast> {}
pub trait ClassElementFactoryExt<'ast> {
fn new_static_block_keep_name<B: GetAstBuilder<'ast> + GetAllocator<'ast>>(
name: &str,
callee: Expression<'ast>,
builder: &B,
) -> ClassElement<'ast> {
ClassElement::new_static_block(
SPAN,
[Statement::new_expression_statement(
SPAN,
Expression::new_call_expression(
SPAN,
callee,
NONE,
{
let mut items = oxc::allocator::Vec::with_capacity_in(2, builder);
items.push(Expression::new_this_expression(SPAN, builder).into());
items.push(
Expression::new_string_literal(
SPAN,
oxc::ast::ast::Str::from_str_in(name, builder),
None,
builder,
)
.into(),
);
items
},
false,
builder,
),
builder,
)],
builder,
)
}
}
impl<'ast> ClassElementFactoryExt<'ast> for ClassElement<'ast> {}
pub trait CallExpressionFactoryExt<'ast> {
fn new_re_export_call<B: GetAstBuilder<'ast> + GetAllocator<'ast>>(
re_export_fn_ref: Expression<'ast>,
first_arg: Expression<'ast>,
second_arg: Expression<'ast>,
builder: &B,
) -> CallExpression<'ast> {
let args = oxc::allocator::Vec::from_iter_in([first_arg.into(), second_arg.into()], builder);
CallExpression::new(SPAN, re_export_fn_ref, NONE, args, false, builder)
}
fn new_then_extract_property<B: GetAstBuilder<'ast> + GetAllocator<'ast>>(
expr: Expression<'ast>,
property_name: &str,
builder: &B,
) -> allocator::Box<'ast, CallExpression<'ast>> {
debug_assert!(is_validate_identifier_name(property_name));
let member = Expression::new_static_member_expression(
SPAN,
Expression::new_identifier(SPAN, "n", builder),
IdentifierName::new(SPAN, oxc::ast::ast::Str::from_str_in(property_name, builder), builder),
false,
builder,
);
then_with_arrow_callback(expr, member, builder)
}
fn new_then_call_esm_wrappers_with_namespace<B: GetAstBuilder<'ast> + GetAllocator<'ast>>(
expr: Expression<'ast>,
wrapper_names: &[&str],
namespace_name: &str,
builder: &B,
) -> allocator::Box<'ast, CallExpression<'ast>> {
let mut expressions = allocator::Vec::with_capacity_in(wrapper_names.len() + 1, builder);
for wrapper_name in wrapper_names {
debug_assert!(is_validate_identifier_name(wrapper_name));
let wrapper_member = Expression::new_static_member_expression(
SPAN,
Expression::new_identifier(SPAN, "n", builder),
IdentifierName::new(SPAN, oxc::ast::ast::Str::from_str_in(wrapper_name, builder), builder),
false,
builder,
);
expressions.push(Expression::new_call_expression(
SPAN,
wrapper_member,
NONE,
[],
false,
builder,
));
}
debug_assert!(is_validate_identifier_name(namespace_name));
let namespace_member = Expression::new_static_member_expression(
SPAN,
Expression::new_identifier(SPAN, "n", builder),
IdentifierName::new(SPAN, oxc::ast::ast::Str::from_str_in(namespace_name, builder), builder),
false,
builder,
);
if expressions.is_empty() {
return then_with_arrow_callback(expr, namespace_member, builder);
}
expressions.push(namespace_member);
let sequence = Expression::new_parenthesized_expression(
SPAN,
Expression::new_sequence_expression(SPAN, expressions, builder),
builder,
);
then_with_arrow_callback(expr, sequence, builder)
}
fn new_then_call_cjs_wrapper_with_to_esm<B: GetAstBuilder<'ast> + GetAllocator<'ast>>(
expr: Expression<'ast>,
property_name: &str,
to_esm_fn_expr: Expression<'ast>,
node_mode: bool,
builder: &B,
) -> allocator::Box<'ast, CallExpression<'ast>> {
let member_expr = Expression::new_static_member_expression(
SPAN,
Expression::new_identifier(SPAN, "n", builder),
IdentifierName::new(SPAN, oxc::ast::ast::Str::from_str_in(property_name, builder), builder),
false,
builder,
);
let wrapper_call = Expression::new_call_expression(SPAN, member_expr, NONE, [], false, builder);
let to_esm_call =
Expression::new_to_esm_wrapper(to_esm_fn_expr, wrapper_call, node_mode, builder);
then_with_arrow_callback(expr, to_esm_call, builder)
}
}
impl<'ast> CallExpressionFactoryExt<'ast> for CallExpression<'ast> {}
pub trait StatementFactoryExt<'ast> {
fn new_var_decl<B: GetAstBuilder<'ast> + GetAllocator<'ast>>(
name: &str,
init: Expression<'ast>,
builder: &B,
) -> Statement<'ast> {
let declarations = oxc::allocator::Vec::from_value_in(
VariableDeclarator::new(
SPAN,
VariableDeclarationKind::Var,
BindingPattern::new_binding_identifier(
SPAN,
oxc::ast::ast::Str::from_str_in(name, builder),
builder,
),
NONE,
Some(init),
false,
builder,
),
builder,
);
Statement::new_variable_declaration(
SPAN,
VariableDeclarationKind::Var,
declarations,
false,
builder,
)
}
fn new_var_decl_no_init<B: GetAstBuilder<'ast> + GetAllocator<'ast>>(
name: &str,
builder: &B,
) -> Statement<'ast> {
let declarations = oxc::allocator::Vec::from_value_in(
VariableDeclarator::new(
SPAN,
VariableDeclarationKind::Var,
BindingPattern::new_binding_identifier(
SPAN,
oxc::ast::ast::Str::from_str_in(name, builder),
builder,
),
NONE,
None,
false,
builder,
),
builder,
);
Statement::new_variable_declaration(
SPAN,
VariableDeclarationKind::Var,
declarations,
false,
builder,
)
}
fn new_export_default_stmt<B: GetAstBuilder<'ast> + GetAllocator<'ast>>(
expr: Expression<'ast>,
builder: &B,
) -> Statement<'ast> {
Statement::new_export_default_declaration(
SPAN,
ExportDefaultDeclarationKind::from(expr),
builder,
)
}
fn new_module_exports_stmt<B: GetAstBuilder<'ast> + GetAllocator<'ast>>(
expr: Expression<'ast>,
builder: &B,
) -> Statement<'ast> {
Statement::new_expression_statement(
SPAN,
Expression::new_assignment_expression(
SPAN,
AssignmentOperator::Assign,
AssignmentTarget::new_static_member_expression(
SPAN,
Expression::new_identifier(SPAN, "module", builder),
IdentifierName::new(SPAN, "exports", builder),
false,
builder,
),
expr,
builder,
),
builder,
)
}
fn new_export_named_stmt<'a, T, I, B>(
declaration: Option<Declaration<'ast>>,
specifiers: I,
builder: &B,
) -> Statement<'ast>
where
T: AsRef<str> + 'a,
I: Iterator<Item = (&'a T, &'a (T, bool))>,
B: GetAstBuilder<'ast> + GetAllocator<'ast>,
{
Statement::new_export_named_declaration(
SPAN,
declaration,
oxc::allocator::Vec::from_iter_in(
specifiers.into_iter().map(|(local, (exported, legal_ident))| {
ExportSpecifier::new(
SPAN,
ModuleExportName::new_identifier_reference(
SPAN,
oxc::ast::ast::Str::from_str_in(local.as_ref(), builder),
builder,
),
if *legal_ident {
ModuleExportName::new_identifier_name(
SPAN,
oxc::ast::ast::Str::from_str_in(exported.as_ref(), builder),
builder,
)
} else {
ModuleExportName::new_string_literal(
SPAN,
oxc::ast::ast::Str::from_str_in(exported.as_ref(), builder),
None,
builder,
)
},
ImportOrExportKind::Value,
builder,
)
}),
builder,
),
None,
ImportOrExportKind::Value,
NONE,
builder,
)
}
fn new_import_star_stmt<B: GetAstBuilder<'ast> + GetAllocator<'ast>>(
source: &str,
as_name: &str,
builder: &B,
) -> Statement<'ast> {
let specifiers = oxc::allocator::Vec::from_value_in(
ImportDeclarationSpecifier::new_import_namespace_specifier(
SPAN,
BindingIdentifier::new(SPAN, oxc::ast::ast::Str::from_str_in(as_name, builder), builder),
builder,
),
builder,
);
Statement::new_import_declaration(
SPAN,
Some(specifiers),
StringLiteral::new(SPAN, oxc::ast::ast::Str::from_str_in(source, builder), None, builder),
None,
NONE,
ImportOrExportKind::Value,
builder,
)
}
#[expect(clippy::too_many_arguments)]
fn new_commonjs_wrapper_stmt<B: GetAstBuilder<'ast> + GetAllocator<'ast>>(
binding_name: &str,
commonjs_expr: Expression<'ast>,
statements: allocator::Vec<'ast, Statement<'ast>>,
ast_usage: EcmaModuleAstUsage,
profiler_names: bool,
stable_id: &str,
is_async: bool,
builder: &B,
) -> Statement<'ast> {
let mut params = FormalParameters::new(
SPAN,
FormalParameterKind::Signature,
oxc::allocator::Vec::with_capacity_in(1, builder),
NONE,
builder,
);
let body = FunctionBody::new(SPAN, [], statements, builder);
if ast_usage.intersects(EcmaModuleAstUsage::ModuleOrExports) {
params.items.push(FormalParameter::new(
SPAN,
[],
BindingPattern::new_binding_identifier(SPAN, "exports", builder),
NONE,
NONE,
false,
None,
false,
false,
builder,
));
}
if ast_usage.contains(EcmaModuleAstUsage::ModuleRef) {
params.items.push(FormalParameter::new(
SPAN,
[],
BindingPattern::new_binding_identifier(SPAN, "module", builder),
NONE,
NONE,
false,
None,
false,
false,
builder,
));
}
let mut commonjs_call_expr =
CallExpression::new_with_pure(SPAN, commonjs_expr, NONE, [], false, true, builder);
let mut arrow_expr =
ArrowFunctionExpression::boxed(SPAN, false, is_async, NONE, params, NONE, body, builder);
arrow_expr.pife = true;
if profiler_names {
let obj_expr = ObjectExpression::boxed(
SPAN,
[ObjectPropertyKind::new_object_property(
SPAN,
PropertyKind::Init,
PropertyKey::new_string_literal(
SPAN,
oxc::ast::ast::Str::from_str_in(stable_id, builder),
None,
builder,
),
Expression::ArrowFunctionExpression(arrow_expr),
true,
false,
false,
builder,
)],
builder,
);
commonjs_call_expr.arguments.push(Argument::ObjectExpression(obj_expr));
} else {
commonjs_call_expr.arguments.push(Argument::ArrowFunctionExpression(arrow_expr));
}
Statement::new_var_decl(
binding_name,
Expression::CallExpression(commonjs_call_expr.into_in(builder.allocator())),
builder,
)
}
fn new_esm_wrapper_stmt<B: GetAstBuilder<'ast> + GetAllocator<'ast>>(
options: EsmWrapperStmtOptions<'ast, '_>,
builder: &B,
) -> Statement<'ast> {
let EsmWrapperStmtOptions {
binding_name,
esm_fn_expr,
statements,
profiler_name,
call_kind,
body_kind,
decl_kind,
} = options;
let params = FormalParameters::new(SPAN, FormalParameterKind::Signature, [], NONE, builder);
let body = FunctionBody::new(SPAN, [], statements, builder);
let mut esm_call_expr = CallExpression::new(SPAN, esm_fn_expr, NONE, [], false, builder);
let mut arrow_expr = ArrowFunctionExpression::boxed(
SPAN,
false,
matches!(body_kind, EsmWrapperBodyKind::Async),
NONE,
params,
NONE,
body,
builder,
);
arrow_expr.pife = matches!(call_kind, EsmWrapperCallKind::Pife);
if let Some(stable_id) = profiler_name {
let obj_expr = ObjectExpression::boxed(
SPAN,
[ObjectPropertyKind::new_object_property(
SPAN,
PropertyKind::Init,
PropertyKey::new_string_literal(
SPAN,
oxc::ast::ast::Str::from_str_in(stable_id, builder),
None,
builder,
),
Expression::ArrowFunctionExpression(arrow_expr),
false,
false,
false,
builder,
)],
builder,
);
esm_call_expr.arguments.push(Argument::ObjectExpression(obj_expr));
} else {
esm_call_expr.arguments.push(Argument::ArrowFunctionExpression(arrow_expr));
}
if matches!(decl_kind, EsmWrapperDeclKind::Var) {
return Statement::new_var_decl(
binding_name,
Expression::CallExpression(esm_call_expr.into_in(builder.allocator())),
builder,
);
}
let assignment_expr = Expression::new_assignment_expression(
SPAN,
AssignmentOperator::Assign,
AssignmentTarget::new_assignment_target_identifier(
SPAN,
oxc::ast::ast::Str::from_str_in(binding_name, builder),
builder,
),
Expression::CallExpression(esm_call_expr.into_in(builder.allocator())),
builder,
);
let call_expr =
Expression::new_call_expression(SPAN, assignment_expr, NONE, [], false, builder);
Statement::new_function_declaration(
SPAN,
FunctionType::FunctionDeclaration,
Some(BindingIdentifier::new(
SPAN,
oxc::ast::ast::Str::from_str_in(binding_name, builder),
builder,
)),
false,
false,
false,
NONE,
NONE,
FormalParameters::new(SPAN, FormalParameterKind::Signature, [], NONE, builder),
NONE,
Some(FunctionBody::new(
SPAN,
[],
[Statement::new_return_statement(SPAN, Some(call_expr), builder)],
builder,
)),
builder,
)
}
}
impl<'ast> StatementFactoryExt<'ast> for Statement<'ast> {}
fn then_with_arrow_callback<'ast, B: GetAstBuilder<'ast> + GetAllocator<'ast>>(
expr: Expression<'ast>,
return_expr: Expression<'ast>,
builder: &B,
) -> allocator::Box<'ast, CallExpression<'ast>> {
let arrow_fn = Argument::new_arrow_function_expression(
SPAN,
true,
false,
NONE,
FormalParameters::new(
SPAN,
FormalParameterKind::ArrowFormalParameters,
[FormalParameter::new(
SPAN,
[],
BindingPattern::new_binding_identifier(SPAN, "n", builder),
NONE,
NONE,
false,
None,
false,
false,
builder,
)],
NONE,
builder,
),
NONE,
FunctionBody::new(
SPAN,
[],
[Statement::new_expression_statement(SPAN, return_expr, builder)],
builder,
),
builder,
);
let callee = Expression::new_static_member_expression(
SPAN,
expr,
IdentifierName::new(SPAN, "then", builder),
false,
builder,
);
CallExpression::boxed(SPAN, callee, NONE, [arrow_fn], false, builder)
}