diff --git a/crates/bindings-typescript/case-conversion-test-client/src/module_bindings/index.ts b/crates/bindings-typescript/case-conversion-test-client/src/module_bindings/index.ts index 63c1524da89..4693792f6de 100644 --- a/crates/bindings-typescript/case-conversion-test-client/src/module_bindings/index.ts +++ b/crates/bindings-typescript/case-conversion-test-client/src/module_bindings/index.ts @@ -97,7 +97,7 @@ const tablesSchema = __schema({ }, Person2Row ), - person_at_level_2: __table( + personAtLevel2: __table( { name: 'Level2Person', indexes: [], diff --git a/crates/bindings-typescript/test-app/src/module_bindings/index.ts b/crates/bindings-typescript/test-app/src/module_bindings/index.ts index 49090beab53..6b58328915a 100644 --- a/crates/bindings-typescript/test-app/src/module_bindings/index.ts +++ b/crates/bindings-typescript/test-app/src/module_bindings/index.ts @@ -65,7 +65,7 @@ const tablesSchema = __schema({ }, PlayerRow ), - unindexed_player: __table( + unindexedPlayer: __table( { name: 'unindexed_player', indexes: [ @@ -107,7 +107,7 @@ const tablesSchema = __schema({ }, UserRow ), - my_user_procedural: __table( + myUserProcedural: __table( { name: 'my_user_procedural', indexes: [], diff --git a/crates/bindings-typescript/test-react-router-app/src/module_bindings/index.ts b/crates/bindings-typescript/test-react-router-app/src/module_bindings/index.ts index b9d553082d4..1d6ae15bf69 100644 --- a/crates/bindings-typescript/test-react-router-app/src/module_bindings/index.ts +++ b/crates/bindings-typescript/test-react-router-app/src/module_bindings/index.ts @@ -65,7 +65,7 @@ const tablesSchema = __schema({ }, CounterRow ), - offline_user: __table( + offlineUser: __table( { name: 'offline_user', indexes: [ diff --git a/crates/bindings-typescript/test-solid-router/src/module_bindings/index.ts b/crates/bindings-typescript/test-solid-router/src/module_bindings/index.ts index b9d553082d4..1d6ae15bf69 100644 --- a/crates/bindings-typescript/test-solid-router/src/module_bindings/index.ts +++ b/crates/bindings-typescript/test-solid-router/src/module_bindings/index.ts @@ -65,7 +65,7 @@ const tablesSchema = __schema({ }, CounterRow ), - offline_user: __table( + offlineUser: __table( { name: 'offline_user', indexes: [ diff --git a/crates/bindings-typescript/tests/client_query.test.ts b/crates/bindings-typescript/tests/client_query.test.ts index b29a44ba185..9da4eaee804 100644 --- a/crates/bindings-typescript/tests/client_query.test.ts +++ b/crates/bindings-typescript/tests/client_query.test.ts @@ -89,7 +89,7 @@ describe('ClientQuery.toSql', () => { it('renders semijoin queries without additional filters', () => { const sql = toSql( tables.player - .rightSemijoin(tables.unindexed_player, (player, other) => + .rightSemijoin(tables.unindexedPlayer, (player, other) => other.id.eq(player.id) ) .build() @@ -104,7 +104,7 @@ describe('ClientQuery.toSql', () => { const sql = toSql( tables.player .where(row => row.id.eq(42)) - .rightSemijoin(tables.unindexed_player, (player, other) => + .rightSemijoin(tables.unindexedPlayer, (player, other) => other.id.eq(player.id) ) .build() @@ -119,7 +119,7 @@ describe('ClientQuery.toSql', () => { const sql = toSql( tables.player .where(row => row.name.eq("O'Brian")) - .rightSemijoin(tables.unindexed_player, (player, other) => + .rightSemijoin(tables.unindexedPlayer, (player, other) => other.id.eq(player.id) ) .build() @@ -134,7 +134,7 @@ describe('ClientQuery.toSql', () => { const sql = toSql( tables.player .where(row => and(row.name.eq('Alice'), row.id.eq(30))) - .rightSemijoin(tables.unindexed_player, (player, other) => + .rightSemijoin(tables.unindexedPlayer, (player, other) => other.id.eq(player.id) ) .build() @@ -196,7 +196,7 @@ describe('ClientQuery.toSql', () => { it('basic semijoin', () => { const sql = toSql( tables.player - .rightSemijoin(tables.unindexed_player, (player, other) => + .rightSemijoin(tables.unindexedPlayer, (player, other) => other.id.eq(player.id) ) .build() @@ -209,7 +209,7 @@ describe('ClientQuery.toSql', () => { it('basic left semijoin', () => { const sql = toSql( tables.player - .leftSemijoin(tables.unindexed_player, (player, other) => + .leftSemijoin(tables.unindexedPlayer, (player, other) => other.id.eq(player.id) ) .build() @@ -252,7 +252,7 @@ describe('ClientQuery.toSql', () => { const sql = toSql( tables.player .where(row => row.id.eq(42)) - .rightSemijoin(tables.unindexed_player, (player, other) => + .rightSemijoin(tables.unindexedPlayer, (player, other) => other.id.eq(player.id) ) .where(row => row.name.eq('Gadget')) @@ -286,7 +286,7 @@ describe('useTable type compatibility', () => { it('rightSemijoin result is assignable to useTable query param', () => { assertType( - tables.player.rightSemijoin(tables.unindexed_player, (p, o) => + tables.player.rightSemijoin(tables.unindexedPlayer, (p, o) => o.id.eq(p.id) ) ); @@ -294,7 +294,7 @@ describe('useTable type compatibility', () => { it('leftSemijoin result is assignable to useTable query param', () => { assertType( - tables.player.leftSemijoin(tables.unindexed_player, (p, o) => + tables.player.leftSemijoin(tables.unindexedPlayer, (p, o) => o.id.eq(p.id) ) ); @@ -303,7 +303,7 @@ describe('useTable type compatibility', () => { it('semijoin with .where() is assignable to useTable query param', () => { assertType( tables.player - .rightSemijoin(tables.unindexed_player, (p, o) => o.id.eq(p.id)) + .rightSemijoin(tables.unindexedPlayer, (p, o) => o.id.eq(p.id)) .where(r => r.name.eq('test')) ); }); @@ -320,7 +320,7 @@ describe('useTable type compatibility', () => { it('semijoin result exposes toSql() returning string', () => { const sql: string = tables.player - .rightSemijoin(tables.unindexed_player, (p, o) => o.id.eq(p.id)) + .rightSemijoin(tables.unindexedPlayer, (p, o) => o.id.eq(p.id)) .toSql(); expect(typeof sql).toBe('string'); }); diff --git a/crates/bindings-typescript/tests/db_connection.test.ts b/crates/bindings-typescript/tests/db_connection.test.ts index 7a0da07bf52..b2b29885f9d 100644 --- a/crates/bindings-typescript/tests/db_connection.test.ts +++ b/crates/bindings-typescript/tests/db_connection.test.ts @@ -847,11 +847,11 @@ describe('DbConnection', () => { // `onUpdate` is only available when the generated view row binding carries // primary-key metadata. - client.db.my_user_procedural.onInsert((_ctx, row) => { + client.db.myUserProcedural.onInsert((_ctx, row) => { expect(row).toEqual(initialRow); initialInsertPromise.resolve(); }); - client.db.my_user_procedural.onUpdate((_ctx, oldRow, newRow) => { + client.db.myUserProcedural.onUpdate((_ctx, oldRow, newRow) => { updates.push({ oldRow, newRow, @@ -876,7 +876,7 @@ describe('DbConnection', () => { await initialInsertPromise.promise; expect(client.db.player.count()).toEqual(1n); - expect(client.db.my_user_procedural.count()).toEqual(1n); + expect(client.db.myUserProcedural.count()).toEqual(1n); // A delete and insert with the same primary key in one transaction should // be coalesced by the client cache into `onUpdate`, not separate delete and @@ -909,8 +909,8 @@ describe('DbConnection', () => { }, ]); expect(client.db.player.count()).toEqual(1n); - expect(client.db.my_user_procedural.count()).toEqual(1n); - expect([...client.db.my_user_procedural.iter()][0]).toEqual(updatedRow); + expect(client.db.myUserProcedural.count()).toEqual(1n); + expect([...client.db.myUserProcedural.iter()][0]).toEqual(updatedRow); }); test('Filtering works', async () => { diff --git a/crates/bindings-typescript/tests/query_error_message.test.ts b/crates/bindings-typescript/tests/query_error_message.test.ts index f016c97cc28..20a9285258b 100644 --- a/crates/bindings-typescript/tests/query_error_message.test.ts +++ b/crates/bindings-typescript/tests/query_error_message.test.ts @@ -28,7 +28,7 @@ import { and } from ${JSON.stringify(imports.query)}; import { tables } from ${JSON.stringify(imports.moduleBindings)}; tables.player - .leftSemijoin(tables.unindexed_player, (l, r) => ${semijoinPredicateExpr}) + .leftSemijoin(tables.unindexedPlayer, (l, r) => ${semijoinPredicateExpr}) .build(); `; diff --git a/crates/bindings-typescript/tests/table_cache.test.ts b/crates/bindings-typescript/tests/table_cache.test.ts index a3b1f2becc6..558faf06b48 100644 --- a/crates/bindings-typescript/tests/table_cache.test.ts +++ b/crates/bindings-typescript/tests/table_cache.test.ts @@ -101,13 +101,13 @@ function runTest( describe('TableCache', () => { describe('Unindexed player table', () => { - const newTable = () => new TableCacheImpl(tables.unindexed_player.tableDef); + const newTable = () => new TableCacheImpl(tables.unindexedPlayer.tableDef); const mkOperation = ( type: 'insert' | 'delete', row: Infer ) => { const rowId = AlgebraicType.intoMapKey( - { tag: 'Product', value: tables.unindexed_player.rowType }, + { tag: 'Product', value: tables.unindexedPlayer.rowType }, row ); return { diff --git a/crates/codegen/src/typescript.rs b/crates/codegen/src/typescript.rs index 28bc1fb4c91..1f326044fb0 100644 --- a/crates/codegen/src/typescript.rs +++ b/crates/codegen/src/typescript.rs @@ -198,8 +198,9 @@ impl Lang for TypeScript { out.indent(1); for table in iter_tables(module, options.visibility) { let type_ref = table.product_type_ref; + let table_accessor = table.accessor_name.deref().to_case(Case::Camel); let table_name_pascalcase = table.accessor_name.deref().to_case(Case::Pascal); - writeln!(out, "{}: __table({{", table.accessor_name); + writeln!(out, "{table_accessor}: __table({{"); out.indent(1); write_table_opts( module, @@ -215,8 +216,9 @@ impl Lang for TypeScript { } for view in iter_views(module) { let type_ref = view.product_type_ref; + let view_accessor = view.accessor_name.deref().to_case(Case::Camel); let view_name_pascalcase = view.accessor_name.deref().to_case(Case::Pascal); - writeln!(out, "{}: __table({{", view.accessor_name); + writeln!(out, "{view_accessor}: __table({{"); out.indent(1); write_table_opts(module, out, type_ref, &view.name, iter::empty(), iter::empty(), false); out.dedent(1); diff --git a/crates/codegen/tests/codegen.rs b/crates/codegen/tests/codegen.rs index 06dc3ebe8fc..7eda97c0a99 100644 --- a/crates/codegen/tests/codegen.rs +++ b/crates/codegen/tests/codegen.rs @@ -39,3 +39,18 @@ declare_tests! { 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({")); +} diff --git a/crates/codegen/tests/snapshots/codegen__codegen_typescript.snap b/crates/codegen/tests/snapshots/codegen__codegen_typescript.snap index 8616888c54f..aa977c6a8ac 100644 --- a/crates/codegen/tests/snapshots/codegen__codegen_typescript.snap +++ b/crates/codegen/tests/snapshots/codegen__codegen_typescript.snap @@ -187,7 +187,7 @@ import TestFRow from "./test_f_table"; /** The schema information for all tables in this module. This is defined the same was as the tables would have been defined in the server. */ const tablesSchema = __schema({ - logged_out_player: __table({ + loggedOutPlayer: __table({ name: 'logged_out_player', indexes: [ { accessor: 'identity', name: 'logged_out_player_identity_idx_btree', algorithm: 'btree', columns: [ @@ -239,21 +239,21 @@ const tablesSchema = __schema({ { name: 'player_player_id_key', constraint: 'unique', columns: ['playerId'] }, ], }, PlayerRow), - test_d: __table({ + testD: __table({ name: 'test_d', indexes: [ ], constraints: [ ], }, TestDRow), - test_f: __table({ + testF: __table({ name: 'test_f', indexes: [ ], constraints: [ ], }, TestFRow), - my_player: __table({ + myPlayer: __table({ name: 'my_player', indexes: [ ], diff --git a/demo/Blackholio/client-ts/src/game/GameManager.ts b/demo/Blackholio/client-ts/src/game/GameManager.ts index e350c108083..1265434a17b 100644 --- a/demo/Blackholio/client-ts/src/game/GameManager.ts +++ b/demo/Blackholio/client-ts/src/game/GameManager.ts @@ -104,7 +104,7 @@ export class GameManager { connection.db.player.onDelete((_ctx, player) => { this.players.delete(player.playerId); }); - connection.db.consume_entity_event.onInsert((_ctx, event) => + connection.db.consumeEntityEvent.onInsert((_ctx, event) => this.consumeEntityEventOnInsert(event) ); } diff --git a/demo/Blackholio/client-ts/src/module_bindings/index.ts b/demo/Blackholio/client-ts/src/module_bindings/index.ts index 6120bf25542..a05ac44a41b 100644 --- a/demo/Blackholio/client-ts/src/module_bindings/index.ts +++ b/demo/Blackholio/client-ts/src/module_bindings/index.ts @@ -79,7 +79,7 @@ const tablesSchema = __schema({ { name: 'config_id_key', constraint: 'unique', columns: ['id'] }, ], }, ConfigRow), - consume_entity_event: __table({ + consumeEntityEvent: __table({ name: 'consume_entity_event', indexes: [ ], diff --git a/templates/hangman-react-ts/src/App.tsx b/templates/hangman-react-ts/src/App.tsx index 6f80c29884e..64486fb9755 100644 --- a/templates/hangman-react-ts/src/App.tsx +++ b/templates/hangman-react-ts/src/App.tsx @@ -192,7 +192,7 @@ function App() { const { identity, isActive: connected } = useSpacetimeDB(); const [rounds] = useTable(tables.currentRound); const [players] = useTable(tables.player); - const [boards] = useTable(tables.my_progress); + const [boards] = useTable(tables.myProgress); const [results] = useTable(tables.roundResult); const setName = useReducer(reducers.setName); const guessLetter = useReducer(reducers.guessLetter); diff --git a/templates/hangman-react-ts/src/module_bindings/index.ts b/templates/hangman-react-ts/src/module_bindings/index.ts index 8ca848b15a5..1c459675772 100644 --- a/templates/hangman-react-ts/src/module_bindings/index.ts +++ b/templates/hangman-react-ts/src/module_bindings/index.ts @@ -108,7 +108,7 @@ const tablesSchema = __schema({ }, RoundResultRow ), - my_progress: __table( + myProgress: __table( { name: 'my_progress', indexes: [], diff --git a/templates/money-exchange-react-ts/src/App.tsx b/templates/money-exchange-react-ts/src/App.tsx index c45ee166e44..85c838bb66c 100644 --- a/templates/money-exchange-react-ts/src/App.tsx +++ b/templates/money-exchange-react-ts/src/App.tsx @@ -118,9 +118,9 @@ function NameForm({ function App() { const { identity, isActive: connected } = useSpacetimeDB(); - const [accounts] = useTable(tables.my_account); + const [accounts] = useTable(tables.myAccount); const [directory] = useTable(tables.directory); - const [changes] = useTable(tables.my_account_changes); + const [changes] = useTable(tables.myAccountChanges); const setName = useReducer(reducers.setName); const sendTransfer = useReducer(reducers.transfer); const [editingName, setEditingName] = useState(false); diff --git a/templates/money-exchange-react-ts/src/module_bindings/index.ts b/templates/money-exchange-react-ts/src/module_bindings/index.ts index b41cebe950a..e5b102c7132 100644 --- a/templates/money-exchange-react-ts/src/module_bindings/index.ts +++ b/templates/money-exchange-react-ts/src/module_bindings/index.ts @@ -80,7 +80,7 @@ const tablesSchema = __schema({ }, DirectoryRow ), - my_account: __table( + myAccount: __table( { name: 'my_account', indexes: [], @@ -88,7 +88,7 @@ const tablesSchema = __schema({ }, MyAccountRow ), - my_account_changes: __table( + myAccountChanges: __table( { name: 'my_account_changes', indexes: [],