@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.
- package/package.json +161 -0
- package/src/__tests__/dedent.test.ts +45 -0
- package/src/__tests__/docker-templates.test.ts +134 -0
- package/src/__tests__/go-structs.test.ts +184 -0
- package/src/__tests__/naming.test.ts +48 -0
- package/src/__tests__/python-dataclasses.test.ts +185 -0
- package/src/__tests__/rust-structs.test.ts +176 -0
- package/src/__tests__/tags-helpers.test.ts +72 -0
- package/src/__tests__/type-mapping.test.ts +332 -0
- package/src/__tests__/typescript.test.ts +202 -0
- package/src/cobol-copybook/index.ts +220 -0
- package/src/cobol-copybook/test/.gitignore +6 -0
- package/src/cobol-copybook/test/Dockerfile +7 -0
- package/src/csharp-records/index.ts +131 -0
- package/src/csharp-records/test/.gitignore +6 -0
- package/src/csharp-records/test/Dockerfile +6 -0
- package/src/diesel/index.ts +247 -0
- package/src/diesel/test/.gitignore +6 -0
- package/src/diesel/test/Dockerfile +16 -0
- package/src/drizzle/index.ts +255 -0
- package/src/drizzle/test/.gitignore +6 -0
- package/src/drizzle/test/Dockerfile +8 -0
- package/src/drizzle/test/test.ts +71 -0
- package/src/efcore/index.ts +190 -0
- package/src/efcore/test/.gitignore +6 -0
- package/src/efcore/test/Dockerfile +7 -0
- package/src/go-structs/index.ts +119 -0
- package/src/go-structs/test/.gitignore +6 -0
- package/src/go-structs/test/Dockerfile +13 -0
- package/src/go-structs/test/test.go +71 -0
- package/src/gorm/index.ts +134 -0
- package/src/gorm/test/.gitignore +6 -0
- package/src/gorm/test/Dockerfile +13 -0
- package/src/gorm/test/test.go +65 -0
- package/src/helpers/atlas.ts +43 -0
- package/src/helpers/enrich.ts +396 -0
- package/src/helpers/naming.ts +19 -0
- package/src/helpers/tags.ts +63 -0
- package/src/index.ts +24 -0
- package/src/java-records/index.ts +179 -0
- package/src/java-records/test/.gitignore +6 -0
- package/src/java-records/test/Dockerfile +11 -0
- package/src/java-records/test/Test.java +93 -0
- package/src/jpa/index.ts +279 -0
- package/src/jpa/test/.gitignore +6 -0
- package/src/jpa/test/Dockerfile +14 -0
- package/src/jpa/test/Test.java +111 -0
- package/src/json-schema/index.ts +351 -0
- package/src/json-schema/test/.gitignore +6 -0
- package/src/json-schema/test/Dockerfile +18 -0
- package/src/knex/index.ts +168 -0
- package/src/knex/test/.gitignore +6 -0
- package/src/knex/test/Dockerfile +7 -0
- package/src/knex/test/test.ts +75 -0
- package/src/kotlin-data/index.ts +147 -0
- package/src/kotlin-data/test/.gitignore +6 -0
- package/src/kotlin-data/test/Dockerfile +14 -0
- package/src/kotlin-data/test/Test.kt +82 -0
- package/src/kysely/index.ts +165 -0
- package/src/kysely/test/.gitignore +6 -0
- package/src/kysely/test/Dockerfile +8 -0
- package/src/kysely/test/test.ts +82 -0
- package/src/prisma/index.ts +387 -0
- package/src/prisma/test/.gitignore +6 -0
- package/src/prisma/test/Dockerfile +7 -0
- package/src/protobuf/index.ts +219 -0
- package/src/protobuf/test/.gitignore +6 -0
- package/src/protobuf/test/Dockerfile +6 -0
- package/src/pydantic/index.ts +272 -0
- package/src/pydantic/test/.gitignore +6 -0
- package/src/pydantic/test/Dockerfile +8 -0
- package/src/pydantic/test/test.py +63 -0
- package/src/python-dataclasses/index.ts +217 -0
- package/src/python-dataclasses/test/.gitignore +6 -0
- package/src/python-dataclasses/test/Dockerfile +8 -0
- package/src/python-dataclasses/test/test.py +63 -0
- package/src/rust-structs/index.ts +152 -0
- package/src/rust-structs/test/.gitignore +6 -0
- package/src/rust-structs/test/Dockerfile +22 -0
- package/src/rust-structs/test/test.rs +82 -0
- package/src/sqlalchemy/index.ts +258 -0
- package/src/sqlalchemy/test/.gitignore +6 -0
- package/src/sqlalchemy/test/Dockerfile +8 -0
- package/src/sqlalchemy/test/test.py +61 -0
- package/src/sqlc/index.ts +148 -0
- package/src/sqlc/test/.gitignore +6 -0
- package/src/sqlc/test/Dockerfile +13 -0
- package/src/sqlc/test/test.go +91 -0
- package/src/tags/dedent.ts +28 -0
- package/src/tags/index.ts +14 -0
- package/src/types/index.ts +8 -0
- package/src/types/pg-to-csharp.ts +136 -0
- package/src/types/pg-to-go.ts +120 -0
- package/src/types/pg-to-java.ts +141 -0
- package/src/types/pg-to-kotlin.ts +119 -0
- package/src/types/pg-to-python.ts +120 -0
- package/src/types/pg-to-rust.ts +121 -0
- package/src/types/pg-to-ts.ts +173 -0
- package/src/typescript/index.ts +168 -0
- package/src/typescript/test/.gitignore +6 -0
- package/src/typescript/test/Dockerfile +8 -0
- package/src/typescript/test/test.ts +89 -0
- package/src/xsd/index.ts +191 -0
- package/src/xsd/test/.gitignore +6 -0
- package/src/xsd/test/Dockerfile +6 -0
- package/src/zod/index.ts +289 -0
- package/src/zod/test/.gitignore +6 -0
- package/src/zod/test/Dockerfile +6 -0
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
import { pgToCsharp } from '../types/pg-to-csharp'
|
|
3
|
+
import { pgToGo } from '../types/pg-to-go'
|
|
4
|
+
import { pgToJava } from '../types/pg-to-java'
|
|
5
|
+
import { pgToKotlin } from '../types/pg-to-kotlin'
|
|
6
|
+
import { pgToPython } from '../types/pg-to-python'
|
|
7
|
+
import { pgToRust } from '../types/pg-to-rust'
|
|
8
|
+
import { pgToTs } from '../types/pg-to-ts'
|
|
9
|
+
|
|
10
|
+
describe('pgToTs', () => {
|
|
11
|
+
it('maps text to string', () => {
|
|
12
|
+
expect(pgToTs('text', false)).toBe('string')
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
it('maps bigint to number by default', () => {
|
|
16
|
+
expect(pgToTs('bigint', false)).toBe('number')
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
it('handles null-union nullable style', () => {
|
|
20
|
+
expect(pgToTs('bigint', true, { nullableStyle: 'null-union' })).toBe('number | null')
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
it('maps text[] to string[]', () => {
|
|
24
|
+
expect(pgToTs('text[]', false)).toBe('string[]')
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
it('maps _text array notation to string[]', () => {
|
|
28
|
+
expect(pgToTs('_text', false)).toBe('string[]')
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
it('maps timestamptz to Date by default', () => {
|
|
32
|
+
expect(pgToTs('timestamptz', false)).toBe('Date')
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
it('maps timestamptz with dateType string option', () => {
|
|
36
|
+
expect(pgToTs('timestamptz', false, { dateType: 'string' })).toBe('string')
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
it('strips length specifier from varchar(255)', () => {
|
|
40
|
+
expect(pgToTs('varchar(255)', false)).toBe('string')
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
it('maps jsonb to unknown', () => {
|
|
44
|
+
expect(pgToTs('jsonb', false)).toBe('unknown')
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
it('maps uuid to string', () => {
|
|
48
|
+
expect(pgToTs('uuid', false)).toBe('string')
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
it('maps boolean to boolean', () => {
|
|
52
|
+
expect(pgToTs('boolean', false)).toBe('boolean')
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
it('maps bytea to Buffer', () => {
|
|
56
|
+
expect(pgToTs('bytea', false)).toBe('Buffer')
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
it('maps numeric to string for precision', () => {
|
|
60
|
+
expect(pgToTs('numeric(10,2)', false)).toBe('string')
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
it('applies bigintType option', () => {
|
|
64
|
+
expect(pgToTs('bigint', false, { bigintType: 'bigint' })).toBe('bigint')
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
it('returns unknown for unrecognized types', () => {
|
|
68
|
+
expect(pgToTs('custom_enum', false)).toBe('unknown')
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
it('handles double precision', () => {
|
|
72
|
+
expect(pgToTs('double precision', false)).toBe('number')
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
// Category-based mapping tests
|
|
76
|
+
it('maps string category to string', () => {
|
|
77
|
+
expect(pgToTs('citext', false, {}, 'string')).toBe('string')
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
it('maps integer category to number', () => {
|
|
81
|
+
expect(pgToTs('some_int', false, {}, 'integer')).toBe('number')
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
it('maps boolean category to boolean', () => {
|
|
85
|
+
expect(pgToTs('custom_bool', false, {}, 'boolean')).toBe('boolean')
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
it('maps time category to Date', () => {
|
|
89
|
+
expect(pgToTs('custom_ts', false, {}, 'time')).toBe('Date')
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
it('maps json category to unknown', () => {
|
|
93
|
+
expect(pgToTs('custom_json', false, {}, 'json')).toBe('unknown')
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
it('maps uuid category to string', () => {
|
|
97
|
+
expect(pgToTs('custom_uuid', false, {}, 'uuid')).toBe('string')
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
it('falls back to raw type when no category', () => {
|
|
101
|
+
expect(pgToTs('text', false, {}, undefined)).toBe('string')
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
it('category with nullable null-union', () => {
|
|
105
|
+
expect(pgToTs('custom_int', true, { nullableStyle: 'null-union' }, 'integer')).toBe('number | null')
|
|
106
|
+
})
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
describe('pgToGo', () => {
|
|
110
|
+
it('maps text to string', () => {
|
|
111
|
+
expect(pgToGo('text', false)).toEqual({ type: 'string', imports: [] })
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
it('maps timestamptz nullable to *time.Time', () => {
|
|
115
|
+
expect(pgToGo('timestamptz', true)).toEqual({ type: '*time.Time', imports: ['time'] })
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
it('maps jsonb to json.RawMessage', () => {
|
|
119
|
+
expect(pgToGo('jsonb', false)).toEqual({ type: 'json.RawMessage', imports: ['encoding/json'] })
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
it('maps uuid to uuid.UUID', () => {
|
|
123
|
+
expect(pgToGo('uuid', false)).toEqual({ type: 'uuid.UUID', imports: ['github.com/google/uuid'] })
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
it('maps text[] to []string', () => {
|
|
127
|
+
expect(pgToGo('text[]', false)).toEqual({ type: '[]string', imports: [] })
|
|
128
|
+
})
|
|
129
|
+
|
|
130
|
+
it('maps nullable text to pointer', () => {
|
|
131
|
+
expect(pgToGo('text', true)).toEqual({ type: '*string', imports: [] })
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
it('returns interface{} for unknown types', () => {
|
|
135
|
+
expect(pgToGo('custom_enum', false)).toEqual({ type: 'interface{}', imports: [] })
|
|
136
|
+
})
|
|
137
|
+
|
|
138
|
+
it('maps bigint to int64', () => {
|
|
139
|
+
expect(pgToGo('bigint', false)).toEqual({ type: 'int64', imports: [] })
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
// Category-based mapping tests
|
|
143
|
+
it('maps string category to string', () => {
|
|
144
|
+
expect(pgToGo('custom_text', false, 'string')).toEqual({ type: 'string', imports: [] })
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
it('maps integer category to int64', () => {
|
|
148
|
+
expect(pgToGo('custom_int', false, 'integer')).toEqual({ type: 'int64', imports: [] })
|
|
149
|
+
})
|
|
150
|
+
|
|
151
|
+
it('maps time category to time.Time with import', () => {
|
|
152
|
+
expect(pgToGo('custom_ts', false, 'time')).toEqual({ type: 'time.Time', imports: ['time'] })
|
|
153
|
+
})
|
|
154
|
+
|
|
155
|
+
it('maps uuid category to uuid.UUID with import', () => {
|
|
156
|
+
expect(pgToGo('custom_uuid', false, 'uuid')).toEqual({ type: 'uuid.UUID', imports: ['github.com/google/uuid'] })
|
|
157
|
+
})
|
|
158
|
+
|
|
159
|
+
it('category with nullable uses pointer', () => {
|
|
160
|
+
expect(pgToGo('custom_int', true, 'integer')).toEqual({ type: '*int64', imports: [] })
|
|
161
|
+
})
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
describe('pgToPython', () => {
|
|
165
|
+
it('maps bigint to int', () => {
|
|
166
|
+
expect(pgToPython('bigint', false)).toBe('int')
|
|
167
|
+
})
|
|
168
|
+
|
|
169
|
+
it('maps timestamptz nullable to Optional[datetime]', () => {
|
|
170
|
+
expect(pgToPython('timestamptz', true)).toBe('Optional[datetime]')
|
|
171
|
+
})
|
|
172
|
+
|
|
173
|
+
it('maps text to str', () => {
|
|
174
|
+
expect(pgToPython('text', false)).toBe('str')
|
|
175
|
+
})
|
|
176
|
+
|
|
177
|
+
it('maps boolean to bool', () => {
|
|
178
|
+
expect(pgToPython('boolean', false)).toBe('bool')
|
|
179
|
+
})
|
|
180
|
+
|
|
181
|
+
it('maps jsonb to dict', () => {
|
|
182
|
+
expect(pgToPython('jsonb', false)).toBe('dict')
|
|
183
|
+
})
|
|
184
|
+
|
|
185
|
+
// Category-based mapping tests
|
|
186
|
+
it('maps string category to str', () => {
|
|
187
|
+
expect(pgToPython('custom_text', false, 'string')).toBe('str')
|
|
188
|
+
})
|
|
189
|
+
|
|
190
|
+
it('maps integer category to int', () => {
|
|
191
|
+
expect(pgToPython('custom_int', false, 'integer')).toBe('int')
|
|
192
|
+
})
|
|
193
|
+
|
|
194
|
+
it('maps decimal category to Decimal', () => {
|
|
195
|
+
expect(pgToPython('custom_num', false, 'decimal')).toBe('Decimal')
|
|
196
|
+
})
|
|
197
|
+
|
|
198
|
+
it('category with nullable uses Optional', () => {
|
|
199
|
+
expect(pgToPython('custom_int', true, 'integer')).toBe('Optional[int]')
|
|
200
|
+
})
|
|
201
|
+
})
|
|
202
|
+
|
|
203
|
+
describe('pgToJava', () => {
|
|
204
|
+
it('maps bigint to long', () => {
|
|
205
|
+
expect(pgToJava('bigint', false)).toEqual({ type: 'long', imports: [] })
|
|
206
|
+
})
|
|
207
|
+
|
|
208
|
+
it('maps uuid to UUID with import', () => {
|
|
209
|
+
expect(pgToJava('uuid', false)).toEqual({ type: 'UUID', imports: ['java.util.UUID'] })
|
|
210
|
+
})
|
|
211
|
+
|
|
212
|
+
it('maps text to String', () => {
|
|
213
|
+
expect(pgToJava('text', false)).toEqual({ type: 'String', imports: [] })
|
|
214
|
+
})
|
|
215
|
+
|
|
216
|
+
it('maps nullable bigint to Long wrapper', () => {
|
|
217
|
+
expect(pgToJava('bigint', true)).toEqual({ type: 'Long', imports: [] })
|
|
218
|
+
})
|
|
219
|
+
|
|
220
|
+
// Category-based mapping tests
|
|
221
|
+
it('maps string category to String', () => {
|
|
222
|
+
expect(pgToJava('custom_text', false, 'string')).toEqual({ type: 'String', imports: [] })
|
|
223
|
+
})
|
|
224
|
+
|
|
225
|
+
it('maps integer category to long', () => {
|
|
226
|
+
expect(pgToJava('custom_int', false, 'integer')).toEqual({ type: 'long', imports: [] })
|
|
227
|
+
})
|
|
228
|
+
|
|
229
|
+
it('maps decimal category to BigDecimal with import', () => {
|
|
230
|
+
expect(pgToJava('custom_num', false, 'decimal')).toEqual({ type: 'BigDecimal', imports: ['java.math.BigDecimal'] })
|
|
231
|
+
})
|
|
232
|
+
|
|
233
|
+
it('category integer nullable uses Long wrapper', () => {
|
|
234
|
+
expect(pgToJava('custom_int', true, 'integer')).toEqual({ type: 'Long', imports: [] })
|
|
235
|
+
})
|
|
236
|
+
})
|
|
237
|
+
|
|
238
|
+
describe('pgToKotlin', () => {
|
|
239
|
+
it('maps text nullable to String?', () => {
|
|
240
|
+
expect(pgToKotlin('text', true)).toBe('String?')
|
|
241
|
+
})
|
|
242
|
+
|
|
243
|
+
it('maps text non-nullable to String', () => {
|
|
244
|
+
expect(pgToKotlin('text', false)).toBe('String')
|
|
245
|
+
})
|
|
246
|
+
|
|
247
|
+
it('maps bigint to Long', () => {
|
|
248
|
+
expect(pgToKotlin('bigint', false)).toBe('Long')
|
|
249
|
+
})
|
|
250
|
+
|
|
251
|
+
// Category-based mapping tests
|
|
252
|
+
it('maps string category to String', () => {
|
|
253
|
+
expect(pgToKotlin('custom_text', false, 'string')).toBe('String')
|
|
254
|
+
})
|
|
255
|
+
|
|
256
|
+
it('maps integer category to Long', () => {
|
|
257
|
+
expect(pgToKotlin('custom_int', false, 'integer')).toBe('Long')
|
|
258
|
+
})
|
|
259
|
+
|
|
260
|
+
it('category with nullable uses ? suffix', () => {
|
|
261
|
+
expect(pgToKotlin('custom_int', true, 'integer')).toBe('Long?')
|
|
262
|
+
})
|
|
263
|
+
})
|
|
264
|
+
|
|
265
|
+
describe('pgToRust', () => {
|
|
266
|
+
it('maps bigint to i64', () => {
|
|
267
|
+
expect(pgToRust('bigint', false)).toEqual({ type: 'i64', imports: [] })
|
|
268
|
+
})
|
|
269
|
+
|
|
270
|
+
it('maps uuid to Uuid with import', () => {
|
|
271
|
+
expect(pgToRust('uuid', false)).toEqual({ type: 'Uuid', imports: ['uuid::Uuid'] })
|
|
272
|
+
})
|
|
273
|
+
|
|
274
|
+
it('maps nullable text to Option<String>', () => {
|
|
275
|
+
expect(pgToRust('text', true)).toEqual({ type: 'Option<String>', imports: [] })
|
|
276
|
+
})
|
|
277
|
+
|
|
278
|
+
// Category-based mapping tests
|
|
279
|
+
it('maps string category to String', () => {
|
|
280
|
+
expect(pgToRust('custom_text', false, 'string')).toEqual({ type: 'String', imports: [] })
|
|
281
|
+
})
|
|
282
|
+
|
|
283
|
+
it('maps integer category to i64', () => {
|
|
284
|
+
expect(pgToRust('custom_int', false, 'integer')).toEqual({ type: 'i64', imports: [] })
|
|
285
|
+
})
|
|
286
|
+
|
|
287
|
+
it('maps json category to serde_json::Value with import', () => {
|
|
288
|
+
expect(pgToRust('custom_json', false, 'json')).toEqual({
|
|
289
|
+
type: 'serde_json::Value',
|
|
290
|
+
imports: ['serde_json::Value'],
|
|
291
|
+
})
|
|
292
|
+
})
|
|
293
|
+
|
|
294
|
+
it('category with nullable uses Option', () => {
|
|
295
|
+
expect(pgToRust('custom_int', true, 'integer')).toEqual({ type: 'Option<i64>', imports: [] })
|
|
296
|
+
})
|
|
297
|
+
})
|
|
298
|
+
|
|
299
|
+
describe('pgToCsharp', () => {
|
|
300
|
+
it('maps timestamptz to DateTimeOffset', () => {
|
|
301
|
+
expect(pgToCsharp('timestamptz', false)).toBe('DateTimeOffset')
|
|
302
|
+
})
|
|
303
|
+
|
|
304
|
+
it('maps integer nullable to int?', () => {
|
|
305
|
+
expect(pgToCsharp('integer', true)).toBe('int?')
|
|
306
|
+
})
|
|
307
|
+
|
|
308
|
+
it('maps text to string', () => {
|
|
309
|
+
expect(pgToCsharp('text', false)).toBe('string')
|
|
310
|
+
})
|
|
311
|
+
|
|
312
|
+
it('maps boolean to bool', () => {
|
|
313
|
+
expect(pgToCsharp('boolean', false)).toBe('bool')
|
|
314
|
+
})
|
|
315
|
+
|
|
316
|
+
// Category-based mapping tests
|
|
317
|
+
it('maps string category to string', () => {
|
|
318
|
+
expect(pgToCsharp('custom_text', false, 'string')).toBe('string')
|
|
319
|
+
})
|
|
320
|
+
|
|
321
|
+
it('maps integer category to long', () => {
|
|
322
|
+
expect(pgToCsharp('custom_int', false, 'integer')).toBe('long')
|
|
323
|
+
})
|
|
324
|
+
|
|
325
|
+
it('maps uuid category to Guid', () => {
|
|
326
|
+
expect(pgToCsharp('custom_uuid', false, 'uuid')).toBe('Guid')
|
|
327
|
+
})
|
|
328
|
+
|
|
329
|
+
it('category with nullable uses ? suffix', () => {
|
|
330
|
+
expect(pgToCsharp('custom_int', true, 'integer')).toBe('long?')
|
|
331
|
+
})
|
|
332
|
+
})
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
import typescript from '../typescript/index.ts'
|
|
3
|
+
|
|
4
|
+
const generate = typescript.generate
|
|
5
|
+
|
|
6
|
+
import type { AtlasRealm } from '@sqldoc/atlas'
|
|
7
|
+
import type { TemplateContext } from '@sqldoc/ns-codegen'
|
|
8
|
+
|
|
9
|
+
const testRealm: AtlasRealm = {
|
|
10
|
+
schemas: [
|
|
11
|
+
{
|
|
12
|
+
name: 'public',
|
|
13
|
+
tables: [
|
|
14
|
+
{
|
|
15
|
+
name: 'users',
|
|
16
|
+
columns: [
|
|
17
|
+
{ name: 'id', type: { T: 'bigserial', null: false, category: 'integer' } },
|
|
18
|
+
{ name: 'email', type: { T: 'character varying', raw: 'varchar(255)', null: false, category: 'string' } },
|
|
19
|
+
{ name: 'name', type: { T: 'text', null: true, category: 'string' } },
|
|
20
|
+
{ name: 'age', type: { T: 'integer', null: true, category: 'integer' } },
|
|
21
|
+
{ name: 'is_active', type: { T: 'boolean', null: false, category: 'boolean' } },
|
|
22
|
+
{ name: 'metadata', type: { T: 'jsonb', null: true, category: 'json' } },
|
|
23
|
+
{ name: 'created_at', type: { T: 'timestamp with time zone', null: false, category: 'time' } },
|
|
24
|
+
{ name: 'tags', type: { T: 'text[]', null: true, category: 'array' } },
|
|
25
|
+
{ name: 'avatar', type: { T: 'bytea', null: true, category: 'binary' } },
|
|
26
|
+
{ name: 'balance', type: { T: 'numeric(10,2)', null: true, category: 'decimal' } },
|
|
27
|
+
{ name: 'external_id', type: { T: 'uuid', null: true, category: 'uuid' } },
|
|
28
|
+
],
|
|
29
|
+
primary_key: { parts: [{ column: 'id' }] },
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
name: 'posts',
|
|
33
|
+
columns: [
|
|
34
|
+
{ name: 'id', type: { T: 'bigserial', null: false, category: 'integer' } },
|
|
35
|
+
{ name: 'user_id', type: { T: 'bigint', null: false, category: 'integer' } },
|
|
36
|
+
{ name: 'title', type: { T: 'text', null: false, category: 'string' } },
|
|
37
|
+
{ name: 'body', type: { T: 'text', null: false, category: 'string' } },
|
|
38
|
+
{ name: 'published_at', type: { T: 'timestamp with time zone', null: true, category: 'time' } },
|
|
39
|
+
{ name: 'view_count', type: { T: 'integer', null: false, category: 'integer' } },
|
|
40
|
+
{ name: 'rating', type: { T: 'double precision', null: true, category: 'float' } },
|
|
41
|
+
],
|
|
42
|
+
primary_key: { parts: [{ column: 'id' }] },
|
|
43
|
+
foreign_keys: [
|
|
44
|
+
{ symbol: 'posts_user_id_fkey', columns: ['user_id'], ref_table: 'users', ref_columns: ['id'] },
|
|
45
|
+
],
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
name: 'comments',
|
|
49
|
+
columns: [
|
|
50
|
+
{ name: 'id', type: { T: 'bigserial', null: false, category: 'integer' } },
|
|
51
|
+
{ name: 'post_id', type: { T: 'bigint', null: false, category: 'integer' } },
|
|
52
|
+
{ name: 'user_id', type: { T: 'bigint', null: false, category: 'integer' } },
|
|
53
|
+
{ name: 'content', type: { T: 'text', null: false, category: 'string' } },
|
|
54
|
+
{ name: 'created_at', type: { T: 'timestamp with time zone', null: false, category: 'time' } },
|
|
55
|
+
],
|
|
56
|
+
primary_key: { parts: [{ column: 'id' }] },
|
|
57
|
+
foreign_keys: [
|
|
58
|
+
{ symbol: 'comments_post_id_fkey', columns: ['post_id'], ref_table: 'posts', ref_columns: ['id'] },
|
|
59
|
+
{ symbol: 'comments_user_id_fkey', columns: ['user_id'], ref_table: 'users', ref_columns: ['id'] },
|
|
60
|
+
],
|
|
61
|
+
},
|
|
62
|
+
],
|
|
63
|
+
},
|
|
64
|
+
],
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function makeCtx(overrides?: Partial<TemplateContext>): TemplateContext {
|
|
68
|
+
return {
|
|
69
|
+
realm: testRealm,
|
|
70
|
+
allFileTags: [],
|
|
71
|
+
docsMeta: [],
|
|
72
|
+
config: {},
|
|
73
|
+
output: './out',
|
|
74
|
+
templateName: 'typescript',
|
|
75
|
+
...overrides,
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
describe('typescript template', () => {
|
|
80
|
+
it('generates interfaces for all tables', () => {
|
|
81
|
+
const result = generate(makeCtx())
|
|
82
|
+
expect(result.files).toHaveLength(1)
|
|
83
|
+
expect(result.files[0].path).toBe('models.ts')
|
|
84
|
+
|
|
85
|
+
const content = result.files[0].content
|
|
86
|
+
expect(content).toContain('export interface Users {')
|
|
87
|
+
expect(content).toContain('export interface Posts {')
|
|
88
|
+
expect(content).toContain('export interface Comments {')
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
it('maps bigserial to number', () => {
|
|
92
|
+
const result = generate(makeCtx())
|
|
93
|
+
const content = result.files[0].content
|
|
94
|
+
expect(content).toContain('id: number')
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
it('uses optional (?) for nullable columns by default', () => {
|
|
98
|
+
const result = generate(makeCtx())
|
|
99
|
+
const content = result.files[0].content
|
|
100
|
+
expect(content).toContain('name?: string')
|
|
101
|
+
expect(content).toContain('age?: number')
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
it('uses null-union style when configured', () => {
|
|
105
|
+
const result = generate(makeCtx({ config: { nullableStyle: 'null-union' } }))
|
|
106
|
+
const content = result.files[0].content
|
|
107
|
+
expect(content).toContain('name: string | null')
|
|
108
|
+
expect(content).toContain('age: number | null')
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
it('maps text[] to string[] array type', () => {
|
|
112
|
+
const result = generate(makeCtx())
|
|
113
|
+
const content = result.files[0].content
|
|
114
|
+
expect(content).toContain('tags?: string[]')
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
it('maps jsonb to unknown', () => {
|
|
118
|
+
const result = generate(makeCtx())
|
|
119
|
+
const content = result.files[0].content
|
|
120
|
+
expect(content).toContain('metadata?: unknown')
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
it('maps bytea to Buffer', () => {
|
|
124
|
+
const result = generate(makeCtx())
|
|
125
|
+
const content = result.files[0].content
|
|
126
|
+
expect(content).toContain('avatar?: Buffer')
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
it('maps uuid to string', () => {
|
|
130
|
+
const result = generate(makeCtx())
|
|
131
|
+
const content = result.files[0].content
|
|
132
|
+
expect(content).toContain('externalId?: string')
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
it('includes header comment', () => {
|
|
136
|
+
const result = generate(makeCtx())
|
|
137
|
+
const content = result.files[0].content
|
|
138
|
+
expect(content).toContain('// Generated by @sqldoc/templates/typescript -- DO NOT EDIT')
|
|
139
|
+
})
|
|
140
|
+
|
|
141
|
+
it('respects @codegen.skip tag', () => {
|
|
142
|
+
const ctx = makeCtx({
|
|
143
|
+
allFileTags: [
|
|
144
|
+
{
|
|
145
|
+
sourceFile: 'test.sql',
|
|
146
|
+
objects: [
|
|
147
|
+
{
|
|
148
|
+
objectName: 'comments',
|
|
149
|
+
target: 'table',
|
|
150
|
+
tags: [{ namespace: 'codegen', tag: 'skip', args: [] }],
|
|
151
|
+
},
|
|
152
|
+
],
|
|
153
|
+
},
|
|
154
|
+
],
|
|
155
|
+
})
|
|
156
|
+
const result = generate(ctx)
|
|
157
|
+
const content = result.files[0].content
|
|
158
|
+
expect(content).not.toContain('export interface Comments')
|
|
159
|
+
expect(content).toContain('export interface Users')
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
it('respects @codegen.rename tag', () => {
|
|
163
|
+
const ctx = makeCtx({
|
|
164
|
+
allFileTags: [
|
|
165
|
+
{
|
|
166
|
+
sourceFile: 'test.sql',
|
|
167
|
+
objects: [
|
|
168
|
+
{
|
|
169
|
+
objectName: 'users',
|
|
170
|
+
target: 'table',
|
|
171
|
+
tags: [{ namespace: 'codegen', tag: 'rename', args: ['Account'] }],
|
|
172
|
+
},
|
|
173
|
+
],
|
|
174
|
+
},
|
|
175
|
+
],
|
|
176
|
+
})
|
|
177
|
+
const result = generate(ctx)
|
|
178
|
+
const content = result.files[0].content
|
|
179
|
+
expect(content).toContain('export interface Account {')
|
|
180
|
+
expect(content).not.toContain('export interface Users {')
|
|
181
|
+
})
|
|
182
|
+
|
|
183
|
+
it('respects @codegen.type override on column', () => {
|
|
184
|
+
const ctx = makeCtx({
|
|
185
|
+
allFileTags: [
|
|
186
|
+
{
|
|
187
|
+
sourceFile: 'test.sql',
|
|
188
|
+
objects: [
|
|
189
|
+
{
|
|
190
|
+
objectName: 'users.metadata',
|
|
191
|
+
target: 'column',
|
|
192
|
+
tags: [{ namespace: 'codegen', tag: 'type', args: ['Record<string, any>'] }],
|
|
193
|
+
},
|
|
194
|
+
],
|
|
195
|
+
},
|
|
196
|
+
],
|
|
197
|
+
})
|
|
198
|
+
const result = generate(ctx)
|
|
199
|
+
const content = result.files[0].content
|
|
200
|
+
expect(content).toContain('metadata?: Record<string, any>')
|
|
201
|
+
})
|
|
202
|
+
})
|