mirror of
https://github.com/tuono-labs/tuono
synced 2026-07-25 12:52:47 -07:00
feat: tuono new command
This commit is contained in:
@@ -18,12 +18,15 @@ name = "tuono"
|
||||
path = "src/lib.rs"
|
||||
|
||||
[dependencies]
|
||||
clap = { version = "4.5.4", features = ["derive"] }
|
||||
clap = { version = "4.5.4", features = ["derive", "cargo"] }
|
||||
watchexec = "4.0.0"
|
||||
miette = "7.2.0"
|
||||
watchexec-signals = "3.0.0"
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
serde = { version = "1.0.202", features = ["derive"] }
|
||||
watchexec-supervisor = "2.0.0"
|
||||
glob = "0.3.1"
|
||||
regex = "1.10.4"
|
||||
reqwest = {version = "0.12.4", features =["blocking", "json"]}
|
||||
serde_json = "1.0"
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ mod source_builder;
|
||||
use source_builder::{bundle_axum_source, create_client_entry_files};
|
||||
|
||||
use self::source_builder::check_tuono_folder;
|
||||
mod scaffold_project;
|
||||
mod watch;
|
||||
|
||||
#[derive(Subcommand, Debug)]
|
||||
@@ -14,7 +15,7 @@ enum Actions {
|
||||
/// Build the production assets
|
||||
Build,
|
||||
/// Scaffold a new project
|
||||
New,
|
||||
New { folder_name: Option<String> },
|
||||
}
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
@@ -45,8 +46,8 @@ pub fn cli() -> std::io::Result<()> {
|
||||
let mut vite_build = Command::new("./node_modules/.bin/tuono-build-prod");
|
||||
let _ = &vite_build.output()?;
|
||||
}
|
||||
Actions::New => {
|
||||
println!("Scaffold new project")
|
||||
Actions::New { folder_name } => {
|
||||
scaffold_project::create_new_project(folder_name);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,165 @@
|
||||
use clap::crate_version;
|
||||
use reqwest::blocking;
|
||||
use serde::Deserialize;
|
||||
use std::env;
|
||||
use std::fs::{self, create_dir, File, OpenOptions};
|
||||
use std::io::{self, prelude::*};
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
const GITHUB_TUONO_REPO_URL: &str =
|
||||
"https://api.github.com/repos/Valerioageno/tuono/git/trees/main?recursive=1";
|
||||
|
||||
const GITHUB_RAW_CONTENT_URL: &str = "https://raw.githubusercontent.com/Valerioageno/tuono/main/";
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
enum GithubFileType {
|
||||
#[serde(rename = "blob")]
|
||||
Blob,
|
||||
#[serde(rename = "tree")]
|
||||
Tree,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
struct GithubResponse<T> {
|
||||
tree: Vec<T>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
struct GithubFile {
|
||||
path: String,
|
||||
#[serde(rename(deserialize = "type"))]
|
||||
element_type: GithubFileType,
|
||||
}
|
||||
|
||||
fn create_file(path: PathBuf, content: String) -> std::io::Result<()> {
|
||||
let mut file = File::create(path)?;
|
||||
let _ = file.write_all(content.as_bytes());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn create_new_project(folder_name: Option<String>) {
|
||||
let folder = folder_name.unwrap_or(".".to_string());
|
||||
|
||||
if folder != "." {
|
||||
create_dir(&folder).unwrap();
|
||||
}
|
||||
|
||||
let client = blocking::Client::builder()
|
||||
.user_agent("")
|
||||
.build()
|
||||
.expect("Failed to build reqwest client");
|
||||
|
||||
let res = client
|
||||
.get(GITHUB_TUONO_REPO_URL)
|
||||
.send()
|
||||
.expect("Failed to call the folder github API")
|
||||
.json::<GithubResponse<GithubFile>>()
|
||||
.expect("Failed to parse the repo structure");
|
||||
|
||||
let new_project_files = res
|
||||
.tree
|
||||
.iter()
|
||||
.filter(|GithubFile { path, .. }| {
|
||||
// TODO: Handle custom example download by CLI argument --template
|
||||
if path.starts_with("examples/tuono/") {
|
||||
return true;
|
||||
}
|
||||
false
|
||||
})
|
||||
.collect::<Vec<&GithubFile>>();
|
||||
|
||||
let folder_name = PathBuf::from(&folder);
|
||||
let current_dir = env::current_dir().expect("Failed to get current working directory");
|
||||
|
||||
let folder_path = current_dir.join(folder_name);
|
||||
|
||||
create_directories(&new_project_files, &folder_path).expect("Failed to create directories");
|
||||
|
||||
for GithubFile {
|
||||
element_type, path, ..
|
||||
} in new_project_files.iter()
|
||||
{
|
||||
if let GithubFileType::Blob = element_type {
|
||||
let file_content = client
|
||||
.get(format!("{GITHUB_RAW_CONTENT_URL}{path}"))
|
||||
.send()
|
||||
.expect("Failed to call the folder github API")
|
||||
.text()
|
||||
.expect("Failed to parse the repo structure");
|
||||
|
||||
let path = PathBuf::from(&path.replace("examples/tuono/", ""));
|
||||
|
||||
let file_path = folder_path.join(&path);
|
||||
|
||||
create_file(file_path, file_content).expect("failed to create file");
|
||||
}
|
||||
}
|
||||
|
||||
update_package_json_version(&folder_path).expect("Failed to update package.json version");
|
||||
update_cargo_toml_version(&folder_path).expect("Failed to update Cargo.toml version");
|
||||
outro(folder);
|
||||
}
|
||||
|
||||
fn create_directories(new_project_files: &Vec<&GithubFile>, folder_path: &Path) -> io::Result<()> {
|
||||
for GithubFile {
|
||||
element_type, path, ..
|
||||
} in new_project_files.iter()
|
||||
{
|
||||
if let GithubFileType::Tree = element_type {
|
||||
let path = PathBuf::from(&path.replace("examples/tuono/", ""));
|
||||
|
||||
let dir_path = folder_path.join(&path);
|
||||
create_dir(&dir_path).unwrap();
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn update_package_json_version(folder_path: &Path) -> io::Result<()> {
|
||||
let v = crate_version!();
|
||||
let package_json_path = folder_path.join(PathBuf::from("package.json"));
|
||||
let package_json = fs::read_to_string(&package_json_path)?;
|
||||
let package_json = package_json.replace("workspace:*", v);
|
||||
|
||||
let mut file = OpenOptions::new()
|
||||
.write(true)
|
||||
.truncate(true)
|
||||
.open(package_json_path)?;
|
||||
|
||||
file.write_all(package_json.as_bytes())?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn update_cargo_toml_version(folder_path: &Path) -> io::Result<()> {
|
||||
let v = crate_version!();
|
||||
let cargo_toml_path = folder_path.join(PathBuf::from("Cargo.toml"));
|
||||
let cargo_toml = fs::read_to_string(&cargo_toml_path)?;
|
||||
let cargo_toml =
|
||||
cargo_toml.replace("{ path = \"../../crates/tuono_lib/\"}", &format!("\"{v}\""));
|
||||
|
||||
let mut file = OpenOptions::new()
|
||||
.write(true)
|
||||
.truncate(true)
|
||||
.open(cargo_toml_path)?;
|
||||
|
||||
file.write_all(cargo_toml.as_bytes())?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn outro(folder_name: String) {
|
||||
println!("Success! 🎉");
|
||||
|
||||
if folder_name != "." {
|
||||
println!("\nGo to the project directory:");
|
||||
println!("cd {folder_name}/");
|
||||
}
|
||||
|
||||
println!("\nInstall the dependencies:");
|
||||
println!("pnpm install");
|
||||
|
||||
println!("\nRun the local environment:");
|
||||
println!("tuono dev");
|
||||
}
|
||||
Reference in New Issue
Block a user