@sqldoc/templates 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (108) hide show
  1. package/package.json +161 -0
  2. package/src/__tests__/dedent.test.ts +45 -0
  3. package/src/__tests__/docker-templates.test.ts +134 -0
  4. package/src/__tests__/go-structs.test.ts +184 -0
  5. package/src/__tests__/naming.test.ts +48 -0
  6. package/src/__tests__/python-dataclasses.test.ts +185 -0
  7. package/src/__tests__/rust-structs.test.ts +176 -0
  8. package/src/__tests__/tags-helpers.test.ts +72 -0
  9. package/src/__tests__/type-mapping.test.ts +332 -0
  10. package/src/__tests__/typescript.test.ts +202 -0
  11. package/src/cobol-copybook/index.ts +220 -0
  12. package/src/cobol-copybook/test/.gitignore +6 -0
  13. package/src/cobol-copybook/test/Dockerfile +7 -0
  14. package/src/csharp-records/index.ts +131 -0
  15. package/src/csharp-records/test/.gitignore +6 -0
  16. package/src/csharp-records/test/Dockerfile +6 -0
  17. package/src/diesel/index.ts +247 -0
  18. package/src/diesel/test/.gitignore +6 -0
  19. package/src/diesel/test/Dockerfile +16 -0
  20. package/src/drizzle/index.ts +255 -0
  21. package/src/drizzle/test/.gitignore +6 -0
  22. package/src/drizzle/test/Dockerfile +8 -0
  23. package/src/drizzle/test/test.ts +71 -0
  24. package/src/efcore/index.ts +190 -0
  25. package/src/efcore/test/.gitignore +6 -0
  26. package/src/efcore/test/Dockerfile +7 -0
  27. package/src/go-structs/index.ts +119 -0
  28. package/src/go-structs/test/.gitignore +6 -0
  29. package/src/go-structs/test/Dockerfile +13 -0
  30. package/src/go-structs/test/test.go +71 -0
  31. package/src/gorm/index.ts +134 -0
  32. package/src/gorm/test/.gitignore +6 -0
  33. package/src/gorm/test/Dockerfile +13 -0
  34. package/src/gorm/test/test.go +65 -0
  35. package/src/helpers/atlas.ts +43 -0
  36. package/src/helpers/enrich.ts +396 -0
  37. package/src/helpers/naming.ts +19 -0
  38. package/src/helpers/tags.ts +63 -0
  39. package/src/index.ts +24 -0
  40. package/src/java-records/index.ts +179 -0
  41. package/src/java-records/test/.gitignore +6 -0
  42. package/src/java-records/test/Dockerfile +11 -0
  43. package/src/java-records/test/Test.java +93 -0
  44. package/src/jpa/index.ts +279 -0
  45. package/src/jpa/test/.gitignore +6 -0
  46. package/src/jpa/test/Dockerfile +14 -0
  47. package/src/jpa/test/Test.java +111 -0
  48. package/src/json-schema/index.ts +351 -0
  49. package/src/json-schema/test/.gitignore +6 -0
  50. package/src/json-schema/test/Dockerfile +18 -0
  51. package/src/knex/index.ts +168 -0
  52. package/src/knex/test/.gitignore +6 -0
  53. package/src/knex/test/Dockerfile +7 -0
  54. package/src/knex/test/test.ts +75 -0
  55. package/src/kotlin-data/index.ts +147 -0
  56. package/src/kotlin-data/test/.gitignore +6 -0
  57. package/src/kotlin-data/test/Dockerfile +14 -0
  58. package/src/kotlin-data/test/Test.kt +82 -0
  59. package/src/kysely/index.ts +165 -0
  60. package/src/kysely/test/.gitignore +6 -0
  61. package/src/kysely/test/Dockerfile +8 -0
  62. package/src/kysely/test/test.ts +82 -0
  63. package/src/prisma/index.ts +387 -0
  64. package/src/prisma/test/.gitignore +6 -0
  65. package/src/prisma/test/Dockerfile +7 -0
  66. package/src/protobuf/index.ts +219 -0
  67. package/src/protobuf/test/.gitignore +6 -0
  68. package/src/protobuf/test/Dockerfile +6 -0
  69. package/src/pydantic/index.ts +272 -0
  70. package/src/pydantic/test/.gitignore +6 -0
  71. package/src/pydantic/test/Dockerfile +8 -0
  72. package/src/pydantic/test/test.py +63 -0
  73. package/src/python-dataclasses/index.ts +217 -0
  74. package/src/python-dataclasses/test/.gitignore +6 -0
  75. package/src/python-dataclasses/test/Dockerfile +8 -0
  76. package/src/python-dataclasses/test/test.py +63 -0
  77. package/src/rust-structs/index.ts +152 -0
  78. package/src/rust-structs/test/.gitignore +6 -0
  79. package/src/rust-structs/test/Dockerfile +22 -0
  80. package/src/rust-structs/test/test.rs +82 -0
  81. package/src/sqlalchemy/index.ts +258 -0
  82. package/src/sqlalchemy/test/.gitignore +6 -0
  83. package/src/sqlalchemy/test/Dockerfile +8 -0
  84. package/src/sqlalchemy/test/test.py +61 -0
  85. package/src/sqlc/index.ts +148 -0
  86. package/src/sqlc/test/.gitignore +6 -0
  87. package/src/sqlc/test/Dockerfile +13 -0
  88. package/src/sqlc/test/test.go +91 -0
  89. package/src/tags/dedent.ts +28 -0
  90. package/src/tags/index.ts +14 -0
  91. package/src/types/index.ts +8 -0
  92. package/src/types/pg-to-csharp.ts +136 -0
  93. package/src/types/pg-to-go.ts +120 -0
  94. package/src/types/pg-to-java.ts +141 -0
  95. package/src/types/pg-to-kotlin.ts +119 -0
  96. package/src/types/pg-to-python.ts +120 -0
  97. package/src/types/pg-to-rust.ts +121 -0
  98. package/src/types/pg-to-ts.ts +173 -0
  99. package/src/typescript/index.ts +168 -0
  100. package/src/typescript/test/.gitignore +6 -0
  101. package/src/typescript/test/Dockerfile +8 -0
  102. package/src/typescript/test/test.ts +89 -0
  103. package/src/xsd/index.ts +191 -0
  104. package/src/xsd/test/.gitignore +6 -0
  105. package/src/xsd/test/Dockerfile +6 -0
  106. package/src/zod/index.ts +289 -0
  107. package/src/zod/test/.gitignore +6 -0
  108. package/src/zod/test/Dockerfile +6 -0
@@ -0,0 +1,71 @@
1
+ package main
2
+
3
+ import (
4
+ "context"
5
+ "fmt"
6
+ "os"
7
+
8
+ "github.com/jackc/pgx/v5"
9
+
10
+ "sqldoc-test/models"
11
+ )
12
+
13
+ var failed int
14
+
15
+ func assert(condition bool, msg string) {
16
+ if !condition {
17
+ fmt.Fprintf(os.Stderr, "FAIL: %s\n", msg)
18
+ failed++
19
+ } else {
20
+ fmt.Printf(" ok: %s\n", msg)
21
+ }
22
+ }
23
+
24
+ func main() {
25
+ ctx := context.Background()
26
+ dbURL := os.Getenv("DATABASE_URL")
27
+ if dbURL == "" {
28
+ fmt.Fprintln(os.Stderr, "DATABASE_URL not set")
29
+ os.Exit(1)
30
+ }
31
+
32
+ conn, err := pgx.Connect(ctx, dbURL)
33
+ if err != nil {
34
+ fmt.Fprintf(os.Stderr, "connect error: %v\n", err)
35
+ os.Exit(1)
36
+ }
37
+ defer conn.Close(ctx)
38
+
39
+ fmt.Println("--- go-structs integration test ---")
40
+
41
+ // 1. Query user into generated struct
42
+ var user models.Users
43
+ err = conn.QueryRow(ctx, "SELECT id, email, name, age, is_active FROM users WHERE id = 1").
44
+ Scan(&user.Id, &user.Email, &user.Name, &user.Age, &user.IsActive)
45
+ if err != nil {
46
+ fmt.Fprintf(os.Stderr, "query user error: %v\n", err)
47
+ os.Exit(1)
48
+ }
49
+ assert(user.Email == "test@example.com", "user.Email matches")
50
+ assert(user.Name != nil && *user.Name == "Test User", "user.Name matches")
51
+ assert(user.Age != nil && *user.Age == 30, "user.Age matches")
52
+ assert(user.IsActive == true, "user.IsActive matches")
53
+
54
+ // 2. Query post into generated struct
55
+ var post models.Posts
56
+ err = conn.QueryRow(ctx, "SELECT id, user_id, title, body, view_count FROM posts WHERE id = 1").
57
+ Scan(&post.Id, &post.UserId, &post.Title, &post.Body, &post.ViewCount)
58
+ if err != nil {
59
+ fmt.Fprintf(os.Stderr, "query post error: %v\n", err)
60
+ os.Exit(1)
61
+ }
62
+ assert(post.Title == "Hello World", "post.Title matches")
63
+ assert(post.UserId == 1, "post.UserId matches")
64
+ assert(post.ViewCount == 42, "post.ViewCount matches")
65
+
66
+ if failed > 0 {
67
+ fmt.Fprintf(os.Stderr, "\n%d assertion(s) failed\n", failed)
68
+ os.Exit(1)
69
+ }
70
+ fmt.Println("\nAll assertions passed!")
71
+ }
@@ -0,0 +1,134 @@
1
+ import { defineTemplate } from '@sqldoc/ns-codegen'
2
+ import { activeTables, enrichRealm } from '../helpers/enrich.ts'
3
+ import { toPascalCase } from '../helpers/naming.ts'
4
+ import { pgToGo } from '../types/pg-to-go.ts'
5
+
6
+ export default defineTemplate({
7
+ name: 'GORM Models',
8
+ description: 'Generate Go structs with GORM struct tags from SQL schema',
9
+ language: 'go',
10
+
11
+ generate(ctx) {
12
+ const schema = enrichRealm(ctx)
13
+ const allImports = new Set<string>()
14
+ const structBlocks: string[] = []
15
+
16
+ // Enums
17
+ for (const e of schema.enums) {
18
+ const typeName = toPascalCase(e.name)
19
+ structBlocks.push(`type ${typeName} string`)
20
+ structBlocks.push('')
21
+ const constLines = e.values.map((v) => {
22
+ const constName = `${typeName}${toPascalCase(v)}`
23
+ return `\t${constName} ${typeName} = "${v}"`
24
+ })
25
+ structBlocks.push(`const (\n${constLines.join('\n')}\n)`)
26
+ }
27
+
28
+ // Composite types (collected from columns)
29
+ const composites = new Map<string, Array<{ name: string; type: string }>>()
30
+ for (const table of schema.tables) {
31
+ for (const col of table.columns) {
32
+ if (col.category === 'composite' && col.compositeFields?.length && !composites.has(col.pgType)) {
33
+ composites.set(col.pgType, col.compositeFields)
34
+ }
35
+ }
36
+ }
37
+ for (const view of schema.views) {
38
+ for (const col of view.columns) {
39
+ if (col.category === 'composite' && col.compositeFields?.length && !composites.has(col.pgType)) {
40
+ composites.set(col.pgType, col.compositeFields)
41
+ }
42
+ }
43
+ }
44
+ for (const [name, fields] of composites) {
45
+ const typeName = toPascalCase(name)
46
+ const fieldLines = fields.map((f) => {
47
+ const mapped = pgToGo(f.type, false)
48
+ for (const imp of mapped.imports) allImports.add(imp)
49
+ const jsonTag = `json:"${f.name}"`
50
+ return `\t${toPascalCase(f.name)} ${mapped.type} \`${jsonTag}\``
51
+ })
52
+ structBlocks.push(`type ${typeName} struct {\n${fieldLines.join('\n')}\n}`)
53
+ }
54
+
55
+ // Helper to resolve Go type for a column
56
+ function resolveGoType(col: any): string {
57
+ if (col.typeOverride) return col.typeOverride
58
+ if (col.category === 'enum' && col.enumValues?.length) {
59
+ return col.nullable ? `*${toPascalCase(col.pgType)}` : toPascalCase(col.pgType)
60
+ }
61
+ if (col.category === 'composite' && col.compositeFields?.length) {
62
+ return col.nullable ? `*${toPascalCase(col.pgType)}` : toPascalCase(col.pgType)
63
+ }
64
+ const mapped = pgToGo(col.pgType, col.nullable, col.category)
65
+ for (const imp of mapped.imports) allImports.add(imp)
66
+ return mapped.type
67
+ }
68
+
69
+ for (const table of activeTables(schema)) {
70
+ const fields: string[] = []
71
+
72
+ for (const col of table.columns) {
73
+ const goType = resolveGoType(col)
74
+
75
+ // Build GORM tag parts
76
+ const gormParts: string[] = []
77
+ if (col.isPrimaryKey) gormParts.push('primaryKey')
78
+ gormParts.push(`type:${col.pgType}`)
79
+ if (!col.nullable) gormParts.push('not null')
80
+ if (col.isSerial) gormParts.push('autoIncrement')
81
+ if (col.defaultValue) gormParts.push(`default:${col.defaultValue}`)
82
+ if (col.foreignKey) {
83
+ gormParts.push(`foreignKey:${col.foreignKey.column}`)
84
+ }
85
+
86
+ const gormTag = `gorm:"${gormParts.join(';')}"`
87
+ const jsonTag = `json:"${col.name},omitempty"`
88
+ const tag = `\`${gormTag} ${jsonTag}\``
89
+
90
+ fields.push(`\t${col.pascalName} ${goType} ${tag}`)
91
+ }
92
+
93
+ structBlocks.push(`type ${table.pascalName} struct {\n${fields.join('\n')}\n}`)
94
+ }
95
+
96
+ // Views (read-only)
97
+ for (const view of schema.views.filter((v) => !v.skipped)) {
98
+ const fields: string[] = []
99
+
100
+ for (const col of view.columns) {
101
+ const goType = resolveGoType(col)
102
+
103
+ const gormTag = `gorm:"type:${col.pgType}"`
104
+ const jsonTag = `json:"${col.name},omitempty"`
105
+ const tag = `\`${gormTag} ${jsonTag}\``
106
+
107
+ fields.push(`\t${col.pascalName} ${goType} ${tag}`)
108
+ }
109
+
110
+ structBlocks.push(
111
+ `// ${view.pascalName} is read-only (from view)\ntype ${view.pascalName} struct {\n${fields.join('\n')}\n}`,
112
+ )
113
+ }
114
+
115
+ let importBlock = ''
116
+ if (allImports.size > 0) {
117
+ const sorted = [...allImports].sort()
118
+ if (sorted.length === 1) {
119
+ importBlock = `import "${sorted[0]}"\n\n`
120
+ } else {
121
+ importBlock = `import (\n${sorted.map((i) => `\t"${i}"`).join('\n')}\n)\n\n`
122
+ }
123
+ }
124
+
125
+ const content = `// Generated by @sqldoc/templates/gorm -- DO NOT EDIT
126
+ package models
127
+
128
+ ${importBlock}${structBlocks.join('\n\n')}\n`
129
+
130
+ return {
131
+ files: [{ path: 'models.go', content }],
132
+ }
133
+ },
134
+ })
@@ -0,0 +1,6 @@
1
+ # Generated by codegen — only Dockerfile and test scripts are tracked
2
+ *
3
+ !.gitignore
4
+ !Dockerfile
5
+ !test.*
6
+ !Test.*
@@ -0,0 +1,13 @@
1
+ FROM golang:1.24-alpine
2
+ WORKDIR /app
3
+ # Set up Go module with models as a sub-package
4
+ RUN go mod init sqldoc-test
5
+ RUN mkdir -p models cmd/test
6
+ COPY models.go models/
7
+ COPY test.go cmd/test/main.go
8
+ ENV GOTOOLCHAIN=auto
9
+ RUN go get github.com/jackc/pgx/v5 gorm.io/gorm gorm.io/driver/postgres && go mod tidy
10
+ # Step 1: typecheck/compile all packages
11
+ RUN go build -o /usr/local/bin/test-runner ./cmd/test
12
+ # Step 2: run integration test against real DB
13
+ CMD ["test-runner"]
@@ -0,0 +1,65 @@
1
+ package main
2
+
3
+ import (
4
+ "fmt"
5
+ "os"
6
+
7
+ "sqldoc-test/models"
8
+
9
+ "gorm.io/driver/postgres"
10
+ "gorm.io/gorm"
11
+ )
12
+
13
+ var failed int
14
+
15
+ func assert(condition bool, msg string) {
16
+ if !condition {
17
+ fmt.Fprintf(os.Stderr, "FAIL: %s\n", msg)
18
+ failed++
19
+ } else {
20
+ fmt.Printf(" ok: %s\n", msg)
21
+ }
22
+ }
23
+
24
+ func main() {
25
+ dbURL := os.Getenv("DATABASE_URL")
26
+ if dbURL == "" {
27
+ fmt.Fprintln(os.Stderr, "DATABASE_URL not set")
28
+ os.Exit(1)
29
+ }
30
+
31
+ db, err := gorm.Open(postgres.Open(dbURL), &gorm.Config{})
32
+ if err != nil {
33
+ fmt.Fprintf(os.Stderr, "connect error: %v\n", err)
34
+ os.Exit(1)
35
+ }
36
+
37
+ fmt.Println("--- gorm integration test ---")
38
+
39
+ // 1. Query user via GORM using generated model
40
+ var user models.Users
41
+ if err := db.First(&user, 1).Error; err != nil {
42
+ fmt.Fprintf(os.Stderr, "query user error: %v\n", err)
43
+ os.Exit(1)
44
+ }
45
+ assert(user.Email == "test@example.com", "user.Email matches")
46
+ assert(user.Name != nil && *user.Name == "Test User", "user.Name matches")
47
+ assert(user.Age != nil && *user.Age == 30, "user.Age matches")
48
+ assert(user.IsActive == true, "user.IsActive matches")
49
+
50
+ // 2. Query post via GORM using generated model
51
+ var post models.Posts
52
+ if err := db.First(&post, 1).Error; err != nil {
53
+ fmt.Fprintf(os.Stderr, "query post error: %v\n", err)
54
+ os.Exit(1)
55
+ }
56
+ assert(post.Title == "Hello World", "post.Title matches")
57
+ assert(post.UserId == 1, "post.UserId matches")
58
+ assert(post.ViewCount == 42, "post.ViewCount matches")
59
+
60
+ if failed > 0 {
61
+ fmt.Fprintf(os.Stderr, "\n%d assertion(s) failed\n", failed)
62
+ os.Exit(1)
63
+ }
64
+ fmt.Println("\nAll assertions passed!")
65
+ }
@@ -0,0 +1,43 @@
1
+ import type { AtlasColumn, AtlasRealm, AtlasTable, AtlasView } from '@sqldoc/atlas'
2
+
3
+ /** Extract all tables from all schemas in a realm */
4
+ export function getTablesFromRealm(realm: AtlasRealm): AtlasTable[] {
5
+ return realm.schemas.flatMap((s) => s.tables ?? [])
6
+ }
7
+
8
+ /** Extract all views from all schemas in a realm */
9
+ export function getViewsFromRealm(realm: AtlasRealm): AtlasView[] {
10
+ return realm.schemas.flatMap((s) => s.views ?? [])
11
+ }
12
+
13
+ /** Check if a column is nullable */
14
+ export function isNullable(column: AtlasColumn): boolean {
15
+ return column.type?.null === true
16
+ }
17
+
18
+ /** Get the column type string, falling back to 'unknown' */
19
+ export function getColumnType(column: AtlasColumn): string {
20
+ return column.type?.T ?? column.type?.raw ?? 'unknown'
21
+ }
22
+
23
+ /** Find all codegen tags for a given SQL object from allFileTags */
24
+ export function findTagsForObject(
25
+ allFileTags: Array<{
26
+ sourceFile: string
27
+ objects: Array<{
28
+ objectName: string
29
+ target: string
30
+ tags: Array<{ namespace: string; tag: string | null; args: Record<string, unknown> | unknown[] }>
31
+ }>
32
+ }>,
33
+ objectName: string,
34
+ ): Array<{ namespace: string; tag: string | null; args: Record<string, unknown> | unknown[] }> {
35
+ for (const file of allFileTags) {
36
+ for (const obj of file.objects) {
37
+ if (obj.objectName === objectName) {
38
+ return obj.tags
39
+ }
40
+ }
41
+ }
42
+ return []
43
+ }