commit 1050e18ed6f7f3aac073274f0048bf03ffe25a64
parent 0740da72976e88a58166aa1bcb62a5913c779cc3
Author: triesap <tyson@radroots.org>
Date: Sun, 28 Jun 2026 00:14:49 +0000
ts: render constants through dto_bindgen
- Move events constants and kinds onto dto_bindgen TypeScriptModule descriptors.
- Move identity constants onto dto_bindgen constant declarations.
- Remove raw TypeScript source handling from the SDK package output path.
- Add module-backed generated output tests for constants and kinds.
Diffstat:
3 files changed, 99 insertions(+), 19 deletions(-)
diff --git a/crates/identity_bindings/Cargo.toml b/crates/identity_bindings/Cargo.toml
@@ -10,4 +10,5 @@ homepage.workspace = true
publish = false
[dependencies]
+dto_bindgen_backend_ts = { workspace = true }
radroots_identity = { workspace = true }
diff --git a/crates/identity_bindings/src/lib.rs b/crates/identity_bindings/src/lib.rs
@@ -1,13 +1,31 @@
pub use radroots_identity as upstream;
+use dto_bindgen_backend_ts::{TypeScriptDeclaration, TypeScriptModule, TypeScriptValue};
use radroots_identity::{
RADROOTS_USERNAME_MAX_LEN, RADROOTS_USERNAME_MIN_LEN, RADROOTS_USERNAME_REGEX,
};
-pub fn constants_module() -> String {
- format!(
- "export const RADROOTS_USERNAME_MIN_LEN = {RADROOTS_USERNAME_MIN_LEN};\nexport const RADROOTS_USERNAME_MAX_LEN = {RADROOTS_USERNAME_MAX_LEN};\nexport const RADROOTS_USERNAME_REGEX = {RADROOTS_USERNAME_REGEX:?};"
- )
+pub fn constants_module() -> TypeScriptModule {
+ TypeScriptModule::new("src/generated/constants.ts")
+ .with_declaration(TypeScriptDeclaration::constant(
+ "RADROOTS_USERNAME_MIN_LEN",
+ None,
+ usize_value(RADROOTS_USERNAME_MIN_LEN),
+ ))
+ .with_declaration(TypeScriptDeclaration::constant(
+ "RADROOTS_USERNAME_MAX_LEN",
+ None,
+ usize_value(RADROOTS_USERNAME_MAX_LEN),
+ ))
+ .with_declaration(TypeScriptDeclaration::constant(
+ "RADROOTS_USERNAME_REGEX",
+ None,
+ TypeScriptValue::string(RADROOTS_USERNAME_REGEX),
+ ))
+}
+
+fn usize_value(value: usize) -> TypeScriptValue {
+ TypeScriptValue::number(i64::try_from(value).expect("TypeScript constant fits in i64"))
}
#[cfg(test)]
@@ -19,7 +37,7 @@ mod tests {
#[test]
fn preserves_username_constant_exports() {
- let rendered = constants_module();
+ let rendered = constants_module().render_source();
assert!(rendered.contains("RADROOTS_USERNAME_MIN_LEN"));
assert!(rendered.contains(&RADROOTS_USERNAME_MIN_LEN.to_string()));
assert!(rendered.contains("RADROOTS_USERNAME_MAX_LEN"));
diff --git a/tools/sdk_xtask_import/src/output.rs b/tools/sdk_xtask_import/src/output.rs
@@ -23,21 +23,21 @@ pub struct GeneratedFile {
pub enum TsSource {
DtoRegistry(DtoTypesModule),
- Raw(String),
+ Module(TypeScriptModule),
}
impl TsSource {
fn render(&self) -> String {
match self {
Self::DtoRegistry(module) => module.body_ts().to_owned(),
- Self::Raw(body) => body.clone(),
+ Self::Module(module) => module.render_source(),
}
}
fn imports(&self) -> Option<&str> {
match self {
Self::DtoRegistry(module) => module.imports_ts(),
- Self::Raw(_) => None,
+ Self::Module(_) => None,
}
}
}
@@ -92,8 +92,10 @@ pub fn package_outputs() -> Result<Vec<PackageOutput>, String> {
spec: spec_by_key("events"),
types_ts: Some(TsSource::DtoRegistry(dto_roots::events_types_module()?)),
types_imports: Vec::new(),
- constants_ts: Some(TsSource::Raw(radroots_events_bindings::constants_module())),
- kinds_ts: Some(TsSource::Raw(radroots_events_bindings::kinds_module())),
+ constants_ts: Some(TsSource::Module(
+ radroots_events_bindings::constants_module(),
+ )),
+ kinds_ts: Some(TsSource::Module(radroots_events_bindings::kinds_module())),
},
PackageOutput {
spec: spec_by_key("events_indexed"),
@@ -108,7 +110,9 @@ pub fn package_outputs() -> Result<Vec<PackageOutput>, String> {
spec: spec_by_key("identity"),
types_ts: None,
types_imports: Vec::new(),
- constants_ts: Some(TsSource::Raw(radroots_identity_bindings::constants_module())),
+ constants_ts: Some(TsSource::Module(
+ radroots_identity_bindings::constants_module(),
+ )),
kinds_ts: None,
},
PackageOutput {
@@ -212,26 +216,31 @@ fn render_index(output: &PackageOutput) -> String {
mod tests {
use super::{PackageOutput, TsSource, package_outputs, render_ts};
use crate::package_matrix::package_specs;
- use dto_bindgen_backend_ts::{DtoTypesModule, TypeScriptImport};
+ use dto_bindgen_backend_ts::{
+ DtoTypesModule, TypeScriptDeclaration, TypeScriptImport, TypeScriptModule, TypeScriptType,
+ };
const TRADE_BINDINGS_TYPES_TS: &str =
include_str!("../../../packages/trade-bindings/src/generated/types.ts");
const REPLICA_DB_SCHEMA_BINDINGS_TYPES_TS: &str =
include_str!("../../../packages/replica-db-schema-bindings/src/generated/types.ts");
+ const EVENTS_BINDINGS_CONSTANTS_TS: &str =
+ include_str!("../../../packages/events-bindings/src/generated/constants.ts");
+ const EVENTS_BINDINGS_KINDS_TS: &str =
+ include_str!("../../../packages/events-bindings/src/generated/kinds.ts");
+ const IDENTITY_BINDINGS_CONSTANTS_TS: &str =
+ include_str!("../../../packages/identity-bindings/src/generated/constants.ts");
#[test]
fn renders_sdk_header() {
- let output = render_ts(&TsSource::Raw("export type A = string;".to_owned()), None);
+ let output = render_ts(&test_module(), None);
assert!(output.starts_with("// @generated by cargo xtask generate ts"));
assert!(output.contains("export type A = string;"));
}
#[test]
fn renders_import_prelude_after_header() {
- let output = render_ts(
- &TsSource::Raw("export type A = string;".to_owned()),
- Some("import type { B } from \"b\";\n\n"),
- );
+ let output = render_ts(&test_module(), Some("import type { B } from \"b\";\n\n"));
assert!(output.starts_with(
"// @generated by cargo xtask generate ts\n// Do not edit by hand.\nimport type"
));
@@ -239,8 +248,8 @@ mod tests {
}
#[test]
- fn renders_raw_sources() {
- let output = render_ts(&TsSource::Raw("export type A = string;".to_owned()), None);
+ fn renders_module_sources() {
+ let output = render_ts(&test_module(), None);
assert_eq!(
output,
"// @generated by cargo xtask generate ts\n// Do not edit by hand.\nexport type A = string;\n"
@@ -339,4 +348,56 @@ mod tests {
assert_eq!(types.contents, REPLICA_DB_SCHEMA_BINDINGS_TYPES_TS);
}
+
+ #[test]
+ fn events_constants_and_kinds_use_modules_and_match_checked_in_files() {
+ let output = package_outputs()
+ .expect("package outputs")
+ .into_iter()
+ .find(|output| output.spec.key == "events")
+ .expect("events output");
+
+ assert!(matches!(output.constants_ts, Some(TsSource::Module(_))));
+ assert!(matches!(output.kinds_ts, Some(TsSource::Module(_))));
+
+ let files = output.files();
+ let constants = files
+ .iter()
+ .find(|file| file.relative_path == "src/generated/constants.ts")
+ .expect("constants file");
+ let kinds = files
+ .iter()
+ .find(|file| file.relative_path == "src/generated/kinds.ts")
+ .expect("kinds file");
+
+ assert_eq!(constants.contents, EVENTS_BINDINGS_CONSTANTS_TS);
+ assert_eq!(kinds.contents, EVENTS_BINDINGS_KINDS_TS);
+ }
+
+ #[test]
+ fn identity_constants_use_module_and_match_checked_in_file() {
+ let output = package_outputs()
+ .expect("package outputs")
+ .into_iter()
+ .find(|output| output.spec.key == "identity")
+ .expect("identity output");
+
+ assert!(matches!(output.constants_ts, Some(TsSource::Module(_))));
+
+ let constants = output
+ .files()
+ .into_iter()
+ .find(|file| file.relative_path == "src/generated/constants.ts")
+ .expect("constants file");
+
+ assert_eq!(constants.contents, IDENTITY_BINDINGS_CONSTANTS_TS);
+ }
+
+ fn test_module() -> TsSource {
+ TsSource::Module(
+ TypeScriptModule::new("src/generated/test.ts").with_declaration(
+ TypeScriptDeclaration::type_alias("A", TypeScriptType::String),
+ ),
+ )
+ }
}