mirror of
https://github.com/denoland/deno.git
synced 2025-02-24 06:02:18 -05:00

This commit adds an unstable lint plugin API. Plugins are specified in the `deno.json` file under `lint.plugins` option like so: ``` { "lint": { "plugins": [ "./plugins/my-plugin.ts", "jsr:@deno/lint-plugin1", "npm:@deno/lint-plugin2" ] } } ``` The API is considered unstable and might be subject to changes in the future. Plugin API was modelled after ESLint API for the most part, but there are no guarantees for compatibility. The AST format exposed to plugins is closely modelled after the AST that `typescript-eslint` uses. Lint plugins use the visitor pattern and can add diagnostics like so: ``` export default { name: "lint-plugin", rules: { "plugin-rule": { create(context) { return { Identifier(node) { if (node.name === "a") { context.report({ node, message: "should be b", fix(fixer) { return fixer.replaceText(node, "_b"); }, }); } }, }; }, }, }, } satisfies Deno.lint.Plugin; ``` Besides reporting errors (diagnostics) plugins can provide automatic fixes that use text replacement to apply changes. --------- Co-authored-by: Marvin Hagemeister <marvin@deno.com> Co-authored-by: David Sherret <dsherret@gmail.com>
2619 lines
74 KiB
Rust
2619 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;
|
|
use crate::util::text_encoding::Utf16Map;
|
|
|
|
pub fn serialize_swc_to_buffer(
|
|
parsed_source: &ParsedSource,
|
|
utf16_map: &Utf16Map,
|
|
) -> 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.map_utf8_spans_to_utf16(utf16_map);
|
|
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, ¶m.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, ¶m.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, ¶m.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, ¶m.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, ¶m.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, ¶m));
|
|
|
|
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, ¶m));
|
|
|
|
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())),
|
|
}
|
|
}
|