tauri_specta/lang/
js.rs

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
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
use std::path::Path;

use heck::ToLowerCamelCase;
use specta::datatype::FunctionResultVariant;
use specta_typescript::{js_doc, ExportError, Typescript};

use crate::{ExportContext, LanguageExt};

use super::js_ts;

const GLOBALS: &str = include_str!("./globals.js");

impl LanguageExt for specta_jsdoc::JSDoc {
    type Error = ExportError;

    fn render(&self, cfg: &ExportContext) -> Result<String, Self::Error> {
        let dependant_types = cfg
            .type_map
            .into_iter()
            .map(|(_sid, ndt)| js_doc::typedef_named_datatype(&self.0, ndt, &cfg.type_map))
            .collect::<Result<Vec<_>, _>>()
            .map(|v| v.join("\n"))?;

        js_ts::render_all_parts::<Self>(
            cfg,
            &dependant_types,
            GLOBALS,
            &self.0.header,
            render_commands(&self.0, cfg)?,
            render_events(&self.0, cfg)?,
            false,
        )
    }

    fn format(&self, path: &Path) -> Result<(), Self::Error> {
        if let Some(formatter) = self.0.formatter {
            formatter(path)?;
        }
        Ok(())
    }
}

fn render_commands(ts: &Typescript, cfg: &ExportContext) -> Result<String, ExportError> {
    let commands = cfg
        .commands
        .iter()
        .map(|function| {
            let jsdoc = {
                let ret_type =
                    js_ts::handle_result(function, &cfg.type_map, ts, cfg.error_handling)?;

                let mut builder = js_doc::Builder::default();

                if let Some(d) = function.deprecated() {
                    builder.push_deprecated(d);
                }

                if !function.docs().is_empty() {
                    builder.extend(function.docs().split("\n"));
                }

                builder.extend(function.args().flat_map(|(name, typ)| {
                    specta_typescript::datatype(
                        ts,
                        &FunctionResultVariant::Value(typ.clone()),
                        &cfg.type_map,
                    )
                    .map(|typ| {
                        let name = name.to_lower_camel_case();

                        format!("@param {{ {typ} }} {name}")
                    })
                }));
                builder.push(&format!("@returns {{ Promise<{ret_type}> }}"));

                builder.build()
            };

            Ok(js_ts::function(
                &jsdoc,
                &function.name().to_lower_camel_case(),
                // TODO: Don't `collect` the whole thing
                &js_ts::arg_names(&function.args().cloned().collect::<Vec<_>>()),
                None,
                &js_ts::command_body(&cfg.plugin_name, &function, false, cfg.error_handling),
            ))
        })
        .collect::<Result<Vec<_>, ExportError>>()?
        .join(",\n");

    Ok(format!(
        r#"export const commands = {{
        {commands}
    }}"#
    ))
}

fn render_events(ts: &Typescript, cfg: &ExportContext) -> Result<String, ExportError> {
    if cfg.events.is_empty() {
        return Ok(Default::default());
    }

    let (events_types, events_map) =
        js_ts::events_data(&cfg.events, ts, &cfg.plugin_name, &cfg.type_map)?;

    let events = {
        let mut builder = js_doc::Builder::default();

        builder.push("@type {typeof __makeEvents__<{");
        builder.extend(events_types);
        builder.push("}>}");

        builder.build()
    };

    Ok(format! {
        r#"
    {events}
    const __typedMakeEvents__ = __makeEvents__;

    export const events = __typedMakeEvents__({{
    {events_map}
    }})"#
    })
}