mod comments;
use bon::builder;
use comments::create_comment_span;
use swc_atoms::Atom;
use swc_common::{DUMMY_SP, SyntaxContext};
use swc_ecma_ast::{
AssignPatProp, BindingIdent, BlockStmt, CallExpr, Callee, Decl, ExportDecl, Expr, ExprOrSpread,
FnExpr, Function, Ident, IdentName, ImportDecl, ImportNamedSpecifier, ImportPhase,
ImportStarAsSpecifier, MemberExpr, ModuleDecl, ModuleExportName, ModuleItem, ObjectPat,
ObjectPatProp, Param, Prop, PropName, ReturnStmt, Stmt, Str, TsEntityName, TsFnParam,
TsIndexSignature, TsInterfaceBody, TsInterfaceDecl, TsKeywordType, TsKeywordTypeKind,
TsLitType, TsModuleBlock, TsModuleDecl, TsModuleName, TsNamespaceBody, TsPropertySignature,
TsQualifiedName, TsType, TsTypeAnn, TsTypeElement, TsTypeLit, TsTypeParamInstantiation,
TsTypeRef, TsUnionOrIntersectionType, TsUnionType,
};
pub use comments::get_comments;
#[builder(finish_fn = build)]
pub fn binding_fn_expr(
name: impl Into<Atom>,
#[builder(default)] stmts: Vec<Stmt>,
#[builder(default)] params: Vec<Param>,
comment: Option<&str>,
) -> FnExpr {
FnExpr {
ident: Some(ident(name)),
function: Box::new(
binding_fn()
.params(params)
.stmts(stmts)
.maybe_comment(comment)
.build(),
),
}
}
#[builder(finish_fn = build)]
pub fn binding_fn(
#[builder(default)] params: Vec<Param>,
#[builder(default)] stmts: Vec<Stmt>,
comment: Option<&str>,
) -> Function {
Function {
params,
decorators: vec![],
span: create_comment_span(comment),
ctxt: SyntaxContext::default(),
body: Some(block_stmt().stmts(stmts).build()),
is_generator: false,
is_async: false,
type_params: None,
return_type: None,
}
}
#[builder(finish_fn = build)]
pub fn binding_param(name: impl Into<Atom>, type_ann: Option<TsType>) -> Param {
Param {
span: DUMMY_SP,
decorators: vec![],
pat: BindingIdent {
id: ident(name),
type_ann: type_ann.map(|t| {
Box::new(TsTypeAnn {
span: DUMMY_SP,
type_ann: Box::new(t),
})
}),
}
.into(),
}
}
pub fn destructuring_param_from_elements(
elements: impl Iterator<Item = (Atom, TsTypeElement)>,
) -> Param {
let (names, type_anns): (Vec<Atom>, Vec<TsTypeElement>) = elements.unzip();
destructuring_param()
.names(names.into_iter())
.type_ann(object_type_from_elements(type_anns.into_iter()))
.build()
}
#[builder(finish_fn = build)]
pub fn destructuring_param(names: impl Iterator<Item: Into<Atom>>, type_ann: TsType) -> Param {
Param {
span: DUMMY_SP,
decorators: vec![],
pat: ObjectPat {
span: DUMMY_SP,
props: names
.map(|name| {
ObjectPatProp::Assign(AssignPatProp {
span: DUMMY_SP,
key: ident(name).into(),
value: None,
})
})
.collect(),
optional: false,
type_ann: Some(Box::new(TsTypeAnn {
span: DUMMY_SP,
type_ann: Box::new(type_ann),
})),
}
.into(),
}
}
#[builder(finish_fn = build)]
pub fn call_args(#[builder(default)] args: Vec<Expr>) -> impl Iterator<Item = ExprOrSpread> {
args.into_iter().map(|arg| ExprOrSpread {
spread: None,
expr: Box::new(arg),
})
}
pub fn ident(name: impl Into<Atom>) -> Ident {
let atom: Atom = name.into();
Ident::new(atom, DUMMY_SP, SyntaxContext::default())
}
#[builder(finish_fn = build)]
pub fn interface_decl_stmt(
name: impl Into<Atom>,
elements: impl Iterator<Item = TsTypeElement>,
) -> Stmt {
Stmt::Decl(Decl::TsInterface(Box::new(TsInterfaceDecl {
span: DUMMY_SP,
id: ident(name),
declare: false,
type_params: None,
extends: vec![],
body: TsInterfaceBody {
span: DUMMY_SP,
body: elements.collect(),
},
})))
}
pub fn nested_namespace_decl(name: impl Into<Atom>, items: Vec<ModuleItem>) -> ModuleDecl {
let name: Atom = name.into();
ModuleDecl::ExportDecl(ExportDecl {
span: DUMMY_SP,
decl: Decl::TsModule(Box::new(TsModuleDecl {
span: DUMMY_SP,
id: TsModuleName::Ident(ident(name)),
body: Some(TsNamespaceBody::TsModuleBlock(TsModuleBlock {
span: DUMMY_SP,
body: items,
})),
declare: false,
global: false,
namespace: true,
})),
})
}
pub fn union_type(types: impl Iterator<Item: Into<TsType>>) -> TsType {
TsType::TsUnionOrIntersectionType(TsUnionOrIntersectionType::TsUnionType(TsUnionType {
span: DUMMY_SP,
types: types.map(|t| Box::new(t.into())).collect(),
}))
}
#[builder(finish_fn = build)]
pub fn type_ref(name: impl Into<Atom>, generics: Option<Vec<TsType>>) -> TsType {
TsType::TsTypeRef(TsTypeRef {
span: DUMMY_SP,
type_name: TsEntityName::Ident(ident(name)),
type_params: generics.map(|tp| {
Box::new(TsTypeParamInstantiation {
span: DUMMY_SP,
params: tp.into_iter().map(Box::new).collect(),
})
}),
})
}
#[builder(finish_fn = build)]
pub fn qualified_type_ref(
name: impl Into<Atom>,
generics: Option<Vec<TsType>>,
qualifier: impl Into<Atom>,
) -> TsType {
TsType::TsTypeRef(TsTypeRef {
span: DUMMY_SP,
type_name: TsEntityName::TsQualifiedName(Box::new(TsQualifiedName {
span: DUMMY_SP,
left: TsEntityName::Ident(ident(qualifier)),
right: ident(name).into(),
})),
type_params: generics.map(|tp| {
Box::new(TsTypeParamInstantiation {
span: DUMMY_SP,
params: tp.into_iter().map(Box::new).collect(),
})
}),
})
}
#[builder(finish_fn = build)]
pub fn block_stmt(#[builder(default)] stmts: Vec<Stmt>) -> BlockStmt {
BlockStmt {
span: DUMMY_SP,
ctxt: SyntaxContext::default(),
stmts,
}
}
#[builder(finish_fn = build)]
pub fn import_decl(
destruct_field_ident: Ident,
destruct_field_rename_ident: Ident,
src: impl Into<Atom>,
) -> ImportDecl {
ImportDecl {
span: DUMMY_SP,
specifiers: vec![
ImportNamedSpecifier {
span: DUMMY_SP,
local: destruct_field_rename_ident,
imported: Some(ModuleExportName::Ident(destruct_field_ident)),
is_type_only: false,
}
.into(),
],
src: Box::new(Str {
span: DUMMY_SP,
value: src.into(),
raw: None,
}),
type_only: false,
with: None,
phase: ImportPhase::Evaluation,
}
}
#[builder(finish_fn = build)]
pub fn import_star_as_decl(src: impl Into<Atom>, rename: impl Into<Atom>) -> ImportDecl {
let rename: Atom = rename.into();
ImportDecl {
span: DUMMY_SP,
specifiers: vec![
ImportStarAsSpecifier {
span: DUMMY_SP,
local: ident(rename.clone()),
}
.into(),
],
src: Box::new(Str {
span: DUMMY_SP,
value: src.into(),
raw: None,
}),
type_only: false,
with: None,
phase: ImportPhase::Evaluation,
}
}
#[builder(finish_fn = build)]
pub fn fn_call_expr(
callee_name: impl Into<Atom> + Copy,
name: impl Into<Atom> + Copy,
args: impl Iterator<Item = ExprOrSpread>,
generics: Option<Vec<TsType>>,
) -> Expr {
Expr::Call(CallExpr {
span: DUMMY_SP,
ctxt: SyntaxContext::default(),
callee: Callee::Expr(
Expr::Member(MemberExpr {
span: DUMMY_SP,
obj: ident(callee_name).into(),
prop: IdentName::new(name.into(), DUMMY_SP).into(),
})
.into(),
),
args: args.collect(),
type_args: generics.map(|g| {
Box::new(TsTypeParamInstantiation {
span: DUMMY_SP,
params: g.into_iter().map(Box::new).collect(),
})
}),
})
}
#[builder]
pub fn object_prop(key: impl Into<Atom>, inner: Option<Expr>) -> Prop {
match inner {
Some(inner) => Prop::KeyValue(swc_ecma_ast::KeyValueProp {
key: PropName::Ident(IdentName {
span: DUMMY_SP,
sym: key.into(),
}),
value: inner.into(),
}),
None => Prop::Shorthand(ident(key)),
}
}
pub fn object_type_from_elements(elements: impl Iterator<Item = TsTypeElement>) -> TsType {
TsType::TsTypeLit(TsTypeLit {
span: DUMMY_SP,
members: elements.collect(),
})
}
pub fn tuple_type_from_elements(elements: impl Iterator<Item = TsType>) -> TsType {
TsType::TsTupleType(swc_ecma_ast::TsTupleType {
span: DUMMY_SP,
elem_types: elements
.map(|ty| swc_ecma_ast::TsTupleElement {
span: DUMMY_SP,
label: None,
ty: Box::new(ty),
})
.collect(),
})
}
pub fn object_expr_from_props(props: impl Iterator<Item = Prop>) -> Expr {
Expr::Object(swc_ecma_ast::ObjectLit {
span: DUMMY_SP,
props: props
.map(|p| swc_ecma_ast::PropOrSpread::Prop(Box::new(p)))
.collect(),
})
}
#[builder(finish_fn = build)]
pub fn object_member_type_element(
key: impl Into<Atom>,
type_ann: TsType,
optional: bool,
) -> TsTypeElement {
TsTypeElement::TsPropertySignature(TsPropertySignature {
span: DUMMY_SP,
key: Box::new(lit_str(key).into()),
type_ann: Some(Box::new(TsTypeAnn {
span: DUMMY_SP,
type_ann: Box::new(type_ann),
})),
computed: false,
optional,
readonly: false,
})
}
pub fn lit_str(value: impl Into<Atom>) -> Str {
Str {
span: DUMMY_SP,
value: value.into(),
raw: None,
}
}
pub fn lit_str_as_const(value: impl Into<Atom>) -> Expr {
Expr::TsConstAssertion(swc_ecma_ast::TsConstAssertion {
span: DUMMY_SP,
expr: Box::new(Expr::Lit(swc_ecma_ast::Lit::Str(lit_str(value)))),
})
}
pub fn lit_str_type(value: impl Into<Atom>) -> TsType {
TsType::TsLitType(TsLitType {
span: DUMMY_SP,
lit: swc_ecma_ast::TsLit::Str(lit_str(value)),
})
}
pub fn return_stmt(expr: impl Into<Expr>) -> Stmt {
Stmt::Return(ReturnStmt {
span: DUMMY_SP,
arg: Some(Box::new(expr.into())),
})
}
pub fn ts_keyword(kind: TsKeywordTypeKind) -> TsType {
TsType::TsKeywordType(TsKeywordType {
span: DUMMY_SP,
kind,
})
}
pub fn index_signature(param_name: &str, key_type: TsType, value_type: TsType) -> TsType {
TsType::TsTypeLit(TsTypeLit {
span: DUMMY_SP,
members: vec![TsTypeElement::TsIndexSignature(TsIndexSignature {
span: DUMMY_SP,
params: vec![TsFnParam::Ident(swc_ecma_ast::BindingIdent {
id: swc_ecma_ast::Ident::new(param_name.into(), DUMMY_SP, SyntaxContext::default()),
type_ann: Some(Box::new(TsTypeAnn {
span: DUMMY_SP,
type_ann: Box::new(key_type),
})),
})],
type_ann: Some(Box::new(TsTypeAnn {
span: DUMMY_SP,
type_ann: Box::new(value_type),
})),
readonly: false,
is_static: false,
})],
})
}
pub fn split_union_type(maybe_union: Box<TsType>) -> impl Iterator<Item = Box<TsType>> {
let mut types = vec![];
match *maybe_union {
TsType::TsUnionOrIntersectionType(TsUnionOrIntersectionType::TsUnionType(union)) => {
for ty in union.types {
if let TsType::TsUnionOrIntersectionType(TsUnionOrIntersectionType::TsUnionType(
inner_union,
)) = *ty
{
types.extend(inner_union.types.into_iter());
} else {
types.push(ty);
}
}
}
_ => types.push(maybe_union),
}
types.into_iter()
}
pub fn never_type() -> TsType {
TsType::TsKeywordType(TsKeywordType {
span: DUMMY_SP,
kind: TsKeywordTypeKind::TsNeverKeyword,
})
}
pub fn null_type() -> TsType {
TsType::TsKeywordType(TsKeywordType {
span: DUMMY_SP,
kind: TsKeywordTypeKind::TsNullKeyword,
})
}
#[builder(finish_fn = build)]
pub fn fn_overload(params: Vec<Param>, return_type: TsType, comment: Option<&str>) -> Function {
Function {
params,
decorators: vec![],
span: create_comment_span(comment),
ctxt: SyntaxContext::default(),
body: None,
is_generator: false,
is_async: false,
type_params: None,
return_type: Some(Box::new(TsTypeAnn {
span: DUMMY_SP,
type_ann: Box::new(return_type),
})),
}
}
#[builder(finish_fn = build)]
pub fn fn_param(name: impl Into<Atom>, type_ann: Option<TsType>) -> TsFnParam {
TsFnParam::Ident(BindingIdent {
id: ident(name),
type_ann: type_ann.map(|t| {
Box::new(TsTypeAnn {
span: DUMMY_SP,
type_ann: Box::new(t),
})
}),
})
}
pub fn into_null_union_type(ty: TsType) -> TsType {
union_type(vec![ty, null_type()].into_iter())
}