1use crate::Result;
4use crate::format::FormatOptions;
5use oxc_allocator::Allocator;
6use oxc_ast::ast::*;
7use oxc_ast::{AstBuilder, NONE};
8use oxc_codegen::Codegen;
9use oxc_span::{Atom, SPAN, SourceType};
10
11pub struct JsBuilder<'a> {
29 ast: AstBuilder<'a>,
30}
31
32impl<'a> JsBuilder<'a> {
33 pub fn new(alloc: &'a Allocator) -> Self {
35 Self {
36 ast: AstBuilder::new(alloc),
37 }
38 }
39
40 pub fn allocator(&self) -> &'a Allocator {
42 self.ast.allocator
43 }
44
45 pub fn ast(&self) -> &AstBuilder<'a> {
47 &self.ast
48 }
49
50 pub fn ident(&self, name: impl Into<Atom<'a>>) -> Expression<'a> {
54 self.ast.expression_identifier(SPAN, name)
55 }
56
57 pub fn string(&self, value: impl Into<Atom<'a>>) -> Expression<'a> {
59 self.ast.expression_string_literal(SPAN, value, None)
60 }
61
62 pub fn number(&self, value: f64) -> Expression<'a> {
64 self.ast
65 .expression_numeric_literal(SPAN, value, None, NumberBase::Decimal)
66 }
67
68 pub fn null(&self) -> Expression<'a> {
70 self.ast.expression_null_literal(SPAN)
71 }
72
73 pub fn bool(&self, value: bool) -> Expression<'a> {
75 self.ast.expression_boolean_literal(SPAN, value)
76 }
77
78 pub fn member(&self, object: Expression<'a>, property: impl Into<Atom<'a>>) -> Expression<'a> {
82 let prop_name = self.ast.identifier_name(SPAN, property);
83 let member = self
84 .ast
85 .member_expression_static(SPAN, object, prop_name, false);
86 Expression::from(member)
87 }
88
89 pub fn computed_member(&self, object: Expression<'a>, index: Expression<'a>) -> Expression<'a> {
93 let member = self
94 .ast
95 .member_expression_computed(SPAN, object, index, false);
96 Expression::from(member)
97 }
98
99 pub fn call(&self, callee: Expression<'a>, args: Vec<Argument<'a>>) -> Expression<'a> {
101 let args_vec = self.ast.vec_from_iter(args);
102 let call = self
103 .ast
104 .call_expression(SPAN, callee, NONE, args_vec, false);
105 Expression::CallExpression(self.ast.alloc(call))
106 }
107
108 pub fn arg(&self, expr: Expression<'a>) -> Argument<'a> {
110 Argument::from(expr)
111 }
112
113 pub fn object(&self, props: Vec<ObjectPropertyKind<'a>>) -> Expression<'a> {
115 let props_vec = self.ast.vec_from_iter(props);
116 self.ast.expression_object(SPAN, props_vec)
117 }
118
119 pub fn prop(&self, key: impl Into<Atom<'a>>, value: Expression<'a>) -> ObjectPropertyKind<'a> {
121 let key_name = self.ast.identifier_name(SPAN, key);
122 let property = self.ast.object_property(
123 SPAN,
124 PropertyKind::Init,
125 PropertyKey::StaticIdentifier(self.ast.alloc(key_name)),
126 value,
127 false,
128 false,
129 false,
130 );
131 ObjectPropertyKind::ObjectProperty(self.ast.alloc(property))
132 }
133
134 pub fn array(&self, elements: Vec<Expression<'a>>) -> Expression<'a> {
136 let array_elements: Vec<_> = elements
137 .into_iter()
138 .map(ArrayExpressionElement::from)
139 .collect();
140 let elements_vec = self.ast.vec_from_iter(array_elements);
141 self.ast.expression_array(SPAN, elements_vec)
142 }
143
144 pub fn arrow_fn(&self, params: Vec<&'a str>, body: Expression<'a>) -> Expression<'a> {
146 let param_items: Vec<_> = params
147 .into_iter()
148 .map(|name| {
149 let pattern = self.ast.binding_pattern(
150 self.ast.binding_pattern_kind_binding_identifier(SPAN, name),
151 NONE,
152 false,
153 );
154 self.ast
155 .formal_parameter(SPAN, self.ast.vec(), pattern, None, false, false)
156 })
157 .collect();
158
159 let items_vec = self.ast.vec_from_iter(param_items);
160 let formal_params = self.ast.formal_parameters(
161 SPAN,
162 FormalParameterKind::ArrowFormalParameters,
163 items_vec,
164 NONE,
165 );
166
167 let return_stmt = self.ast.statement_return(SPAN, Some(body));
169 let stmts = self.ast.vec1(return_stmt);
170 let function_body = self.ast.function_body(SPAN, self.ast.vec(), stmts);
171
172 self.ast.expression_arrow_function(
173 SPAN,
174 false, false, NONE,
177 formal_params,
178 NONE,
179 self.ast.alloc(function_body),
180 )
181 }
182
183 pub fn arrow_fn_block(
185 &self,
186 params: Vec<&'a str>,
187 stmts: Vec<Statement<'a>>,
188 ) -> Expression<'a> {
189 let param_items: Vec<_> = params
190 .into_iter()
191 .map(|name| {
192 let pattern = self.ast.binding_pattern(
193 self.ast.binding_pattern_kind_binding_identifier(SPAN, name),
194 NONE,
195 false,
196 );
197 self.ast
198 .formal_parameter(SPAN, self.ast.vec(), pattern, None, false, false)
199 })
200 .collect();
201
202 let items_vec = self.ast.vec_from_iter(param_items);
203 let formal_params = self.ast.formal_parameters(
204 SPAN,
205 FormalParameterKind::ArrowFormalParameters,
206 items_vec,
207 NONE,
208 );
209
210 let body_stmts = self.ast.vec_from_iter(stmts);
211 let function_body = self.ast.function_body(SPAN, self.ast.vec(), body_stmts);
212
213 self.ast.expression_arrow_function(
214 SPAN,
215 false, false, Option::<TSTypeParameterDeclaration>::None,
218 formal_params,
219 Option::<TSTypeAnnotation>::None,
220 self.ast.alloc(function_body),
221 )
222 }
223
224 pub fn const_decl(&self, name: impl Into<Atom<'a>>, init: Expression<'a>) -> Statement<'a> {
228 let pattern = self.ast.binding_pattern(
229 self.ast.binding_pattern_kind_binding_identifier(SPAN, name),
230 NONE,
231 false,
232 );
233 let declarator = self.ast.variable_declarator(
234 SPAN,
235 VariableDeclarationKind::Const,
236 pattern,
237 Some(init),
238 false,
239 );
240 let declarations = self.ast.vec1(declarator);
241 let var_decl = self.ast.variable_declaration(
242 SPAN,
243 VariableDeclarationKind::Const,
244 declarations,
245 false,
246 );
247 Statement::VariableDeclaration(self.ast.alloc(var_decl))
248 }
249
250 pub fn let_decl(
252 &self,
253 name: impl Into<Atom<'a>>,
254 init: Option<Expression<'a>>,
255 ) -> Statement<'a> {
256 let pattern = self.ast.binding_pattern(
257 self.ast.binding_pattern_kind_binding_identifier(SPAN, name),
258 NONE,
259 false,
260 );
261 let declarator =
262 self.ast
263 .variable_declarator(SPAN, VariableDeclarationKind::Let, pattern, init, false);
264 let declarations = self.ast.vec1(declarator);
265 let var_decl =
266 self.ast
267 .variable_declaration(SPAN, VariableDeclarationKind::Let, declarations, false);
268 Statement::VariableDeclaration(self.ast.alloc(var_decl))
269 }
270
271 pub fn expr_stmt(&self, expr: Expression<'a>) -> Statement<'a> {
273 self.ast.statement_expression(SPAN, expr)
274 }
275
276 pub fn if_stmt(
278 &self,
279 test: Expression<'a>,
280 consequent: Vec<Statement<'a>>,
281 alternate: Option<Vec<Statement<'a>>>,
282 ) -> Statement<'a> {
283 let consequent_stmts = self.ast.vec_from_iter(consequent);
284 let consequent_block = self.ast.statement_block(SPAN, consequent_stmts);
285
286 let alternate_block = alternate.map(|stmts| {
287 let alt_stmts = self.ast.vec_from_iter(stmts);
288 self.ast.statement_block(SPAN, alt_stmts)
289 });
290
291 Statement::IfStatement(self.ast.alloc(IfStatement {
292 span: SPAN,
293 test,
294 consequent: consequent_block,
295 alternate: alternate_block,
296 }))
297 }
298
299 pub fn return_stmt(&self, expr: Option<Expression<'a>>) -> Statement<'a> {
301 Statement::ReturnStatement(self.ast.alloc(ReturnStatement {
302 span: SPAN,
303 argument: expr,
304 }))
305 }
306
307 pub fn throw(&self, expr: Expression<'a>) -> Statement<'a> {
309 Statement::ThrowStatement(self.ast.alloc(ThrowStatement {
310 span: SPAN,
311 argument: expr,
312 }))
313 }
314
315 pub fn import_default(
319 &self,
320 local: impl Into<Atom<'a>>,
321 source: impl Into<Atom<'a>>,
322 ) -> ModuleDeclaration<'a> {
323 let binding = self.ast.binding_identifier(SPAN, local);
324 let specifier = self.ast.import_default_specifier(SPAN, binding);
325 let specifiers = self
326 .ast
327 .vec1(ImportDeclarationSpecifier::ImportDefaultSpecifier(
328 self.ast.alloc(specifier),
329 ));
330 let source_literal = self.ast.string_literal(SPAN, source, None);
331 self.ast.module_declaration_import_declaration(
332 SPAN,
333 Some(specifiers),
334 source_literal,
335 None, NONE, ImportOrExportKind::Value,
338 )
339 }
340
341 pub fn import_side_effect(&self, source: impl Into<Atom<'a>>) -> ModuleDeclaration<'a> {
343 let source_literal = self.ast.string_literal(SPAN, source, None);
344 self.ast.module_declaration_import_declaration(
345 SPAN,
346 None, source_literal,
348 None, NONE, ImportOrExportKind::Value,
351 )
352 }
353
354 pub fn import_named(
356 &self,
357 names: Vec<impl Into<Atom<'a>>>,
358 source: impl Into<Atom<'a>>,
359 ) -> ModuleDeclaration<'a> {
360 let specifiers: Vec<_> = names
361 .into_iter()
362 .map(|name| {
363 let atom = name.into();
364 let imported_name = self.ast.identifier_name(SPAN, atom);
365 let local_binding = self.ast.binding_identifier(SPAN, atom);
366 let specifier = self.ast.import_specifier(
367 SPAN,
368 ModuleExportName::IdentifierName(imported_name),
369 local_binding,
370 ImportOrExportKind::Value,
371 );
372 ImportDeclarationSpecifier::ImportSpecifier(self.ast.alloc(specifier))
373 })
374 .collect();
375
376 let specifiers_vec = self.ast.vec_from_iter(specifiers);
377 let source_literal = self.ast.string_literal(SPAN, source, None);
378 self.ast.module_declaration_import_declaration(
379 SPAN,
380 Some(specifiers_vec),
381 source_literal,
382 None, NONE, ImportOrExportKind::Value,
385 )
386 }
387
388 pub fn export_default(&self, expr: Expression<'a>) -> ModuleDeclaration<'a> {
390 let kind: ExportDefaultDeclarationKind = expr.into();
392 self.ast
393 .module_declaration_export_default_declaration(SPAN, kind)
394 }
395
396 pub fn export_const(
398 &self,
399 name: impl Into<Atom<'a>>,
400 init: Expression<'a>,
401 ) -> ModuleDeclaration<'a> {
402 let decl = self.const_decl(name, init);
403 let declaration = match decl {
404 Statement::VariableDeclaration(var_decl) => Declaration::VariableDeclaration(var_decl),
405 _ => unreachable!(),
406 };
407 ModuleDeclaration::ExportNamedDeclaration(self.ast.alloc(ExportNamedDeclaration {
408 span: SPAN,
409 declaration: Some(declaration),
410 specifiers: self.ast.vec(),
411 source: None,
412 export_kind: ImportOrExportKind::Value,
413 with_clause: None,
414 }))
415 }
416
417 pub fn program(&self, statements: Vec<Statement<'a>>) -> Result<String> {
421 self.program_with_format(statements, &FormatOptions::default())
422 }
423
424 pub fn program_with_format(
426 &self,
427 statements: Vec<Statement<'a>>,
428 _opts: &FormatOptions,
429 ) -> Result<String> {
430 let body_vec = self.ast.vec_from_iter(statements);
431 let program = self.ast.program(
432 SPAN,
433 SourceType::mjs(),
434 "",
435 self.ast.vec(), None, self.ast.vec(), body_vec,
439 );
440
441 let codegen = Codegen::new();
443 let result = codegen.build(&program);
444
445 Ok(result.code)
446 }
447
448 pub fn not(&self, expr: Expression<'a>) -> Expression<'a> {
452 self.ast
453 .expression_unary(SPAN, UnaryOperator::LogicalNot, expr)
454 }
455
456 pub fn binary(
458 &self,
459 left: Expression<'a>,
460 op: BinaryOperator,
461 right: Expression<'a>,
462 ) -> Expression<'a> {
463 self.ast.expression_binary(SPAN, left, op, right)
464 }
465
466 pub fn logical(
468 &self,
469 left: Expression<'a>,
470 op: LogicalOperator,
471 right: Expression<'a>,
472 ) -> Expression<'a> {
473 self.ast.expression_logical(SPAN, left, op, right)
474 }
475
476 pub fn conditional(
478 &self,
479 test: Expression<'a>,
480 consequent: Expression<'a>,
481 alternate: Expression<'a>,
482 ) -> Expression<'a> {
483 self.ast
484 .expression_conditional(SPAN, test, consequent, alternate)
485 }
486
487 pub fn new_expr(&self, callee: Expression<'a>, args: Vec<Argument<'a>>) -> Expression<'a> {
489 let args_vec = self.ast.vec_from_iter(args);
490 self.ast.expression_new(SPAN, callee, NONE, args_vec)
491 }
492
493 pub fn template_literal(
505 &self,
506 parts: Vec<impl Into<Atom<'a>>>,
507 expressions: Vec<Expression<'a>>,
508 ) -> Expression<'a> {
509 let quasis: Vec<_> = parts
512 .into_iter()
513 .enumerate()
514 .map(|(i, part)| {
515 let atom = part.into();
516 let tail = i == expressions.len(); let value = TemplateElementValue {
518 raw: atom,
519 cooked: Some(atom),
520 };
521 TemplateElement {
522 span: SPAN,
523 value,
524 tail,
525 lone_surrogates: false,
526 }
527 })
528 .collect();
529
530 let quasis_vec = self.ast.vec_from_iter(quasis);
531 let expressions_vec = self.ast.vec_from_iter(expressions);
532
533 self.ast
534 .expression_template_literal(SPAN, quasis_vec, expressions_vec)
535 }
536
537 pub fn spread(&self, expr: Expression<'a>) -> SpreadElement<'a> {
545 SpreadElement {
546 span: SPAN,
547 argument: expr,
548 }
549 }
550}
551
552impl<'a> Default for JsBuilder<'a> {
553 fn default() -> Self {
554 panic!("JsBuilder requires an allocator - use JsBuilder::new(allocator)");
555 }
556}