mutano 3.0.2 → 3.0.4
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/README.md +1 -1
- package/dist/main.d.ts +163 -8
- package/dist/main.js +891 -38
- package/package.json +8 -8
- package/dist/constants.d.ts +0 -6
- package/dist/database/connection.d.ts +0 -25
- package/dist/database/prisma.d.ts +0 -20
- package/dist/generators/content-generator.d.ts +0 -12
- package/dist/generators/type-generator.d.ts +0 -9
- package/dist/types/index.d.ts +0 -107
- package/dist/types/mappings.d.ts +0 -91
- package/dist/utils/filters.d.ts +0 -23
- package/dist/utils/magic-comments.d.ts +0 -33
package/dist/main.js
CHANGED
|
@@ -1,27 +1,890 @@
|
|
|
1
|
-
import * as path from
|
|
2
|
-
import camelCase from
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
5
|
-
import
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
1
|
+
import * as path from 'node:path';
|
|
2
|
+
import camelCase from 'camelcase';
|
|
3
|
+
import { writeFile } from 'node:fs/promises';
|
|
4
|
+
import { ensureDir } from 'fs-extra/esm';
|
|
5
|
+
import knex from 'knex';
|
|
6
|
+
import { readFileSync } from 'node:fs';
|
|
7
|
+
import { createPrismaSchemaBuilder } from '@mrleebo/prisma-ast';
|
|
8
|
+
|
|
9
|
+
function filterEntities(entities, included, ignored) {
|
|
10
|
+
let filtered = [...entities];
|
|
11
|
+
if (included?.length) {
|
|
12
|
+
filtered = filtered.filter((entity) => included.includes(entity));
|
|
13
|
+
}
|
|
14
|
+
if (ignored?.length) {
|
|
15
|
+
const ignoredRegex = ignored.filter((ignoreString) => {
|
|
16
|
+
return ignoreString.startsWith("/") && ignoreString.endsWith("/");
|
|
17
|
+
});
|
|
18
|
+
const ignoredNames = ignored.filter(
|
|
19
|
+
(entity) => !ignoredRegex.includes(entity)
|
|
20
|
+
);
|
|
21
|
+
if (ignoredNames.length) {
|
|
22
|
+
filtered = filtered.filter((entity) => !ignoredNames.includes(entity));
|
|
23
|
+
}
|
|
24
|
+
if (ignoredRegex.length) {
|
|
25
|
+
filtered = filtered.filter((entity) => {
|
|
26
|
+
let useEntity = true;
|
|
27
|
+
for (const text of ignoredRegex) {
|
|
28
|
+
const pattern = text.substring(1, text.length - 1);
|
|
29
|
+
if (entity.match(pattern) !== null) useEntity = false;
|
|
30
|
+
}
|
|
31
|
+
return useEntity;
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return filtered;
|
|
36
|
+
}
|
|
37
|
+
function filterTables(tables, includedTables, ignoredTables) {
|
|
38
|
+
return filterEntities(tables, includedTables, ignoredTables);
|
|
39
|
+
}
|
|
40
|
+
function filterViews(views, includedViews, ignoredViews) {
|
|
41
|
+
return filterEntities(views, includedViews, ignoredViews);
|
|
42
|
+
}
|
|
43
|
+
function createEntityList(tables, views) {
|
|
44
|
+
const allEntities = [
|
|
45
|
+
...tables.map((name) => ({ name, type: "table" })),
|
|
46
|
+
...views.map((name) => ({ name, type: "view" }))
|
|
47
|
+
];
|
|
48
|
+
return allEntities.sort((a, b) => a.name.localeCompare(b.name));
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const dateTypes = {
|
|
52
|
+
mysql: ["date", "datetime", "timestamp"],
|
|
53
|
+
postgres: [
|
|
54
|
+
"timestamp",
|
|
55
|
+
"timestamp with time zone",
|
|
56
|
+
"timestamp without time zone",
|
|
57
|
+
"date"
|
|
58
|
+
],
|
|
59
|
+
sqlite: ["date", "datetime"],
|
|
60
|
+
prisma: ["DateTime"]
|
|
61
|
+
};
|
|
62
|
+
const stringTypes = {
|
|
63
|
+
mysql: [
|
|
64
|
+
"tinytext",
|
|
65
|
+
"text",
|
|
66
|
+
"mediumtext",
|
|
67
|
+
"longtext",
|
|
68
|
+
"json",
|
|
69
|
+
"time",
|
|
70
|
+
"year",
|
|
71
|
+
"char",
|
|
72
|
+
"varchar"
|
|
73
|
+
],
|
|
74
|
+
postgres: [
|
|
75
|
+
"text",
|
|
76
|
+
"character varying",
|
|
77
|
+
"varchar",
|
|
78
|
+
"char",
|
|
79
|
+
"character",
|
|
80
|
+
"json",
|
|
81
|
+
"jsonb",
|
|
82
|
+
"uuid",
|
|
83
|
+
"time",
|
|
84
|
+
"timetz",
|
|
85
|
+
"interval",
|
|
86
|
+
"name",
|
|
87
|
+
"citext"
|
|
88
|
+
],
|
|
89
|
+
sqlite: [
|
|
90
|
+
"text",
|
|
91
|
+
"character",
|
|
92
|
+
"varchar",
|
|
93
|
+
"varying character",
|
|
94
|
+
"nchar",
|
|
95
|
+
"native character",
|
|
96
|
+
"nvarchar",
|
|
97
|
+
"clob",
|
|
98
|
+
"json"
|
|
99
|
+
],
|
|
100
|
+
prisma: ["String", "Bytes", "Json"]
|
|
101
|
+
};
|
|
102
|
+
const bigIntTypes = {
|
|
103
|
+
mysql: ["bigint"],
|
|
104
|
+
postgres: ["bigint"],
|
|
105
|
+
sqlite: ["bigint", "unsigned big int", "int8"],
|
|
106
|
+
prisma: ["BigInt"]
|
|
107
|
+
};
|
|
108
|
+
const numberTypes = {
|
|
109
|
+
mysql: [
|
|
110
|
+
"tinyint",
|
|
111
|
+
"smallint",
|
|
112
|
+
"mediumint",
|
|
113
|
+
"int",
|
|
114
|
+
"float",
|
|
115
|
+
"double",
|
|
116
|
+
"bit",
|
|
117
|
+
"year"
|
|
118
|
+
],
|
|
119
|
+
postgres: [
|
|
120
|
+
"smallint",
|
|
121
|
+
"integer",
|
|
122
|
+
"real",
|
|
123
|
+
"double precision",
|
|
124
|
+
"smallserial",
|
|
125
|
+
"serial",
|
|
126
|
+
"bigserial",
|
|
127
|
+
"bit",
|
|
128
|
+
"bit varying"
|
|
129
|
+
],
|
|
130
|
+
sqlite: [
|
|
131
|
+
"integer",
|
|
132
|
+
"real",
|
|
133
|
+
"numeric",
|
|
134
|
+
"double",
|
|
135
|
+
"double precision",
|
|
136
|
+
"float",
|
|
137
|
+
"int",
|
|
138
|
+
"int2",
|
|
139
|
+
"mediumint",
|
|
140
|
+
"tinyint",
|
|
141
|
+
"smallint"
|
|
142
|
+
],
|
|
143
|
+
prisma: ["Int", "Float"]
|
|
144
|
+
};
|
|
145
|
+
const decimalTypes = {
|
|
146
|
+
mysql: ["decimal"],
|
|
147
|
+
postgres: ["decimal", "numeric", "money"],
|
|
148
|
+
sqlite: ["decimal"],
|
|
149
|
+
prisma: ["Decimal"]
|
|
150
|
+
};
|
|
151
|
+
const booleanTypes = {
|
|
152
|
+
mysql: ["boolean"],
|
|
153
|
+
postgres: ["boolean"],
|
|
154
|
+
sqlite: ["boolean"],
|
|
155
|
+
prisma: ["Boolean"]
|
|
156
|
+
};
|
|
157
|
+
const enumTypes = {
|
|
158
|
+
mysql: ["enum"],
|
|
159
|
+
postgres: ["enum", "USER-DEFINED"],
|
|
160
|
+
sqlite: [],
|
|
161
|
+
prisma: []
|
|
162
|
+
};
|
|
163
|
+
const enumRegex = /enum\(([^)]+)\)/;
|
|
164
|
+
function getTypeMappings(dbType) {
|
|
165
|
+
return {
|
|
166
|
+
dateTypes: dateTypes[dbType],
|
|
167
|
+
stringTypes: stringTypes[dbType],
|
|
168
|
+
bigIntTypes: bigIntTypes[dbType],
|
|
169
|
+
numberTypes: numberTypes[dbType],
|
|
170
|
+
decimalTypes: decimalTypes[dbType],
|
|
171
|
+
booleanTypes: booleanTypes[dbType],
|
|
172
|
+
enumTypes: enumTypes[dbType]
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
function isJsonType(type) {
|
|
176
|
+
return type.toLowerCase().includes("json");
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const extractTypeExpression = (comment, prefix) => {
|
|
180
|
+
const start = comment.indexOf(prefix);
|
|
181
|
+
if (start === -1) return null;
|
|
182
|
+
const typeLen = prefix.length;
|
|
183
|
+
let position = start + typeLen;
|
|
184
|
+
let depth = 1;
|
|
185
|
+
while (position < comment.length && depth > 0) {
|
|
186
|
+
const char = comment[position];
|
|
187
|
+
if (char === "(" || char === "{" || char === "<" || char === "[") {
|
|
188
|
+
depth++;
|
|
189
|
+
} else if (char === ")" || char === "}" || char === ">" || char === "]") {
|
|
190
|
+
depth--;
|
|
191
|
+
if (depth === 0) {
|
|
192
|
+
const extracted = comment.substring(start + typeLen, position);
|
|
193
|
+
return extracted;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
position++;
|
|
197
|
+
}
|
|
198
|
+
return null;
|
|
199
|
+
};
|
|
200
|
+
const extractTSExpression = (comment) => extractTypeExpression(comment, "@ts(");
|
|
201
|
+
const extractKyselyExpression = (comment) => extractTypeExpression(comment, "@kysely(");
|
|
202
|
+
const extractZodExpression = (comment) => extractTypeExpression(comment, "@zod(");
|
|
203
|
+
|
|
204
|
+
function getType(op, desc, config, destination) {
|
|
205
|
+
const { Default, Extra, Null, Type, Comment, EnumOptions } = desc;
|
|
206
|
+
const schemaType = config.origin.type;
|
|
207
|
+
const type = schemaType === "prisma" ? Type : Type.toLowerCase();
|
|
208
|
+
const isNull = Null === "YES";
|
|
209
|
+
const hasDefaultValue = Default !== null;
|
|
210
|
+
const isGenerated = Extra.toLowerCase().includes("auto_increment") || Extra.toLowerCase().includes("default_generated");
|
|
211
|
+
const isTsDestination = destination.type === "ts";
|
|
212
|
+
const isKyselyDestination = destination.type === "kysely";
|
|
213
|
+
const isZodDestination = destination.type === "zod";
|
|
214
|
+
const typeMappings = getTypeMappings(schemaType);
|
|
215
|
+
if (isTsDestination || isKyselyDestination) {
|
|
216
|
+
const isJsonField = isJsonType(type);
|
|
217
|
+
if (isKyselyDestination && isJsonField) {
|
|
218
|
+
if (config.magicComments) {
|
|
219
|
+
const kyselyOverrideType = extractKyselyExpression(Comment);
|
|
220
|
+
if (kyselyOverrideType) {
|
|
221
|
+
const shouldBeNullable2 = isNull || ["insertable", "updateable"].includes(op) && (hasDefaultValue || isGenerated) || op === "updateable" && !isNull && !hasDefaultValue;
|
|
222
|
+
return shouldBeNullable2 ? kyselyOverrideType.includes("| null") ? kyselyOverrideType : `${kyselyOverrideType} | null` : kyselyOverrideType;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
const shouldBeNullable = isNull || ["insertable", "updateable"].includes(op) && (hasDefaultValue || isGenerated) || op === "updateable" && !isNull && !hasDefaultValue;
|
|
226
|
+
return shouldBeNullable ? "Json | null" : "Json";
|
|
227
|
+
}
|
|
228
|
+
if (isKyselyDestination && config.magicComments) {
|
|
229
|
+
const kyselyOverrideType = extractKyselyExpression(Comment);
|
|
230
|
+
if (kyselyOverrideType) {
|
|
231
|
+
const shouldBeNullable = isNull || ["insertable", "updateable"].includes(op) && (hasDefaultValue || isGenerated) || op === "updateable" && !isNull && !hasDefaultValue;
|
|
232
|
+
return shouldBeNullable ? kyselyOverrideType.includes("| null") ? kyselyOverrideType : `${kyselyOverrideType} | null` : kyselyOverrideType;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
if ((isTsDestination || isKyselyDestination) && config.magicComments) {
|
|
236
|
+
const tsOverrideType = extractTSExpression(Comment);
|
|
237
|
+
if (tsOverrideType) {
|
|
238
|
+
const shouldBeNullable = isNull || ["insertable", "updateable"].includes(op) && (hasDefaultValue || isGenerated) || op === "updateable" && !isNull && !hasDefaultValue;
|
|
239
|
+
return shouldBeNullable ? tsOverrideType.includes("| null") ? tsOverrideType : `${tsOverrideType} | null` : tsOverrideType;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
if (isZodDestination && config.magicComments) {
|
|
244
|
+
const zodOverrideType = extractZodExpression(Comment);
|
|
245
|
+
if (zodOverrideType) {
|
|
246
|
+
const shouldBeNullable = isNull || ["insertable", "updateable"].includes(op) && (hasDefaultValue || isGenerated) || op === "updateable" && !isNull && !hasDefaultValue;
|
|
247
|
+
const nullishOption = destination.nullish;
|
|
248
|
+
const nullableMethod = nullishOption ? "nullish" : "nullable";
|
|
249
|
+
return shouldBeNullable ? zodOverrideType.includes(`.${nullableMethod}()`) || zodOverrideType.includes(".optional()") ? zodOverrideType : `${zodOverrideType}.${nullableMethod}()` : zodOverrideType;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
const overrideTypes = config.origin.overrideTypes;
|
|
253
|
+
if (overrideTypes && overrideTypes[Type]) {
|
|
254
|
+
const overrideType = overrideTypes[Type];
|
|
255
|
+
const shouldBeNullable = isNull || ["insertable", "updateable"].includes(op) && (hasDefaultValue || isGenerated) || op === "updateable" && !isNull && !hasDefaultValue;
|
|
256
|
+
if (isZodDestination) {
|
|
257
|
+
const nullishOption = destination.nullish;
|
|
258
|
+
const nullableMethod = nullishOption ? "nullish" : "nullable";
|
|
259
|
+
return shouldBeNullable ? `${overrideType}.${nullableMethod}()` : overrideType;
|
|
260
|
+
} else {
|
|
261
|
+
return shouldBeNullable ? `${overrideType} | null` : overrideType;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
const enumTypesForSchema = typeMappings.enumTypes[schemaType] || [];
|
|
265
|
+
const isEnum = enumTypesForSchema.includes(type);
|
|
266
|
+
if (isEnum) {
|
|
267
|
+
let enumValues = [];
|
|
268
|
+
if (schemaType === "mysql" && type === "enum") {
|
|
269
|
+
const match = Type.match(enumRegex);
|
|
270
|
+
if (match) {
|
|
271
|
+
enumValues = match[1].split(",").map((v) => v.trim().replace(/'/g, ""));
|
|
272
|
+
}
|
|
273
|
+
} else if (schemaType === "postgres" && EnumOptions) {
|
|
274
|
+
enumValues = EnumOptions;
|
|
275
|
+
}
|
|
276
|
+
if (enumValues.length > 0) {
|
|
277
|
+
const shouldBeNullable = isNull || ["insertable", "updateable"].includes(op) && (hasDefaultValue || isGenerated) || op === "updateable" && !isNull && !hasDefaultValue;
|
|
278
|
+
if (isZodDestination) {
|
|
279
|
+
const enumString = `z.enum([${enumValues.map((v) => `'${v}'`).join(",")}])`;
|
|
280
|
+
const nullishOption = destination.nullish;
|
|
281
|
+
const nullableMethod = nullishOption ? "nullish" : "nullable";
|
|
282
|
+
return shouldBeNullable ? `${enumString}.${nullableMethod}()` : enumString;
|
|
283
|
+
} else if (isTsDestination) {
|
|
284
|
+
const enumType = destination.enumType;
|
|
285
|
+
if (enumType === "enum") {
|
|
286
|
+
const enumString = enumValues.map((v) => `'${v}'`).join(" | ");
|
|
287
|
+
return shouldBeNullable ? `${enumString} | null` : enumString;
|
|
288
|
+
} else {
|
|
289
|
+
const enumString = enumValues.map((v) => `'${v}'`).join(" | ");
|
|
290
|
+
return shouldBeNullable ? `${enumString} | null` : enumString;
|
|
291
|
+
}
|
|
292
|
+
} else if (isKyselyDestination) {
|
|
293
|
+
const enumString = enumValues.map((v) => `'${v}'`).join(" | ");
|
|
294
|
+
return shouldBeNullable ? `${enumString} | null` : enumString;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
return generateStandardType(op, desc, config, destination, typeMappings);
|
|
299
|
+
}
|
|
300
|
+
function generateStandardType(op, desc, config, destination, typeMappings) {
|
|
301
|
+
const { Default, Extra, Null, Type } = desc;
|
|
302
|
+
const schemaType = config.origin.type;
|
|
303
|
+
const type = schemaType === "prisma" ? Type : Type.toLowerCase();
|
|
304
|
+
const isNull = Null === "YES";
|
|
305
|
+
const hasDefaultValue = Default !== null;
|
|
306
|
+
const isGenerated = Extra.toLowerCase().includes("auto_increment") || Extra.toLowerCase().includes("default_generated");
|
|
307
|
+
const isZodDestination = destination.type === "zod";
|
|
308
|
+
const isKyselyDestination = destination.type === "kysely";
|
|
309
|
+
const shouldBeNullable = isNull || ["insertable", "updateable"].includes(op) && (hasDefaultValue || isGenerated) || op === "updateable" && !isNull && !hasDefaultValue;
|
|
310
|
+
let baseType;
|
|
311
|
+
if (typeMappings.dateTypes.includes(type)) {
|
|
312
|
+
if (isZodDestination) {
|
|
313
|
+
const useDateType = destination.useDateType;
|
|
314
|
+
if (useDateType) {
|
|
315
|
+
baseType = "z.union([z.number(), z.string(), z.date()]).pipe(z.coerce.date())";
|
|
316
|
+
} else {
|
|
317
|
+
baseType = "z.date()";
|
|
318
|
+
}
|
|
319
|
+
} else {
|
|
320
|
+
baseType = "Date";
|
|
321
|
+
}
|
|
322
|
+
} else if (typeMappings.bigIntTypes.includes(type)) {
|
|
323
|
+
if (isZodDestination) {
|
|
324
|
+
baseType = "z.string()";
|
|
325
|
+
} else if (isKyselyDestination) {
|
|
326
|
+
baseType = "BigInt";
|
|
327
|
+
} else {
|
|
328
|
+
baseType = "string";
|
|
329
|
+
}
|
|
330
|
+
} else if (typeMappings.decimalTypes.includes(type)) {
|
|
331
|
+
if (isZodDestination) {
|
|
332
|
+
baseType = "z.string()";
|
|
333
|
+
} else if (isKyselyDestination) {
|
|
334
|
+
baseType = "Decimal";
|
|
335
|
+
} else {
|
|
336
|
+
baseType = "string";
|
|
337
|
+
}
|
|
338
|
+
} else if (typeMappings.numberTypes.includes(type)) {
|
|
339
|
+
if (isZodDestination) {
|
|
340
|
+
baseType = "z.number()";
|
|
341
|
+
if (!shouldBeNullable && !hasDefaultValue) {
|
|
342
|
+
baseType += ".nonnegative()";
|
|
343
|
+
}
|
|
344
|
+
} else {
|
|
345
|
+
baseType = "number";
|
|
346
|
+
}
|
|
347
|
+
} else if (typeMappings.booleanTypes.includes(type)) {
|
|
348
|
+
baseType = isZodDestination ? "z.boolean()" : "boolean";
|
|
349
|
+
} else if (typeMappings.stringTypes.includes(type)) {
|
|
350
|
+
if (isZodDestination) {
|
|
351
|
+
const useTrim = destination.useTrim;
|
|
352
|
+
const requiredString = destination.requiredString;
|
|
353
|
+
baseType = "z.string()";
|
|
354
|
+
if (useTrim) baseType += ".trim()";
|
|
355
|
+
if (requiredString && !shouldBeNullable) baseType += ".min(1)";
|
|
356
|
+
} else {
|
|
357
|
+
baseType = "string";
|
|
358
|
+
}
|
|
359
|
+
} else {
|
|
360
|
+
baseType = isZodDestination ? "z.string()" : "string";
|
|
361
|
+
}
|
|
362
|
+
if (shouldBeNullable) {
|
|
363
|
+
if (isZodDestination) {
|
|
364
|
+
const nullishOption = destination.nullish;
|
|
365
|
+
const nullableMethod = nullishOption ? "nullish" : "nullable";
|
|
366
|
+
return `${baseType}.${nullableMethod}()`;
|
|
367
|
+
} else {
|
|
368
|
+
return `${baseType} | null`;
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
return baseType;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
function generateViewContent({
|
|
375
|
+
view,
|
|
376
|
+
describes,
|
|
377
|
+
config,
|
|
378
|
+
destination,
|
|
379
|
+
isCamelCase,
|
|
380
|
+
enumDeclarations: _enumDeclarations,
|
|
381
|
+
defaultZodHeader
|
|
382
|
+
}) {
|
|
383
|
+
let content = "";
|
|
384
|
+
if (destination.type === "kysely") {
|
|
385
|
+
const pascalView = camelCase(view, { pascalCase: true });
|
|
386
|
+
content += `// Kysely type definitions for ${view} (view)
|
|
387
|
+
|
|
388
|
+
`;
|
|
389
|
+
content += `// This interface defines the structure of the '${view}' view (read-only)
|
|
390
|
+
`;
|
|
391
|
+
content += `export interface ${pascalView}View {
|
|
392
|
+
`;
|
|
393
|
+
for (const desc of describes) {
|
|
394
|
+
const fieldName = isCamelCase ? camelCase(desc.Field) : desc.Field;
|
|
395
|
+
const fieldType = getType("selectable", desc, config, destination);
|
|
396
|
+
content += ` ${fieldName}: ${fieldType};
|
|
397
|
+
`;
|
|
398
|
+
}
|
|
399
|
+
content += "}\n\n";
|
|
400
|
+
content += `// Helper types for ${view} (view - read-only)
|
|
401
|
+
`;
|
|
402
|
+
content += `export type Selectable${pascalView}View = Selectable<${pascalView}View>;
|
|
403
|
+
`;
|
|
404
|
+
} else if (destination.type === "ts") {
|
|
405
|
+
const pascalView = camelCase(view, { pascalCase: true });
|
|
406
|
+
content += `// TypeScript interface for ${view} (view - read-only)
|
|
407
|
+
`;
|
|
408
|
+
content += `export interface ${pascalView}View {
|
|
409
|
+
`;
|
|
410
|
+
for (const desc of describes) {
|
|
411
|
+
const fieldName = isCamelCase ? camelCase(desc.Field) : desc.Field;
|
|
412
|
+
const fieldType = getType("selectable", desc, config, destination);
|
|
413
|
+
content += ` ${fieldName}: ${fieldType};
|
|
414
|
+
`;
|
|
415
|
+
}
|
|
416
|
+
content += "}\n";
|
|
417
|
+
} else if (destination.type === "zod") {
|
|
418
|
+
const version = destination.version || 3;
|
|
419
|
+
const header = destination.header || defaultZodHeader(version);
|
|
420
|
+
if (!content.includes(header)) {
|
|
421
|
+
content += header;
|
|
422
|
+
}
|
|
423
|
+
content += `// View schema (read-only)
|
|
424
|
+
`;
|
|
425
|
+
content += `export const ${view}_view = z.object({
|
|
426
|
+
`;
|
|
427
|
+
for (const desc of describes) {
|
|
428
|
+
const fieldName = isCamelCase ? camelCase(desc.Field) : desc.Field;
|
|
429
|
+
const fieldType = getType("selectable", desc, config, destination);
|
|
430
|
+
content += ` ${fieldName}: ${fieldType},
|
|
431
|
+
`;
|
|
432
|
+
}
|
|
433
|
+
content += "})\n\n";
|
|
434
|
+
const pascalView = camelCase(view, { pascalCase: true });
|
|
435
|
+
content += `export type ${camelCase(`${pascalView}ViewType`, {
|
|
436
|
+
pascalCase: true
|
|
437
|
+
})} = z.infer<typeof ${view}_view>
|
|
438
|
+
`;
|
|
439
|
+
}
|
|
440
|
+
return content;
|
|
441
|
+
}
|
|
442
|
+
function generateContent({
|
|
443
|
+
table,
|
|
444
|
+
describes,
|
|
445
|
+
config,
|
|
446
|
+
destination,
|
|
447
|
+
isCamelCase,
|
|
448
|
+
enumDeclarations: _enumDeclarations,
|
|
449
|
+
defaultZodHeader
|
|
450
|
+
}) {
|
|
451
|
+
let content = "";
|
|
452
|
+
if (destination.type === "ts") {
|
|
453
|
+
return generateTypeScriptContent({
|
|
454
|
+
table,
|
|
455
|
+
describes,
|
|
456
|
+
config,
|
|
457
|
+
destination,
|
|
458
|
+
isCamelCase
|
|
459
|
+
});
|
|
460
|
+
} else if (destination.type === "kysely") {
|
|
461
|
+
return generateKyselyContent({
|
|
462
|
+
table,
|
|
463
|
+
describes,
|
|
464
|
+
config,
|
|
465
|
+
destination,
|
|
466
|
+
isCamelCase
|
|
467
|
+
});
|
|
468
|
+
} else if (destination.type === "zod") {
|
|
469
|
+
return generateZodContent({
|
|
470
|
+
table,
|
|
471
|
+
describes,
|
|
472
|
+
config,
|
|
473
|
+
destination,
|
|
474
|
+
isCamelCase,
|
|
475
|
+
defaultZodHeader
|
|
476
|
+
});
|
|
477
|
+
}
|
|
478
|
+
return content;
|
|
479
|
+
}
|
|
480
|
+
function generateTypeScriptContent({
|
|
481
|
+
table,
|
|
482
|
+
describes,
|
|
483
|
+
config,
|
|
484
|
+
destination,
|
|
485
|
+
isCamelCase
|
|
486
|
+
}) {
|
|
487
|
+
let content = "";
|
|
488
|
+
const pascalTable = camelCase(table, { pascalCase: true });
|
|
489
|
+
content += `// TypeScript interfaces for ${table}
|
|
490
|
+
|
|
491
|
+
`;
|
|
492
|
+
content += `export interface ${pascalTable} {
|
|
493
|
+
`;
|
|
494
|
+
for (const desc of describes) {
|
|
495
|
+
const fieldName = isCamelCase ? camelCase(desc.Field) : desc.Field;
|
|
496
|
+
const fieldType = getType("table", desc, config, destination);
|
|
497
|
+
content += ` ${fieldName}: ${fieldType};
|
|
498
|
+
`;
|
|
499
|
+
}
|
|
500
|
+
content += "}\n\n";
|
|
501
|
+
content += `export interface Insertable${pascalTable} {
|
|
502
|
+
`;
|
|
503
|
+
for (const desc of describes) {
|
|
504
|
+
const fieldName = isCamelCase ? camelCase(desc.Field) : desc.Field;
|
|
505
|
+
const fieldType = getType("insertable", desc, config, destination);
|
|
506
|
+
content += ` ${fieldName}: ${fieldType};
|
|
507
|
+
`;
|
|
508
|
+
}
|
|
509
|
+
content += "}\n\n";
|
|
510
|
+
content += `export interface Updateable${pascalTable} {
|
|
511
|
+
`;
|
|
512
|
+
for (const desc of describes) {
|
|
513
|
+
const fieldName = isCamelCase ? camelCase(desc.Field) : desc.Field;
|
|
514
|
+
const fieldType = getType("updateable", desc, config, destination);
|
|
515
|
+
content += ` ${fieldName}: ${fieldType};
|
|
516
|
+
`;
|
|
517
|
+
}
|
|
518
|
+
content += "}\n\n";
|
|
519
|
+
content += `export interface Selectable${pascalTable} {
|
|
520
|
+
`;
|
|
521
|
+
for (const desc of describes) {
|
|
522
|
+
const fieldName = isCamelCase ? camelCase(desc.Field) : desc.Field;
|
|
523
|
+
const fieldType = getType("selectable", desc, config, destination);
|
|
524
|
+
content += ` ${fieldName}: ${fieldType};
|
|
525
|
+
`;
|
|
526
|
+
}
|
|
527
|
+
content += "}\n";
|
|
528
|
+
return content;
|
|
529
|
+
}
|
|
530
|
+
function generateKyselyContent({
|
|
531
|
+
table,
|
|
532
|
+
describes,
|
|
533
|
+
config,
|
|
534
|
+
destination,
|
|
535
|
+
isCamelCase
|
|
536
|
+
}) {
|
|
537
|
+
let content = "";
|
|
538
|
+
const pascalTable = camelCase(table, { pascalCase: true });
|
|
539
|
+
content += `// Kysely type definitions for ${table}
|
|
540
|
+
|
|
541
|
+
`;
|
|
542
|
+
content += `// This interface defines the structure of the '${table}' table
|
|
543
|
+
`;
|
|
544
|
+
content += `export interface ${pascalTable}Table {
|
|
545
|
+
`;
|
|
546
|
+
for (const desc of describes) {
|
|
547
|
+
const fieldName = isCamelCase ? camelCase(desc.Field) : desc.Field;
|
|
548
|
+
let fieldType = getType("table", desc, config, destination);
|
|
549
|
+
const isAutoIncrement = desc.Extra.toLowerCase().includes("auto_increment");
|
|
550
|
+
const isDefaultGenerated = desc.Extra.toLowerCase().includes("default_generated");
|
|
551
|
+
if (isAutoIncrement || isDefaultGenerated) {
|
|
552
|
+
fieldType = `Generated<${fieldType.replace(" | null", "")}>${fieldType.includes(" | null") ? " | null" : ""}`;
|
|
553
|
+
}
|
|
554
|
+
content += ` ${fieldName}: ${fieldType};
|
|
555
|
+
`;
|
|
556
|
+
}
|
|
557
|
+
content += "}\n\n";
|
|
558
|
+
content += `// Use these types for inserting, selecting and updating the table
|
|
559
|
+
`;
|
|
560
|
+
content += `export type ${pascalTable} = Selectable<${pascalTable}Table>;
|
|
561
|
+
`;
|
|
562
|
+
content += `export type New${pascalTable} = Insertable<${pascalTable}Table>;
|
|
563
|
+
`;
|
|
564
|
+
content += `export type ${pascalTable}Update = Updateable<${pascalTable}Table>;
|
|
565
|
+
`;
|
|
566
|
+
return content;
|
|
567
|
+
}
|
|
568
|
+
function generateZodContent({
|
|
569
|
+
table,
|
|
570
|
+
describes,
|
|
571
|
+
config,
|
|
572
|
+
destination,
|
|
573
|
+
isCamelCase,
|
|
574
|
+
defaultZodHeader
|
|
575
|
+
}) {
|
|
576
|
+
let content = "";
|
|
577
|
+
const version = destination.version || 3;
|
|
578
|
+
const header = destination.header || defaultZodHeader(version);
|
|
579
|
+
if (!content.includes(header)) {
|
|
580
|
+
content += header;
|
|
581
|
+
}
|
|
582
|
+
content += `export const ${table} = z.object({
|
|
583
|
+
`;
|
|
584
|
+
for (const desc of describes) {
|
|
585
|
+
const fieldName = isCamelCase ? camelCase(desc.Field) : desc.Field;
|
|
586
|
+
const fieldType = getType("table", desc, config, destination);
|
|
587
|
+
content += ` ${fieldName}: ${fieldType},
|
|
588
|
+
`;
|
|
589
|
+
}
|
|
590
|
+
content += "})\n\n";
|
|
591
|
+
content += `export const insertable_${table} = z.object({
|
|
592
|
+
`;
|
|
593
|
+
for (const desc of describes) {
|
|
594
|
+
const fieldName = isCamelCase ? camelCase(desc.Field) : desc.Field;
|
|
595
|
+
const fieldType = getType("insertable", desc, config, destination);
|
|
596
|
+
content += ` ${fieldName}: ${fieldType},
|
|
597
|
+
`;
|
|
598
|
+
}
|
|
599
|
+
content += "})\n\n";
|
|
600
|
+
content += `export const updateable_${table} = z.object({
|
|
601
|
+
`;
|
|
602
|
+
for (const desc of describes) {
|
|
603
|
+
const fieldName = isCamelCase ? camelCase(desc.Field) : desc.Field;
|
|
604
|
+
const fieldType = getType("updateable", desc, config, destination);
|
|
605
|
+
content += ` ${fieldName}: ${fieldType},
|
|
606
|
+
`;
|
|
607
|
+
}
|
|
608
|
+
content += "})\n\n";
|
|
609
|
+
content += `export const selectable_${table} = z.object({
|
|
610
|
+
`;
|
|
611
|
+
for (const desc of describes) {
|
|
612
|
+
const fieldName = isCamelCase ? camelCase(desc.Field) : desc.Field;
|
|
613
|
+
const fieldType = getType("selectable", desc, config, destination);
|
|
614
|
+
content += ` ${fieldName}: ${fieldType},
|
|
615
|
+
`;
|
|
616
|
+
}
|
|
617
|
+
content += "})\n\n";
|
|
618
|
+
content += `export type ${camelCase(`${table}Type`, { pascalCase: true })} = z.infer<typeof ${table}>
|
|
619
|
+
`;
|
|
620
|
+
content += `export type Insertable${camelCase(`${table}Type`, { pascalCase: true })} = z.infer<typeof insertable_${table}>
|
|
621
|
+
`;
|
|
622
|
+
content += `export type Updateable${camelCase(`${table}Type`, { pascalCase: true })} = z.infer<typeof updateable_${table}>
|
|
623
|
+
`;
|
|
624
|
+
content += `export type Selectable${camelCase(`${table}Type`, { pascalCase: true })} = z.infer<typeof selectable_${table}>
|
|
625
|
+
`;
|
|
626
|
+
return content;
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
function createDatabaseConnection(config) {
|
|
630
|
+
const { origin } = config;
|
|
631
|
+
switch (origin.type) {
|
|
632
|
+
case "mysql":
|
|
633
|
+
return knex({
|
|
634
|
+
client: "mysql2",
|
|
635
|
+
connection: {
|
|
636
|
+
host: origin.host,
|
|
637
|
+
port: origin.port,
|
|
638
|
+
user: origin.user,
|
|
639
|
+
password: origin.password,
|
|
640
|
+
database: origin.database,
|
|
641
|
+
ssl: origin.ssl
|
|
642
|
+
}
|
|
643
|
+
});
|
|
644
|
+
case "postgres":
|
|
645
|
+
return knex({
|
|
646
|
+
client: "pg",
|
|
647
|
+
connection: {
|
|
648
|
+
host: origin.host,
|
|
649
|
+
port: origin.port,
|
|
650
|
+
user: origin.user,
|
|
651
|
+
password: origin.password,
|
|
652
|
+
database: origin.database,
|
|
653
|
+
ssl: origin.ssl
|
|
654
|
+
},
|
|
655
|
+
searchPath: origin.schema ? [origin.schema] : ["public"]
|
|
656
|
+
});
|
|
657
|
+
case "sqlite":
|
|
658
|
+
return knex({
|
|
659
|
+
client: "sqlite3",
|
|
660
|
+
connection: {
|
|
661
|
+
filename: origin.path
|
|
662
|
+
},
|
|
663
|
+
useNullAsDefault: true
|
|
664
|
+
});
|
|
665
|
+
default:
|
|
666
|
+
throw new Error(`Unsupported database type: ${origin.type}`);
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
async function extractTables(db, config) {
|
|
670
|
+
const { origin } = config;
|
|
671
|
+
switch (origin.type) {
|
|
672
|
+
case "mysql":
|
|
673
|
+
const mysqlTables = await db.raw(`
|
|
674
|
+
SELECT table_name
|
|
675
|
+
FROM information_schema.tables
|
|
676
|
+
WHERE table_schema = ? AND table_type = 'BASE TABLE'
|
|
677
|
+
`, [origin.database]);
|
|
678
|
+
return mysqlTables[0].map((row) => row.table_name);
|
|
679
|
+
case "postgres":
|
|
680
|
+
const schema = origin.schema || "public";
|
|
681
|
+
const postgresTables = await db.raw(`
|
|
682
|
+
SELECT table_name
|
|
683
|
+
FROM information_schema.tables
|
|
684
|
+
WHERE table_schema = ? AND table_type = 'BASE TABLE'
|
|
685
|
+
`, [schema]);
|
|
686
|
+
return postgresTables.rows.map((row) => row.table_name);
|
|
687
|
+
case "sqlite":
|
|
688
|
+
const sqliteTables = await db.raw(`
|
|
689
|
+
SELECT name
|
|
690
|
+
FROM sqlite_master
|
|
691
|
+
WHERE type = 'table' AND name NOT LIKE 'sqlite_%'
|
|
692
|
+
`);
|
|
693
|
+
return sqliteTables.map((row) => row.name);
|
|
694
|
+
default:
|
|
695
|
+
return [];
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
async function extractViews(db, config) {
|
|
699
|
+
const { origin } = config;
|
|
700
|
+
switch (origin.type) {
|
|
701
|
+
case "mysql":
|
|
702
|
+
const mysqlViews = await db.raw(`
|
|
703
|
+
SELECT table_name
|
|
704
|
+
FROM information_schema.tables
|
|
705
|
+
WHERE table_schema = ? AND table_type = 'VIEW'
|
|
706
|
+
`, [origin.database]);
|
|
707
|
+
return mysqlViews[0].map((row) => row.table_name);
|
|
708
|
+
case "postgres":
|
|
709
|
+
const schema = origin.schema || "public";
|
|
710
|
+
const postgresViews = await db.raw(`
|
|
711
|
+
SELECT table_name
|
|
712
|
+
FROM information_schema.tables
|
|
713
|
+
WHERE table_schema = ? AND table_type = 'VIEW'
|
|
714
|
+
`, [schema]);
|
|
715
|
+
return postgresViews.rows.map((row) => row.table_name);
|
|
716
|
+
case "sqlite":
|
|
717
|
+
const sqliteViews = await db.raw(`
|
|
718
|
+
SELECT name
|
|
719
|
+
FROM sqlite_master
|
|
720
|
+
WHERE type = 'view'
|
|
721
|
+
`);
|
|
722
|
+
return sqliteViews.map((row) => row.name);
|
|
723
|
+
default:
|
|
724
|
+
return [];
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
async function extractColumnDescriptions(db, config, tableName) {
|
|
728
|
+
const { origin } = config;
|
|
729
|
+
switch (origin.type) {
|
|
730
|
+
case "mysql":
|
|
731
|
+
const mysqlColumns = await db.raw(`
|
|
732
|
+
SELECT
|
|
733
|
+
column_name as \`Field\`,
|
|
734
|
+
column_default as \`Default\`,
|
|
735
|
+
extra as \`Extra\`,
|
|
736
|
+
is_nullable as \`Null\`,
|
|
737
|
+
column_type as \`Type\`,
|
|
738
|
+
column_comment as \`Comment\`
|
|
739
|
+
FROM information_schema.columns
|
|
740
|
+
WHERE table_schema = ? AND table_name = ?
|
|
741
|
+
ORDER BY ordinal_position
|
|
742
|
+
`, [origin.database, tableName]);
|
|
743
|
+
return mysqlColumns[0].map((row) => ({
|
|
744
|
+
Field: row.Field,
|
|
745
|
+
Default: row.Default,
|
|
746
|
+
Extra: row.Extra || "",
|
|
747
|
+
Null: row.Null,
|
|
748
|
+
Type: row.Type,
|
|
749
|
+
Comment: row.Comment || ""
|
|
750
|
+
}));
|
|
751
|
+
case "postgres":
|
|
752
|
+
const schema = origin.schema || "public";
|
|
753
|
+
const postgresColumns = await db.raw(`
|
|
754
|
+
SELECT
|
|
755
|
+
column_name as "Field",
|
|
756
|
+
column_default as "Default",
|
|
757
|
+
'' as "Extra",
|
|
758
|
+
is_nullable as "Null",
|
|
759
|
+
data_type as "Type",
|
|
760
|
+
'' as "Comment"
|
|
761
|
+
FROM information_schema.columns
|
|
762
|
+
WHERE table_schema = ? AND table_name = ?
|
|
763
|
+
ORDER BY ordinal_position
|
|
764
|
+
`, [schema, tableName]);
|
|
765
|
+
return postgresColumns.rows.map((row) => ({
|
|
766
|
+
Field: row.Field,
|
|
767
|
+
Default: row.Default,
|
|
768
|
+
Extra: row.Extra || "",
|
|
769
|
+
Null: row.Null,
|
|
770
|
+
Type: row.Type,
|
|
771
|
+
Comment: row.Comment || ""
|
|
772
|
+
}));
|
|
773
|
+
case "sqlite":
|
|
774
|
+
const sqliteColumns = await db.raw(`PRAGMA table_info(${tableName})`);
|
|
775
|
+
return sqliteColumns.map((row) => ({
|
|
776
|
+
Field: row.name,
|
|
777
|
+
Default: row.dflt_value,
|
|
778
|
+
Extra: row.pk ? "PRIMARY KEY" : "",
|
|
779
|
+
Null: row.notnull ? "NO" : "YES",
|
|
780
|
+
Type: row.type,
|
|
781
|
+
Comment: ""
|
|
782
|
+
}));
|
|
783
|
+
default:
|
|
784
|
+
return [];
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
function extractPrismaEntities(config) {
|
|
789
|
+
if (config.origin.type !== "prisma") {
|
|
790
|
+
return { tables: [], views: [], enumDeclarations: {} };
|
|
791
|
+
}
|
|
792
|
+
const schemaContent = readFileSync(config.origin.path, "utf-8");
|
|
793
|
+
const schema = createPrismaSchemaBuilder(schemaContent);
|
|
794
|
+
const prismaModels = schema.findAllByType("model", {});
|
|
795
|
+
const tables = prismaModels.filter((m) => m !== null).map((model) => model.name);
|
|
796
|
+
const prismaViews = schema.findAllByType("view", {});
|
|
797
|
+
const views = prismaViews.filter((v) => v !== null).map((view) => view.name);
|
|
798
|
+
const enumDeclarations = {};
|
|
799
|
+
const prismaEnums = schema.findAllByType("enum", {});
|
|
800
|
+
for (const prismaEnum of prismaEnums) {
|
|
801
|
+
if (prismaEnum && "name" in prismaEnum && "enumerators" in prismaEnum) {
|
|
802
|
+
const enumName = prismaEnum.name;
|
|
803
|
+
const enumerators = prismaEnum.enumerators;
|
|
804
|
+
enumDeclarations[enumName] = enumerators.map((e) => e.name);
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
return { tables, views, enumDeclarations };
|
|
808
|
+
}
|
|
809
|
+
function extractPrismaColumnDescriptions(config, entityName, enumDeclarations) {
|
|
810
|
+
if (config.origin.type !== "prisma") {
|
|
811
|
+
return [];
|
|
812
|
+
}
|
|
813
|
+
const schemaContent = readFileSync(config.origin.path, "utf-8");
|
|
814
|
+
const schema = createPrismaSchemaBuilder(schemaContent);
|
|
815
|
+
let entity = schema.findByType("model", { name: entityName });
|
|
816
|
+
if (!entity) {
|
|
817
|
+
entity = schema.findByType("view", { name: entityName });
|
|
818
|
+
}
|
|
819
|
+
if (!entity || !("properties" in entity)) {
|
|
820
|
+
return [];
|
|
821
|
+
}
|
|
822
|
+
const fields = entity.properties.filter(
|
|
823
|
+
(p) => p.type === "field" && p.array !== true && !p.attributes?.find((a) => a.name === "relation")
|
|
824
|
+
);
|
|
825
|
+
return fields.map((field) => {
|
|
826
|
+
let defaultGenerated = false;
|
|
827
|
+
let defaultValue = null;
|
|
828
|
+
if (field.attributes) {
|
|
829
|
+
for (const attr of field.attributes) {
|
|
830
|
+
if (attr.name === "default") {
|
|
831
|
+
if (attr.args && attr.args.length > 0) {
|
|
832
|
+
const arg = attr.args[0];
|
|
833
|
+
if (typeof arg === "object" && "value" in arg) {
|
|
834
|
+
if (arg.value === "autoincrement()" || arg.value === "cuid()" || arg.value === "uuid()") {
|
|
835
|
+
defaultGenerated = true;
|
|
836
|
+
} else {
|
|
837
|
+
defaultValue = String(arg.value);
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
const isOptional = field.optional === true;
|
|
845
|
+
let enumOptions;
|
|
846
|
+
const fieldType = String(field.fieldType);
|
|
847
|
+
if (enumDeclarations[fieldType]) {
|
|
848
|
+
enumOptions = enumDeclarations[fieldType];
|
|
849
|
+
}
|
|
850
|
+
return {
|
|
851
|
+
Field: field.name,
|
|
852
|
+
Default: defaultValue,
|
|
853
|
+
Extra: defaultGenerated ? "auto_increment" : "",
|
|
854
|
+
Null: isOptional ? "YES" : "NO",
|
|
855
|
+
Type: fieldType,
|
|
856
|
+
Comment: "",
|
|
857
|
+
// Prisma doesn't have column comments in the same way
|
|
858
|
+
EnumOptions: enumOptions
|
|
859
|
+
};
|
|
860
|
+
});
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
const defaultKyselyHeader = "import { Generated, Insertable, Selectable, Updateable, ColumnType } from 'kysely';\n\n";
|
|
864
|
+
const defaultZodHeader = (version) => "import { z } from 'zod" + (version === 3 ? "" : "/v4") + "';\n\n";
|
|
865
|
+
const kyselyJsonTypes = `// JSON type definitions
|
|
866
|
+
export type Json = ColumnType<JsonValue, string, string>;
|
|
867
|
+
|
|
868
|
+
export type JsonArray = JsonValue[];
|
|
869
|
+
|
|
870
|
+
export type JsonObject = {
|
|
871
|
+
[x: string]: JsonValue | undefined;
|
|
872
|
+
};
|
|
873
|
+
|
|
874
|
+
export type JsonPrimitive = boolean | number | string | null;
|
|
875
|
+
|
|
876
|
+
export type JsonValue = JsonArray | JsonObject | JsonPrimitive;
|
|
877
|
+
|
|
878
|
+
export type Generated<T> = T extends ColumnType<infer S, infer I, infer U>
|
|
879
|
+
? ColumnType<S, I | undefined, U>
|
|
880
|
+
: ColumnType<T, T | undefined, T>
|
|
881
|
+
|
|
882
|
+
export type Decimal = ColumnType<string, number | string, number | string>
|
|
883
|
+
|
|
884
|
+
export type BigInt = ColumnType<string, number | string, number | string>
|
|
885
|
+
|
|
886
|
+
`;
|
|
887
|
+
|
|
25
888
|
async function generate(config) {
|
|
26
889
|
let tables = [];
|
|
27
890
|
let views = [];
|
|
@@ -141,8 +1004,8 @@ export interface ${schemaName} {
|
|
|
141
1004
|
}
|
|
142
1005
|
for (const [filePath, content] of Object.entries(results)) {
|
|
143
1006
|
const fullPath = path.resolve(filePath);
|
|
144
|
-
await
|
|
145
|
-
await
|
|
1007
|
+
await ensureDir(path.dirname(fullPath));
|
|
1008
|
+
await writeFile(fullPath, content);
|
|
146
1009
|
if (!config.silent) {
|
|
147
1010
|
console.log(`Created: ${filePath}`);
|
|
148
1011
|
}
|
|
@@ -154,15 +1017,5 @@ export interface ${schemaName} {
|
|
|
154
1017
|
}
|
|
155
1018
|
}
|
|
156
1019
|
}
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
defaultZodHeader,
|
|
160
|
-
extractKyselyExpression,
|
|
161
|
-
extractTSExpression,
|
|
162
|
-
extractTypeExpression,
|
|
163
|
-
extractZodExpression,
|
|
164
|
-
generate,
|
|
165
|
-
generateContent2 as generateContent,
|
|
166
|
-
generateViewContent2 as generateViewContent,
|
|
167
|
-
getType
|
|
168
|
-
};
|
|
1020
|
+
|
|
1021
|
+
export { defaultKyselyHeader, defaultZodHeader, extractKyselyExpression, extractTSExpression, extractTypeExpression, extractZodExpression, generate, generateContent, generateViewContent, getType };
|