Skip to main content

modrpc_codegen/codegen/js/
project.rs

1use std::path::Path;
2use std::fs;
3use std::io::Write;
4
5use genco::{self, prelude::*};
6
7use crate::{
8    ast::Schema,
9    codegen,
10    Database,
11};
12
13const PROTO_PACKAGE_JSON: &'static str = include_str!("templates/proto/package.json");
14const PROTO_TSCONFIG_JSON: &'static str = include_str!("templates/proto/tsconfig.json");
15const PROTO_INDEX_TS: &'static str = include_str!("templates/proto/index.ts");
16
17pub fn js_project_gen(
18    root_dir: impl AsRef<Path>,
19    project_name: &str,
20    schema: &Schema,
21) -> std::io::Result<()> {
22    let pkg_name = &format!("{}-modrpc", project_name);
23    let pkg_root = root_dir.as_ref().join(pkg_name).join("typescript");
24
25    fs::create_dir_all(&pkg_root)?;
26
27    js_proto_package_gen(&pkg_root, &format!("{}-modrpc", project_name), schema)?;
28
29    Ok(())
30}
31
32pub fn js_proto_package_gen(
33    pkg_root: impl AsRef<Path>,
34    pkg_name: &str,
35    schema: &Schema,
36) -> std::io::Result<()> {
37    let pkg_root = pkg_root.as_ref();
38
39    let local_mproto_module = mproto_codegen::Module::from_type_defs(
40        schema.type_defs.clone(),
41    );
42    let mproto_db = mproto_codegen::Database::new(local_mproto_module);
43
44    let mut db = Database::new(mproto_db);
45    codegen::load_imports_recursive(&mut db, schema).unwrap();
46    for interface in &schema.interfaces {
47        let _ = db.local().add_interface(interface.clone());
48
49        // Define per-interface *InitState and per-role *Config structs
50        codegen::define_interface_config_and_state_structs(
51            &mut db,
52            interface,
53        );
54    }
55
56    let src_dir = pkg_root.join("src");
57
58    fs::create_dir_all(&pkg_root)?;
59    fs::create_dir_all(&src_dir)?;
60
61    // Write package.json
62    let package_json_path = pkg_root.join("package.json");
63    if !package_json_path.exists() {
64        fs::write(
65            package_json_path,
66            PROTO_PACKAGE_JSON.replace("PKG_NAME", pkg_name).as_bytes(),
67        )?;
68    } else {
69        println!(
70            "{} already exists - skipping as it might contain hand-written code.",
71            package_json_path.display(),
72        );
73    }
74
75    // Write tsconfig.json
76    fs::write(pkg_root.join("tsconfig.json"), PROTO_TSCONFIG_JSON.as_bytes())?;
77
78    // Write index.ts
79    fs::write(src_dir.join("index.ts"), PROTO_INDEX_TS.as_bytes())?;
80
81    // Write proto.ts
82    let mut proto_tokens = genco::lang::js::Tokens::new();
83    /*for interface in &schema.interfaces {
84        proto_tokens = quote! {
85            $proto_tokens
86
87            $(codegen::js::js_interface_init_state_tokens(&mut db, interface))
88        };
89    }*/
90    for type_def in db.mproto_db().local().type_defs() {
91        let mproto_cx = &mproto_codegen::codegen::CodegenCx::new(db.mproto_db(), None, true);
92        proto_tokens = quote! {
93            $proto_tokens
94
95            $(mproto_codegen::codegen::js::js_type_def(mproto_cx, type_def))
96        };
97    }
98    write_js_file(
99        src_dir.join("proto.ts"),
100        "",
101        &proto_tokens
102    )?;
103
104    // Write objects.ts
105    let mut object_class_tokens = quote! {
106        import * as mproto from "mproto";
107    };
108    for interface in &schema.interfaces {
109        if interface.methods.len() == 0 { continue; }
110
111        for role_name in &interface.roles {
112            object_class_tokens = quote! {
113                $object_class_tokens
114
115                $(codegen::js::js_wit_object_class(&db, interface, role_name))
116            };
117        }
118    }
119    write_js_file(
120        src_dir.join("objects.ts"),
121        "",
122        &object_class_tokens
123    )?;
124
125    Ok(())
126}
127
128fn write_js_file(
129    path: impl AsRef<Path> + std::fmt::Debug,
130    header: &str,
131    tokens: &genco::lang::js::Tokens,
132) -> std::io::Result<()> {
133    let fmt = genco::fmt::Config::from_lang::<genco::lang::JavaScript>()
134        .with_indentation(genco::fmt::Indentation::Space(2));
135    let config = genco::lang::js::Config::default();
136    let mut file = fs::File::create(&path)?;
137
138    file.write_all(header.as_bytes())?;
139
140    let mut w = genco::fmt::IoWriter::new(file);
141    tokens
142        .format_file(&mut w.as_formatter(&fmt), &config)
143        .expect(&format!("format {:?} file", path));
144
145    Ok(())
146}