teo_generator/admin/
mod.rs

1pub mod sign_in_index_ts;
2pub mod sign_in_keys_ts;
3pub mod preferences_ts;
4pub mod default_preferences_ts;
5pub mod sign_in_form_tsx;
6pub mod translations_index_ts;
7pub mod translations_init_ts;
8pub mod translations_lang_index_ts;
9pub mod translations_languages_ts;
10pub mod pages_index_index_ts;
11pub mod pages_page_stack_default_item_keys_tsx;
12pub mod pages_render_default_stack_item_tsx;
13pub mod pages_page_dashboard;
14pub mod pages_page_form;
15pub mod pages_page_form_page;
16pub mod pages_page_index;
17pub mod pages_page_records;
18pub mod pages_page_records_list;
19pub mod webpack_config_ts;
20pub mod enum_definitions_ts;
21
22use inflector::Inflector;
23use itertools::Itertools;
24use teo_runtime::config::admin::Admin;
25use teo_runtime::namespace::Namespace;
26use teo_result::Result;
27use serde::Deserialize;
28use serde_json::json;
29use teo_runtime::config::client::{Client, ClientLanguage, TypeScriptHTTPProvider};
30use teo_runtime::config::server::Server;
31use once_cell::sync::Lazy;
32use crate::admin::default_preferences_ts::generate_default_preferences_ts;
33use crate::admin::enum_definitions_ts::generate_enum_definitions_ts;
34use crate::admin::pages_index_index_ts::generate_pages_index_index_ts;
35use crate::admin::pages_page_dashboard::generate_pages_page_dashboard_tsx;
36use crate::admin::pages_page_form::generate_pages_page_form_tsx;
37use crate::admin::pages_page_form_page::generate_pages_page_form_page_tsx;
38use crate::admin::pages_page_index::generate_pages_page_index_tsx;
39use crate::admin::pages_page_records::generate_pages_page_records_tsx;
40use crate::admin::pages_page_records_list::generate_pages_page_records_list_tsx;
41use crate::admin::pages_page_stack_default_item_keys_tsx::generate_pages_stack_default_item_keys_tsx;
42use crate::admin::pages_render_default_stack_item_tsx::generate_pages_render_default_stack_item_tsx;
43use crate::admin::preferences_ts::generate_preferences_ts;
44use crate::admin::sign_in_form_tsx::generate_sign_in_form_tsx;
45use crate::admin::sign_in_index_ts::generate_sign_in_index_ts;
46use crate::admin::sign_in_keys_ts::generate_sign_in_keys_ts;
47use crate::admin::translations_index_ts::generate_translations_index_ts;
48use crate::admin::translations_init_ts::generate_translations_init_ts;
49use crate::admin::translations_lang_index_ts::generate_translations_lang_index_ts;
50use crate::admin::translations_languages_ts::generate_translations_languages_ts;
51use crate::admin::webpack_config_ts::generate_webpack_config_ts;
52use crate::utils::file::FileUtil;
53use crate::utils::update_package_json_version::update_package_json_version;
54
55static FILE_ADDRESS: Lazy<String> = Lazy::new(|| {
56    format!("https://raw.githubusercontent.com/teodevgroup/teo-admin-dev/{}/", env!("CARGO_PKG_VERSION"))
57});
58
59static FILE_JSON: &'static str = ".generator/data/fileList.json";
60
61#[derive(Deserialize)]
62struct FileList {
63    generated: Vec<String>,
64    extended: Vec<String>,
65}
66
67pub async fn generate(main_namespace: &Namespace, admin: &Admin, server: &Server) -> Result<()> {
68    let dest_dir = std::env::current_dir()?.join(admin.dest.as_str());
69    let file_util = FileUtil::new(dest_dir.clone());
70    file_util.ensure_root_directory().await?;
71    // download remote sources
72    let file_list = reqwest::get(FILE_ADDRESS.to_owned() + FILE_JSON)
73        .await?
74        .json::<FileList>()
75        .await?;
76    for extended_file in &file_list.extended {
77        let file_location = dest_dir.join(extended_file);
78        if !file_location.exists() {
79            create_file_from_remote_source(extended_file, &file_util).await?;
80        }
81    }
82    for generated_file in &file_list.generated {
83        create_file_from_remote_source(generated_file, &file_util).await?;
84    }
85    // ensure custom directories
86    let custom_lib = dest_dir.as_path().join("src/lib/custom");
87    let custom_components = dest_dir.as_path().join("src/components/custom");
88    file_util.ensure_directory(custom_lib.as_os_str().to_str().unwrap()).await?;
89    file_util.ensure_directory(custom_components.as_os_str().to_str().unwrap()).await?;
90
91    // dynamic generated files
92
93    // sign in
94    generate_sign_in_index_ts(main_namespace, &file_util).await?;
95    generate_sign_in_keys_ts(main_namespace, &file_util).await?;
96    generate_sign_in_form_tsx(main_namespace, &file_util).await?;
97
98    // preferences
99    generate_preferences_ts(main_namespace, &file_util).await?;
100    generate_default_preferences_ts(main_namespace, &admin.languages, &file_util).await?;
101
102    // enum definitions
103    generate_enum_definitions_ts(main_namespace, &file_util).await?;
104
105    // language
106    // generated
107    create_file_from_remote_source("src/lib/generated/translations/static.ts", &file_util).await?;
108    generate_translations_index_ts(main_namespace, &file_util).await?;
109    generate_translations_init_ts(&admin.languages, &file_util).await?;
110    generate_translations_languages_ts(&admin.languages, &file_util).await?;
111    // extended
112    let index_ts = "src/lib/extended/translations/index.ts";
113    let file_location = dest_dir.join(index_ts);
114    if !file_location.exists() {
115        create_file_from_remote_source(index_ts, &file_util).await?;
116    }
117    for lang in admin.languages.iter() {
118        // generated
119        create_file_from_remote_source(&format!("src/lib/generated/translations/{}/static.ts", lang.as_str()), &file_util).await?;
120        generate_translations_lang_index_ts(lang.as_str(), main_namespace, &file_util).await?;
121        // extended
122        let location = dest_dir.join(format!("src/lib/extended/translations/{}.ts", lang.as_str()));
123        if !file_location.exists() {
124            create_file_from_remote_source(location.to_str().unwrap(), &file_util).await?;
125        }
126    }
127
128    // -- pages
129
130    // _Index
131    generate_pages_index_index_ts(main_namespace, &file_util).await?;
132
133    // Common
134    generate_pages_stack_default_item_keys_tsx(main_namespace, &file_util).await?;
135    generate_pages_render_default_stack_item_tsx(main_namespace, &file_util).await?;
136
137    // Model
138    for m in main_namespace.collect_models(|m| m.data().get("admin:ignore").is_none()) {
139        let model_variable_name = m.path().iter().map(|s| s.to_pascal_case()).join("");
140        let path = m.path().join("/");
141        generate_pages_page_dashboard_tsx(main_namespace, m, &model_variable_name, &path, &file_util).await?;
142        generate_pages_page_form_tsx(main_namespace, m, &model_variable_name, &path, &file_util).await?;
143        generate_pages_page_form_page_tsx(main_namespace, m, &model_variable_name, &path, &file_util).await?;
144        generate_pages_page_index_tsx(main_namespace, m, &model_variable_name, &path, &file_util).await?;
145        generate_pages_page_records_tsx(main_namespace, m, &model_variable_name, &path, &file_util).await?;
146        generate_pages_page_records_list_tsx(main_namespace, m, &model_variable_name, &path, &file_util).await?;
147    }
148    // readme
149    file_util.generate_file("README.md", include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/templates/admin/readme.md.jinja"))).await?;
150
151    // webpack.config.ts
152    generate_webpack_config_ts(server.bind.1.to_string(), &file_util).await?;
153
154    // package.json
155    let remote_json_string = fetch_remote_content("package.json").await?;
156    let remote_json_data: serde_json::Value = serde_json::from_str(&remote_json_string).unwrap();
157    let dependencies = remote_json_data.get("dependencies").unwrap();
158    let mut dev_dependencies = remote_json_data.get("devDependencies").unwrap().clone();
159    dev_dependencies.as_object_mut().unwrap().shift_remove("glob").unwrap();
160    let new_json_data = json!({
161        "name": "admin-dashboard",
162        "version": "0.0.1",
163        "description": "This project is generated with Teo.",
164        "private": true,
165        "scripts": {
166            "start": "npx webpack-dev-server",
167        },
168        "dependencies": dependencies.clone(),
169        "devDependencies": dev_dependencies.clone(),
170    });
171    if file_util.generate_file_if_not_exist("package.json", serde_json::to_string(&new_json_data).unwrap()).await? {
172        // if exists, update package.json with a minor version and deps
173        let json_data = std::fs::read_to_string(file_util.get_file_path("package.json"))
174            .expect("Unable to read package.json");
175        file_util.generate_file("package.json", update_json_version_and_deps(json_data, dependencies, &dev_dependencies)).await?;
176    }
177    // generate TypeScript client
178    crate::client::generate(main_namespace, &Client {
179        provider: ClientLanguage::TypeScript(TypeScriptHTTPProvider::Fetch),
180        dest: dest_dir.as_path().join("src/lib/generated/teo").to_str().unwrap().to_owned(),
181        package: false,
182        host: admin.host.clone(),
183        object_name: "teo".to_owned(),
184        git_commit: false,
185    }).await?;
186    Ok(())
187}
188
189fn update_json_version_and_deps(json_data: String, dependencies: &serde_json::Value, dev_dependencies: &serde_json::Value) -> String {
190    let version_updated_json_data = update_package_json_version(json_data);
191    let mut json_value: serde_json::Value = serde_json::from_str(&version_updated_json_data).unwrap();
192    let deps = json_value.get_mut("dependencies").unwrap();
193    let deps_object = deps.as_object_mut().unwrap();
194    let source_deps = dependencies.as_object().unwrap();
195    for (k, v) in source_deps {
196        if deps_object.get(k).is_none() {
197            deps_object.insert(k.to_owned(), v.clone());
198        }
199    }
200    let dev_deps = json_value.get_mut("devDependencies").unwrap();
201    let dev_deps_object = dev_deps.as_object_mut().unwrap();
202    let source_dev_deps = dev_dependencies.as_object().unwrap();
203    for (k, v) in source_dev_deps {
204        if dev_deps_object.get(k).is_none() {
205            dev_deps_object.insert(k.to_owned(), v.clone());
206        }
207    }
208    serde_json::to_string(&json_value).unwrap()
209}
210
211async fn fetch_remote_content(location: &str) -> Result<String> {
212    Ok(reqwest::get(FILE_ADDRESS.to_owned() + location)
213        .await?
214        .text()
215        .await?)
216}
217
218async fn create_file_from_remote_source(location: &str, file_util: &FileUtil) -> Result<()> {
219    let content = fetch_remote_content(location).await?;
220    file_util.ensure_directory_and_generate_file(location, content).await
221}