@@ -9,6 +9,7 @@ import type {
99 PostgresView ,
1010} from '../../lib/index.js'
1111import type { GeneratorMetadata } from '../../lib/generators.js'
12+ import { PostgresForeignTable } from '../../lib/types.js'
1213
1314type Operation = 'Select' | 'Insert' | 'Update'
1415export type AccessControl = 'internal' | 'public' | 'private' | 'package'
@@ -57,7 +58,7 @@ function pgEnumToSwiftEnum(pgEnum: PostgresType): SwiftEnum {
5758}
5859
5960function pgTypeToSwiftStruct (
60- table : PostgresTable | PostgresView | PostgresMaterializedView ,
61+ table : PostgresTable | PostgresForeignTable | PostgresView | PostgresMaterializedView ,
6162 columns : PostgresColumn [ ] | undefined ,
6263 operation : Operation ,
6364 {
@@ -205,75 +206,80 @@ function generateStruct(
205206export const apply = async ( {
206207 schemas,
207208 tables,
209+ foreignTables,
208210 views,
209211 materializedViews,
210212 columns,
211213 types,
212214 accessControl,
213215} : GeneratorMetadata & SwiftGeneratorOptions ) : Promise < string > => {
214- const columnsByTableId = columns
215- . sort ( ( { name : a } , { name : b } ) => a . localeCompare ( b ) )
216- . reduce (
217- ( acc , curr ) => {
218- acc [ curr . table_id ] ??= [ ]
219- acc [ curr . table_id ] . push ( curr )
220- return acc
221- } ,
222- { } as Record < string , PostgresColumn [ ] >
223- )
224-
225- const compositeTypes = types . filter ( ( type ) => type . attributes . length > 0 )
226- const enums = types
227- . filter ( ( type ) => type . enums . length > 0 )
228- . sort ( ( { name : a } , { name : b } ) => a . localeCompare ( b ) )
229-
230- const swiftEnums = enums . map ( ( enum_ ) => {
231- return { schema : enum_ . schema , enum_ : pgEnumToSwiftEnum ( enum_ ) }
232- } )
233-
234- const swiftStructForTables = tables . flatMap ( ( table ) =>
235- ( [ 'Select' , 'Insert' , 'Update' ] as Operation [ ] ) . map ( ( operation ) =>
236- pgTypeToSwiftStruct ( table , columnsByTableId [ table . id ] , operation , { types, views, tables } )
237- )
238- )
239-
240- const swiftStructForViews = views . map ( ( view ) =>
241- pgTypeToSwiftStruct ( view , columnsByTableId [ view . id ] , 'Select' , { types, views, tables } )
216+ const columnsByTableId = Object . fromEntries < PostgresColumn [ ] > (
217+ [ ...tables , ...foreignTables , ...views , ...materializedViews ] . map ( ( t ) => [ t . id , [ ] ] )
242218 )
243219
244- const swiftStructForMaterializedViews = materializedViews . map ( ( materializedView ) =>
245- pgTypeToSwiftStruct ( materializedView , columnsByTableId [ materializedView . id ] , 'Select' , {
246- types,
247- views,
248- tables,
249- } )
250- )
251-
252- const swiftStructForCompositeTypes = compositeTypes . map ( ( type ) =>
253- pgCompositeTypeToSwiftStruct ( type , { types, views, tables } )
254- )
220+ columns
221+ . filter ( ( c ) => c . table_id in columnsByTableId )
222+ . sort ( ( { name : a } , { name : b } ) => a . localeCompare ( b ) )
223+ . forEach ( ( c ) => columnsByTableId [ c . table_id ] . push ( c ) )
255224
256225 let output = [
257226 'import Foundation' ,
258227 'import Supabase' ,
259228 '' ,
260- ...schemas . flatMap ( ( schema ) => [
261- `${ accessControl } enum ${ formatForSwiftSchemaName ( schema . name ) } {` ,
262- ...swiftEnums . flatMap ( ( { enum_ } ) => generateEnum ( enum_ , { accessControl, level : 1 } ) ) ,
263- ...swiftStructForTables . flatMap ( ( struct ) =>
264- generateStruct ( struct , { accessControl, level : 1 } )
265- ) ,
266- ...swiftStructForViews . flatMap ( ( struct ) =>
267- generateStruct ( struct , { accessControl, level : 1 } )
268- ) ,
269- ...swiftStructForMaterializedViews . flatMap ( ( struct ) =>
270- generateStruct ( struct , { accessControl, level : 1 } )
271- ) ,
272- ...swiftStructForCompositeTypes . flatMap ( ( struct ) =>
273- generateStruct ( struct , { accessControl, level : 1 } )
274- ) ,
275- '}' ,
276- ] ) ,
229+ ...schemas
230+ . sort ( ( { name : a } , { name : b } ) => a . localeCompare ( b ) )
231+ . flatMap ( ( schema ) => {
232+ const schemaTables = [ ...tables , ...foreignTables ]
233+ . filter ( ( table ) => table . schema === schema . name )
234+ . sort ( ( { name : a } , { name : b } ) => a . localeCompare ( b ) )
235+
236+ const schemaViews = [ ...views , ...materializedViews ]
237+ . filter ( ( table ) => table . schema === schema . name )
238+ . sort ( ( { name : a } , { name : b } ) => a . localeCompare ( b ) )
239+
240+ const schemaEnums = types
241+ . filter ( ( type ) => type . schema === schema . name && type . enums . length > 0 )
242+ . sort ( ( { name : a } , { name : b } ) => a . localeCompare ( b ) )
243+
244+ const schemaCompositeTypes = types
245+ . filter ( ( type ) => type . schema === schema . name && type . attributes . length > 0 )
246+ . sort ( ( { name : a } , { name : b } ) => a . localeCompare ( b ) )
247+
248+ return [
249+ `${ accessControl } enum ${ formatForSwiftSchemaName ( schema . name ) } {` ,
250+ ...schemaEnums . flatMap ( ( enum_ ) =>
251+ generateEnum ( pgEnumToSwiftEnum ( enum_ ) , { accessControl, level : 1 } )
252+ ) ,
253+ ...schemaTables . flatMap ( ( table ) =>
254+ ( [ 'Select' , 'Insert' , 'Update' ] as Operation [ ] )
255+ . map ( ( operation ) =>
256+ pgTypeToSwiftStruct ( table , columnsByTableId [ table . id ] , operation , {
257+ types,
258+ views,
259+ tables,
260+ } )
261+ )
262+ . flatMap ( ( struct ) => generateStruct ( struct , { accessControl, level : 1 } ) )
263+ ) ,
264+ ...schemaViews . flatMap ( ( view ) =>
265+ generateStruct (
266+ pgTypeToSwiftStruct ( view , columnsByTableId [ view . id ] , 'Select' , {
267+ types,
268+ views,
269+ tables,
270+ } ) ,
271+ { accessControl, level : 1 }
272+ )
273+ ) ,
274+ ...schemaCompositeTypes . flatMap ( ( type ) =>
275+ generateStruct ( pgCompositeTypeToSwiftStruct ( type , { types, views, tables } ) , {
276+ accessControl,
277+ level : 1 ,
278+ } )
279+ ) ,
280+ '}' ,
281+ ]
282+ } ) ,
277283 ]
278284
279285 return output . join ( '\n' )
@@ -360,15 +366,34 @@ function ident(level: number, options: { width: number } = { width: 2 }): string
360366 * formatForSwiftTypeName('pokemon_center') // PokemonCenter
361367 * formatForSwiftTypeName('victory-road') // VictoryRoad
362368 * formatForSwiftTypeName('pokemon league') // PokemonLeague
369+ * formatForSwiftTypeName('_key_id_context') // _KeyIdContext
363370 * ```
364371 */
365372function formatForSwiftTypeName ( name : string ) : string {
366- return name
367- . split ( / [ ^ a - z A - Z 0 - 9 ] / )
368- . map ( ( word ) => `${ word [ 0 ] . toUpperCase ( ) } ${ word . slice ( 1 ) } ` )
369- . join ( '' )
373+ // Preserve the initial underscore if it exists
374+ let prefix = ''
375+ if ( name . startsWith ( '_' ) ) {
376+ prefix = '_'
377+ name = name . slice ( 1 ) // Remove the initial underscore for processing
378+ }
379+
380+ return (
381+ prefix +
382+ name
383+ . split ( / [ ^ a - z A - Z 0 - 9 ] + / )
384+ . map ( ( word ) => {
385+ if ( word ) {
386+ return `${ word [ 0 ] . toUpperCase ( ) } ${ word . slice ( 1 ) } `
387+ } else {
388+ return ''
389+ }
390+ } )
391+ . join ( '' )
392+ )
370393}
371394
395+ const SWIFT_KEYWORDS = [ 'in' , 'default' ]
396+
372397/**
373398 * Converts a Postgres name to pascalCase.
374399 *
@@ -381,11 +406,13 @@ function formatForSwiftTypeName(name: string): string {
381406 * ```
382407 */
383408function formatForSwiftPropertyName ( name : string ) : string {
384- return name
409+ const propertyName = name
385410 . split ( / [ ^ a - z A - Z 0 - 9 ] / )
386411 . map ( ( word , index ) => {
387412 const lowerWord = word . toLowerCase ( )
388413 return index !== 0 ? lowerWord . charAt ( 0 ) . toUpperCase ( ) + lowerWord . slice ( 1 ) : lowerWord
389414 } )
390415 . join ( '' )
416+
417+ return SWIFT_KEYWORDS . includes ( propertyName ) ? `\`${ propertyName } \`` : propertyName
391418}
0 commit comments