use crate::Result;
use crate::format::FormatOptions;
use oxc_allocator::Allocator;
use oxc_ast::ast::*;
use oxc_ast::{AstBuilder, NONE};
use oxc_codegen::Codegen;
use oxc_span::{Atom, SPAN, SourceType};
pub struct JsBuilder<'a> {
ast: AstBuilder<'a>,
}
impl<'a> JsBuilder<'a> {
pub fn new(alloc: &'a Allocator) -> Self {
Self {
ast: AstBuilder::new(alloc),
}
}
pub fn allocator(&self) -> &'a Allocator {
self.ast.allocator
}
pub fn ast(&self) -> &AstBuilder<'a> {
&self.ast
}
pub fn ident(&self, name: impl Into<Atom<'a>>) -> Expression<'a> {
self.ast.expression_identifier(SPAN, name)
}
pub fn string(&self, value: impl Into<Atom<'a>>) -> Expression<'a> {
self.ast.expression_string_literal(SPAN, value, None)
}
pub fn number(&self, value: f64) -> Expression<'a> {
self.ast
.expression_numeric_literal(SPAN, value, None, NumberBase::Decimal)
}
pub fn null(&self) -> Expression<'a> {
self.ast.expression_null_literal(SPAN)
}
pub fn bool(&self, value: bool) -> Expression<'a> {
self.ast.expression_boolean_literal(SPAN, value)
}
pub fn member(&self, object: Expression<'a>, property: impl Into<Atom<'a>>) -> Expression<'a> {
let prop_name = self.ast.identifier_name(SPAN, property);
let member = self
.ast
.member_expression_static(SPAN, object, prop_name, false);
Expression::from(member)
}
pub fn computed_member(&self, object: Expression<'a>, index: Expression<'a>) -> Expression<'a> {
let member = self
.ast
.member_expression_computed(SPAN, object, index, false);
Expression::from(member)
}
pub fn call(&self, callee: Expression<'a>, args: Vec<Argument<'a>>) -> Expression<'a> {
let args_vec = self.ast.vec_from_iter(args);
let call = self
.ast
.call_expression(SPAN, callee, NONE, args_vec, false);
Expression::CallExpression(self.ast.alloc(call))
}
pub fn arg(&self, expr: Expression<'a>) -> Argument<'a> {
Argument::from(expr)
}
pub fn object(&self, props: Vec<ObjectPropertyKind<'a>>) -> Expression<'a> {
let props_vec = self.ast.vec_from_iter(props);
self.ast.expression_object(SPAN, props_vec)
}
pub fn prop(&self, key: impl Into<Atom<'a>>, value: Expression<'a>) -> ObjectPropertyKind<'a> {
let key_name = self.ast.identifier_name(SPAN, key);
let property = self.ast.object_property(
SPAN,
PropertyKind::Init,
PropertyKey::StaticIdentifier(self.ast.alloc(key_name)),
value,
false,
false,
false,
);
ObjectPropertyKind::ObjectProperty(self.ast.alloc(property))
}
pub fn array(&self, elements: Vec<Expression<'a>>) -> Expression<'a> {
let array_elements: Vec<_> = elements
.into_iter()
.map(ArrayExpressionElement::from)
.collect();
let elements_vec = self.ast.vec_from_iter(array_elements);
self.ast.expression_array(SPAN, elements_vec)
}
pub fn arrow_fn(&self, params: Vec<&'a str>, body: Expression<'a>) -> Expression<'a> {
let param_items: Vec<_> = params
.into_iter()
.map(|name| {
let pattern = self.ast.binding_pattern(
self.ast.binding_pattern_kind_binding_identifier(SPAN, name),
NONE,
false,
);
self.ast
.formal_parameter(SPAN, self.ast.vec(), pattern, None, false, false)
})
.collect();
let items_vec = self.ast.vec_from_iter(param_items);
let formal_params = self.ast.formal_parameters(
SPAN,
FormalParameterKind::ArrowFormalParameters,
items_vec,
NONE,
);
let return_stmt = self.ast.statement_return(SPAN, Some(body));
let stmts = self.ast.vec1(return_stmt);
let function_body = self.ast.function_body(SPAN, self.ast.vec(), stmts);
self.ast.expression_arrow_function(
SPAN,
false, false, NONE,
formal_params,
NONE,
self.ast.alloc(function_body),
)
}
pub fn arrow_fn_block(
&self,
params: Vec<&'a str>,
stmts: Vec<Statement<'a>>,
) -> Expression<'a> {
let param_items: Vec<_> = params
.into_iter()
.map(|name| {
let pattern = self.ast.binding_pattern(
self.ast.binding_pattern_kind_binding_identifier(SPAN, name),
NONE,
false,
);
self.ast
.formal_parameter(SPAN, self.ast.vec(), pattern, None, false, false)
})
.collect();
let items_vec = self.ast.vec_from_iter(param_items);
let formal_params = self.ast.formal_parameters(
SPAN,
FormalParameterKind::ArrowFormalParameters,
items_vec,
NONE,
);
let body_stmts = self.ast.vec_from_iter(stmts);
let function_body = self.ast.function_body(SPAN, self.ast.vec(), body_stmts);
self.ast.expression_arrow_function(
SPAN,
false, false, Option::<TSTypeParameterDeclaration>::None,
formal_params,
Option::<TSTypeAnnotation>::None,
self.ast.alloc(function_body),
)
}
pub fn const_decl(&self, name: impl Into<Atom<'a>>, init: Expression<'a>) -> Statement<'a> {
let pattern = self.ast.binding_pattern(
self.ast.binding_pattern_kind_binding_identifier(SPAN, name),
NONE,
false,
);
let declarator = self.ast.variable_declarator(
SPAN,
VariableDeclarationKind::Const,
pattern,
Some(init),
false,
);
let declarations = self.ast.vec1(declarator);
let var_decl = self.ast.variable_declaration(
SPAN,
VariableDeclarationKind::Const,
declarations,
false,
);
Statement::VariableDeclaration(self.ast.alloc(var_decl))
}
pub fn let_decl(
&self,
name: impl Into<Atom<'a>>,
init: Option<Expression<'a>>,
) -> Statement<'a> {
let pattern = self.ast.binding_pattern(
self.ast.binding_pattern_kind_binding_identifier(SPAN, name),
NONE,
false,
);
let declarator =
self.ast
.variable_declarator(SPAN, VariableDeclarationKind::Let, pattern, init, false);
let declarations = self.ast.vec1(declarator);
let var_decl =
self.ast
.variable_declaration(SPAN, VariableDeclarationKind::Let, declarations, false);
Statement::VariableDeclaration(self.ast.alloc(var_decl))
}
pub fn expr_stmt(&self, expr: Expression<'a>) -> Statement<'a> {
self.ast.statement_expression(SPAN, expr)
}
pub fn if_stmt(
&self,
test: Expression<'a>,
consequent: Vec<Statement<'a>>,
alternate: Option<Vec<Statement<'a>>>,
) -> Statement<'a> {
let consequent_stmts = self.ast.vec_from_iter(consequent);
let consequent_block = self.ast.statement_block(SPAN, consequent_stmts);
let alternate_block = alternate.map(|stmts| {
let alt_stmts = self.ast.vec_from_iter(stmts);
self.ast.statement_block(SPAN, alt_stmts)
});
Statement::IfStatement(self.ast.alloc(IfStatement {
span: SPAN,
test,
consequent: consequent_block,
alternate: alternate_block,
}))
}
pub fn return_stmt(&self, expr: Option<Expression<'a>>) -> Statement<'a> {
Statement::ReturnStatement(self.ast.alloc(ReturnStatement {
span: SPAN,
argument: expr,
}))
}
pub fn throw(&self, expr: Expression<'a>) -> Statement<'a> {
Statement::ThrowStatement(self.ast.alloc(ThrowStatement {
span: SPAN,
argument: expr,
}))
}
pub fn import_default(
&self,
local: impl Into<Atom<'a>>,
source: impl Into<Atom<'a>>,
) -> ModuleDeclaration<'a> {
let binding = self.ast.binding_identifier(SPAN, local);
let specifier = self.ast.import_default_specifier(SPAN, binding);
let specifiers = self
.ast
.vec1(ImportDeclarationSpecifier::ImportDefaultSpecifier(
self.ast.alloc(specifier),
));
let source_literal = self.ast.string_literal(SPAN, source, None);
self.ast.module_declaration_import_declaration(
SPAN,
Some(specifiers),
source_literal,
None, NONE, ImportOrExportKind::Value,
)
}
pub fn import_side_effect(&self, source: impl Into<Atom<'a>>) -> ModuleDeclaration<'a> {
let source_literal = self.ast.string_literal(SPAN, source, None);
self.ast.module_declaration_import_declaration(
SPAN,
None, source_literal,
None, NONE, ImportOrExportKind::Value,
)
}
pub fn import_named(
&self,
names: Vec<impl Into<Atom<'a>>>,
source: impl Into<Atom<'a>>,
) -> ModuleDeclaration<'a> {
let specifiers: Vec<_> = names
.into_iter()
.map(|name| {
let atom = name.into();
let imported_name = self.ast.identifier_name(SPAN, atom);
let local_binding = self.ast.binding_identifier(SPAN, atom);
let specifier = self.ast.import_specifier(
SPAN,
ModuleExportName::IdentifierName(imported_name),
local_binding,
ImportOrExportKind::Value,
);
ImportDeclarationSpecifier::ImportSpecifier(self.ast.alloc(specifier))
})
.collect();
let specifiers_vec = self.ast.vec_from_iter(specifiers);
let source_literal = self.ast.string_literal(SPAN, source, None);
self.ast.module_declaration_import_declaration(
SPAN,
Some(specifiers_vec),
source_literal,
None, NONE, ImportOrExportKind::Value,
)
}
pub fn export_default(&self, expr: Expression<'a>) -> ModuleDeclaration<'a> {
let kind: ExportDefaultDeclarationKind = expr.into();
self.ast
.module_declaration_export_default_declaration(SPAN, kind)
}
pub fn export_const(
&self,
name: impl Into<Atom<'a>>,
init: Expression<'a>,
) -> ModuleDeclaration<'a> {
let decl = self.const_decl(name, init);
let declaration = match decl {
Statement::VariableDeclaration(var_decl) => Declaration::VariableDeclaration(var_decl),
_ => unreachable!(),
};
ModuleDeclaration::ExportNamedDeclaration(self.ast.alloc(ExportNamedDeclaration {
span: SPAN,
declaration: Some(declaration),
specifiers: self.ast.vec(),
source: None,
export_kind: ImportOrExportKind::Value,
with_clause: None,
}))
}
pub fn program(&self, statements: Vec<Statement<'a>>) -> Result<String> {
self.program_with_format(statements, &FormatOptions::default())
}
pub fn program_with_format(
&self,
statements: Vec<Statement<'a>>,
_opts: &FormatOptions,
) -> Result<String> {
let body_vec = self.ast.vec_from_iter(statements);
let program = self.ast.program(
SPAN,
SourceType::mjs(),
"",
self.ast.vec(), None, self.ast.vec(), body_vec,
);
let codegen = Codegen::new();
let result = codegen.build(&program);
Ok(result.code)
}
pub fn not(&self, expr: Expression<'a>) -> Expression<'a> {
self.ast
.expression_unary(SPAN, UnaryOperator::LogicalNot, expr)
}
pub fn binary(
&self,
left: Expression<'a>,
op: BinaryOperator,
right: Expression<'a>,
) -> Expression<'a> {
self.ast.expression_binary(SPAN, left, op, right)
}
pub fn logical(
&self,
left: Expression<'a>,
op: LogicalOperator,
right: Expression<'a>,
) -> Expression<'a> {
self.ast.expression_logical(SPAN, left, op, right)
}
pub fn conditional(
&self,
test: Expression<'a>,
consequent: Expression<'a>,
alternate: Expression<'a>,
) -> Expression<'a> {
self.ast
.expression_conditional(SPAN, test, consequent, alternate)
}
pub fn new_expr(&self, callee: Expression<'a>, args: Vec<Argument<'a>>) -> Expression<'a> {
let args_vec = self.ast.vec_from_iter(args);
self.ast.expression_new(SPAN, callee, NONE, args_vec)
}
pub fn template_literal(
&self,
parts: Vec<impl Into<Atom<'a>>>,
expressions: Vec<Expression<'a>>,
) -> Expression<'a> {
let quasis: Vec<_> = parts
.into_iter()
.enumerate()
.map(|(i, part)| {
let atom = part.into();
let tail = i == expressions.len(); let value = TemplateElementValue {
raw: atom,
cooked: Some(atom),
};
TemplateElement {
span: SPAN,
value,
tail,
lone_surrogates: false,
}
})
.collect();
let quasis_vec = self.ast.vec_from_iter(quasis);
let expressions_vec = self.ast.vec_from_iter(expressions);
self.ast
.expression_template_literal(SPAN, quasis_vec, expressions_vec)
}
pub fn spread(&self, expr: Expression<'a>) -> SpreadElement<'a> {
SpreadElement {
span: SPAN,
argument: expr,
}
}
}