use swc_ecma_ast::*;
use swc_ecma_hooks::VisitMutHook;
pub(crate) struct NoopHook;
impl<C> VisitMutHook<C> for NoopHook {}
pub(crate) struct OrderedChain<A, B, C>(pub A, pub B, std::marker::PhantomData<C>)
where
A: VisitMutHook<C>,
B: VisitMutHook<C>;
impl<A, B, C> OrderedChain<A, B, C>
where
A: VisitMutHook<C>,
B: VisitMutHook<C>,
{
pub fn new(a: A, b: B) -> Self {
Self(a, b, std::marker::PhantomData)
}
}
macro_rules! chained_method {
($enter_name:ident, $exit_name:ident, $T:ty, $C:ident) => {
fn $enter_name(&mut self, node: &mut $T, ctx: &mut $C) {
self.0.$enter_name(node, ctx);
self.1.$enter_name(node, ctx);
}
fn $exit_name(&mut self, node: &mut $T, ctx: &mut $C) {
self.0.$exit_name(node, ctx);
self.1.$exit_name(node, ctx);
}
};
}
impl<A, B, C> VisitMutHook<C> for OrderedChain<A, B, C>
where
A: VisitMutHook<C>,
B: VisitMutHook<C>,
{
chained_method!(enter_private_name, exit_private_name, PrivateName, C);
chained_method!(enter_labeled_stmt, exit_labeled_stmt, LabeledStmt, C);
chained_method!(enter_continue_stmt, exit_continue_stmt, ContinueStmt, C);
chained_method!(enter_break_stmt, exit_break_stmt, BreakStmt, C);
chained_method!(enter_program, exit_program, Program, C);
chained_method!(enter_module, exit_module, Module, C);
chained_method!(enter_script, exit_script, Script, C);
chained_method!(enter_stmt, exit_stmt, Stmt, C);
chained_method!(enter_stmts, exit_stmts, Vec<Stmt>, C);
chained_method!(enter_block_stmt, exit_block_stmt, BlockStmt, C);
chained_method!(enter_module_item, exit_module_item, ModuleItem, C);
chained_method!(enter_module_items, exit_module_items, Vec<ModuleItem>, C);
chained_method!(enter_expr, exit_expr, Expr, C);
chained_method!(enter_call_expr, exit_call_expr, CallExpr, C);
chained_method!(enter_new_expr, exit_new_expr, NewExpr, C);
chained_method!(enter_tagged_tpl, exit_tagged_tpl, TaggedTpl, C);
chained_method!(enter_var_decl, exit_var_decl, VarDecl, C);
chained_method!(enter_var_declarator, exit_var_declarator, VarDeclarator, C);
chained_method!(enter_fn_decl, exit_fn_decl, FnDecl, C);
chained_method!(enter_fn_expr, exit_fn_expr, FnExpr, C);
chained_method!(enter_function, exit_function, Function, C);
chained_method!(
enter_export_default_decl,
exit_export_default_decl,
ExportDefaultDecl,
C
);
chained_method!(
enter_export_default_expr,
exit_export_default_expr,
ExportDefaultExpr,
C
);
chained_method!(enter_export_decl, exit_export_decl, ExportDecl, C);
chained_method!(enter_ident, exit_ident, Ident, C);
}
pub(crate) struct HookBuilder<H, C>
where
H: VisitMutHook<C>,
{
hook: H,
_marker: std::marker::PhantomData<C>,
}
impl<H, C> HookBuilder<H, C>
where
H: VisitMutHook<C>,
{
pub fn new(hook: H) -> Self {
Self {
hook,
_marker: std::marker::PhantomData,
}
}
pub fn chain<B>(self, hook: B) -> HookBuilder<OrderedChain<H, B, C>, C>
where
B: VisitMutHook<C>,
{
HookBuilder {
hook: OrderedChain::new(self.hook, hook),
_marker: std::marker::PhantomData,
}
}
#[allow(dead_code)]
pub fn chain_optional<B>(self, hook: Option<B>) -> HookBuilder<OrderedChain<H, Option<B>, C>, C>
where
B: VisitMutHook<C>,
{
self.chain(hook)
}
pub fn build(self) -> H {
self.hook
}
}