use std::path::{Path, PathBuf};
use anyhow::{Context, Result};
use fraiseql_core::schema::CompiledSchema;
#[derive(Debug, Clone, Copy)]
pub enum ClientLanguage {
TypeScript,
}
pub fn run(
language: ClientLanguage,
schema_path: Option<&Path>,
out_dir: &Path,
force: bool,
) -> Result<()> {
let schema_path = match schema_path {
Some(p) => p.to_path_buf(),
None => auto_detect_compiled_schema()?,
};
let raw = std::fs::read_to_string(&schema_path)
.with_context(|| format!("Failed to read compiled schema {}", schema_path.display()))?;
let schema: CompiledSchema = serde_json::from_str(&raw).with_context(|| {
format!("Failed to parse {} as a compiled schema", schema_path.display())
})?;
let files = match language {
ClientLanguage::TypeScript => fraiseql_codegen::client::typescript::generate(&schema)
.map_err(|e| anyhow::anyhow!("client generation failed: {e}"))?,
};
if out_dir.exists() && !force && contains_generated_client(out_dir) {
anyhow::bail!(
"Output directory {} already contains a generated client. Pass --force to overwrite.",
out_dir.display()
);
}
for (rel_path, content) in &files {
let full_path = out_dir.join(rel_path);
if let Some(parent) = full_path.parent() {
std::fs::create_dir_all(parent)
.with_context(|| format!("Failed to create {}", parent.display()))?;
}
std::fs::write(&full_path, content)
.with_context(|| format!("Failed to write {}", full_path.display()))?;
println!(" wrote {}", full_path.display());
}
println!(
"\nGenerated {} files into {}. Type-check with `tsc --strict --noEmit {}/index.ts`.",
files.len(),
out_dir.display(),
out_dir.display(),
);
Ok(())
}
fn contains_generated_client(out_dir: &Path) -> bool {
let marker = out_dir.join("types.ts");
std::fs::read_to_string(marker)
.is_ok_and(|content| content.contains("AUTO-GENERATED by fraiseql-codegen"))
}
fn auto_detect_compiled_schema() -> Result<PathBuf> {
let candidates = [
"schema.compiled.json",
"target/fraiseql/schema.compiled.json",
"build/schema.compiled.json",
];
for candidate in candidates {
let path = PathBuf::from(candidate);
if path.exists() {
return Ok(path);
}
}
anyhow::bail!(
"No compiled schema found. Compile first (`fraiseql compile`) or pass an explicit path: \
fraiseql generate-client typescript --schema <path> --out <dir>"
)
}