swc_ecma_codegen_macros/
lib.rs1extern crate proc_macro;
2
3use proc_macro2::TokenStream;
4use swc_macros_common::prelude::*;
5use syn::{
6 fold::Fold,
7 parse::{Parse, Parser},
8 token::Token,
9 *,
10};
11
12#[proc_macro_attribute]
35pub fn node_impl(
36 _attr: proc_macro::TokenStream,
37 item: proc_macro::TokenStream,
38) -> proc_macro::TokenStream {
39 let item: ItemImpl = syn::parse(item).expect("failed to parse input as an item");
40
41 let mut output = TokenStream::new();
42
43 for i in item.items {
44 match i {
45 ImplItem::Fn(i) => {
46 let item = expand_node_impl_method(&item.self_ty, i);
47
48 output.extend(item.to_token_stream());
49 }
50
51 _ => {
52 panic!("Unexpected item: {i:?}");
53 }
54 }
55 }
56
57 output.into()
58}
59
60fn expand_node_impl_method(node_type: &Type, src: ImplItemFn) -> ItemImpl {
62 let emit_block = ReplaceEmit { emit: true }.fold_block(src.block.clone());
63
64 parse_quote!(
65 impl crate::Node for #node_type {
66 fn emit_with<W, S>(&self, emitter: &mut crate::Emitter<'_, W, S>) -> crate::Result
67 where
68 W: crate::text_writer::WriteJs,
69 S: swc_common::SourceMapper + swc_ecma_ast::SourceMapperExt,
70 {
71 #emit_block
72 }
73
74 }
75 )
76}
77
78struct ReplaceEmit {
79 emit: bool,
80}
81
82impl Fold for ReplaceEmit {
83 fn fold_macro(&mut self, mut i: Macro) -> Macro {
84 let name = i.path.clone().into_token_stream().to_string();
85
86 if "emit" == &*name {
87 let args: Punctuated<Expr, Token![,]> = parse_args(i.tokens);
88 let args: TokenStream = args
89 .into_pairs()
90 .flat_map(|arg| arg.into_token_stream())
91 .collect();
92
93 let is_emit = self.emit;
94 i = parse_quote!(emit_node_inner!(emitter, #is_emit, #args));
95 }
96
97 i
98 }
99}
100
101fn parse_args<T, P>(tokens: TokenStream) -> Punctuated<T, P>
102where
103 T: Parse,
104 P: Parse + Token,
105{
106 let parser = Punctuated::parse_separated_nonempty;
107 parser.parse2(tokens).expect("failed parse args")
108}