1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2025-01-20 04:36:16 -05:00
denoland-deno/cli/tools/lint/ast_buffer/swc.rs
Marvin Hagemeister 3fb8fc1ba7
feat(unstable): refactor js lint plugin AST (#27615)
This PR changes the underlying buffer backed AST format we use for
JavaScript-based linting plugins. It adds support for various new types,
makes traversal code a lot easier and is more polished compared to
previous iterations.

Here is a quick summary (in no particular order):

- Node prop data is separate from traversal, which makes traversal code
so much easier to reason about. Previously, it was interleaved with node
prop data
- spans are in a separate table as well, as they are rarely needed.
- schema is separate from SWC conversion logic, which makes 
- supports recursive plain objects
- supports numbers
- supports bigint
- supports regex
- adds all SWC nodes

Apologies, this is kinda a big PR, but it's worth it imo.

_Marking as draft because I need to update some tests tomorrow._
2025-01-14 13:31:02 +01:00

2614 lines
74 KiB
Rust

// Copyright 2018-2025 the Deno authors. MIT license.
use deno_ast::swc::ast::AssignTarget;
use deno_ast::swc::ast::AssignTargetPat;
use deno_ast::swc::ast::BindingIdent;
use deno_ast::swc::ast::BlockStmtOrExpr;
use deno_ast::swc::ast::Callee;
use deno_ast::swc::ast::ClassMember;
use deno_ast::swc::ast::Decl;
use deno_ast::swc::ast::Decorator;
use deno_ast::swc::ast::DefaultDecl;
use deno_ast::swc::ast::ExportSpecifier;
use deno_ast::swc::ast::Expr;
use deno_ast::swc::ast::ExprOrSpread;
use deno_ast::swc::ast::FnExpr;
use deno_ast::swc::ast::ForHead;
use deno_ast::swc::ast::Function;
use deno_ast::swc::ast::Ident;
use deno_ast::swc::ast::IdentName;
use deno_ast::swc::ast::ImportSpecifier;
use deno_ast::swc::ast::JSXAttrName;
use deno_ast::swc::ast::JSXAttrOrSpread;
use deno_ast::swc::ast::JSXAttrValue;
use deno_ast::swc::ast::JSXElement;
use deno_ast::swc::ast::JSXElementChild;
use deno_ast::swc::ast::JSXElementName;
use deno_ast::swc::ast::JSXExpr;
use deno_ast::swc::ast::JSXExprContainer;
use deno_ast::swc::ast::JSXFragment;
use deno_ast::swc::ast::JSXMemberExpr;
use deno_ast::swc::ast::JSXNamespacedName;
use deno_ast::swc::ast::JSXObject;
use deno_ast::swc::ast::JSXOpeningElement;
use deno_ast::swc::ast::Key;
use deno_ast::swc::ast::Lit;
use deno_ast::swc::ast::MemberExpr;
use deno_ast::swc::ast::MemberProp;
use deno_ast::swc::ast::ModuleDecl;
use deno_ast::swc::ast::ModuleExportName;
use deno_ast::swc::ast::ModuleItem;
use deno_ast::swc::ast::ObjectLit;
use deno_ast::swc::ast::ObjectPatProp;
use deno_ast::swc::ast::OptChainBase;
use deno_ast::swc::ast::Param;
use deno_ast::swc::ast::ParamOrTsParamProp;
use deno_ast::swc::ast::Pat;
use deno_ast::swc::ast::PrivateName;
use deno_ast::swc::ast::Program;
use deno_ast::swc::ast::Prop;
use deno_ast::swc::ast::PropName;
use deno_ast::swc::ast::PropOrSpread;
use deno_ast::swc::ast::SimpleAssignTarget;
use deno_ast::swc::ast::Stmt;
use deno_ast::swc::ast::SuperProp;
use deno_ast::swc::ast::TsEntityName;
use deno_ast::swc::ast::TsEnumMemberId;
use deno_ast::swc::ast::TsExprWithTypeArgs;
use deno_ast::swc::ast::TsFnOrConstructorType;
use deno_ast::swc::ast::TsFnParam;
use deno_ast::swc::ast::TsIndexSignature;
use deno_ast::swc::ast::TsLit;
use deno_ast::swc::ast::TsLitType;
use deno_ast::swc::ast::TsModuleName;
use deno_ast::swc::ast::TsModuleRef;
use deno_ast::swc::ast::TsNamespaceBody;
use deno_ast::swc::ast::TsParamPropParam;
use deno_ast::swc::ast::TsThisTypeOrIdent;
use deno_ast::swc::ast::TsType;
use deno_ast::swc::ast::TsTypeAnn;
use deno_ast::swc::ast::TsTypeElement;
use deno_ast::swc::ast::TsTypeParam;
use deno_ast::swc::ast::TsTypeParamDecl;
use deno_ast::swc::ast::TsTypeParamInstantiation;
use deno_ast::swc::ast::TsTypeQueryExpr;
use deno_ast::swc::ast::TsUnionOrIntersectionType;
use deno_ast::swc::ast::VarDeclOrExpr;
use deno_ast::swc::common::Span;
use deno_ast::swc::common::Spanned;
use deno_ast::swc::common::SyntaxContext;
use deno_ast::view::Accessibility;
use deno_ast::view::AssignOp;
use deno_ast::view::BinaryOp;
use deno_ast::view::MethodKind;
use deno_ast::view::TsKeywordTypeKind;
use deno_ast::view::TsTypeOperatorOp;
use deno_ast::view::UnaryOp;
use deno_ast::view::UpdateOp;
use deno_ast::view::VarDeclKind;
use deno_ast::ParsedSource;
use super::buffer::AstBufSerializer;
use super::buffer::NodeRef;
use super::ts_estree::AstNode;
use super::ts_estree::TsEsTreeBuilder;
use super::ts_estree::TsKeywordKind;
pub fn serialize_swc_to_buffer(parsed_source: &ParsedSource) -> Vec<u8> {
let mut ctx = TsEsTreeBuilder::new();
let program = &parsed_source.program();
match program.as_ref() {
Program::Module(module) => {
let children = module
.body
.iter()
.map(|item| match item {
ModuleItem::ModuleDecl(module_decl) => {
serialize_module_decl(&mut ctx, module_decl)
}
ModuleItem::Stmt(stmt) => serialize_stmt(&mut ctx, stmt),
})
.collect::<Vec<_>>();
ctx.write_program(&module.span, "module", children);
}
Program::Script(script) => {
let children = script
.body
.iter()
.map(|stmt| serialize_stmt(&mut ctx, stmt))
.collect::<Vec<_>>();
ctx.write_program(&script.span, "script", children);
}
}
ctx.serialize()
}
fn serialize_module_decl(
ctx: &mut TsEsTreeBuilder,
module_decl: &ModuleDecl,
) -> NodeRef {
match module_decl {
ModuleDecl::Import(node) => {
let src = serialize_lit(ctx, &Lit::Str(node.src.as_ref().clone()));
let attrs = serialize_import_attrs(ctx, &node.with);
let specifiers = node
.specifiers
.iter()
.map(|spec| match spec {
ImportSpecifier::Named(spec) => {
let local = serialize_ident(ctx, &spec.local, None);
let imported = spec
.imported
.as_ref()
.map_or(serialize_ident(ctx, &spec.local, None), |v| {
serialize_module_export_name(ctx, v)
});
ctx.write_import_spec(
&spec.span,
spec.is_type_only,
local,
imported,
)
}
ImportSpecifier::Default(spec) => {
let local = serialize_ident(ctx, &spec.local, None);
ctx.write_import_default_spec(&spec.span, local)
}
ImportSpecifier::Namespace(spec) => {
let local = serialize_ident(ctx, &spec.local, None);
ctx.write_import_ns_spec(&spec.span, local)
}
})
.collect::<Vec<_>>();
ctx.write_import_decl(&node.span, node.type_only, src, specifiers, attrs)
}
ModuleDecl::ExportDecl(node) => {
let decl = serialize_decl(ctx, &node.decl);
ctx.write_export_decl(&node.span, decl)
}
ModuleDecl::ExportNamed(node) => {
let attrs = serialize_import_attrs(ctx, &node.with);
let source = node
.src
.as_ref()
.map(|src| serialize_lit(ctx, &Lit::Str(*src.clone())));
if let Some(ExportSpecifier::Namespace(ns)) = node.specifiers.first() {
let exported = serialize_module_export_name(ctx, &ns.name);
ctx.write_export_all_decl(
&node.span,
node.type_only,
exported,
source,
attrs,
)
} else {
let specifiers = node
.specifiers
.iter()
.map(|spec| {
match spec {
ExportSpecifier::Named(spec) => {
let local = serialize_module_export_name(ctx, &spec.orig);
let exported = spec.exported.as_ref().map_or(
serialize_module_export_name(ctx, &spec.orig),
|exported| serialize_module_export_name(ctx, exported),
);
ctx.write_export_spec(
&spec.span,
spec.is_type_only,
local,
exported,
)
}
// Already handled earlier
ExportSpecifier::Namespace(_) => unreachable!(),
// this is not syntactically valid
ExportSpecifier::Default(_) => unreachable!(),
}
})
.collect::<Vec<_>>();
ctx.write_export_named_decl(&node.span, specifiers, source, attrs)
}
}
ModuleDecl::ExportDefaultDecl(node) => {
let (is_type_only, decl) = match &node.decl {
DefaultDecl::Class(node) => {
let ident = node
.ident
.as_ref()
.map(|ident| serialize_ident(ctx, ident, None));
let super_class = node
.class
.super_class
.as_ref()
.map(|expr| serialize_expr(ctx, expr.as_ref()));
let implements = node
.class
.implements
.iter()
.map(|item| serialize_ts_expr_with_type_args(ctx, item))
.collect::<Vec<_>>();
let members = node
.class
.body
.iter()
.filter_map(|member| serialize_class_member(ctx, member))
.collect::<Vec<_>>();
let body = ctx.write_class_body(&node.class.span, members);
let decl = ctx.write_class_decl(
&node.class.span,
false,
node.class.is_abstract,
ident,
super_class,
implements,
body,
);
(false, decl)
}
DefaultDecl::Fn(node) => {
let ident = node
.ident
.as_ref()
.map(|ident| serialize_ident(ctx, ident, None));
let fn_obj = node.function.as_ref();
let type_params =
maybe_serialize_ts_type_param_decl(ctx, &fn_obj.type_params);
let params = fn_obj
.params
.iter()
.map(|param| serialize_pat(ctx, &param.pat))
.collect::<Vec<_>>();
let return_type =
maybe_serialize_ts_type_ann(ctx, &fn_obj.return_type);
let body = fn_obj
.body
.as_ref()
.map(|block| serialize_stmt(ctx, &Stmt::Block(block.clone())));
let decl = ctx.write_fn_decl(
&fn_obj.span,
false,
fn_obj.is_async,
fn_obj.is_generator,
ident,
type_params,
return_type,
body,
params,
);
(false, decl)
}
DefaultDecl::TsInterfaceDecl(node) => {
let ident_id = serialize_ident(ctx, &node.id, None);
let type_param =
maybe_serialize_ts_type_param_decl(ctx, &node.type_params);
let extend_ids = node
.extends
.iter()
.map(|item| {
let expr = serialize_expr(ctx, &item.expr);
let type_args = item
.type_args
.clone()
.map(|params| serialize_ts_param_inst(ctx, params.as_ref()));
ctx.write_ts_interface_heritage(&item.span, expr, type_args)
})
.collect::<Vec<_>>();
let body_elem_ids = node
.body
.body
.iter()
.map(|item| serialize_ts_type_elem(ctx, item))
.collect::<Vec<_>>();
let body_pos =
ctx.write_ts_interface_body(&node.body.span, body_elem_ids);
let decl = ctx.write_ts_interface_decl(
&node.span,
node.declare,
ident_id,
type_param,
extend_ids,
body_pos,
);
(true, decl)
}
};
ctx.write_export_default_decl(&node.span, is_type_only, decl)
}
ModuleDecl::ExportDefaultExpr(node) => {
let expr = serialize_expr(ctx, &node.expr);
ctx.write_export_default_decl(&node.span, false, expr)
}
ModuleDecl::ExportAll(node) => {
let src = serialize_lit(ctx, &Lit::Str(node.src.as_ref().clone()));
let attrs = serialize_import_attrs(ctx, &node.with);
ctx.write_export_all_decl(&node.span, node.type_only, src, None, attrs)
}
ModuleDecl::TsImportEquals(node) => {
let ident = serialize_ident(ctx, &node.id, None);
let module_ref = match &node.module_ref {
TsModuleRef::TsEntityName(entity) => {
serialize_ts_entity_name(ctx, entity)
}
TsModuleRef::TsExternalModuleRef(external) => {
let expr = serialize_lit(ctx, &Lit::Str(external.expr.clone()));
ctx.write_ts_external_mod_ref(&external.span, expr)
}
};
ctx.write_export_ts_import_equals(
&node.span,
node.is_type_only,
ident,
module_ref,
)
}
ModuleDecl::TsExportAssignment(node) => {
let expr = serialize_expr(ctx, &node.expr);
ctx.write_export_assign(&node.span, expr)
}
ModuleDecl::TsNamespaceExport(node) => {
let decl = serialize_ident(ctx, &node.id, None);
ctx.write_export_ts_namespace(&node.span, decl)
}
}
}
fn serialize_import_attrs(
ctx: &mut TsEsTreeBuilder,
raw_attrs: &Option<Box<ObjectLit>>,
) -> Vec<NodeRef> {
raw_attrs.as_ref().map_or(vec![], |obj| {
obj
.props
.iter()
.map(|prop| {
let (key, value) = match prop {
// Invalid syntax
PropOrSpread::Spread(_) => unreachable!(),
PropOrSpread::Prop(prop) => {
match prop.as_ref() {
Prop::Shorthand(ident) => (
serialize_ident(ctx, ident, None),
serialize_ident(ctx, ident, None),
),
Prop::KeyValue(kv) => (
serialize_prop_name(ctx, &kv.key),
serialize_expr(ctx, kv.value.as_ref()),
),
// Invalid syntax
Prop::Assign(_)
| Prop::Getter(_)
| Prop::Setter(_)
| Prop::Method(_) => unreachable!(),
}
}
};
ctx.write_import_attr(&prop.span(), key, value)
})
.collect::<Vec<_>>()
})
}
fn serialize_stmt(ctx: &mut TsEsTreeBuilder, stmt: &Stmt) -> NodeRef {
match stmt {
Stmt::Block(node) => {
let children = node
.stmts
.iter()
.map(|stmt| serialize_stmt(ctx, stmt))
.collect::<Vec<_>>();
ctx.write_block_stmt(&node.span, children)
}
Stmt::Empty(_) => NodeRef(0),
Stmt::Debugger(node) => ctx.write_debugger_stmt(&node.span),
Stmt::With(node) => {
let obj = serialize_expr(ctx, &node.obj);
let body = serialize_stmt(ctx, &node.body);
ctx.write_with_stmt(&node.span, obj, body)
}
Stmt::Return(node) => {
let arg = node.arg.as_ref().map(|arg| serialize_expr(ctx, arg));
ctx.write_return_stmt(&node.span, arg)
}
Stmt::Labeled(node) => {
let ident = serialize_ident(ctx, &node.label, None);
let stmt = serialize_stmt(ctx, &node.body);
ctx.write_labeled_stmt(&node.span, ident, stmt)
}
Stmt::Break(node) => {
let arg = node
.label
.as_ref()
.map(|label| serialize_ident(ctx, label, None));
ctx.write_break_stmt(&node.span, arg)
}
Stmt::Continue(node) => {
let arg = node
.label
.as_ref()
.map(|label| serialize_ident(ctx, label, None));
ctx.write_continue_stmt(&node.span, arg)
}
Stmt::If(node) => {
let test = serialize_expr(ctx, node.test.as_ref());
let cons = serialize_stmt(ctx, node.cons.as_ref());
let alt = node.alt.as_ref().map(|alt| serialize_stmt(ctx, alt));
ctx.write_if_stmt(&node.span, test, cons, alt)
}
Stmt::Switch(node) => {
let disc = serialize_expr(ctx, &node.discriminant);
let cases = node
.cases
.iter()
.map(|case| {
let test = case.test.as_ref().map(|test| serialize_expr(ctx, test));
let cons = case
.cons
.iter()
.map(|cons| serialize_stmt(ctx, cons))
.collect::<Vec<_>>();
ctx.write_switch_case(&case.span, test, cons)
})
.collect::<Vec<_>>();
ctx.write_switch_stmt(&node.span, disc, cases)
}
Stmt::Throw(node) => {
let arg = serialize_expr(ctx, &node.arg);
ctx.write_throw_stmt(&node.span, arg)
}
Stmt::Try(node) => {
let block = serialize_stmt(ctx, &Stmt::Block(node.block.clone()));
let handler = node.handler.as_ref().map(|catch| {
let param = catch.param.as_ref().map(|param| serialize_pat(ctx, param));
let body = serialize_stmt(ctx, &Stmt::Block(catch.body.clone()));
ctx.write_catch_clause(&catch.span, param, body)
});
let finalizer = node
.finalizer
.as_ref()
.map(|finalizer| serialize_stmt(ctx, &Stmt::Block(finalizer.clone())));
ctx.write_try_stmt(&node.span, block, handler, finalizer)
}
Stmt::While(node) => {
let test = serialize_expr(ctx, node.test.as_ref());
let stmt = serialize_stmt(ctx, node.body.as_ref());
ctx.write_while_stmt(&node.span, test, stmt)
}
Stmt::DoWhile(node) => {
let expr = serialize_expr(ctx, node.test.as_ref());
let stmt = serialize_stmt(ctx, node.body.as_ref());
ctx.write_do_while_stmt(&node.span, expr, stmt)
}
Stmt::For(node) => {
let init = node.init.as_ref().map(|init| match init {
VarDeclOrExpr::VarDecl(var_decl) => {
serialize_stmt(ctx, &Stmt::Decl(Decl::Var(var_decl.clone())))
}
VarDeclOrExpr::Expr(expr) => serialize_expr(ctx, expr),
});
let test = node.test.as_ref().map(|expr| serialize_expr(ctx, expr));
let update = node.update.as_ref().map(|expr| serialize_expr(ctx, expr));
let body = serialize_stmt(ctx, node.body.as_ref());
ctx.write_for_stmt(&node.span, init, test, update, body)
}
Stmt::ForIn(node) => {
let left = serialize_for_head(ctx, &node.left);
let right = serialize_expr(ctx, node.right.as_ref());
let body = serialize_stmt(ctx, node.body.as_ref());
ctx.write_for_in_stmt(&node.span, left, right, body)
}
Stmt::ForOf(node) => {
let left = serialize_for_head(ctx, &node.left);
let right = serialize_expr(ctx, node.right.as_ref());
let body = serialize_stmt(ctx, node.body.as_ref());
ctx.write_for_of_stmt(&node.span, node.is_await, left, right, body)
}
Stmt::Decl(node) => serialize_decl(ctx, node),
Stmt::Expr(node) => {
let expr = serialize_expr(ctx, node.expr.as_ref());
ctx.write_expr_stmt(&node.span, expr)
}
}
}
fn serialize_expr(ctx: &mut TsEsTreeBuilder, expr: &Expr) -> NodeRef {
match expr {
Expr::This(node) => ctx.write_this_expr(&node.span),
Expr::Array(node) => {
let elems = node
.elems
.iter()
.map(|item| {
item
.as_ref()
.map_or(NodeRef(0), |item| serialize_expr_or_spread(ctx, item))
})
.collect::<Vec<_>>();
ctx.write_arr_expr(&node.span, elems)
}
Expr::Object(node) => {
let props = node
.props
.iter()
.map(|prop| serialize_prop_or_spread(ctx, prop))
.collect::<Vec<_>>();
ctx.write_obj_expr(&node.span, props)
}
Expr::Fn(node) => {
let fn_obj = node.function.as_ref();
let ident = node
.ident
.as_ref()
.map(|ident| serialize_ident(ctx, ident, None));
let type_params =
maybe_serialize_ts_type_param_decl(ctx, &fn_obj.type_params);
let params = fn_obj
.params
.iter()
.map(|param| serialize_pat(ctx, &param.pat))
.collect::<Vec<_>>();
let return_id = maybe_serialize_ts_type_ann(ctx, &fn_obj.return_type);
let body = fn_obj
.body
.as_ref()
.map(|block| serialize_stmt(ctx, &Stmt::Block(block.clone())));
ctx.write_fn_expr(
&fn_obj.span,
fn_obj.is_async,
fn_obj.is_generator,
ident,
type_params,
params,
return_id,
body,
)
}
Expr::Unary(node) => {
let arg = serialize_expr(ctx, &node.arg);
let op = match node.op {
UnaryOp::Minus => "-",
UnaryOp::Plus => "+",
UnaryOp::Bang => "!",
UnaryOp::Tilde => "~",
UnaryOp::TypeOf => "typeof",
UnaryOp::Void => "void",
UnaryOp::Delete => "delete",
};
ctx.write_unary_expr(&node.span, op, arg)
}
Expr::Update(node) => {
let arg = serialize_expr(ctx, node.arg.as_ref());
let op = match node.op {
UpdateOp::PlusPlus => "++",
UpdateOp::MinusMinus => "--",
};
ctx.write_update_expr(&node.span, node.prefix, op, arg)
}
Expr::Bin(node) => {
let (node_type, flag_str) = match node.op {
BinaryOp::LogicalAnd => (AstNode::LogicalExpression, "&&"),
BinaryOp::LogicalOr => (AstNode::LogicalExpression, "||"),
BinaryOp::NullishCoalescing => (AstNode::LogicalExpression, "??"),
BinaryOp::EqEq => (AstNode::BinaryExpression, "=="),
BinaryOp::NotEq => (AstNode::BinaryExpression, "!="),
BinaryOp::EqEqEq => (AstNode::BinaryExpression, "==="),
BinaryOp::NotEqEq => (AstNode::BinaryExpression, "!="),
BinaryOp::Lt => (AstNode::BinaryExpression, "<"),
BinaryOp::LtEq => (AstNode::BinaryExpression, "<="),
BinaryOp::Gt => (AstNode::BinaryExpression, ">"),
BinaryOp::GtEq => (AstNode::BinaryExpression, ">="),
BinaryOp::LShift => (AstNode::BinaryExpression, "<<"),
BinaryOp::RShift => (AstNode::BinaryExpression, ">>"),
BinaryOp::ZeroFillRShift => (AstNode::BinaryExpression, ">>>"),
BinaryOp::Add => (AstNode::BinaryExpression, "+"),
BinaryOp::Sub => (AstNode::BinaryExpression, "-"),
BinaryOp::Mul => (AstNode::BinaryExpression, "*"),
BinaryOp::Div => (AstNode::BinaryExpression, "/"),
BinaryOp::Mod => (AstNode::BinaryExpression, "%"),
BinaryOp::BitOr => (AstNode::BinaryExpression, "|"),
BinaryOp::BitXor => (AstNode::BinaryExpression, "^"),
BinaryOp::BitAnd => (AstNode::BinaryExpression, "&"),
BinaryOp::In => (AstNode::BinaryExpression, "in"),
BinaryOp::InstanceOf => (AstNode::BinaryExpression, "instanceof"),
BinaryOp::Exp => (AstNode::BinaryExpression, "**"),
};
let left = serialize_expr(ctx, node.left.as_ref());
let right = serialize_expr(ctx, node.right.as_ref());
match node_type {
AstNode::LogicalExpression => {
ctx.write_logical_expr(&node.span, flag_str, left, right)
}
AstNode::BinaryExpression => {
ctx.write_bin_expr(&node.span, flag_str, left, right)
}
_ => unreachable!(),
}
}
Expr::Assign(node) => {
let left = match &node.left {
AssignTarget::Simple(simple_assign_target) => {
match simple_assign_target {
SimpleAssignTarget::Ident(target) => {
serialize_binding_ident(ctx, target)
}
SimpleAssignTarget::Member(target) => {
serialize_expr(ctx, &Expr::Member(target.clone()))
}
SimpleAssignTarget::SuperProp(target) => {
serialize_expr(ctx, &Expr::SuperProp(target.clone()))
}
SimpleAssignTarget::Paren(target) => {
serialize_expr(ctx, &target.expr)
}
SimpleAssignTarget::OptChain(target) => {
serialize_expr(ctx, &Expr::OptChain(target.clone()))
}
SimpleAssignTarget::TsAs(target) => {
serialize_expr(ctx, &Expr::TsAs(target.clone()))
}
SimpleAssignTarget::TsSatisfies(target) => {
serialize_expr(ctx, &Expr::TsSatisfies(target.clone()))
}
SimpleAssignTarget::TsNonNull(target) => {
serialize_expr(ctx, &Expr::TsNonNull(target.clone()))
}
SimpleAssignTarget::TsTypeAssertion(target) => {
serialize_expr(ctx, &Expr::TsTypeAssertion(target.clone()))
}
SimpleAssignTarget::TsInstantiation(target) => {
serialize_expr(ctx, &Expr::TsInstantiation(target.clone()))
}
SimpleAssignTarget::Invalid(_) => unreachable!(),
}
}
AssignTarget::Pat(target) => match target {
AssignTargetPat::Array(array_pat) => {
serialize_pat(ctx, &Pat::Array(array_pat.clone()))
}
AssignTargetPat::Object(object_pat) => {
serialize_pat(ctx, &Pat::Object(object_pat.clone()))
}
AssignTargetPat::Invalid(_) => unreachable!(),
},
};
let right = serialize_expr(ctx, node.right.as_ref());
let op = match node.op {
AssignOp::Assign => "=",
AssignOp::AddAssign => "+=",
AssignOp::SubAssign => "-=",
AssignOp::MulAssign => "*=",
AssignOp::DivAssign => "/=",
AssignOp::ModAssign => "%=",
AssignOp::LShiftAssign => "<<=",
AssignOp::RShiftAssign => ">>=",
AssignOp::ZeroFillRShiftAssign => ">>>=",
AssignOp::BitOrAssign => "|=",
AssignOp::BitXorAssign => "^=",
AssignOp::BitAndAssign => "&=",
AssignOp::ExpAssign => "**=",
AssignOp::AndAssign => "&&=",
AssignOp::OrAssign => "||=",
AssignOp::NullishAssign => "??=",
};
ctx.write_assignment_expr(&node.span, op, left, right)
}
Expr::Member(node) => serialize_member_expr(ctx, node, false),
Expr::SuperProp(node) => {
let obj = ctx.write_super(&node.obj.span);
let mut computed = false;
let prop = match &node.prop {
SuperProp::Ident(ident_name) => serialize_ident_name(ctx, ident_name),
SuperProp::Computed(prop) => {
computed = true;
serialize_expr(ctx, &prop.expr)
}
};
ctx.write_member_expr(&node.span, false, computed, obj, prop)
}
Expr::Cond(node) => {
let test = serialize_expr(ctx, node.test.as_ref());
let cons = serialize_expr(ctx, node.cons.as_ref());
let alt = serialize_expr(ctx, node.alt.as_ref());
ctx.write_conditional_expr(&node.span, test, cons, alt)
}
Expr::Call(node) => {
if let Callee::Import(_) = node.callee {
let source = node
.args
.first()
.map_or(NodeRef(0), |arg| serialize_expr_or_spread(ctx, arg));
let options = node
.args
.get(1)
.map_or(NodeRef(0), |arg| serialize_expr_or_spread(ctx, arg));
ctx.write_import_expr(&node.span, source, options)
} else {
let callee = match &node.callee {
Callee::Super(super_node) => ctx.write_super(&super_node.span),
Callee::Import(_) => unreachable!("Already handled"),
Callee::Expr(expr) => serialize_expr(ctx, expr),
};
let type_arg = node
.type_args
.clone()
.map(|param_node| serialize_ts_param_inst(ctx, param_node.as_ref()));
let args = node
.args
.iter()
.map(|arg| serialize_expr_or_spread(ctx, arg))
.collect::<Vec<_>>();
ctx.write_call_expr(&node.span, false, callee, type_arg, args)
}
}
Expr::New(node) => {
let callee = serialize_expr(ctx, node.callee.as_ref());
let args: Vec<NodeRef> = node.args.as_ref().map_or(vec![], |args| {
args
.iter()
.map(|arg| serialize_expr_or_spread(ctx, arg))
.collect::<Vec<_>>()
});
let type_args = node
.type_args
.clone()
.map(|param_node| serialize_ts_param_inst(ctx, param_node.as_ref()));
ctx.write_new_expr(&node.span, callee, type_args, args)
}
Expr::Seq(node) => {
let children = node
.exprs
.iter()
.map(|expr| serialize_expr(ctx, expr))
.collect::<Vec<_>>();
ctx.write_sequence_expr(&node.span, children)
}
Expr::Ident(node) => serialize_ident(ctx, node, None),
Expr::Lit(node) => serialize_lit(ctx, node),
Expr::Tpl(node) => {
let quasis = node
.quasis
.iter()
.map(|quasi| {
ctx.write_template_elem(
&quasi.span,
quasi.tail,
&quasi.raw,
&quasi
.cooked
.as_ref()
.map_or("".to_string(), |v| v.to_string()),
)
})
.collect::<Vec<_>>();
let exprs = node
.exprs
.iter()
.map(|expr| serialize_expr(ctx, expr))
.collect::<Vec<_>>();
ctx.write_template_lit(&node.span, quasis, exprs)
}
Expr::TaggedTpl(node) => {
let tag = serialize_expr(ctx, &node.tag);
let type_param = node
.type_params
.clone()
.map(|params| serialize_ts_param_inst(ctx, params.as_ref()));
let quasi = serialize_expr(ctx, &Expr::Tpl(*node.tpl.clone()));
ctx.write_tagged_template_expr(&node.span, tag, type_param, quasi)
}
Expr::Arrow(node) => {
let type_param =
maybe_serialize_ts_type_param_decl(ctx, &node.type_params);
let params = node
.params
.iter()
.map(|param| serialize_pat(ctx, param))
.collect::<Vec<_>>();
let body = match node.body.as_ref() {
BlockStmtOrExpr::BlockStmt(block_stmt) => {
serialize_stmt(ctx, &Stmt::Block(block_stmt.clone()))
}
BlockStmtOrExpr::Expr(expr) => serialize_expr(ctx, expr.as_ref()),
};
let return_type = maybe_serialize_ts_type_ann(ctx, &node.return_type);
ctx.write_arrow_fn_expr(
&node.span,
node.is_async,
node.is_generator,
type_param,
params,
return_type,
body,
)
}
Expr::Class(node) => {
let ident = node
.ident
.as_ref()
.map(|ident| serialize_ident(ctx, ident, None));
let super_class = node
.class
.super_class
.as_ref()
.map(|expr| serialize_expr(ctx, expr.as_ref()));
let implements = node
.class
.implements
.iter()
.map(|item| serialize_ts_expr_with_type_args(ctx, item))
.collect::<Vec<_>>();
let members = node
.class
.body
.iter()
.filter_map(|member| serialize_class_member(ctx, member))
.collect::<Vec<_>>();
let body = ctx.write_class_body(&node.class.span, members);
ctx.write_class_expr(
&node.class.span,
false,
node.class.is_abstract,
ident,
super_class,
implements,
body,
)
}
Expr::Yield(node) => {
let arg = node
.arg
.as_ref()
.map(|arg| serialize_expr(ctx, arg.as_ref()));
ctx.write_yield_expr(&node.span, node.delegate, arg)
}
Expr::MetaProp(node) => {
let prop = ctx.write_identifier(&node.span, "meta", false, None);
ctx.write_meta_prop(&node.span, prop)
}
Expr::Await(node) => {
let arg = serialize_expr(ctx, node.arg.as_ref());
ctx.write_await_expr(&node.span, arg)
}
Expr::Paren(node) => {
// Paren nodes are treated as a syntax only thing in TSEStree
// and are never materialized to actual AST nodes.
serialize_expr(ctx, &node.expr)
}
Expr::JSXMember(node) => serialize_jsx_member_expr(ctx, node),
Expr::JSXNamespacedName(node) => serialize_jsx_namespaced_name(ctx, node),
Expr::JSXEmpty(node) => ctx.write_jsx_empty_expr(&node.span),
Expr::JSXElement(node) => serialize_jsx_element(ctx, node),
Expr::JSXFragment(node) => serialize_jsx_fragment(ctx, node),
Expr::TsTypeAssertion(node) => {
let expr = serialize_expr(ctx, &node.expr);
let type_ann = serialize_ts_type(ctx, &node.type_ann);
ctx.write_ts_type_assertion(&node.span, expr, type_ann)
}
Expr::TsConstAssertion(node) => {
let expr = serialize_expr(ctx, node.expr.as_ref());
let type_name = ctx.write_identifier(&node.span, "const", false, None);
let type_ann = ctx.write_ts_type_ref(&node.span, type_name, None);
ctx.write_ts_as_expr(&node.span, expr, type_ann)
}
Expr::TsNonNull(node) => {
let expr = serialize_expr(ctx, node.expr.as_ref());
ctx.write_ts_non_null(&node.span, expr)
}
Expr::TsAs(node) => {
let expr = serialize_expr(ctx, node.expr.as_ref());
let type_ann = serialize_ts_type(ctx, node.type_ann.as_ref());
ctx.write_ts_as_expr(&node.span, expr, type_ann)
}
Expr::TsInstantiation(_) => {
// Invalid syntax
unreachable!()
}
Expr::TsSatisfies(node) => {
let expr = serialize_expr(ctx, node.expr.as_ref());
let type_ann = serialize_ts_type(ctx, node.type_ann.as_ref());
ctx.write_ts_satisfies_expr(&node.span, expr, type_ann)
}
Expr::PrivateName(node) => serialize_private_name(ctx, node),
Expr::OptChain(node) => {
let expr = match node.base.as_ref() {
OptChainBase::Member(member_expr) => {
serialize_member_expr(ctx, member_expr, true)
}
OptChainBase::Call(opt_call) => {
let callee = serialize_expr(ctx, &opt_call.callee);
let type_param_id = opt_call
.type_args
.clone()
.map(|params| serialize_ts_param_inst(ctx, params.as_ref()));
let args = opt_call
.args
.iter()
.map(|arg| serialize_expr_or_spread(ctx, arg))
.collect::<Vec<_>>();
ctx.write_call_expr(&opt_call.span, true, callee, type_param_id, args)
}
};
ctx.write_chain_expr(&node.span, expr)
}
Expr::Invalid(_) => {
unreachable!()
}
}
}
fn serialize_prop_or_spread(
ctx: &mut TsEsTreeBuilder,
prop: &PropOrSpread,
) -> NodeRef {
match prop {
PropOrSpread::Spread(spread_element) => serialize_spread(
ctx,
spread_element.expr.as_ref(),
&spread_element.dot3_token,
),
PropOrSpread::Prop(prop) => {
let mut shorthand = false;
let mut computed = false;
let mut method = false;
let mut kind = "init";
let (key, value) = match prop.as_ref() {
Prop::Shorthand(ident) => {
shorthand = true;
let value = serialize_ident(ctx, ident, None);
let value2 = serialize_ident(ctx, ident, None);
(value, value2)
}
Prop::KeyValue(key_value_prop) => {
if let PropName::Computed(_) = key_value_prop.key {
computed = true;
}
let key = serialize_prop_name(ctx, &key_value_prop.key);
let value = serialize_expr(ctx, key_value_prop.value.as_ref());
(key, value)
}
Prop::Assign(assign_prop) => {
let left = serialize_ident(ctx, &assign_prop.key, None);
let right = serialize_expr(ctx, assign_prop.value.as_ref());
let child_pos = ctx.write_assign_pat(&assign_prop.span, left, right);
(left, child_pos)
}
Prop::Getter(getter_prop) => {
kind = "get";
let key = serialize_prop_name(ctx, &getter_prop.key);
let value = serialize_expr(
ctx,
&Expr::Fn(FnExpr {
ident: None,
function: Box::new(Function {
params: vec![],
decorators: vec![],
span: getter_prop.span,
ctxt: SyntaxContext::empty(),
body: getter_prop.body.clone(),
is_generator: false,
is_async: false,
type_params: None,
return_type: getter_prop.type_ann.clone(),
}),
}),
);
(key, value)
}
Prop::Setter(setter_prop) => {
kind = "set";
let key_id = serialize_prop_name(ctx, &setter_prop.key);
let param = Param::from(*setter_prop.param.clone());
let value_id = serialize_expr(
ctx,
&Expr::Fn(FnExpr {
ident: None,
function: Box::new(Function {
params: vec![param],
decorators: vec![],
span: setter_prop.span,
ctxt: SyntaxContext::empty(),
body: setter_prop.body.clone(),
is_generator: false,
is_async: false,
type_params: None,
return_type: None,
}),
}),
);
(key_id, value_id)
}
Prop::Method(method_prop) => {
method = true;
let key_id = serialize_prop_name(ctx, &method_prop.key);
let value_id = serialize_expr(
ctx,
&Expr::Fn(FnExpr {
ident: None,
function: method_prop.function.clone(),
}),
);
(key_id, value_id)
}
};
ctx.write_property(
&prop.span(),
shorthand,
computed,
method,
kind,
key,
value,
)
}
}
}
fn serialize_member_expr(
ctx: &mut TsEsTreeBuilder,
node: &MemberExpr,
optional: bool,
) -> NodeRef {
let mut computed = false;
let obj = serialize_expr(ctx, node.obj.as_ref());
let prop = match &node.prop {
MemberProp::Ident(ident_name) => serialize_ident_name(ctx, ident_name),
MemberProp::PrivateName(private_name) => {
serialize_private_name(ctx, private_name)
}
MemberProp::Computed(computed_prop_name) => {
computed = true;
serialize_expr(ctx, computed_prop_name.expr.as_ref())
}
};
ctx.write_member_expr(&node.span, optional, computed, obj, prop)
}
fn serialize_expr_or_spread(
ctx: &mut TsEsTreeBuilder,
arg: &ExprOrSpread,
) -> NodeRef {
if let Some(spread) = &arg.spread {
serialize_spread(ctx, &arg.expr, spread)
} else {
serialize_expr(ctx, arg.expr.as_ref())
}
}
fn serialize_ident(
ctx: &mut TsEsTreeBuilder,
ident: &Ident,
type_ann: Option<NodeRef>,
) -> NodeRef {
ctx.write_identifier(
&ident.span,
ident.sym.as_str(),
ident.optional,
type_ann,
)
}
fn serialize_module_export_name(
ctx: &mut TsEsTreeBuilder,
name: &ModuleExportName,
) -> NodeRef {
match &name {
ModuleExportName::Ident(ident) => serialize_ident(ctx, ident, None),
ModuleExportName::Str(lit) => serialize_lit(ctx, &Lit::Str(lit.clone())),
}
}
fn serialize_decl(ctx: &mut TsEsTreeBuilder, decl: &Decl) -> NodeRef {
match decl {
Decl::Class(node) => {
let ident = serialize_ident(ctx, &node.ident, None);
let super_class = node
.class
.super_class
.as_ref()
.map(|expr| serialize_expr(ctx, expr.as_ref()));
let implements = node
.class
.implements
.iter()
.map(|item| serialize_ts_expr_with_type_args(ctx, item))
.collect::<Vec<_>>();
let members = node
.class
.body
.iter()
.filter_map(|member| serialize_class_member(ctx, member))
.collect::<Vec<_>>();
let body = ctx.write_class_body(&node.class.span, members);
ctx.write_class_decl(
&node.class.span,
false,
node.class.is_abstract,
Some(ident),
super_class,
implements,
body,
)
}
Decl::Fn(node) => {
let ident_id = serialize_ident(ctx, &node.ident, None);
let type_param_id =
maybe_serialize_ts_type_param_decl(ctx, &node.function.type_params);
let return_type =
maybe_serialize_ts_type_ann(ctx, &node.function.return_type);
let body = node
.function
.body
.as_ref()
.map(|body| serialize_stmt(ctx, &Stmt::Block(body.clone())));
let params = node
.function
.params
.iter()
.map(|param| serialize_pat(ctx, &param.pat))
.collect::<Vec<_>>();
ctx.write_fn_decl(
&node.function.span,
node.declare,
node.function.is_async,
node.function.is_generator,
Some(ident_id),
type_param_id,
return_type,
body,
params,
)
}
Decl::Var(node) => {
let children = node
.decls
.iter()
.map(|decl| {
let ident = serialize_pat(ctx, &decl.name);
let init = decl
.init
.as_ref()
.map(|init| serialize_expr(ctx, init.as_ref()));
ctx.write_var_declarator(&decl.span, ident, init)
})
.collect::<Vec<_>>();
let kind = match node.kind {
VarDeclKind::Var => "var",
VarDeclKind::Let => "let",
VarDeclKind::Const => "const",
};
ctx.write_var_decl(&node.span, node.declare, kind, children)
}
Decl::Using(node) => {
let kind = if node.is_await {
"await using"
} else {
"using"
};
let children = node
.decls
.iter()
.map(|decl| {
let ident = serialize_pat(ctx, &decl.name);
let init = decl
.init
.as_ref()
.map(|init| serialize_expr(ctx, init.as_ref()));
ctx.write_var_declarator(&decl.span, ident, init)
})
.collect::<Vec<_>>();
ctx.write_var_decl(&node.span, false, kind, children)
}
Decl::TsInterface(node) => {
let ident_id = serialize_ident(ctx, &node.id, None);
let type_param =
maybe_serialize_ts_type_param_decl(ctx, &node.type_params);
let extend_ids = node
.extends
.iter()
.map(|item| {
let expr = serialize_expr(ctx, &item.expr);
let type_args = item
.type_args
.clone()
.map(|params| serialize_ts_param_inst(ctx, params.as_ref()));
ctx.write_ts_interface_heritage(&item.span, expr, type_args)
})
.collect::<Vec<_>>();
let body_elem_ids = node
.body
.body
.iter()
.map(|item| serialize_ts_type_elem(ctx, item))
.collect::<Vec<_>>();
let body_pos =
ctx.write_ts_interface_body(&node.body.span, body_elem_ids);
ctx.write_ts_interface(
&node.span,
node.declare,
ident_id,
type_param,
extend_ids,
body_pos,
)
}
Decl::TsTypeAlias(node) => {
let ident = serialize_ident(ctx, &node.id, None);
let type_ann = serialize_ts_type(ctx, &node.type_ann);
let type_param =
maybe_serialize_ts_type_param_decl(ctx, &node.type_params);
ctx.write_ts_type_alias(
&node.span,
node.declare,
ident,
type_param,
type_ann,
)
}
Decl::TsEnum(node) => {
let id = serialize_ident(ctx, &node.id, None);
let members = node
.members
.iter()
.map(|member| {
let ident = match &member.id {
TsEnumMemberId::Ident(ident) => serialize_ident(ctx, ident, None),
TsEnumMemberId::Str(lit_str) => {
serialize_lit(ctx, &Lit::Str(lit_str.clone()))
}
};
let init = member.init.as_ref().map(|init| serialize_expr(ctx, init));
ctx.write_ts_enum_member(&member.span, ident, init)
})
.collect::<Vec<_>>();
let body = ctx.write_ts_enum_body(&node.span, members);
ctx.write_ts_enum(&node.span, node.declare, node.is_const, id, body)
}
Decl::TsModule(node) => {
let ident = match &node.id {
TsModuleName::Ident(ident) => serialize_ident(ctx, ident, None),
TsModuleName::Str(str_lit) => {
serialize_lit(ctx, &Lit::Str(str_lit.clone()))
}
};
let body = node
.body
.as_ref()
.map(|body| serialize_ts_namespace_body(ctx, body));
ctx.write_ts_module_decl(
&node.span,
node.declare,
node.global,
ident,
body,
)
}
}
}
fn serialize_ts_namespace_body(
ctx: &mut TsEsTreeBuilder,
node: &TsNamespaceBody,
) -> NodeRef {
match node {
TsNamespaceBody::TsModuleBlock(mod_block) => {
let items = mod_block
.body
.iter()
.map(|item| match item {
ModuleItem::ModuleDecl(decl) => serialize_module_decl(ctx, decl),
ModuleItem::Stmt(stmt) => serialize_stmt(ctx, stmt),
})
.collect::<Vec<_>>();
ctx.write_ts_module_block(&mod_block.span, items)
}
TsNamespaceBody::TsNamespaceDecl(node) => {
let ident = serialize_ident(ctx, &node.id, None);
let body = serialize_ts_namespace_body(ctx, &node.body);
ctx.write_ts_module_decl(
&node.span,
node.declare,
node.global,
ident,
Some(body),
)
}
}
}
fn serialize_ts_type_elem(
ctx: &mut TsEsTreeBuilder,
node: &TsTypeElement,
) -> NodeRef {
match node {
TsTypeElement::TsCallSignatureDecl(ts_call) => {
let type_ann =
maybe_serialize_ts_type_param_decl(ctx, &ts_call.type_params);
let return_type = maybe_serialize_ts_type_ann(ctx, &ts_call.type_ann);
let params = ts_call
.params
.iter()
.map(|param| serialize_ts_fn_param(ctx, param))
.collect::<Vec<_>>();
ctx.write_ts_call_sig_decl(&ts_call.span, type_ann, params, return_type)
}
TsTypeElement::TsConstructSignatureDecl(sig) => {
let type_params =
maybe_serialize_ts_type_param_decl(ctx, &sig.type_params);
let params = sig
.params
.iter()
.map(|param| serialize_ts_fn_param(ctx, param))
.collect::<Vec<_>>();
// Must be present
let return_type =
maybe_serialize_ts_type_ann(ctx, &sig.type_ann).unwrap();
ctx.write_ts_construct_sig(&sig.span, type_params, params, return_type)
}
TsTypeElement::TsPropertySignature(sig) => {
let key = serialize_expr(ctx, &sig.key);
let type_ann = maybe_serialize_ts_type_ann(ctx, &sig.type_ann);
ctx.write_ts_property_sig(
&sig.span,
sig.computed,
sig.optional,
sig.readonly,
key,
type_ann,
)
}
TsTypeElement::TsGetterSignature(sig) => {
let key = serialize_expr(ctx, sig.key.as_ref());
let return_type = maybe_serialize_ts_type_ann(ctx, &sig.type_ann);
ctx.write_ts_getter_sig(&sig.span, key, return_type)
}
TsTypeElement::TsSetterSignature(sig) => {
let key = serialize_expr(ctx, sig.key.as_ref());
let param = serialize_ts_fn_param(ctx, &sig.param);
ctx.write_ts_setter_sig(&sig.span, key, param)
}
TsTypeElement::TsMethodSignature(sig) => {
let key = serialize_expr(ctx, &sig.key);
let type_parms =
maybe_serialize_ts_type_param_decl(ctx, &sig.type_params);
let params = sig
.params
.iter()
.map(|param| serialize_ts_fn_param(ctx, param))
.collect::<Vec<_>>();
let return_type = maybe_serialize_ts_type_ann(ctx, &sig.type_ann);
ctx.write_ts_method_sig(
&sig.span,
sig.computed,
sig.optional,
key,
type_parms,
params,
return_type,
)
}
TsTypeElement::TsIndexSignature(sig) => serialize_ts_index_sig(ctx, sig),
}
}
fn serialize_ts_index_sig(
ctx: &mut TsEsTreeBuilder,
node: &TsIndexSignature,
) -> NodeRef {
let params = node
.params
.iter()
.map(|param| serialize_ts_fn_param(ctx, param))
.collect::<Vec<_>>();
let type_ann = maybe_serialize_ts_type_ann(ctx, &node.type_ann);
ctx.write_ts_index_sig(&node.span, node.readonly, params, type_ann)
}
fn accessibility_to_str(accessibility: &Accessibility) -> String {
match accessibility {
Accessibility::Public => "public".to_string(),
Accessibility::Protected => "protected".to_string(),
Accessibility::Private => "private".to_string(),
}
}
fn serialize_private_name(
ctx: &mut TsEsTreeBuilder,
node: &PrivateName,
) -> NodeRef {
ctx.write_private_identifier(&node.span, node.name.as_str())
}
fn serialize_jsx_element(
ctx: &mut TsEsTreeBuilder,
node: &JSXElement,
) -> NodeRef {
let open = serialize_jsx_opening_element(ctx, &node.opening);
let close = node.closing.as_ref().map(|closing| {
let name = serialize_jsx_element_name(ctx, &closing.name);
ctx.write_jsx_closing_elem(&closing.span, name)
});
let children = serialize_jsx_children(ctx, &node.children);
ctx.write_jsx_elem(&node.span, open, close, children)
}
fn serialize_jsx_fragment(
ctx: &mut TsEsTreeBuilder,
node: &JSXFragment,
) -> NodeRef {
let opening = ctx.write_jsx_opening_frag(&node.opening.span);
let closing = ctx.write_jsx_closing_frag(&node.closing.span);
let children = serialize_jsx_children(ctx, &node.children);
ctx.write_jsx_frag(&node.span, opening, closing, children)
}
fn serialize_jsx_children(
ctx: &mut TsEsTreeBuilder,
children: &[JSXElementChild],
) -> Vec<NodeRef> {
children
.iter()
.map(|child| {
match child {
JSXElementChild::JSXText(text) => {
ctx.write_jsx_text(&text.span, &text.raw, &text.value)
}
JSXElementChild::JSXExprContainer(container) => {
serialize_jsx_container_expr(ctx, container)
}
JSXElementChild::JSXElement(el) => serialize_jsx_element(ctx, el),
JSXElementChild::JSXFragment(frag) => serialize_jsx_fragment(ctx, frag),
// No parser supports this
JSXElementChild::JSXSpreadChild(_) => unreachable!(),
}
})
.collect::<Vec<_>>()
}
fn serialize_jsx_member_expr(
ctx: &mut TsEsTreeBuilder,
node: &JSXMemberExpr,
) -> NodeRef {
let obj = match &node.obj {
JSXObject::JSXMemberExpr(member) => serialize_jsx_member_expr(ctx, member),
JSXObject::Ident(ident) => serialize_jsx_identifier(ctx, ident),
};
let prop = serialize_ident_name_as_jsx_identifier(ctx, &node.prop);
ctx.write_jsx_member_expr(&node.span, obj, prop)
}
fn serialize_jsx_element_name(
ctx: &mut TsEsTreeBuilder,
node: &JSXElementName,
) -> NodeRef {
match &node {
JSXElementName::Ident(ident) => serialize_jsx_identifier(ctx, ident),
JSXElementName::JSXMemberExpr(member) => {
serialize_jsx_member_expr(ctx, member)
}
JSXElementName::JSXNamespacedName(ns) => {
serialize_jsx_namespaced_name(ctx, ns)
}
}
}
fn serialize_jsx_opening_element(
ctx: &mut TsEsTreeBuilder,
node: &JSXOpeningElement,
) -> NodeRef {
let name = serialize_jsx_element_name(ctx, &node.name);
let type_args = node
.type_args
.as_ref()
.map(|arg| serialize_ts_param_inst(ctx, arg));
let attrs = node
.attrs
.iter()
.map(|attr| match attr {
JSXAttrOrSpread::JSXAttr(attr) => {
let name = match &attr.name {
JSXAttrName::Ident(name) => {
serialize_ident_name_as_jsx_identifier(ctx, name)
}
JSXAttrName::JSXNamespacedName(node) => {
serialize_jsx_namespaced_name(ctx, node)
}
};
let value = attr.value.as_ref().map(|value| match value {
JSXAttrValue::Lit(lit) => serialize_lit(ctx, lit),
JSXAttrValue::JSXExprContainer(container) => {
serialize_jsx_container_expr(ctx, container)
}
JSXAttrValue::JSXElement(el) => serialize_jsx_element(ctx, el),
JSXAttrValue::JSXFragment(frag) => serialize_jsx_fragment(ctx, frag),
});
ctx.write_jsx_attr(&attr.span, name, value)
}
JSXAttrOrSpread::SpreadElement(spread) => {
let arg = serialize_expr(ctx, &spread.expr);
ctx.write_jsx_spread_attr(&spread.dot3_token, arg)
}
})
.collect::<Vec<_>>();
ctx.write_jsx_opening_elem(
&node.span,
node.self_closing,
name,
attrs,
type_args,
)
}
fn serialize_jsx_container_expr(
ctx: &mut TsEsTreeBuilder,
node: &JSXExprContainer,
) -> NodeRef {
let expr = match &node.expr {
JSXExpr::JSXEmptyExpr(expr) => ctx.write_jsx_empty_expr(&expr.span),
JSXExpr::Expr(expr) => serialize_expr(ctx, expr),
};
ctx.write_jsx_expr_container(&node.span, expr)
}
fn serialize_jsx_namespaced_name(
ctx: &mut TsEsTreeBuilder,
node: &JSXNamespacedName,
) -> NodeRef {
let ns = ctx.write_jsx_identifier(&node.ns.span, &node.ns.sym);
let name = ctx.write_jsx_identifier(&node.name.span, &node.name.sym);
ctx.write_jsx_namespaced_name(&node.span, ns, name)
}
fn serialize_ident_name_as_jsx_identifier(
ctx: &mut TsEsTreeBuilder,
node: &IdentName,
) -> NodeRef {
ctx.write_jsx_identifier(&node.span, &node.sym)
}
fn serialize_jsx_identifier(
ctx: &mut TsEsTreeBuilder,
node: &Ident,
) -> NodeRef {
ctx.write_jsx_identifier(&node.span, &node.sym)
}
fn serialize_pat(ctx: &mut TsEsTreeBuilder, pat: &Pat) -> NodeRef {
match pat {
Pat::Ident(node) => serialize_binding_ident(ctx, node),
Pat::Array(node) => {
let type_ann = maybe_serialize_ts_type_ann(ctx, &node.type_ann);
let children = node
.elems
.iter()
.map(|pat| pat.as_ref().map_or(NodeRef(0), |v| serialize_pat(ctx, v)))
.collect::<Vec<_>>();
ctx.write_arr_pat(&node.span, node.optional, type_ann, children)
}
Pat::Rest(node) => {
let type_ann = maybe_serialize_ts_type_ann(ctx, &node.type_ann);
let arg = serialize_pat(ctx, &node.arg);
ctx.write_rest_elem(&node.span, type_ann, arg)
}
Pat::Object(node) => {
let type_ann = maybe_serialize_ts_type_ann(ctx, &node.type_ann);
let children = node
.props
.iter()
.map(|prop| match prop {
ObjectPatProp::KeyValue(key_value_prop) => {
let computed = matches!(key_value_prop.key, PropName::Computed(_));
let key = serialize_prop_name(ctx, &key_value_prop.key);
let value = serialize_pat(ctx, key_value_prop.value.as_ref());
ctx.write_property(
&key_value_prop.span(),
false,
computed,
false,
"init",
key,
value,
)
}
ObjectPatProp::Assign(assign_pat_prop) => {
let ident = serialize_binding_ident(ctx, &assign_pat_prop.key);
let value = assign_pat_prop
.value
.as_ref()
.map_or(NodeRef(0), |value| serialize_expr(ctx, value));
ctx.write_property(
&assign_pat_prop.span,
false,
false,
false,
"init",
ident,
value,
)
}
ObjectPatProp::Rest(rest_pat) => {
serialize_pat(ctx, &Pat::Rest(rest_pat.clone()))
}
})
.collect::<Vec<_>>();
ctx.write_obj_pat(&node.span, node.optional, type_ann, children)
}
Pat::Assign(node) => {
let left = serialize_pat(ctx, &node.left);
let right = serialize_expr(ctx, &node.right);
ctx.write_assign_pat(&node.span, left, right)
}
Pat::Invalid(_) => unreachable!(),
Pat::Expr(node) => serialize_expr(ctx, node),
}
}
fn serialize_for_head(
ctx: &mut TsEsTreeBuilder,
for_head: &ForHead,
) -> NodeRef {
match for_head {
ForHead::VarDecl(var_decl) => {
serialize_decl(ctx, &Decl::Var(var_decl.clone()))
}
ForHead::UsingDecl(using_decl) => {
serialize_decl(ctx, &Decl::Using(using_decl.clone()))
}
ForHead::Pat(pat) => serialize_pat(ctx, pat),
}
}
fn serialize_spread(
ctx: &mut TsEsTreeBuilder,
expr: &Expr,
span: &Span,
) -> NodeRef {
let expr = serialize_expr(ctx, expr);
ctx.write_spread(span, expr)
}
fn serialize_ident_name(
ctx: &mut TsEsTreeBuilder,
ident_name: &IdentName,
) -> NodeRef {
ctx.write_identifier(&ident_name.span, ident_name.sym.as_str(), false, None)
}
fn serialize_prop_name(
ctx: &mut TsEsTreeBuilder,
prop_name: &PropName,
) -> NodeRef {
match prop_name {
PropName::Ident(ident_name) => serialize_ident_name(ctx, ident_name),
PropName::Str(str_prop) => serialize_lit(ctx, &Lit::Str(str_prop.clone())),
PropName::Num(number) => serialize_lit(ctx, &Lit::Num(number.clone())),
PropName::Computed(node) => serialize_expr(ctx, &node.expr),
PropName::BigInt(big_int) => {
serialize_lit(ctx, &Lit::BigInt(big_int.clone()))
}
}
}
fn serialize_lit(ctx: &mut TsEsTreeBuilder, lit: &Lit) -> NodeRef {
match lit {
Lit::Str(node) => {
let raw_value = if let Some(v) = &node.raw {
v.to_string()
} else {
format!("{}", node.value).to_string()
};
ctx.write_str_lit(&node.span, &node.value, &raw_value)
}
Lit::Bool(node) => ctx.write_bool_lit(&node.span, node.value),
Lit::Null(node) => ctx.write_null_lit(&node.span),
Lit::Num(node) => {
let raw_value = if let Some(v) = &node.raw {
v.to_string()
} else {
format!("{}", node.value).to_string()
};
let value = node.raw.as_ref().unwrap();
ctx.write_num_lit(&node.span, value, &raw_value)
}
Lit::BigInt(node) => {
let raw_bigint_value = if let Some(v) = &node.raw {
let mut s = v.to_string();
s.pop();
s.to_string()
} else {
format!("{}", node.value).to_string()
};
let raw_value = if let Some(v) = &node.raw {
v.to_string()
} else {
format!("{}", node.value).to_string()
};
ctx.write_bigint_lit(
&node.span,
&node.value.to_string(),
&raw_value,
&raw_bigint_value,
)
}
Lit::Regex(node) => {
let raw = format!("/{}/{}", node.exp.as_str(), node.flags.as_str());
ctx.write_regex_lit(
&node.span,
node.exp.as_str(),
node.flags.as_str(),
&raw,
&raw,
)
}
Lit::JSXText(node) => {
ctx.write_jsx_text(&node.span, &node.raw, &node.value)
}
}
}
fn serialize_class_member(
ctx: &mut TsEsTreeBuilder,
member: &ClassMember,
) -> Option<NodeRef> {
match member {
ClassMember::Constructor(node) => {
let a11y = node.accessibility.as_ref().map(accessibility_to_str);
let key = serialize_prop_name(ctx, &node.key);
let params = node
.params
.iter()
.map(|param| match param {
ParamOrTsParamProp::TsParamProp(prop) => {
let a11y = node.accessibility.as_ref().map(accessibility_to_str);
let decorators = prop
.decorators
.iter()
.map(|deco| serialize_decorator(ctx, deco))
.collect::<Vec<_>>();
let paramter = match &prop.param {
TsParamPropParam::Ident(binding_ident) => {
serialize_binding_ident(ctx, binding_ident)
}
TsParamPropParam::Assign(assign_pat) => {
serialize_pat(ctx, &Pat::Assign(assign_pat.clone()))
}
};
ctx.write_ts_param_prop(
&prop.span,
prop.is_override,
prop.readonly,
a11y,
decorators,
paramter,
)
}
ParamOrTsParamProp::Param(param) => serialize_pat(ctx, &param.pat),
})
.collect::<Vec<_>>();
let body = node
.body
.as_ref()
.map(|body| serialize_stmt(ctx, &Stmt::Block(body.clone())));
let value = ctx.write_fn_expr(
&node.span, false, false, None, None, params, None, body,
);
Some(ctx.write_class_method(
&node.span,
false,
false,
node.is_optional,
false,
false,
"constructor",
a11y,
key,
value,
))
}
ClassMember::Method(node) => {
let key = serialize_prop_name(ctx, &node.key);
Some(serialize_class_method(
ctx,
&node.span,
node.is_abstract,
node.is_override,
node.is_optional,
node.is_static,
node.accessibility,
&node.kind,
key,
&node.function,
))
}
ClassMember::PrivateMethod(node) => {
let key = serialize_private_name(ctx, &node.key);
Some(serialize_class_method(
ctx,
&node.span,
node.is_abstract,
node.is_override,
node.is_optional,
node.is_static,
node.accessibility,
&node.kind,
key,
&node.function,
))
}
ClassMember::ClassProp(node) => {
let a11y = node.accessibility.as_ref().map(accessibility_to_str);
let key = serialize_prop_name(ctx, &node.key);
let value = node.value.as_ref().map(|expr| serialize_expr(ctx, expr));
let decorators = node
.decorators
.iter()
.map(|deco| serialize_decorator(ctx, deco))
.collect::<Vec<_>>();
Some(ctx.write_class_prop(
&node.span,
node.declare,
false,
node.is_optional,
node.is_override,
node.readonly,
node.is_static,
a11y,
decorators,
key,
value,
))
}
ClassMember::PrivateProp(node) => {
let a11y = node.accessibility.as_ref().map(accessibility_to_str);
let decorators = node
.decorators
.iter()
.map(|deco| serialize_decorator(ctx, deco))
.collect::<Vec<_>>();
let key = serialize_private_name(ctx, &node.key);
let value = node.value.as_ref().map(|expr| serialize_expr(ctx, expr));
Some(ctx.write_class_prop(
&node.span,
false,
false,
node.is_optional,
node.is_override,
node.readonly,
node.is_static,
a11y,
decorators,
key,
value,
))
}
ClassMember::TsIndexSignature(node) => {
Some(serialize_ts_index_sig(ctx, node))
}
ClassMember::Empty(_) => None,
ClassMember::StaticBlock(node) => {
let body = serialize_stmt(ctx, &Stmt::Block(node.body.clone()));
Some(ctx.write_static_block(&node.span, body))
}
ClassMember::AutoAccessor(node) => {
let a11y = node.accessibility.as_ref().map(accessibility_to_str);
let decorators = node
.decorators
.iter()
.map(|deco| serialize_decorator(ctx, deco))
.collect::<Vec<_>>();
let key = match &node.key {
Key::Private(private_name) => serialize_private_name(ctx, private_name),
Key::Public(prop_name) => serialize_prop_name(ctx, prop_name),
};
let value = node.value.as_ref().map(|expr| serialize_expr(ctx, expr));
Some(ctx.write_accessor_property(
&node.span,
false,
false,
false,
node.is_override,
false,
node.is_static,
a11y,
decorators,
key,
value,
))
}
}
}
#[allow(clippy::too_many_arguments)]
fn serialize_class_method(
ctx: &mut TsEsTreeBuilder,
span: &Span,
is_abstract: bool,
is_override: bool,
is_optional: bool,
is_static: bool,
accessibility: Option<Accessibility>,
method_kind: &MethodKind,
key: NodeRef,
function: &Function,
) -> NodeRef {
let kind = match method_kind {
MethodKind::Method => "method",
MethodKind::Getter => "getter",
MethodKind::Setter => "setter",
};
let type_params =
maybe_serialize_ts_type_param_decl(ctx, &function.type_params);
let params = function
.params
.iter()
.map(|param| serialize_pat(ctx, &param.pat))
.collect::<Vec<_>>();
let return_type = maybe_serialize_ts_type_ann(ctx, &function.return_type);
let body = function
.body
.as_ref()
.map(|body| serialize_stmt(ctx, &Stmt::Block(body.clone())));
let value = if let Some(body) = body {
ctx.write_fn_expr(
&function.span,
function.is_async,
function.is_generator,
None,
type_params,
params,
return_type,
Some(body),
)
} else {
ctx.write_ts_empty_body_fn_expr(
span,
false,
false,
function.is_async,
function.is_generator,
None,
type_params,
params,
return_type,
)
};
let a11y = accessibility.as_ref().map(accessibility_to_str);
if is_abstract {
ctx.write_ts_abstract_method_def(
span,
false,
is_optional,
is_override,
false,
a11y,
key,
value,
)
} else {
ctx.write_class_method(
span,
false,
false,
is_optional,
is_override,
is_static,
kind,
a11y,
key,
value,
)
}
}
fn serialize_ts_expr_with_type_args(
ctx: &mut TsEsTreeBuilder,
node: &TsExprWithTypeArgs,
) -> NodeRef {
let expr = serialize_expr(ctx, &node.expr);
let type_args = node
.type_args
.as_ref()
.map(|arg| serialize_ts_param_inst(ctx, arg));
ctx.write_ts_class_implements(&node.span, expr, type_args)
}
fn serialize_decorator(ctx: &mut TsEsTreeBuilder, node: &Decorator) -> NodeRef {
let expr = serialize_expr(ctx, &node.expr);
ctx.write_decorator(&node.span, expr)
}
fn serialize_binding_ident(
ctx: &mut TsEsTreeBuilder,
node: &BindingIdent,
) -> NodeRef {
let type_ann = maybe_serialize_ts_type_ann(ctx, &node.type_ann);
ctx.write_identifier(&node.span, &node.sym, node.optional, type_ann)
}
fn serialize_ts_param_inst(
ctx: &mut TsEsTreeBuilder,
node: &TsTypeParamInstantiation,
) -> NodeRef {
let params = node
.params
.iter()
.map(|param| serialize_ts_type(ctx, param))
.collect::<Vec<_>>();
ctx.write_ts_type_param_inst(&node.span, params)
}
fn serialize_ts_type(ctx: &mut TsEsTreeBuilder, node: &TsType) -> NodeRef {
match node {
TsType::TsKeywordType(node) => {
let kind = match node.kind {
TsKeywordTypeKind::TsAnyKeyword => TsKeywordKind::Any,
TsKeywordTypeKind::TsUnknownKeyword => TsKeywordKind::Unknown,
TsKeywordTypeKind::TsNumberKeyword => TsKeywordKind::Number,
TsKeywordTypeKind::TsObjectKeyword => TsKeywordKind::Object,
TsKeywordTypeKind::TsBooleanKeyword => TsKeywordKind::Boolean,
TsKeywordTypeKind::TsBigIntKeyword => TsKeywordKind::BigInt,
TsKeywordTypeKind::TsStringKeyword => TsKeywordKind::String,
TsKeywordTypeKind::TsSymbolKeyword => TsKeywordKind::Symbol,
TsKeywordTypeKind::TsVoidKeyword => TsKeywordKind::Void,
TsKeywordTypeKind::TsUndefinedKeyword => TsKeywordKind::Undefined,
TsKeywordTypeKind::TsNullKeyword => TsKeywordKind::Null,
TsKeywordTypeKind::TsNeverKeyword => TsKeywordKind::Never,
TsKeywordTypeKind::TsIntrinsicKeyword => TsKeywordKind::Intrinsic,
};
ctx.write_ts_keyword(kind, &node.span)
}
TsType::TsThisType(node) => ctx.write_ts_this_type(&node.span),
TsType::TsFnOrConstructorType(node) => match node {
TsFnOrConstructorType::TsFnType(node) => {
let param_ids = node
.params
.iter()
.map(|param| serialize_ts_fn_param(ctx, param))
.collect::<Vec<_>>();
ctx.write_ts_fn_type(&node.span, param_ids)
}
TsFnOrConstructorType::TsConstructorType(node) => {
// interface Foo { new<T>(arg1: any): any }
let type_params = node
.type_params
.as_ref()
.map(|param| serialize_ts_type_param_decl(ctx, param));
let params = node
.params
.iter()
.map(|param| serialize_ts_fn_param(ctx, param))
.collect::<Vec<_>>();
let return_type = serialize_ts_type_ann(ctx, node.type_ann.as_ref());
ctx.write_ts_construct_sig(&node.span, type_params, params, return_type)
}
},
TsType::TsTypeRef(node) => {
let name = serialize_ts_entity_name(ctx, &node.type_name);
let type_args = node
.type_params
.clone()
.map(|param| serialize_ts_param_inst(ctx, &param));
ctx.write_ts_type_ref(&node.span, name, type_args)
}
TsType::TsTypeQuery(node) => {
let expr_name = match &node.expr_name {
TsTypeQueryExpr::TsEntityName(entity) => {
serialize_ts_entity_name(ctx, entity)
}
TsTypeQueryExpr::Import(child) => {
serialize_ts_type(ctx, &TsType::TsImportType(child.clone()))
}
};
let type_args = node
.type_args
.clone()
.map(|param| serialize_ts_param_inst(ctx, &param));
ctx.write_ts_type_query(&node.span, expr_name, type_args)
}
TsType::TsTypeLit(node) => {
let members = node
.members
.iter()
.map(|member| serialize_ts_type_elem(ctx, member))
.collect::<Vec<_>>();
ctx.write_ts_type_lit(&node.span, members)
}
TsType::TsArrayType(node) => {
let elem = serialize_ts_type(ctx, &node.elem_type);
ctx.write_ts_array_type(&node.span, elem)
}
TsType::TsTupleType(node) => {
let children = node
.elem_types
.iter()
.map(|elem| {
if let Some(label) = &elem.label {
let label = serialize_pat(ctx, label);
let type_id = serialize_ts_type(ctx, elem.ty.as_ref());
ctx.write_ts_named_tuple_member(&elem.span, label, type_id)
} else {
serialize_ts_type(ctx, elem.ty.as_ref())
}
})
.collect::<Vec<_>>();
ctx.write_ts_tuple_type(&node.span, children)
}
TsType::TsOptionalType(node) => {
let type_ann = serialize_ts_type(ctx, &node.type_ann);
ctx.write_ts_optional_type(&node.span, type_ann)
}
TsType::TsRestType(node) => {
let type_ann = serialize_ts_type(ctx, &node.type_ann);
ctx.write_ts_rest_type(&node.span, type_ann)
}
TsType::TsUnionOrIntersectionType(node) => match node {
TsUnionOrIntersectionType::TsUnionType(node) => {
let children = node
.types
.iter()
.map(|item| serialize_ts_type(ctx, item))
.collect::<Vec<_>>();
ctx.write_ts_union_type(&node.span, children)
}
TsUnionOrIntersectionType::TsIntersectionType(node) => {
let children = node
.types
.iter()
.map(|item| serialize_ts_type(ctx, item))
.collect::<Vec<_>>();
ctx.write_ts_intersection_type(&node.span, children)
}
},
TsType::TsConditionalType(node) => {
let check = serialize_ts_type(ctx, &node.check_type);
let extends = serialize_ts_type(ctx, &node.extends_type);
let v_true = serialize_ts_type(ctx, &node.true_type);
let v_false = serialize_ts_type(ctx, &node.false_type);
ctx.write_ts_conditional_type(&node.span, check, extends, v_true, v_false)
}
TsType::TsInferType(node) => {
let param = serialize_ts_type_param(ctx, &node.type_param);
ctx.write_ts_infer_type(&node.span, param)
}
TsType::TsParenthesizedType(node) => {
// Not materialized in TSEstree
serialize_ts_type(ctx, &node.type_ann)
}
TsType::TsTypeOperator(node) => {
let type_ann = serialize_ts_type(ctx, &node.type_ann);
let op = match node.op {
TsTypeOperatorOp::KeyOf => "keyof",
TsTypeOperatorOp::Unique => "unique",
TsTypeOperatorOp::ReadOnly => "readonly",
};
ctx.write_ts_type_op(&node.span, op, type_ann)
}
TsType::TsIndexedAccessType(node) => {
let index = serialize_ts_type(ctx, &node.index_type);
let obj = serialize_ts_type(ctx, &node.obj_type);
ctx.write_ts_indexed_access_type(&node.span, index, obj)
}
TsType::TsMappedType(node) => {
let name = maybe_serialize_ts_type(ctx, &node.name_type);
let type_ann = maybe_serialize_ts_type(ctx, &node.type_ann);
let type_param = serialize_ts_type_param(ctx, &node.type_param);
ctx.write_ts_mapped_type(
&node.span,
node.readonly,
node.optional,
name,
type_ann,
type_param,
)
}
TsType::TsLitType(node) => serialize_ts_lit_type(ctx, node),
TsType::TsTypePredicate(node) => {
let param_name = match &node.param_name {
TsThisTypeOrIdent::TsThisType(node) => {
ctx.write_ts_this_type(&node.span)
}
TsThisTypeOrIdent::Ident(ident) => serialize_ident(ctx, ident, None),
};
let type_ann = maybe_serialize_ts_type_ann(ctx, &node.type_ann);
ctx.write_ts_type_predicate(
&node.span,
node.asserts,
param_name,
type_ann,
)
}
TsType::TsImportType(node) => {
let arg = serialize_ts_lit_type(
ctx,
&TsLitType {
lit: TsLit::Str(node.arg.clone()),
span: node.arg.span,
},
);
let type_arg = node
.type_args
.clone()
.map(|param_node| serialize_ts_param_inst(ctx, param_node.as_ref()));
let qualifier = node
.qualifier
.clone()
.map(|quali| serialize_ts_entity_name(ctx, &quali));
ctx.write_ts_import_type(&node.span, arg, qualifier, type_arg)
}
}
}
fn serialize_ts_lit_type(
ctx: &mut TsEsTreeBuilder,
node: &TsLitType,
) -> NodeRef {
match &node.lit {
TsLit::Number(lit) => {
let lit = serialize_lit(ctx, &Lit::Num(lit.clone()));
ctx.write_ts_lit_type(&node.span, lit)
}
TsLit::Str(lit) => {
let lit = serialize_lit(ctx, &Lit::Str(lit.clone()));
ctx.write_ts_lit_type(&node.span, lit)
}
TsLit::Bool(lit) => {
let lit = serialize_lit(ctx, &Lit::Bool(*lit));
ctx.write_ts_lit_type(&node.span, lit)
}
TsLit::BigInt(lit) => {
let lit = serialize_lit(ctx, &Lit::BigInt(lit.clone()));
ctx.write_ts_lit_type(&node.span, lit)
}
TsLit::Tpl(lit) => {
let quasis = lit
.quasis
.iter()
.map(|quasi| {
ctx.write_template_elem(
&quasi.span,
quasi.tail,
&quasi.raw,
&quasi
.cooked
.as_ref()
.map_or("".to_string(), |v| v.to_string()),
)
})
.collect::<Vec<_>>();
let types = lit
.types
.iter()
.map(|ts_type| serialize_ts_type(ctx, ts_type))
.collect::<Vec<_>>();
ctx.write_ts_tpl_lit(&node.span, quasis, types)
}
}
}
fn serialize_ts_entity_name(
ctx: &mut TsEsTreeBuilder,
node: &TsEntityName,
) -> NodeRef {
match &node {
TsEntityName::TsQualifiedName(node) => {
let left = serialize_ts_entity_name(ctx, &node.left);
let right = serialize_ident_name(ctx, &node.right);
ctx.write_ts_qualified_name(&node.span, left, right)
}
TsEntityName::Ident(ident) => serialize_ident(ctx, ident, None),
}
}
fn maybe_serialize_ts_type_ann(
ctx: &mut TsEsTreeBuilder,
node: &Option<Box<TsTypeAnn>>,
) -> Option<NodeRef> {
node
.as_ref()
.map(|type_ann| serialize_ts_type_ann(ctx, type_ann))
}
fn serialize_ts_type_ann(
ctx: &mut TsEsTreeBuilder,
node: &TsTypeAnn,
) -> NodeRef {
let v_type = serialize_ts_type(ctx, &node.type_ann);
ctx.write_ts_type_ann(&node.span, v_type)
}
fn maybe_serialize_ts_type(
ctx: &mut TsEsTreeBuilder,
node: &Option<Box<TsType>>,
) -> Option<NodeRef> {
node.as_ref().map(|item| serialize_ts_type(ctx, item))
}
fn serialize_ts_type_param(
ctx: &mut TsEsTreeBuilder,
node: &TsTypeParam,
) -> NodeRef {
let name = serialize_ident(ctx, &node.name, None);
let constraint = maybe_serialize_ts_type(ctx, &node.constraint);
let default = maybe_serialize_ts_type(ctx, &node.default);
ctx.write_ts_type_param(
&node.span,
node.is_in,
node.is_out,
node.is_const,
name,
constraint,
default,
)
}
fn maybe_serialize_ts_type_param_decl(
ctx: &mut TsEsTreeBuilder,
node: &Option<Box<TsTypeParamDecl>>,
) -> Option<NodeRef> {
node
.as_ref()
.map(|node| serialize_ts_type_param_decl(ctx, node))
}
fn serialize_ts_type_param_decl(
ctx: &mut TsEsTreeBuilder,
node: &TsTypeParamDecl,
) -> NodeRef {
let params = node
.params
.iter()
.map(|param| serialize_ts_type_param(ctx, param))
.collect::<Vec<_>>();
ctx.write_ts_type_param_decl(&node.span, params)
}
fn serialize_ts_fn_param(
ctx: &mut TsEsTreeBuilder,
node: &TsFnParam,
) -> NodeRef {
match node {
TsFnParam::Ident(ident) => serialize_binding_ident(ctx, ident),
TsFnParam::Array(pat) => serialize_pat(ctx, &Pat::Array(pat.clone())),
TsFnParam::Rest(pat) => serialize_pat(ctx, &Pat::Rest(pat.clone())),
TsFnParam::Object(pat) => serialize_pat(ctx, &Pat::Object(pat.clone())),
}
}