swc_ecma_codegen_macros/
lib.rs

1extern 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///
13///
14/// # Example
15///
16/// ```ignore
17/// impl MacroNode for ParamOrTsParamProp {
18///     fn emit(&mut self, emitter: &mut Macro) -> Result {
19///         match self {
20///             ParamOrTsParamProp::Param(n) => emit!(n),
21///             ParamOrTsParamProp::TsParamProp(n) => emit!(n),
22///         }
23///     }
24/// }
25/// ```
26///
27///
28/// ## `emit!()`.
29///
30/// `emit!()` macro in `#[node_impl]` functions are special.
31///
32/// Those are replaced with `emit_with` and `adjust_span` methods, depending on
33/// the context.
34#[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
60/// Returns `(emitter_method, adjuster_method)`
61fn 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}