modrpc_codegen/codegen/js/
project.rs1use 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 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 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 fs::write(
68 pkg_root.join("tsconfig.json"),
69 PROTO_TSCONFIG_JSON.as_bytes(),
70 )?;
71
72 fs::write(src_dir.join("index.ts"), PROTO_INDEX_TS.as_bytes())?;
74
75 let mut proto_tokens = genco::lang::js::Tokens::new();
77 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 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}