Skip to content

Commit 3061cee

Browse files
authored
Improve TypeScript generated code (fixes and improvements) (#603)
* Export enum options as objects/functions Thanks to that devs could now use it more intuitively, similarly to Rust, for example: let role = Role.Admin; // it returns { tag: "Admin", value: undefined }; let role1 = Role.Custom("Foo") // returns { tag: "Custom", value: "Foo" }; * 64 bit ints need to be represented as BigInt, not number In JS all numbers are 64bit floats, but that means integers are only up to 53 bits. Thus any 64+ bits integers need to be handled as BigInts * Use camel case for product names * Don't automatically register reducers and components * Improve reducers and components * Use this.db for tables * Fix u64 and i64 to be a BigInt * Table proxy changes * Clippy * Remove some of the reducer/table methods from autogen Some of the methods were moved to the parent classes * Remove _tableProxy import * Remove count() and all() from typescript generation --------- Signed-off-by: Piotr Sarnacki <[email protected]>
1 parent 45ce5b4 commit 3061cee

File tree

1 file changed

+70
-169
lines changed

1 file changed

+70
-169
lines changed

crates/cli/src/subcommands/generate/typescript.rs

Lines changed: 70 additions & 169 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,9 @@ fn maybe_primitive(b: &BuiltinType) -> MaybePrimitive {
2929
| BuiltinType::U16
3030
| BuiltinType::I32
3131
| BuiltinType::U32
32-
| BuiltinType::I64
33-
| BuiltinType::U64
3432
| BuiltinType::F32
3533
| BuiltinType::F64 => "number",
36-
BuiltinType::I128 | BuiltinType::U128 => "BigInt",
34+
BuiltinType::I128 | BuiltinType::U128 | BuiltinType::I64 | BuiltinType::U64 => "BigInt",
3735
BuiltinType::String => "string",
3836
BuiltinType::Array(ty) => return MaybePrimitive::Array(ty),
3937
BuiltinType::Map(m) => return MaybePrimitive::Map(m),
@@ -86,8 +84,8 @@ fn typescript_as_type(b: &BuiltinType) -> &str {
8684
BuiltinType::U16 => "Number",
8785
BuiltinType::I32 => "Number",
8886
BuiltinType::U32 => "Number",
89-
BuiltinType::I64 => "Number",
90-
BuiltinType::U64 => "Number",
87+
BuiltinType::I64 => "BigInt",
88+
BuiltinType::U64 => "BigInt",
9189
BuiltinType::I128 => "BigInt",
9290
BuiltinType::U128 => "BigInt",
9391
BuiltinType::F32 => "Number",
@@ -261,10 +259,16 @@ fn convert_product_type<'a>(
261259
for (_, elem) in product_type.elements.iter().enumerate() {
262260
writeln!(
263261
f,
264-
"{INDENT}new ProductTypeElement({}, {}),",
262+
"{INDENT}new ProductTypeElement(\"{}\", {}),",
265263
elem.name
266264
.to_owned()
267-
.map(|s| format!("\"{s}\""))
265+
.map(|s| {
266+
if s == "__identity_bytes" {
267+
s
268+
} else {
269+
typescript_field_name(s.to_case(Case::Camel))
270+
}
271+
})
268272
.unwrap_or("null".into()),
269273
convert_algebraic_type(ctx, &elem.algebraic_type, ref_prefix)
270274
)?;
@@ -440,9 +444,24 @@ pub fn autogen_typescript_sum(ctx: &GenCtx, name: &str, sum_type: &SumType) -> S
440444
};
441445
writeln!(
442446
output,
443-
"export type {variant_name} = {{ tag: \"{variant_name}\"; value: {a_type} }};"
447+
"export type {variant_name} = {{ tag: \"{variant_name}\", value: {a_type} }};"
444448
)
445449
.unwrap();
450+
451+
// export an object or a function representing an enum value, so people
452+
// can pass it as an argument
453+
match variant.algebraic_type {
454+
AlgebraicType::Product(_) => writeln!(
455+
output,
456+
"export const {variant_name} = {{ tag: \"{variant_name}\", value: undefined }};"
457+
)
458+
.unwrap(),
459+
_ => writeln!(
460+
output,
461+
"export const {variant_name} = (value: {a_type}): {variant_name} => {{ return {{ tag: \"{variant_name}\", value }} }};"
462+
)
463+
.unwrap(),
464+
};
446465
}
447466

448467
writeln!(output).unwrap();
@@ -651,7 +670,7 @@ fn autogen_typescript_product_table_common(
651670
writeln!(output).unwrap();
652671

653672
writeln!(output, "// @ts-ignore").unwrap();
654-
writeln!(output, "import {{ __SPACETIMEDB__, AlgebraicType, ProductType, BuiltinType, ProductTypeElement, SumType, SumTypeVariant, IDatabaseTable, AlgebraicValue, ReducerEvent, Identity, Address }} from \"@clockworklabs/spacetimedb-sdk\";").unwrap();
673+
writeln!(output, "import {{ __SPACETIMEDB__, AlgebraicType, ProductType, BuiltinType, ProductTypeElement, SumType, SumTypeVariant, DatabaseTable, AlgebraicValue, ReducerEvent, Identity, Address, ClientDB, SpacetimeDBClient }} from \"@clockworklabs/spacetimedb-sdk\";").unwrap();
655674

656675
let mut imports = Vec::new();
657676
generate_imports(ctx, &product_type.elements, &mut imports, None);
@@ -663,11 +682,12 @@ fn autogen_typescript_product_table_common(
663682

664683
writeln!(output).unwrap();
665684

666-
writeln!(output, "export class {struct_name_pascal_case} extends IDatabaseTable").unwrap();
685+
writeln!(output, "export class {struct_name_pascal_case} extends DatabaseTable").unwrap();
667686
writeln!(output, "{{").unwrap();
668687
{
669688
indent_scope!(output);
670689

690+
writeln!(output, "public static db: ClientDB = __SPACETIMEDB__.clientDB;").unwrap();
671691
writeln!(output, "public static tableName = \"{struct_name_pascal_case}\";").unwrap();
672692

673693
let mut constructor_signature = Vec::new();
@@ -785,110 +805,11 @@ fn autogen_typescript_product_table_common(
785805
);
786806

787807
writeln!(output).unwrap();
788-
789-
writeln!(
790-
output,
791-
"public static onInsert(callback: (value: {struct_name_pascal_case}, reducerEvent: ReducerEvent | undefined) => void)"
792-
)
793-
.unwrap();
794-
writeln!(output, "{{").unwrap();
795-
{
796-
indent_scope!(output);
797-
writeln!(
798-
output,
799-
"__SPACETIMEDB__.clientDB.getTable(\"{struct_name_pascal_case}\").onInsert(callback);"
800-
)
801-
.unwrap();
802-
}
803-
writeln!(output, "}}").unwrap();
804-
writeln!(output).unwrap();
805-
806-
writeln!(output, "public static onUpdate(callback: (oldValue: {struct_name_pascal_case}, newValue: {struct_name_pascal_case}, reducerEvent: ReducerEvent | undefined) => void)").unwrap();
807-
writeln!(output, "{{").unwrap();
808-
{
809-
indent_scope!(output);
810-
writeln!(
811-
output,
812-
"__SPACETIMEDB__.clientDB.getTable(\"{struct_name_pascal_case}\").onUpdate(callback);"
813-
)
814-
.unwrap();
815-
}
816-
writeln!(output, "}}").unwrap();
817-
writeln!(output).unwrap();
818-
819-
writeln!(
820-
output,
821-
"public static onDelete(callback: (value: {struct_name_pascal_case}, reducerEvent: ReducerEvent | undefined) => void)"
822-
)
823-
.unwrap();
824-
writeln!(output, "{{").unwrap();
825-
{
826-
indent_scope!(output);
827-
writeln!(
828-
output,
829-
"__SPACETIMEDB__.clientDB.getTable(\"{struct_name_pascal_case}\").onDelete(callback);"
830-
)
831-
.unwrap();
832-
}
833-
writeln!(output, "}}").unwrap();
834-
writeln!(output).unwrap();
835-
836-
writeln!(
837-
output,
838-
"public static removeOnInsert(callback: (value: {struct_name_pascal_case}, reducerEvent: ReducerEvent | undefined) => void)"
839-
)
840-
.unwrap();
841-
writeln!(output, "{{").unwrap();
842-
{
843-
indent_scope!(output);
844-
writeln!(
845-
output,
846-
"__SPACETIMEDB__.clientDB.getTable(\"{struct_name_pascal_case}\").removeOnInsert(callback);"
847-
)
848-
.unwrap();
849-
}
850-
writeln!(output, "}}").unwrap();
851-
writeln!(output).unwrap();
852-
853-
writeln!(output, "public static removeOnUpdate(callback: (oldValue: {struct_name_pascal_case}, newValue: {struct_name_pascal_case}, reducerEvent: ReducerEvent | undefined) => void)").unwrap();
854-
writeln!(output, "{{").unwrap();
855-
{
856-
indent_scope!(output);
857-
writeln!(
858-
output,
859-
"__SPACETIMEDB__.clientDB.getTable(\"{struct_name_pascal_case}\").removeOnUpdate(callback);"
860-
)
861-
.unwrap();
862-
}
863-
writeln!(output, "}}").unwrap();
864-
writeln!(output).unwrap();
865-
866-
writeln!(
867-
output,
868-
"public static removeOnDelete(callback: (value: {struct_name_pascal_case}, reducerEvent: ReducerEvent | undefined) => void)"
869-
)
870-
.unwrap();
871-
writeln!(output, "{{").unwrap();
872-
{
873-
indent_scope!(output);
874-
writeln!(
875-
output,
876-
"__SPACETIMEDB__.clientDB.getTable(\"{struct_name_pascal_case}\").removeOnDelete(callback);"
877-
)
878-
.unwrap();
879-
}
880-
writeln!(output, "}}").unwrap();
881-
writeln!(output).unwrap();
882808
}
883809
}
884810
writeln!(output, "}}").unwrap();
885811

886812
writeln!(output, "\nexport default {struct_name_pascal_case};").unwrap();
887-
writeln!(
888-
output,
889-
"\n__SPACETIMEDB__.registerComponent(\"{struct_name_pascal_case}\", {struct_name_pascal_case});"
890-
)
891-
.unwrap();
892813

893814
output.into_inner()
894815
}
@@ -936,6 +857,7 @@ fn autogen_typescript_product_value_to_struct(
936857
output.into_inner()
937858
}
938859

860+
#[allow(dead_code)]
939861
fn indented_block<R>(output: &mut CodeIndenter<String>, f: impl FnOnce(&mut CodeIndenter<String>) -> R) -> R {
940862
writeln!(output, "{{").unwrap();
941863
let res = f(&mut output.indented(1));
@@ -950,28 +872,6 @@ fn autogen_typescript_access_funcs_for_struct(
950872
table_name: &str,
951873
table: &TableSchema,
952874
) {
953-
writeln!(output, "public static count(): number").unwrap();
954-
indented_block(output, |output| {
955-
writeln!(
956-
output,
957-
"return __SPACETIMEDB__.clientDB.getTable(\"{table_name}\").count();",
958-
)
959-
.unwrap();
960-
});
961-
962-
writeln!(output).unwrap();
963-
964-
writeln!(output, "public static all(): {table_name}[]").unwrap();
965-
indented_block(output, |output| {
966-
writeln!(
967-
output,
968-
"return __SPACETIMEDB__.clientDB.getTable(\"{table_name}\").getInstances() as unknown as {table_name}[];",
969-
)
970-
.unwrap();
971-
});
972-
973-
writeln!(output).unwrap();
974-
975875
let constraints = table.column_constraints();
976876
for col in table.columns() {
977877
let is_unique = constraints[&NonEmpty::new(col.col_pos)].has_unique();
@@ -1036,7 +936,7 @@ fn autogen_typescript_access_funcs_for_struct(
1036936
}
1037937
writeln!(
1038938
output,
1039-
"for(let instance of __SPACETIMEDB__.clientDB.getTable(\"{table_name}\").getInstances())"
939+
"for(let instance of this.db.getTable(\"{table_name}\").getInstances())"
1040940
)
1041941
.unwrap();
1042942
writeln!(output, "{{").unwrap();
@@ -1140,7 +1040,7 @@ pub fn autogen_typescript_reducer(ctx: &GenCtx, reducer: &ReducerDef) -> String
11401040
writeln!(output).unwrap();
11411041

11421042
writeln!(output, "// @ts-ignore").unwrap();
1143-
writeln!(output, "import {{ __SPACETIMEDB__, AlgebraicType, ProductType, BuiltinType, ProductTypeElement, IDatabaseTable, AlgebraicValue, ReducerArgsAdapter, SumTypeVariant, Serializer, Identity, Address, ReducerEvent }} from \"@clockworklabs/spacetimedb-sdk\";").unwrap();
1043+
writeln!(output, "import {{ __SPACETIMEDB__, AlgebraicType, ProductType, BuiltinType, ProductTypeElement, DatabaseTable, AlgebraicValue, ReducerArgsAdapter, SumTypeVariant, Serializer, Identity, Address, ReducerEvent, Reducer, SpacetimeDBClient }} from \"@clockworklabs/spacetimedb-sdk\";").unwrap();
11441044

11451045
let mut imports = Vec::new();
11461046
generate_imports(
@@ -1168,26 +1068,35 @@ pub fn autogen_typescript_reducer(ctx: &GenCtx, reducer: &ReducerDef) -> String
11681068
let arg_type_str = ty_fmt(ctx, &arg.algebraic_type, "");
11691069

11701070
func_arguments.push(format!("{arg_name}: {arg_type_str}"));
1171-
arg_names.push(format!("{}", serialize_type(ctx, &arg.algebraic_type, &arg_name, "")));
1071+
arg_names.push(arg_name.to_string());
11721072
}
11731073

1174-
writeln!(output, "export class {reducer_name_pascal_case}Reducer").unwrap();
1074+
let full_reducer_name = format!("{reducer_name_pascal_case}Reducer");
1075+
1076+
writeln!(output, "export class {full_reducer_name} extends Reducer").unwrap();
11751077
writeln!(output, "{{").unwrap();
11761078

11771079
{
11781080
indent_scope!(output);
11791081

1180-
writeln!(output, "public static call({})", func_arguments.join(", ")).unwrap();
1181-
writeln!(output, "{{").unwrap();
1082+
writeln!(
1083+
output,
1084+
"public static reducerName: string = \"{reducer_name_pascal_case}\";"
1085+
)
1086+
.unwrap();
1087+
writeln!(output, "public static call({}) {{", func_arguments.join(", ")).unwrap();
11821088
{
11831089
indent_scope!(output);
11841090

1185-
writeln!(output, "if (__SPACETIMEDB__.spacetimeDBClient) {{").unwrap();
1186-
writeln!(
1187-
output,
1188-
"const serializer = __SPACETIMEDB__.spacetimeDBClient.getSerializer();"
1189-
)
1190-
.unwrap();
1091+
writeln!(output, "this.getReducer().call({});", arg_names.join(", ")).unwrap();
1092+
}
1093+
writeln!(output, "}}\n").unwrap();
1094+
1095+
writeln!(output, "public call({}) {{", func_arguments.join(", ")).unwrap();
1096+
{
1097+
indent_scope!(output);
1098+
1099+
writeln!(output, "const serializer = this.client.getSerializer();").unwrap();
11911100

11921101
let mut arg_names = Vec::new();
11931102
for arg in reducer.args.iter() {
@@ -1204,12 +1113,7 @@ pub fn autogen_typescript_reducer(ctx: &GenCtx, reducer: &ReducerDef) -> String
12041113
arg_names.push(arg_name);
12051114
}
12061115

1207-
writeln!(
1208-
output,
1209-
"\t__SPACETIMEDB__.spacetimeDBClient.call(\"{func_name}\", serializer);"
1210-
)
1211-
.unwrap();
1212-
writeln!(output, "}}").unwrap();
1116+
writeln!(output, "this.client.call(\"{func_name}\", serializer);").unwrap();
12131117
}
12141118
// Closing brace for reducer
12151119
writeln!(output, "}}").unwrap();
@@ -1256,23 +1160,36 @@ pub fn autogen_typescript_reducer(ctx: &GenCtx, reducer: &ReducerDef) -> String
12561160
writeln!(output, "}}").unwrap();
12571161

12581162
writeln!(output).unwrap();
1163+
1164+
writeln!(
1165+
output,
1166+
"public static on(callback: (reducerEvent: ReducerEvent, {}) => void) {{",
1167+
func_arguments.join(", ")
1168+
)
1169+
.unwrap();
1170+
{
1171+
indent_scope!(output);
1172+
1173+
writeln!(output, "this.getReducer().on(callback);").unwrap();
1174+
}
1175+
writeln!(output, "}}").unwrap();
1176+
12591177
// OnCreatePlayerEvent(dbEvent.Status, Identity.From(dbEvent.CallerIdentity.ToByteArray()), args[0].ToObject<string>());
12601178
writeln!(
12611179
output,
1262-
"public static on(callback: (reducerEvent: ReducerEvent, reducerArgs: any[]) => void)"
1180+
"public on(callback: (reducerEvent: ReducerEvent, {}) => void)",
1181+
func_arguments.join(", ")
12631182
)
12641183
.unwrap();
12651184
writeln!(output, "{{").unwrap();
12661185
{
12671186
indent_scope!(output);
12681187

1269-
writeln!(output, "if (__SPACETIMEDB__.spacetimeDBClient) {{").unwrap();
12701188
writeln!(
12711189
output,
1272-
"\t__SPACETIMEDB__.spacetimeDBClient.on(\"reducer:{reducer_name_pascal_case}\", callback);"
1190+
"this.client.on(\"reducer:{reducer_name_pascal_case}\", callback);"
12731191
)
12741192
.unwrap();
1275-
writeln!(output, "}}").unwrap();
12761193
}
12771194

12781195
// Closing brace for Event parsing function
@@ -1283,22 +1200,6 @@ pub fn autogen_typescript_reducer(ctx: &GenCtx, reducer: &ReducerDef) -> String
12831200

12841201
writeln!(output).unwrap();
12851202

1286-
writeln!(
1287-
output,
1288-
"__SPACETIMEDB__.reducers.set(\"{reducer_name_pascal_case}\", {reducer_name_pascal_case}Reducer);"
1289-
)
1290-
.unwrap();
1291-
1292-
writeln!(output, "if (__SPACETIMEDB__.spacetimeDBClient) {{").unwrap();
1293-
1294-
{
1295-
indent_scope!(output);
1296-
1297-
writeln!(output, "__SPACETIMEDB__.spacetimeDBClient.registerReducer(\"{reducer_name_pascal_case}\", {reducer_name_pascal_case}Reducer);").unwrap();
1298-
}
1299-
1300-
writeln!(output, "}}").unwrap();
1301-
13021203
writeln!(output, "\nexport default {reducer_name_pascal_case}Reducer").unwrap();
13031204

13041205
output.into_inner()

0 commit comments

Comments
 (0)