commit 028402a0f70d317a6a913a38fa0a4f5b774add73
parent 95d5b062e14aee762b3d3f84d0530894094418f6
Author: triesap <tyson@radroots.org>
Date: Fri, 20 Feb 2026 23:47:35 +0000
build: repoint export make target to `xtask`
Diffstat:
3 files changed, 82 insertions(+), 1 deletion(-)
diff --git a/Makefile b/Makefile
@@ -26,7 +26,7 @@ help:
@printf "%s\n" $(BUILD_TARGETS)
export-ts-sdk-bindings:
- ./scripts/export-ts-sdk-bindings.sh
+ cargo run -q -p xtask -- sdk export-ts
build-tangle-db-wasm:
wasm-pack build crates/tangle-db-wasm --release --target web \
diff --git a/crates/xtask/src/export_ts.rs b/crates/xtask/src/export_ts.rs
@@ -5,6 +5,7 @@ use serde::Serialize;
use sha2::{Digest, Sha256};
use std::fs;
use std::path::{Path, PathBuf};
+use std::process::Command;
#[derive(Serialize)]
struct ExportManifest {
@@ -49,6 +50,19 @@ fn required_artifact_value<'a>(value: &'a Option<String>, field: &str) -> Result
.ok_or_else(|| format!("missing ts artifacts.{field}"))
}
+fn crate_supports_ts_rs(workspace_root: &Path, crate_dir: &str) -> Result<bool, String> {
+ let manifest = workspace_root
+ .join("crates")
+ .join(crate_dir)
+ .join("Cargo.toml");
+ if !manifest.exists() {
+ return Ok(false);
+ }
+ let raw =
+ fs::read_to_string(&manifest).map_err(|e| format!("read {}: {e}", manifest.display()))?;
+ Ok(raw.contains("ts-rs"))
+}
+
fn copy_if_exists(src: &Path, dst: &Path) -> Result<bool, String> {
if !src.exists() {
return Ok(false);
@@ -251,3 +265,59 @@ pub fn write_ts_export_manifest(workspace_root: &Path, out_dir: &Path) -> Result
.map_err(|e| format!("write {}: {e}", manifest_path.display()))?;
Ok(manifest_path)
}
+
+pub fn generate_ts_rs_sources(workspace_root: &Path) -> Result<PathBuf, String> {
+ let bundle = contract::load_contract_bundle(workspace_root)?;
+ contract::validate_contract_bundle(&bundle)?;
+ let ts_export = ts_export_mapping(&bundle)?;
+ let source_root = workspace_root.join("target").join("ts-rs");
+ if source_root.exists() {
+ fs::remove_dir_all(&source_root)
+ .map_err(|e| format!("remove {}: {e}", source_root.display()))?;
+ }
+ fs::create_dir_all(&source_root)
+ .map_err(|e| format!("create {}: {e}", source_root.display()))?;
+ let mut generated = 0usize;
+ for (crate_name, package_name) in &ts_export.packages {
+ if crate_name.ends_with("-wasm") {
+ continue;
+ }
+ let crate_dir = crate_name.strip_prefix("radroots-").unwrap_or(crate_name);
+ if !crate_supports_ts_rs(workspace_root, crate_dir)? {
+ continue;
+ }
+ let package_dir = package_name
+ .strip_prefix("@radroots/")
+ .unwrap_or(package_name);
+ let export_dir = source_root.join(package_dir);
+ fs::create_dir_all(&export_dir)
+ .map_err(|e| format!("create {}: {e}", export_dir.display()))?;
+ let status = Command::new("cargo")
+ .arg("test")
+ .arg("-q")
+ .arg("-p")
+ .arg(crate_name)
+ .arg("--features")
+ .arg("ts-rs")
+ .env("RADROOTS_TS_RS_EXPORT_DIR", &export_dir)
+ .current_dir(workspace_root)
+ .status()
+ .map_err(|e| format!("run cargo test for {crate_name}: {e}"))?;
+ if !status.success() {
+ return Err(format!("cargo test failed for {crate_name}"));
+ }
+ generated += 1;
+ }
+ if generated == 0 {
+ return Err("no ts-rs model sources were generated".to_string());
+ }
+ Ok(source_root)
+}
+
+pub fn export_ts_bundle(workspace_root: &Path, out_dir: &Path) -> Result<PathBuf, String> {
+ generate_ts_rs_sources(workspace_root)?;
+ export_ts_models(workspace_root, out_dir)?;
+ export_ts_constants(workspace_root, out_dir)?;
+ export_ts_wasm_artifacts(workspace_root, out_dir)?;
+ write_ts_export_manifest(workspace_root, out_dir)
+}
diff --git a/crates/xtask/src/main.rs b/crates/xtask/src/main.rs
@@ -9,6 +9,7 @@ use std::process::ExitCode;
fn usage() {
eprintln!("usage:");
+ eprintln!(" cargo xtask sdk export-ts [--out <dir>]");
eprintln!(" cargo xtask sdk export-ts-models [--out <dir>]");
eprintln!(" cargo xtask sdk export-ts-constants [--out <dir>]");
eprintln!(" cargo xtask sdk export-ts-wasm [--out <dir>]");
@@ -69,6 +70,15 @@ fn export_manifest(args: &[String]) -> Result<(), String> {
Ok(())
}
+fn export_ts(args: &[String]) -> Result<(), String> {
+ let root = workspace_root()?;
+ let out_dir = parse_out_dir(args, &root)?;
+ let manifest = export_ts::export_ts_bundle(&root, &out_dir)?;
+ eprintln!("exported ts sdk bundle to {}", out_dir.display());
+ eprintln!("wrote export manifest {}", manifest.display());
+ Ok(())
+}
+
fn validate_contract() -> Result<(), String> {
let root = workspace_root()?;
let bundle = contract::load_contract_bundle(&root)?;
@@ -83,6 +93,7 @@ fn validate_contract() -> Result<(), String> {
fn run_sdk(args: &[String]) -> Result<(), String> {
match args.first().map(String::as_str) {
+ Some("export-ts") => export_ts(&args[1..]),
Some("export-ts-models") => export_ts_models(&args[1..]),
Some("export-ts-constants") => export_ts_constants(&args[1..]),
Some("export-ts-wasm") => export_ts_wasm(&args[1..]),