modrpc_codegen/codegen/js/
project.rs

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