mirror of
https://github.com/tuono-labs/tuono
synced 2026-07-25 12:52:47 -07:00
feat: load config in tuono CLI (#399)
This commit is contained in:
@@ -30,6 +30,7 @@ reqwest = { version = "0.12.4", features = ["blocking", "json"] }
|
||||
serde_json = "1.0"
|
||||
fs_extra = "1.3.0"
|
||||
http = "1.1.0"
|
||||
tuono_internal = {path = "../tuono_internal", version = "0.17.3"}
|
||||
|
||||
[dev-dependencies]
|
||||
tempfile = "3.14.0"
|
||||
|
||||
+49
-4
@@ -1,3 +1,4 @@
|
||||
use crate::mode::Mode;
|
||||
use glob::glob;
|
||||
use glob::GlobError;
|
||||
use http::Method;
|
||||
@@ -11,6 +12,7 @@ use std::path::PathBuf;
|
||||
use std::process::Child;
|
||||
use std::process::Command;
|
||||
use std::process::Stdio;
|
||||
use tuono_internal::config::Config;
|
||||
|
||||
use crate::route::Route;
|
||||
|
||||
@@ -33,11 +35,12 @@ const BUILD_JS_SCRIPT: &str = "./node_modules/.bin/tuono-build-prod";
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
const BUILD_TUONO_CONFIG: &str = "./node_modules/.bin/tuono-build-config";
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct App {
|
||||
pub route_map: HashMap<String, Route>,
|
||||
pub base_path: PathBuf,
|
||||
pub has_app_state: bool,
|
||||
pub config: Option<Config>,
|
||||
}
|
||||
|
||||
fn has_app_state(base_path: PathBuf) -> std::io::Result<bool> {
|
||||
@@ -56,6 +59,7 @@ impl App {
|
||||
route_map: HashMap::new(),
|
||||
base_path: base_path.clone(),
|
||||
has_app_state: has_app_state(base_path).unwrap_or(false),
|
||||
config: None,
|
||||
};
|
||||
|
||||
app.collect_routes();
|
||||
@@ -138,6 +142,38 @@ impl App {
|
||||
self.route_map.iter().any(|(_, route)| route.is_dynamic)
|
||||
}
|
||||
|
||||
pub fn check_server_availability(&self, mode: Mode) {
|
||||
// At this point the config should be available
|
||||
let config = self.config.as_ref().unwrap();
|
||||
|
||||
let rust_listener =
|
||||
std::net::TcpListener::bind(format!("{}:{}", config.server.host, config.server.port));
|
||||
|
||||
if let Err(_e) = rust_listener {
|
||||
eprintln!("Error: Failed to bind to port {}", config.server.port);
|
||||
eprintln!(
|
||||
"Please ensure that port {} is not already in use by another process or application.",
|
||||
config.server.port
|
||||
);
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
||||
if mode == Mode::Dev {
|
||||
let vite_port = config.server.port + 1;
|
||||
let vite_listener =
|
||||
std::net::TcpListener::bind(format!("{}:{}", config.server.host, vite_port));
|
||||
|
||||
if let Err(_e) = vite_listener {
|
||||
eprintln!("Error: Failed to bind to port {}", vite_port);
|
||||
eprintln!(
|
||||
"Please ensure that port {} is not already in use by another process or application.",
|
||||
vite_port
|
||||
);
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn build_react_prod(&self) {
|
||||
if !Path::new(BUILD_JS_SCRIPT).exists() {
|
||||
eprintln!("Failed to find the build script. Please run `npm install`");
|
||||
@@ -163,16 +199,25 @@ impl App {
|
||||
.expect("Failed to run the rust server")
|
||||
}
|
||||
|
||||
pub fn build_tuono_config(&self) -> Result<std::process::Output, std::io::Error> {
|
||||
pub fn build_tuono_config(&mut self) -> Result<std::process::Output, std::io::Error> {
|
||||
if !Path::new(BUILD_TUONO_CONFIG).exists() {
|
||||
eprintln!("Failed to find the build script. Please run `npm install`");
|
||||
std::process::exit(1);
|
||||
}
|
||||
Command::new(BUILD_TUONO_CONFIG)
|
||||
let config_build_command = Command::new(BUILD_TUONO_CONFIG)
|
||||
.stdin(Stdio::piped())
|
||||
.stdout(Stdio::piped())
|
||||
.stderr(Stdio::piped())
|
||||
.output()
|
||||
.output();
|
||||
|
||||
if let Ok(config) = Config::get() {
|
||||
self.config = Some(config);
|
||||
} else {
|
||||
eprintln!("[CLI] Failed to read tuono.config.ts");
|
||||
std::process::exit(1);
|
||||
};
|
||||
|
||||
config_build_command
|
||||
}
|
||||
pub fn get_used_http_methods(&self) -> HashSet<Method> {
|
||||
let mut acc = HashSet::new();
|
||||
|
||||
+10
-35
@@ -11,9 +11,6 @@ use crate::scaffold_project;
|
||||
use crate::source_builder::{bundle_axum_source, check_tuono_folder, create_client_entry_files};
|
||||
use crate::watch;
|
||||
|
||||
const TUONO_PORT: u16 = 3000;
|
||||
const VITE_PORT: u16 = 3001;
|
||||
|
||||
#[derive(Subcommand, Debug)]
|
||||
enum Actions {
|
||||
/// Start the development environment
|
||||
@@ -54,47 +51,22 @@ fn init_tuono_folder(mode: Mode) -> std::io::Result<App> {
|
||||
Ok(app)
|
||||
}
|
||||
|
||||
fn check_ports(mode: Mode) {
|
||||
let rust_listener = std::net::TcpListener::bind(format!("0.0.0.0:{TUONO_PORT}"));
|
||||
|
||||
if let Err(_e) = rust_listener {
|
||||
eprintln!("Error: Failed to bind to port {}", TUONO_PORT);
|
||||
eprintln!(
|
||||
"Please ensure that port {} is not already in use by another process or application.",
|
||||
TUONO_PORT
|
||||
);
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
||||
if mode == Mode::Dev {
|
||||
let vite_listener = std::net::TcpListener::bind(format!("0.0.0.0:{VITE_PORT}"));
|
||||
|
||||
if let Err(_e) = vite_listener {
|
||||
eprintln!("Error: Failed to bind to port {}", VITE_PORT);
|
||||
eprintln!(
|
||||
"Please ensure that port {} is not already in use by another process or application.",
|
||||
VITE_PORT
|
||||
);
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn app() -> std::io::Result<()> {
|
||||
let args = Args::parse();
|
||||
|
||||
match args.action {
|
||||
Actions::Dev => {
|
||||
check_ports(Mode::Dev);
|
||||
let mut app = init_tuono_folder(Mode::Dev)?;
|
||||
|
||||
let app = init_tuono_folder(Mode::Dev)?;
|
||||
app.build_tuono_config()
|
||||
.expect("Failed to build tuono.config.ts");
|
||||
|
||||
app.check_server_availability(Mode::Dev);
|
||||
|
||||
watch::watch().unwrap();
|
||||
}
|
||||
Actions::Build { ssg, no_js_emit } => {
|
||||
let app = init_tuono_folder(Mode::Prod)?;
|
||||
let mut app = init_tuono_folder(Mode::Prod)?;
|
||||
|
||||
if no_js_emit {
|
||||
println!("Rust build successfully finished");
|
||||
@@ -110,11 +82,11 @@ pub fn app() -> std::io::Result<()> {
|
||||
app.build_tuono_config()
|
||||
.expect("Failed to build tuono.config.ts");
|
||||
|
||||
app.check_server_availability(Mode::Prod);
|
||||
|
||||
app.build_react_prod();
|
||||
|
||||
if ssg {
|
||||
check_ports(Mode::Prod);
|
||||
|
||||
println!("SSG: generation started");
|
||||
|
||||
let static_dir = PathBuf::from("out/static");
|
||||
@@ -145,8 +117,11 @@ pub fn app() -> std::io::Result<()> {
|
||||
// Wait for server
|
||||
let mut is_server_ready = false;
|
||||
|
||||
let config = app.config.as_ref().unwrap();
|
||||
|
||||
while !is_server_ready {
|
||||
let server_url = format!("http://localhost:{}", TUONO_PORT);
|
||||
let server_url =
|
||||
format!("http://{}:{}", config.server.host, config.server.port);
|
||||
if reqwest.get(server_url).send().is_ok() {
|
||||
is_server_ready = true
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#[derive(PartialEq, Eq)]
|
||||
#[derive(PartialEq, Eq, Debug, Clone)]
|
||||
pub enum Mode {
|
||||
Prod,
|
||||
Dev,
|
||||
|
||||
@@ -7,6 +7,12 @@ use utils::TempTuonoProject;
|
||||
const POST_API_FILE: &str = r"#[tuono_lib::api(POST)]";
|
||||
const GET_API_FILE: &str = r"#[tuono_lib::api(GET)]";
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
const BUILD_TUONO_CONFIG: &str = ".\\node_modules\\.bin\\tuono-build-config.cmd";
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
const BUILD_TUONO_CONFIG: &str = "./node_modules/.bin/tuono-build-config";
|
||||
|
||||
#[test]
|
||||
#[serial]
|
||||
fn it_successfully_create_the_index_route() {
|
||||
@@ -144,7 +150,7 @@ fn it_successfully_create_catch_all_routes() {
|
||||
#[test]
|
||||
#[serial]
|
||||
fn it_fails_without_installed_build_config_script() {
|
||||
TempTuonoProject::new();
|
||||
let _guard = TempTuonoProject::new();
|
||||
|
||||
let mut test_tuono_build = Command::cargo_bin("tuono").unwrap();
|
||||
test_tuono_build
|
||||
@@ -158,12 +164,10 @@ fn it_fails_without_installed_build_config_script() {
|
||||
#[serial]
|
||||
fn it_fails_without_installed_build_script() {
|
||||
let temp_tuono_project = TempTuonoProject::new();
|
||||
|
||||
temp_tuono_project
|
||||
.add_file_with_content("./node_modules/.bin/tuono-build-config", "#!/bin/bash");
|
||||
temp_tuono_project.add_file_with_content(BUILD_TUONO_CONFIG, "#!/bin/bash");
|
||||
Command::new("chmod")
|
||||
.arg("+x")
|
||||
.arg("./node_modules/.bin/tuono-build-config")
|
||||
.arg(BUILD_TUONO_CONFIG)
|
||||
.assert()
|
||||
.success();
|
||||
let mut test_tuono_build = Command::cargo_bin("tuono").unwrap();
|
||||
@@ -171,5 +175,5 @@ fn it_fails_without_installed_build_script() {
|
||||
.arg("build")
|
||||
.assert()
|
||||
.failure()
|
||||
.stderr("Failed to find the build script. Please run `npm install`\n");
|
||||
.stderr("[CLI] Failed to read tuono.config.ts\n");
|
||||
}
|
||||
|
||||
@@ -3,13 +3,13 @@ use std::fs::read_to_string;
|
||||
use std::io;
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[derive(Deserialize, Debug, Clone)]
|
||||
pub struct ServerConfig {
|
||||
pub host: String,
|
||||
pub port: u16,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[derive(Deserialize, Debug, Clone)]
|
||||
pub struct Config {
|
||||
pub server: ServerConfig,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user