mirror of
https://github.com/clockworklabs/SpacetimeDB.git
synced 2026-06-27 08:18:48 -04:00
84cfe5a920
## Compatibility note This PR changes the generated TypeScript table/view handles for snake_case module accessors to the target-language TypeScript accessor casing, but keeps the old snake_case handles as deprecated aliases. Generated TS clients now expose the intended TypeScript accessor casing: - `ctx.db.databaseTree` - `tables.loggedOutPlayer` - `tables.myProgress` The old snake_case handles continue to work and are marked deprecated in the generated type surface: - `ctx.db.database_tree` still works as an alias for `ctx.db.databaseTree` - `tables.logged_out_player` still works as an alias for `tables.loggedOutPlayer` - `tables.my_progress` still works as an alias for `tables.myProgress` The database canonical names stay unchanged. For example, the generated TypeScript handle is camelCase, but the table metadata still has `name: 'logged_out_player'`. ## Remaining API breakage risk This should be non-breaking for normal generated TypeScript client usage, including: - direct table access through `conn.db.snake_case` - callback contexts like `ctx.db.snake_case` - exported query builders like `tables.snake_case` - type inference for deprecated aliases There are still a few edge cases where users may notice an API shape change: - Code that enumerates generated table handles with `Object.keys`, `Object.entries`, `for...in`, or similar will now see both the target-language handle and the deprecated snake_case alias. - Code with pathological table/view accessor collisions may not get every possible alias. The normal case is `logged_out_player` -> generated handle `loggedOutPlayer` plus deprecated alias `logged_out_player`. Pathological cases are shapes like: - `foo_bar` and `fooBar`: both want the generated TypeScript handle `fooBar`, so generated clients cannot provide two distinct `tables.fooBar` entries. - `foo_bar` and some other table/view whose target-language handle is already `foo_bar`: the deprecated `foo_bar` alias for `foo_bar` -> `fooBar` would shadow the other generated handle, so the generator skips that alias. - Code that compares the exact generated `index.ts` text, emitted declaration text, or public type names will see new helper/types such as `DbView`, `Tables`, and the alias metadata. TypeScript modules themselves are not expected to break from this unless they consume generated TypeScript client bindings or depend on exact generated client object keys. ## Terminology The casing proposal uses **canonical name** for the database/internal name. That is language-independent. It uses **accessor name** for the source/module/client-facing identifier that codegen derives language-specific handles from. This PR keeps database canonical names unchanged. It changes the generated TypeScript accessor handles to match TypeScript casing while retaining the old generated handles as deprecated aliases. ## Why The TypeScript code generator was using `table.accessor_name` and `view.accessor_name` directly as object keys in `tablesSchema`. That preserved the raw module accessor spelling instead of applying the target-language `Case::Camel` conversion for TypeScript handles. Per the casing policy, client codegen should use the server accessor name as its source and apply the target-language conversion. ## What changed - Convert generated TypeScript table and view handle keys with `Case::Camel`. - Generate deprecated snake_case aliases for table/view handles when the old generated handle differs from the target-language TypeScript handle. - Keep runtime table metadata and database canonical names unchanged. - Avoid duplicate runtime table definitions. - Add a type-only aliased schema so callback contexts infer deprecated aliases too. - Add regression coverage for TypeScript-cased handles and deprecated aliases. - Update checked-in generated TS bindings and references that change under this fix. Generated code in this repo that changes as a result: - `crates/bindings-typescript/test-app/src/module_bindings/index.ts` - `crates/bindings-typescript/test-react-router-app/src/module_bindings/index.ts` - `crates/bindings-typescript/test-solid-router/src/module_bindings/index.ts` - `crates/bindings-typescript/case-conversion-test-client/src/module_bindings/index.ts` - `demo/Blackholio/client-ts/src/module_bindings/index.ts` - `templates/hangman-react-ts/src/module_bindings/index.ts` - `templates/money-exchange-react-ts/src/module_bindings/index.ts` - `crates/codegen/tests/snapshots/codegen__codegen_typescript.snap` I also updated the corresponding client/test references to the generated TypeScript-cased handles. ## Verification - `cargo fmt --all --check` - `cargo test -p spacetimedb-codegen typescript` - `pnpm --dir crates/bindings-typescript test -- tests/client_query.test.ts tests/db_connection.test.ts` - `pnpm --dir crates/bindings-typescript exec vitest run --typecheck.enabled tests/client_query.test.ts tests/db_connection.test.ts` - `git diff --check` The explicit Vitest typecheck command still reports existing global typecheck errors from unrelated files loaded by the test project, including missing `spacetimesys@2.x` ambient modules and existing test type issues, but the touched alias tests report `Type Errors no errors` and the earlier generated-code `declare override` issue is gone. --------- Signed-off-by: Ryan <r.ekhoff@clockworklabs.io> Co-authored-by: clockwork-labs-bot <clockwork-labs-bot@users.noreply.github.com> Co-authored-by: Zeke Foppa <196249+bfops@users.noreply.github.com> Co-authored-by: Ryan <r.ekhoff@clockworklabs.io>
63 lines
2.6 KiB
Rust
63 lines
2.6 KiB
Rust
use spacetimedb_codegen::{generate, CodegenOptions, Csharp, Rust, TypeScript};
|
|
use spacetimedb_data_structures::map::HashMap;
|
|
use spacetimedb_schema::def::ModuleDef;
|
|
use spacetimedb_testing::modules::{CompilationMode, CompiledModule};
|
|
use std::sync::OnceLock;
|
|
|
|
fn compiled_module() -> &'static ModuleDef {
|
|
static COMPILED_MODULE: OnceLock<ModuleDef> = OnceLock::new();
|
|
COMPILED_MODULE
|
|
.get_or_init(|| CompiledModule::compile("module-test", CompilationMode::Debug).extract_schema_blocking())
|
|
}
|
|
|
|
macro_rules! declare_tests {
|
|
($($name:ident => $lang:expr,)*) => ($(
|
|
#[test]
|
|
fn $name() {
|
|
let module = compiled_module();
|
|
let outfiles = generate(&module, &$lang, &CodegenOptions::default())
|
|
.into_iter()
|
|
.map(|f| (f.filename, f.code))
|
|
.collect::<HashMap<_, _>>();
|
|
let mut settings = insta::Settings::clone_current();
|
|
settings.set_sort_maps(true);
|
|
// Ignore the autogenerated comments with version info, since it changes with every
|
|
// build.
|
|
settings.add_filter(r"// This was generated using spacetimedb cli version \d+\.\d+\.\d+ .*", "VERSION_COMMENT");
|
|
// Ignore the place where the CLI version is put in the typescript REMOTE_MODULE info,
|
|
// so it isn't constantly changing.
|
|
settings.add_filter(r#"cliVersion: "\d+\.\d+\.\d+","#, r#"cliVersion: "X.Y.Z","#);
|
|
settings.bind(|| {
|
|
insta::assert_toml_snapshot!(outfiles);
|
|
});
|
|
}
|
|
)*);
|
|
}
|
|
|
|
declare_tests! {
|
|
test_codegen_csharp => Csharp { namespace: "SpacetimeDB" },
|
|
test_codegen_typescript => TypeScript,
|
|
test_codegen_rust => Rust,
|
|
}
|
|
|
|
#[test]
|
|
fn test_typescript_table_handles_are_camel_case() {
|
|
let module = compiled_module();
|
|
let index = generate(module, &TypeScript, &CodegenOptions::default())
|
|
.into_iter()
|
|
.find(|file| file.filename == "index.ts")
|
|
.expect("typescript codegen should emit index.ts")
|
|
.code;
|
|
|
|
assert!(index.contains("loggedOutPlayer: __table({"));
|
|
assert!(!index.contains("logged_out_player: __table({"));
|
|
assert!(index.contains("myPlayer: __table({"));
|
|
assert!(!index.contains("my_player: __table({"));
|
|
assert!(index.contains(r#""logged_out_player": "loggedOutPlayer""#));
|
|
assert!(index.contains(r#"readonly "logged_out_player": __TablesBase["loggedOutPlayer"];"#));
|
|
assert!(index.contains(r#"readonly "logged_out_player": __DbViewBase["loggedOutPlayer"];"#));
|
|
assert!(index.contains(
|
|
r#"/** @deprecated Use `loggedOutPlayer` instead. This alias will be removed in the next major version. */"#
|
|
));
|
|
}
|