1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
/// Reexported for convinience.
use abi_stable::{
    library::RootModule,
    package_version_strings,
    sabi_types::VersionStrings,
    std_types::{RResult, RStr, RString},
    StableAbi,
};
use anyhow::Context;
use serde::de::DeserializeOwned;
use swc_ecma_ast::Program;

#[repr(C)]
#[derive(StableAbi)]
#[sabi(kind(Prefix(prefix_ref = "SwcPluginRef")))]
#[sabi(missing_field(panic))]
pub struct SwcPlugin {
    #[sabi(last_prefix_field)]
    pub process_js:
        Option<extern "C" fn(config_str: RStr, ast_json: RString) -> RResult<RString, RString>>,
}

impl RootModule for SwcPluginRef {
    abi_stable::declare_root_module_statics! {SwcPluginRef}

    const BASE_NAME: &'static str = "swc_plugin";
    const NAME: &'static str = "swc_plugin";
    const VERSION_STRINGS: VersionStrings = package_version_strings!();
}

#[doc(hidden)]
pub fn invoke_js_plugin<C, F>(
    op: fn(C) -> F,
    config_json: RStr,
    ast_json: RString,
) -> RResult<RString, RString>
where
    C: DeserializeOwned,
    F: swc_ecma_visit::Fold,
{
    use swc_ecma_visit::FoldWith;

    let config = serde_json::from_str(config_json.as_str())
        .context("failed to deserialize config string as json");
    let config: C = match config {
        Ok(v) => v,
        Err(err) => return RResult::RErr(format!("{:?}", err).into()),
    };

    let ast =
        serde_json::from_str(ast_json.as_str()).context("failed to deserialize ast string as json");
    let ast: Program = match ast {
        Ok(v) => v,
        Err(err) => return RResult::RErr(format!("{:?}", err).into()),
    };

    let mut tr = op(config);

    let ast = ast.fold_with(&mut tr);

    let res = match serde_json::to_string(&ast) {
        Ok(v) => v,
        Err(err) => {
            return RResult::RErr(
                format!(
                    "failed to serialize swc_ecma_ast::Program as json: {:?}",
                    err
                )
                .into(),
            )
        }
    };

    RResult::ROk(res.into())
}

#[macro_export]
macro_rules! define_js_plugin {
    ($fn_name:ident) => {
        #[abi_stable::export_root_module]
        pub fn swc_library() -> $crate::SwcPluginRef {
            extern "C" fn swc_js_plugin(
                config_json: abi_stable::std_types::RStr,
                ast_json: abi_stable::std_types::RString,
            ) -> abi_stable::std_types::RResult<
                abi_stable::std_types::RString,
                abi_stable::std_types::RString,
            > {
                $crate::invoke_js_plugin($fn_name, config_json, ast_json)
            }
            use abi_stable::prefix_type::PrefixTypeTrait;

            $crate::SwcPlugin {
                process_js: Some(swc_js_plugin),
            }
            .leak_into_prefix()
        }
    };
}