mutano 3.0.1 → 3.0.3

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/dist/main.js CHANGED
@@ -1,58 +1,61 @@
1
- import { readFileSync } from "node:fs";
2
- import path from "node:path";
3
- const enumDeclarations = {};
4
- import {
5
- createPrismaSchemaBuilder
6
- } from "@mrleebo/prisma-ast";
7
- import camelCase from "camelcase";
8
- import fs from "fs-extra";
9
- import knex from "knex";
10
- const extractTypeExpression = (comment, prefix) => {
11
- const start = comment.indexOf(prefix);
12
- if (start === -1) return null;
13
- const typeLen = prefix.length;
14
- let position = start + typeLen;
15
- let depth = 1;
16
- while (position < comment.length && depth > 0) {
17
- const char = comment[position];
18
- if (char === "(" || char === "{" || char === "<" || char === "[") {
19
- depth++;
20
- } else if (char === ")" || char === "}" || char === ">" || char === "]") {
21
- depth--;
22
- if (depth === 0) {
23
- const extracted = comment.substring(start + typeLen, position);
24
- return extracted;
25
- }
1
+ import * as path from 'node:path';
2
+ import camelCase from 'camelcase';
3
+ import * as fs from 'fs-extra';
4
+ import knex from 'knex';
5
+ import { readFileSync } from 'node:fs';
6
+ import { createPrismaSchemaBuilder } from '@mrleebo/prisma-ast';
7
+
8
+ function filterEntities(entities, included, ignored) {
9
+ let filtered = [...entities];
10
+ if (included?.length) {
11
+ filtered = filtered.filter((entity) => included.includes(entity));
12
+ }
13
+ if (ignored?.length) {
14
+ const ignoredRegex = ignored.filter((ignoreString) => {
15
+ return ignoreString.startsWith("/") && ignoreString.endsWith("/");
16
+ });
17
+ const ignoredNames = ignored.filter(
18
+ (entity) => !ignoredRegex.includes(entity)
19
+ );
20
+ if (ignoredNames.length) {
21
+ filtered = filtered.filter((entity) => !ignoredNames.includes(entity));
22
+ }
23
+ if (ignoredRegex.length) {
24
+ filtered = filtered.filter((entity) => {
25
+ let useEntity = true;
26
+ for (const text of ignoredRegex) {
27
+ const pattern = text.substring(1, text.length - 1);
28
+ if (entity.match(pattern) !== null) useEntity = false;
29
+ }
30
+ return useEntity;
31
+ });
26
32
  }
27
- position++;
28
33
  }
29
- return null;
30
- };
31
- const extractTSExpression = (comment) => extractTypeExpression(comment, "@ts(");
32
- const extractKyselyExpression = (comment) => extractTypeExpression(comment, "@kysely(");
33
- const extractZodExpression = (comment) => extractTypeExpression(comment, "@zod(");
34
- const prismaValidTypes = [
35
- "BigInt",
36
- "Boolean",
37
- "Bytes",
38
- "DateTime",
39
- "Decimal",
40
- "Float",
41
- "Int",
42
- "Json",
43
- "String",
44
- "Enum"
45
- ];
34
+ return filtered;
35
+ }
36
+ function filterTables(tables, includedTables, ignoredTables) {
37
+ return filterEntities(tables, includedTables, ignoredTables);
38
+ }
39
+ function filterViews(views, includedViews, ignoredViews) {
40
+ return filterEntities(views, includedViews, ignoredViews);
41
+ }
42
+ function createEntityList(tables, views) {
43
+ const allEntities = [
44
+ ...tables.map((name) => ({ name, type: "table" })),
45
+ ...views.map((name) => ({ name, type: "view" }))
46
+ ];
47
+ return allEntities.sort((a, b) => a.name.localeCompare(b.name));
48
+ }
49
+
46
50
  const dateTypes = {
47
51
  mysql: ["date", "datetime", "timestamp"],
48
52
  postgres: [
49
- "date",
50
53
  "timestamp",
51
- "timestamptz",
54
+ "timestamp with time zone",
52
55
  "timestamp without time zone",
53
- "timestamp with time zone"
56
+ "date"
54
57
  ],
55
- sqlite: ["datetime"],
58
+ sqlite: ["date", "datetime"],
56
59
  prisma: ["DateTime"]
57
60
  };
58
61
  const stringTypes = {
@@ -98,767 +101,767 @@ const stringTypes = {
98
101
  const bigIntTypes = {
99
102
  mysql: ["bigint"],
100
103
  postgres: ["bigint"],
101
- sqlite: ["bigint"],
104
+ sqlite: ["bigint", "unsigned big int", "int8"],
102
105
  prisma: ["BigInt"]
103
106
  };
104
107
  const numberTypes = {
105
- mysql: ["smallint", "mediumint", "int", "float", "double"],
108
+ mysql: [
109
+ "tinyint",
110
+ "smallint",
111
+ "mediumint",
112
+ "int",
113
+ "float",
114
+ "double",
115
+ "bit",
116
+ "year"
117
+ ],
106
118
  postgres: [
107
119
  "smallint",
108
120
  "integer",
109
121
  "real",
110
122
  "double precision",
123
+ "smallserial",
111
124
  "serial",
112
- "bigserial"
125
+ "bigserial",
126
+ "bit",
127
+ "bit varying"
113
128
  ],
114
129
  sqlite: [
115
- "int",
116
130
  "integer",
117
- "tinyint",
118
- "smallint",
119
- "mediumint",
120
- "unsigned big int",
121
- "int2",
122
- "int8",
123
131
  "real",
132
+ "numeric",
124
133
  "double",
125
134
  "double precision",
126
- "float"
135
+ "float",
136
+ "int",
137
+ "int2",
138
+ "mediumint",
139
+ "tinyint",
140
+ "smallint"
127
141
  ],
128
142
  prisma: ["Int", "Float"]
129
143
  };
130
144
  const decimalTypes = {
131
145
  mysql: ["decimal"],
132
- postgres: ["decimal", "numeric"],
133
- sqlite: ["numeric", "decimal"],
146
+ postgres: ["decimal", "numeric", "money"],
147
+ sqlite: ["decimal"],
134
148
  prisma: ["Decimal"]
135
149
  };
136
150
  const booleanTypes = {
137
- mysql: ["tinyint"],
138
- postgres: ["boolean", "bool"],
151
+ mysql: ["boolean"],
152
+ postgres: ["boolean"],
139
153
  sqlite: ["boolean"],
140
154
  prisma: ["Boolean"]
141
155
  };
142
156
  const enumTypes = {
143
157
  mysql: ["enum"],
144
- postgres: ["USER-DEFINED"],
158
+ postgres: ["enum", "USER-DEFINED"],
145
159
  sqlite: [],
146
- // SQLite doesn't have native enum types
147
- prisma: ["Enum"]
160
+ prisma: []
148
161
  };
149
162
  const enumRegex = /enum\(([^)]+)\)/;
150
- function getType(op, desc, config, destination, tableName) {
151
- const schemaType = config.origin.type;
163
+ function getTypeMappings(dbType) {
164
+ return {
165
+ dateTypes: dateTypes[dbType],
166
+ stringTypes: stringTypes[dbType],
167
+ bigIntTypes: bigIntTypes[dbType],
168
+ numberTypes: numberTypes[dbType],
169
+ decimalTypes: decimalTypes[dbType],
170
+ booleanTypes: booleanTypes[dbType],
171
+ enumTypes: enumTypes[dbType]
172
+ };
173
+ }
174
+ function isJsonType(type) {
175
+ return type.toLowerCase().includes("json");
176
+ }
177
+
178
+ const extractTypeExpression = (comment, prefix) => {
179
+ const start = comment.indexOf(prefix);
180
+ if (start === -1) return null;
181
+ const typeLen = prefix.length;
182
+ let position = start + typeLen;
183
+ let depth = 1;
184
+ while (position < comment.length && depth > 0) {
185
+ const char = comment[position];
186
+ if (char === "(" || char === "{" || char === "<" || char === "[") {
187
+ depth++;
188
+ } else if (char === ")" || char === "}" || char === ">" || char === "]") {
189
+ depth--;
190
+ if (depth === 0) {
191
+ const extracted = comment.substring(start + typeLen, position);
192
+ return extracted;
193
+ }
194
+ }
195
+ position++;
196
+ }
197
+ return null;
198
+ };
199
+ const extractTSExpression = (comment) => extractTypeExpression(comment, "@ts(");
200
+ const extractKyselyExpression = (comment) => extractTypeExpression(comment, "@kysely(");
201
+ const extractZodExpression = (comment) => extractTypeExpression(comment, "@zod(");
202
+
203
+ function getType(op, desc, config, destination) {
152
204
  const { Default, Extra, Null, Type, Comment, EnumOptions } = desc;
153
- const isZodDestination = destination.type === "zod";
205
+ const schemaType = config.origin.type;
206
+ const type = schemaType === "prisma" ? Type : Type.toLowerCase();
207
+ const isNull = Null === "YES";
208
+ const hasDefaultValue = Default !== null;
209
+ const isGenerated = Extra.toLowerCase().includes("auto_increment") || Extra.toLowerCase().includes("default_generated");
154
210
  const isTsDestination = destination.type === "ts";
155
211
  const isKyselyDestination = destination.type === "kysely";
156
- const isNullish = isZodDestination && destination.type === "zod" && destination.nullish === true;
157
- const isTrim = isZodDestination && destination.type === "zod" && destination.useTrim === true && op !== "selectable";
158
- const isUseDateType = isZodDestination && destination.type === "zod" && destination.useDateType === true;
159
- const hasDefaultValue = Default !== null && op !== "selectable";
160
- const isGenerated = ["DEFAULT_GENERATED", "auto_increment"].includes(Extra);
161
- const isNull = Null === "YES";
162
- if (isGenerated && !isNull && ["insertable", "updateable"].includes(op))
163
- return;
164
- const isRequiredString = destination.type === "zod" && destination.requiredString === true && op !== "selectable";
165
- const type = schemaType === "mysql" ? Type.split("(")[0].split(" ")[0] : Type;
212
+ const isZodDestination = destination.type === "zod";
213
+ const typeMappings = getTypeMappings(schemaType);
166
214
  if (isTsDestination || isKyselyDestination) {
215
+ const isJsonField = isJsonType(type);
216
+ if (isKyselyDestination && isJsonField) {
217
+ if (config.magicComments) {
218
+ const kyselyOverrideType = extractKyselyExpression(Comment);
219
+ if (kyselyOverrideType) {
220
+ const shouldBeNullable2 = isNull || ["insertable", "updateable"].includes(op) && (hasDefaultValue || isGenerated) || op === "updateable" && !isNull && !hasDefaultValue;
221
+ return shouldBeNullable2 ? kyselyOverrideType.includes("| null") ? kyselyOverrideType : `${kyselyOverrideType} | null` : kyselyOverrideType;
222
+ }
223
+ }
224
+ const shouldBeNullable = isNull || ["insertable", "updateable"].includes(op) && (hasDefaultValue || isGenerated) || op === "updateable" && !isNull && !hasDefaultValue;
225
+ return shouldBeNullable ? "Json | null" : "Json";
226
+ }
167
227
  if (isKyselyDestination && config.magicComments) {
168
228
  const kyselyOverrideType = extractKyselyExpression(Comment);
169
229
  if (kyselyOverrideType) {
170
- const shouldBeNullable2 = isNull || ["insertable", "updateable"].includes(op) && (hasDefaultValue || isGenerated) || op === "updateable" && !isNull && !hasDefaultValue;
171
- return shouldBeNullable2 ? kyselyOverrideType.includes("| null") ? kyselyOverrideType : `${kyselyOverrideType} | null` : kyselyOverrideType;
230
+ const shouldBeNullable = isNull || ["insertable", "updateable"].includes(op) && (hasDefaultValue || isGenerated) || op === "updateable" && !isNull && !hasDefaultValue;
231
+ return shouldBeNullable ? kyselyOverrideType.includes("| null") ? kyselyOverrideType : `${kyselyOverrideType} | null` : kyselyOverrideType;
172
232
  }
173
233
  }
174
- const tsOverrideType = config.magicComments ? extractTSExpression(Comment) : null;
175
- const shouldBeNullable = isNull || ["insertable", "updateable"].includes(op) && (hasDefaultValue || isGenerated) || op === "updateable" && !isNull && !hasDefaultValue;
176
- if (tsOverrideType) {
177
- return shouldBeNullable ? tsOverrideType.includes("| null") ? tsOverrideType : `${tsOverrideType} | null` : tsOverrideType;
178
- }
179
- if (dateTypes[schemaType].includes(type)) {
180
- return shouldBeNullable ? "Date | null" : "Date";
181
- }
182
- if (stringTypes[schemaType].includes(type)) {
183
- return shouldBeNullable ? "string | null" : "string";
184
- }
185
- if (numberTypes[schemaType].includes(type)) {
186
- return shouldBeNullable ? "number | null" : "number";
187
- }
188
- if (booleanTypes[schemaType].includes(type)) {
189
- return shouldBeNullable ? "boolean | null" : "boolean";
190
- }
191
- if (bigIntTypes[schemaType].includes(type) || type === "BigInt") {
192
- if (isKyselyDestination) {
193
- return shouldBeNullable ? "BigInt | null" : "BigInt";
234
+ if ((isTsDestination || isKyselyDestination) && config.magicComments) {
235
+ const tsOverrideType = extractTSExpression(Comment);
236
+ if (tsOverrideType) {
237
+ const shouldBeNullable = isNull || ["insertable", "updateable"].includes(op) && (hasDefaultValue || isGenerated) || op === "updateable" && !isNull && !hasDefaultValue;
238
+ return shouldBeNullable ? tsOverrideType.includes("| null") ? tsOverrideType : `${tsOverrideType} | null` : tsOverrideType;
194
239
  }
195
- return shouldBeNullable ? "string | null" : "string";
196
240
  }
197
- if (decimalTypes[schemaType].includes(type) || type === "Decimal") {
198
- if (isKyselyDestination) {
199
- return shouldBeNullable ? "Decimal | null" : "Decimal";
200
- }
201
- return shouldBeNullable ? "string | null" : "string";
241
+ }
242
+ if (isZodDestination && config.magicComments) {
243
+ const zodOverrideType = extractZodExpression(Comment);
244
+ if (zodOverrideType) {
245
+ const shouldBeNullable = isNull || ["insertable", "updateable"].includes(op) && (hasDefaultValue || isGenerated) || op === "updateable" && !isNull && !hasDefaultValue;
246
+ const nullishOption = destination.nullish;
247
+ const nullableMethod = nullishOption ? "nullish" : "nullable";
248
+ return shouldBeNullable ? zodOverrideType.includes(`.${nullableMethod}()`) || zodOverrideType.includes(".optional()") ? zodOverrideType : `${zodOverrideType}.${nullableMethod}()` : zodOverrideType;
202
249
  }
203
- if (schemaType !== "sqlite" && enumTypes[schemaType].includes(type)) {
204
- const enumType = destination.type === "ts" ? destination.enumType || "union" : "union";
205
- let enumValues = [];
206
- if (schemaType === "mysql") {
207
- const matches = Type.match(enumRegex);
208
- if (matches?.[1]) {
209
- enumValues = matches[1].split(",").map((v) => v.trim()).sort();
210
- }
211
- } else if (EnumOptions && EnumOptions.length > 0) {
212
- enumValues = EnumOptions.map((e) => `'${e}'`).sort();
213
- }
214
- if (enumValues.length === 0) {
215
- return isNull ? "string | null" : "string";
250
+ }
251
+ const overrideTypes = config.origin.overrideTypes;
252
+ if (overrideTypes && overrideTypes[Type]) {
253
+ const overrideType = overrideTypes[Type];
254
+ const shouldBeNullable = isNull || ["insertable", "updateable"].includes(op) && (hasDefaultValue || isGenerated) || op === "updateable" && !isNull && !hasDefaultValue;
255
+ if (isZodDestination) {
256
+ const nullishOption = destination.nullish;
257
+ const nullableMethod = nullishOption ? "nullish" : "nullable";
258
+ return shouldBeNullable ? `${overrideType}.${nullableMethod}()` : overrideType;
259
+ } else {
260
+ return shouldBeNullable ? `${overrideType} | null` : overrideType;
261
+ }
262
+ }
263
+ const enumTypesForSchema = typeMappings.enumTypes[schemaType] || [];
264
+ const isEnum = enumTypesForSchema.includes(type);
265
+ if (isEnum) {
266
+ let enumValues = [];
267
+ if (schemaType === "mysql" && type === "enum") {
268
+ const match = Type.match(enumRegex);
269
+ if (match) {
270
+ enumValues = match[1].split(",").map((v) => v.trim().replace(/'/g, ""));
216
271
  }
217
- if (enumType === "enum") {
218
- const enumName = camelCase(`${desc.Field}_enum`, { pascalCase: true });
219
- const enumDeclaration = `enum ${enumName} {
220
- ${enumValues.map((v) => {
221
- const cleanName = v.replace(/['"]/g, "");
222
- return `${cleanName} = ${v}`;
223
- }).join(",\n ")}
224
- }`;
225
- if (tableName) {
226
- if (!enumDeclarations[tableName]) {
227
- enumDeclarations[tableName] = [];
228
- }
229
- if (!enumDeclarations[tableName].includes(enumDeclaration)) {
230
- enumDeclarations[tableName].push(enumDeclaration);
231
- }
272
+ } else if (schemaType === "postgres" && EnumOptions) {
273
+ enumValues = EnumOptions;
274
+ }
275
+ if (enumValues.length > 0) {
276
+ const shouldBeNullable = isNull || ["insertable", "updateable"].includes(op) && (hasDefaultValue || isGenerated) || op === "updateable" && !isNull && !hasDefaultValue;
277
+ if (isZodDestination) {
278
+ const enumString = `z.enum([${enumValues.map((v) => `'${v}'`).join(",")}])`;
279
+ const nullishOption = destination.nullish;
280
+ const nullableMethod = nullishOption ? "nullish" : "nullable";
281
+ return shouldBeNullable ? `${enumString}.${nullableMethod}()` : enumString;
282
+ } else if (isTsDestination) {
283
+ const enumType = destination.enumType;
284
+ if (enumType === "enum") {
285
+ const enumString = enumValues.map((v) => `'${v}'`).join(" | ");
286
+ return shouldBeNullable ? `${enumString} | null` : enumString;
287
+ } else {
288
+ const enumString = enumValues.map((v) => `'${v}'`).join(" | ");
289
+ return shouldBeNullable ? `${enumString} | null` : enumString;
232
290
  }
233
- return shouldBeNullable ? `${enumName} | null` : enumName;
291
+ } else if (isKyselyDestination) {
292
+ const enumString = enumValues.map((v) => `'${v}'`).join(" | ");
293
+ return shouldBeNullable ? `${enumString} | null` : enumString;
234
294
  }
235
- const unionType = enumValues.join(" | ");
236
- return shouldBeNullable ? `(${unionType}) | null` : unionType;
237
- }
238
- return "any";
239
- }
240
- const zDate = [
241
- "z.union([z.number(), z.string(), z.date()]).pipe(z.coerce.date())"
242
- ];
243
- const string = [isTrim ? "z.string().trim()" : "z.string()"];
244
- const number = ["z.number()"];
245
- const boolean = [
246
- "z.union([z.number(),z.string(),z.boolean()]).pipe(z.coerce.boolean())"
247
- ];
248
- const dateField = isUseDateType ? zDate : string;
249
- const nullable = isNullish && op !== "selectable" ? "nullish()" : "nullable()";
250
- const optional = "optional()";
251
- const nonnegative = "nonnegative()";
252
- const isUpdateableFormat = op === "updateable" && !isNull && !hasDefaultValue;
253
- const min1 = "min(1)";
254
- const zodOverrideType = config.magicComments ? extractZodExpression(Comment) : null;
255
- let typeOverride = zodOverrideType;
256
- if (!typeOverride && config.origin.overrideTypes) {
257
- if (config.origin.type === "mysql") {
258
- typeOverride = config.origin.overrideTypes[type] || null;
259
- } else if (config.origin.type === "postgres") {
260
- typeOverride = config.origin.overrideTypes[type] || null;
261
- } else if (config.origin.type === "sqlite") {
262
- typeOverride = config.origin.overrideTypes[type] || null;
263
- } else if (config.origin.type === "prisma") {
264
- typeOverride = config.origin.overrideTypes[type] || null;
265
295
  }
266
296
  }
267
- const generateDateLikeField = () => {
268
- const field = typeOverride ? [typeOverride] : dateField;
269
- if (isNull && !typeOverride) field.push(nullable);
270
- else if (hasDefaultValue || !hasDefaultValue && isGenerated)
271
- field.push(optional);
272
- if (hasDefaultValue && !isGenerated) field.push(`default('${Default}')`);
273
- if (isUpdateableFormat) field.push(optional);
274
- return field.join(".");
275
- };
276
- const generateStringLikeField = () => {
277
- const field = typeOverride ? [typeOverride] : string;
278
- if (isNull && !typeOverride) field.push(nullable);
279
- else if (hasDefaultValue || !hasDefaultValue && isGenerated)
280
- field.push(optional);
281
- else if (isRequiredString && !typeOverride) field.push(min1);
282
- if (hasDefaultValue && !isGenerated) field.push(`default('${Default}')`);
283
- if (isUpdateableFormat) field.push(optional);
284
- return field.join(".");
285
- };
286
- const generateBooleanLikeField = () => {
287
- const field = typeOverride ? [typeOverride] : boolean;
288
- if (isNull && !typeOverride) field.push(nullable);
289
- else if (hasDefaultValue || !hasDefaultValue && isGenerated)
290
- field.push(optional);
291
- if (hasDefaultValue && !isGenerated) {
292
- if (Default === "true" || Default === "false") {
293
- field.push(`default(${Default})`);
297
+ return generateStandardType(op, desc, config, destination, typeMappings);
298
+ }
299
+ function generateStandardType(op, desc, config, destination, typeMappings) {
300
+ const { Default, Extra, Null, Type } = desc;
301
+ const schemaType = config.origin.type;
302
+ const type = schemaType === "prisma" ? Type : Type.toLowerCase();
303
+ const isNull = Null === "YES";
304
+ const hasDefaultValue = Default !== null;
305
+ const isGenerated = Extra.toLowerCase().includes("auto_increment") || Extra.toLowerCase().includes("default_generated");
306
+ const isZodDestination = destination.type === "zod";
307
+ const isKyselyDestination = destination.type === "kysely";
308
+ const shouldBeNullable = isNull || ["insertable", "updateable"].includes(op) && (hasDefaultValue || isGenerated) || op === "updateable" && !isNull && !hasDefaultValue;
309
+ let baseType;
310
+ if (typeMappings.dateTypes.includes(type)) {
311
+ if (isZodDestination) {
312
+ const useDateType = destination.useDateType;
313
+ if (useDateType) {
314
+ baseType = "z.union([z.number(), z.string(), z.date()]).pipe(z.coerce.date())";
294
315
  } else {
295
- field.push(`default(${Boolean(+Default)})`);
316
+ baseType = "z.date()";
296
317
  }
318
+ } else {
319
+ baseType = "Date";
297
320
  }
298
- if (isUpdateableFormat) field.push(optional);
299
- return field.join(".");
300
- };
301
- const generateNumberLikeField = () => {
302
- const unsigned = Type.endsWith(" unsigned");
303
- const field = typeOverride ? [typeOverride] : number;
304
- if (unsigned && !typeOverride) field.push(nonnegative);
305
- if (isNull && !typeOverride) field.push(nullable);
306
- else if (hasDefaultValue || !hasDefaultValue && isGenerated)
307
- field.push(optional);
308
- if (hasDefaultValue && !isGenerated) field.push(`default(${Default})`);
309
- if (isUpdateableFormat) field.push(optional);
310
- return field.join(".");
311
- };
312
- const generateEnumLikeField = () => {
313
- let enumValues = [];
314
- if (schemaType === "mysql") {
315
- const matches = Type.match(enumRegex);
316
- if (matches?.[1]) {
317
- enumValues = matches[1].split(",").map((v) => v.trim()).sort();
318
- }
319
- } else if (EnumOptions && EnumOptions.length > 0) {
320
- enumValues = [...EnumOptions].sort().map((e) => `'${e}'`);
321
+ } else if (typeMappings.bigIntTypes.includes(type)) {
322
+ if (isZodDestination) {
323
+ baseType = "z.string()";
324
+ } else if (isKyselyDestination) {
325
+ baseType = "BigInt";
326
+ } else {
327
+ baseType = "string";
321
328
  }
322
- const value = enumValues.join(",");
323
- const field = [`z.enum([${value}])`];
324
- if (isNull) field.push(nullable);
325
- else if (hasDefaultValue || !hasDefaultValue && isGenerated)
326
- field.push(optional);
327
- if (hasDefaultValue && !isGenerated) field.push(`default('${Default}')`);
328
- if (isUpdateableFormat) field.push(optional);
329
- return field.join(".");
330
- };
331
- if (dateTypes[schemaType].includes(type)) return generateDateLikeField();
332
- if (stringTypes[schemaType].includes(type)) return generateStringLikeField();
333
- if (numberTypes[schemaType].includes(type)) return generateNumberLikeField();
334
- if (bigIntTypes[schemaType].includes(type) || type === "BigInt") {
335
- if (isKyselyDestination) {
336
- const isNull2 = Null === "YES";
337
- const hasDefaultValue2 = Default !== null;
338
- const isGenerated2 = Extra.toLowerCase().includes("auto_increment") || Extra.toLowerCase().includes("default_generated");
339
- const shouldBeNullable = isNull2 || ["insertable", "updateable"].includes(op) && (hasDefaultValue2 || isGenerated2) || op === "updateable" && !isNull2 && !hasDefaultValue2;
340
- return shouldBeNullable ? "BigInt | null" : "BigInt";
329
+ } else if (typeMappings.decimalTypes.includes(type)) {
330
+ if (isZodDestination) {
331
+ baseType = "z.string()";
332
+ } else if (isKyselyDestination) {
333
+ baseType = "Decimal";
334
+ } else {
335
+ baseType = "string";
341
336
  }
342
- return generateStringLikeField();
337
+ } else if (typeMappings.numberTypes.includes(type)) {
338
+ if (isZodDestination) {
339
+ baseType = "z.number()";
340
+ if (!shouldBeNullable && !hasDefaultValue) {
341
+ baseType += ".nonnegative()";
342
+ }
343
+ } else {
344
+ baseType = "number";
345
+ }
346
+ } else if (typeMappings.booleanTypes.includes(type)) {
347
+ baseType = isZodDestination ? "z.boolean()" : "boolean";
348
+ } else if (typeMappings.stringTypes.includes(type)) {
349
+ if (isZodDestination) {
350
+ const useTrim = destination.useTrim;
351
+ const requiredString = destination.requiredString;
352
+ baseType = "z.string()";
353
+ if (useTrim) baseType += ".trim()";
354
+ if (requiredString && !shouldBeNullable) baseType += ".min(1)";
355
+ } else {
356
+ baseType = "string";
357
+ }
358
+ } else {
359
+ baseType = isZodDestination ? "z.string()" : "string";
343
360
  }
344
- if (decimalTypes[schemaType].includes(type) || type === "Decimal") {
345
- if (isKyselyDestination) {
346
- const isNull2 = Null === "YES";
347
- const hasDefaultValue2 = Default !== null;
348
- const isGenerated2 = Extra.toLowerCase().includes("auto_increment") || Extra.toLowerCase().includes("default_generated");
349
- const shouldBeNullable = isNull2 || ["insertable", "updateable"].includes(op) && (hasDefaultValue2 || isGenerated2) || op === "updateable" && !isNull2 && !hasDefaultValue2;
350
- return shouldBeNullable ? "Decimal | null" : "Decimal";
361
+ if (shouldBeNullable) {
362
+ if (isZodDestination) {
363
+ const nullishOption = destination.nullish;
364
+ const nullableMethod = nullishOption ? "nullish" : "nullable";
365
+ return `${baseType}.${nullableMethod}()`;
366
+ } else {
367
+ return `${baseType} | null`;
351
368
  }
352
- return generateStringLikeField();
353
369
  }
354
- if (booleanTypes[schemaType].includes(type)) return generateBooleanLikeField();
355
- if (schemaType !== "sqlite" && enumTypes[schemaType].includes(type))
356
- return generateEnumLikeField();
357
- throw new Error(`Unsupported column type: ${type}`);
370
+ return baseType;
358
371
  }
359
- function generateContent({
360
- table,
372
+
373
+ function generateViewContent({
374
+ view,
361
375
  describes,
362
376
  config,
363
377
  destination,
364
378
  isCamelCase,
365
- enumDeclarations: enumDeclarations2,
366
- defaultZodHeader: defaultZodHeader2
379
+ enumDeclarations: _enumDeclarations,
380
+ defaultZodHeader
367
381
  }) {
368
382
  let content = "";
369
- const schemaType = config.origin.type;
370
383
  if (destination.type === "kysely") {
371
- content += `// Kysely type definitions for ${table}
384
+ const pascalView = camelCase(view, { pascalCase: true });
385
+ content += `// Kysely type definitions for ${view} (view)
386
+
387
+ `;
388
+ content += `// This interface defines the structure of the '${view}' view (read-only)
389
+ `;
390
+ content += `export interface ${pascalView}View {
372
391
  `;
373
- content += `
374
- // This interface defines the structure of the '${table}' table
375
- export interface ${camelCase(table, { pascalCase: true })} {`;
376
392
  for (const desc of describes) {
377
- const field = isCamelCase ? camelCase(desc.Field) : desc.Field;
378
- const type = getType("table", desc, config, destination, table);
379
- if (type) {
380
- let kyselyType = type;
381
- const isAutoIncrement = desc.Extra.toLowerCase().includes("auto_increment");
382
- const isDefaultGenerated = desc.Extra.toLowerCase().includes("default_generated");
383
- const isNullable = desc.Null === "YES";
384
- const isJsonField = desc.Type.toLowerCase().includes("json");
385
- const hasDefaultValue = desc.Default !== null;
386
- const isEnum = schemaType !== "sqlite" && enumTypes[schemaType].includes(
387
- schemaType === "mysql" ? desc.Type.split("(")[0].split(" ")[0] : desc.Type
388
- );
389
- const kyselyOverrideType = config.magicComments ? extractKyselyExpression(desc.Comment) : null;
390
- if (kyselyOverrideType) {
391
- kyselyType = kyselyOverrideType;
392
- if (isNullable && !kyselyType.includes("| null")) {
393
- kyselyType = `${kyselyType} | null`;
394
- }
395
- if (isAutoIncrement || isDefaultGenerated || hasDefaultValue && (isEnum || kyselyType === "string" || kyselyType === "boolean" || kyselyType === "number" || kyselyType === "Decimal" || kyselyType === "BigInt" || kyselyType.includes("boolean | null") || kyselyType.includes("string | null") || kyselyType.includes("number | null") || kyselyType.includes("Decimal | null") || kyselyType.includes("BigInt | null"))) {
396
- kyselyType = `Generated<${kyselyType}>`;
397
- }
398
- } else if (isJsonField) {
399
- kyselyType = isNullable ? "Json | null" : "Json";
400
- } else {
401
- if (isNullable && !isJsonField) {
402
- if (!kyselyType.includes("| null")) {
403
- kyselyType = `${kyselyType} | null`;
404
- }
405
- }
406
- if (isAutoIncrement || isDefaultGenerated || hasDefaultValue && (isEnum || kyselyType === "string" || kyselyType === "boolean" || kyselyType === "number" || kyselyType === "Decimal" || kyselyType === "BigInt" || kyselyType.includes("boolean | null") || kyselyType.includes("string | null") || kyselyType.includes("number | null") || kyselyType.includes("Decimal | null") || kyselyType.includes("BigInt | null"))) {
407
- kyselyType = `Generated<${kyselyType}>`;
408
- }
409
- }
410
- content = `${content}
411
- ${field}: ${kyselyType};`;
412
- }
393
+ const fieldName = isCamelCase ? camelCase(desc.Field) : desc.Field;
394
+ const fieldType = getType("selectable", desc, config, destination);
395
+ content += ` ${fieldName}: ${fieldType};
396
+ `;
413
397
  }
414
- content = `${content}
415
- }
416
-
417
- // Helper types for ${table}
418
- export type Selectable${camelCase(table, { pascalCase: true })} = Selectable<${camelCase(table, { pascalCase: true })}>;
419
- export type Insertable${camelCase(table, { pascalCase: true })} = Insertable<${camelCase(table, { pascalCase: true })}>;
420
- export type Updateable${camelCase(table, { pascalCase: true })} = Updateable<${camelCase(table, { pascalCase: true })}>;
398
+ content += "}\n\n";
399
+ content += `// Helper types for ${view} (view - read-only)
400
+ `;
401
+ content += `export type Selectable${pascalView}View = Selectable<${pascalView}View>;
421
402
  `;
422
403
  } else if (destination.type === "ts") {
423
- const modelType = destination.modelType || "interface";
424
- const isInterface = modelType === "interface";
425
- const header = destination.header;
426
- content = header ? `${header}
427
-
428
- ` : "";
429
- content += `// TypeScript ${isInterface ? "interfaces" : "types"} for ${table}`;
430
- if (enumDeclarations2[table] && enumDeclarations2[table].length > 0) {
431
- content += "\n\n// Enum declarations";
432
- for (const enumDecl of enumDeclarations2[table]) {
433
- content += `
434
- ${enumDecl}`;
435
- }
436
- content += "\n";
437
- }
438
- if (isInterface) {
439
- content += `
440
- export interface ${camelCase(table, { pascalCase: true })} {`;
441
- } else {
442
- content += `
443
- export type ${camelCase(table, { pascalCase: true })} = {`;
444
- }
404
+ const pascalView = camelCase(view, { pascalCase: true });
405
+ content += `// TypeScript interface for ${view} (view - read-only)
406
+ `;
407
+ content += `export interface ${pascalView}View {
408
+ `;
445
409
  for (const desc of describes) {
446
- const field = isCamelCase ? camelCase(desc.Field) : desc.Field;
447
- const type = getType("table", desc, config, destination, table);
448
- if (type) {
449
- content = `${content}
450
- ${field}: ${type};`;
451
- }
452
- }
453
- content = `${content}
454
- }
455
-
410
+ const fieldName = isCamelCase ? camelCase(desc.Field) : desc.Field;
411
+ const fieldType = getType("selectable", desc, config, destination);
412
+ content += ` ${fieldName}: ${fieldType};
456
413
  `;
457
- if (isInterface) {
458
- content += `export interface Insertable${camelCase(table, { pascalCase: true })} {`;
459
- } else {
460
- content += `export type Insertable${camelCase(table, { pascalCase: true })} = {`;
461
414
  }
415
+ content += "}\n";
416
+ } else if (destination.type === "zod") {
417
+ const version = destination.version || 3;
418
+ const header = destination.header || defaultZodHeader(version);
419
+ if (!content.includes(header)) {
420
+ content += header;
421
+ }
422
+ content += `// View schema (read-only)
423
+ `;
424
+ content += `export const ${view}_view = z.object({
425
+ `;
462
426
  for (const desc of describes) {
463
- const field = isCamelCase ? camelCase(desc.Field) : desc.Field;
464
- const type = getType("insertable", desc, config, destination, table);
465
- if (type) {
466
- content = `${content}
467
- ${field}: ${type};`;
468
- }
427
+ const fieldName = isCamelCase ? camelCase(desc.Field) : desc.Field;
428
+ const fieldType = getType("selectable", desc, config, destination);
429
+ content += ` ${fieldName}: ${fieldType},
430
+ `;
469
431
  }
470
- content = `${content}
432
+ content += "})\n\n";
433
+ const pascalView = camelCase(view, { pascalCase: true });
434
+ content += `export type ${camelCase(`${pascalView}ViewType`, {
435
+ pascalCase: true
436
+ })} = z.infer<typeof ${view}_view>
437
+ `;
438
+ }
439
+ return content;
471
440
  }
441
+ function generateContent({
442
+ table,
443
+ describes,
444
+ config,
445
+ destination,
446
+ isCamelCase,
447
+ enumDeclarations: _enumDeclarations,
448
+ defaultZodHeader
449
+ }) {
450
+ let content = "";
451
+ if (destination.type === "ts") {
452
+ return generateTypeScriptContent({
453
+ table,
454
+ describes,
455
+ config,
456
+ destination,
457
+ isCamelCase
458
+ });
459
+ } else if (destination.type === "kysely") {
460
+ return generateKyselyContent({
461
+ table,
462
+ describes,
463
+ config,
464
+ destination,
465
+ isCamelCase
466
+ });
467
+ } else if (destination.type === "zod") {
468
+ return generateZodContent({
469
+ table,
470
+ describes,
471
+ config,
472
+ destination,
473
+ isCamelCase,
474
+ defaultZodHeader
475
+ });
476
+ }
477
+ return content;
478
+ }
479
+ function generateTypeScriptContent({
480
+ table,
481
+ describes,
482
+ config,
483
+ destination,
484
+ isCamelCase
485
+ }) {
486
+ let content = "";
487
+ const pascalTable = camelCase(table, { pascalCase: true });
488
+ content += `// TypeScript interfaces for ${table}
472
489
 
473
490
  `;
474
- if (isInterface) {
475
- content += `export interface Updateable${camelCase(table, { pascalCase: true })} {`;
476
- } else {
477
- content += `export type Updateable${camelCase(table, { pascalCase: true })} = {`;
478
- }
479
- for (const desc of describes) {
480
- const field = isCamelCase ? camelCase(desc.Field) : desc.Field;
481
- const type = getType("updateable", desc, config, destination, table);
482
- if (type) {
483
- content = `${content}
484
- ${field}: ${type};`;
485
- }
486
- }
487
- content = `${content}
491
+ content += `export interface ${pascalTable} {
492
+ `;
493
+ for (const desc of describes) {
494
+ const fieldName = isCamelCase ? camelCase(desc.Field) : desc.Field;
495
+ const fieldType = getType("table", desc, config, destination);
496
+ content += ` ${fieldName}: ${fieldType};
497
+ `;
498
+ }
499
+ content += "}\n\n";
500
+ content += `export interface Insertable${pascalTable} {
501
+ `;
502
+ for (const desc of describes) {
503
+ const fieldName = isCamelCase ? camelCase(desc.Field) : desc.Field;
504
+ const fieldType = getType("insertable", desc, config, destination);
505
+ content += ` ${fieldName}: ${fieldType};
506
+ `;
507
+ }
508
+ content += "}\n\n";
509
+ content += `export interface Updateable${pascalTable} {
510
+ `;
511
+ for (const desc of describes) {
512
+ const fieldName = isCamelCase ? camelCase(desc.Field) : desc.Field;
513
+ const fieldType = getType("updateable", desc, config, destination);
514
+ content += ` ${fieldName}: ${fieldType};
515
+ `;
516
+ }
517
+ content += "}\n\n";
518
+ content += `export interface Selectable${pascalTable} {
519
+ `;
520
+ for (const desc of describes) {
521
+ const fieldName = isCamelCase ? camelCase(desc.Field) : desc.Field;
522
+ const fieldType = getType("selectable", desc, config, destination);
523
+ content += ` ${fieldName}: ${fieldType};
524
+ `;
525
+ }
526
+ content += "}\n";
527
+ return content;
488
528
  }
529
+ function generateKyselyContent({
530
+ table,
531
+ describes,
532
+ config,
533
+ destination,
534
+ isCamelCase
535
+ }) {
536
+ let content = "";
537
+ const pascalTable = camelCase(table, { pascalCase: true });
538
+ content += `// Kysely type definitions for ${table}
489
539
 
490
540
  `;
491
- if (isInterface) {
492
- content += `export interface Selectable${camelCase(table, { pascalCase: true })} {`;
493
- } else {
494
- content += `export type Selectable${camelCase(table, { pascalCase: true })} = {`;
495
- }
496
- for (const desc of describes) {
497
- const field = isCamelCase ? camelCase(desc.Field) : desc.Field;
498
- const type = getType("selectable", desc, config, destination, table);
499
- if (type) {
500
- content = `${content}
501
- ${field}: ${type};`;
502
- }
503
- }
504
- content = `${content}
541
+ content += `// This interface defines the structure of the '${table}' table
542
+ `;
543
+ content += `export interface ${pascalTable}Table {
544
+ `;
545
+ for (const desc of describes) {
546
+ const fieldName = isCamelCase ? camelCase(desc.Field) : desc.Field;
547
+ let fieldType = getType("table", desc, config, destination);
548
+ const isAutoIncrement = desc.Extra.toLowerCase().includes("auto_increment");
549
+ const isDefaultGenerated = desc.Extra.toLowerCase().includes("default_generated");
550
+ if (isAutoIncrement || isDefaultGenerated) {
551
+ fieldType = `Generated<${fieldType.replace(" | null", "")}>${fieldType.includes(" | null") ? " | null" : ""}`;
552
+ }
553
+ content += ` ${fieldName}: ${fieldType};
554
+ `;
555
+ }
556
+ content += "}\n\n";
557
+ content += `// Use these types for inserting, selecting and updating the table
558
+ `;
559
+ content += `export type ${pascalTable} = Selectable<${pascalTable}Table>;
560
+ `;
561
+ content += `export type New${pascalTable} = Insertable<${pascalTable}Table>;
562
+ `;
563
+ content += `export type ${pascalTable}Update = Updateable<${pascalTable}Table>;
564
+ `;
565
+ return content;
505
566
  }
567
+ function generateZodContent({
568
+ table,
569
+ describes,
570
+ config,
571
+ destination,
572
+ isCamelCase,
573
+ defaultZodHeader
574
+ }) {
575
+ let content = "";
576
+ const version = destination.version || 3;
577
+ const header = destination.header || defaultZodHeader(version);
578
+ if (!content.includes(header)) {
579
+ content += header;
580
+ }
581
+ content += `export const ${table} = z.object({
506
582
  `;
507
- } else if (destination.type === "zod") {
508
- const header = destination.header;
509
- content = header ? header + "\n\n" : defaultZodHeader2(destination.version || 3);
510
- content += `export const ${table} = z.object({`;
511
- for (const desc of describes) {
512
- const field = isCamelCase ? camelCase(desc.Field) : desc.Field;
513
- const type = getType("table", desc, config, destination, table);
514
- if (type) {
515
- content = `${content}
516
- ${field}: ${type},`;
517
- }
518
- }
519
- content = `${content}
520
- })
521
-
522
- export const insertable_${table} = z.object({`;
523
- for (const desc of describes) {
524
- const field = isCamelCase ? camelCase(desc.Field) : desc.Field;
525
- const type = getType("insertable", desc, config, destination, table);
526
- if (type) {
527
- content = `${content}
528
- ${field}: ${type},`;
529
- }
530
- }
531
- content = `${content}
532
- })
583
+ for (const desc of describes) {
584
+ const fieldName = isCamelCase ? camelCase(desc.Field) : desc.Field;
585
+ const fieldType = getType("table", desc, config, destination);
586
+ content += ` ${fieldName}: ${fieldType},
587
+ `;
588
+ }
589
+ content += "})\n\n";
590
+ content += `export const insertable_${table} = z.object({
591
+ `;
592
+ for (const desc of describes) {
593
+ const fieldName = isCamelCase ? camelCase(desc.Field) : desc.Field;
594
+ const fieldType = getType("insertable", desc, config, destination);
595
+ content += ` ${fieldName}: ${fieldType},
596
+ `;
597
+ }
598
+ content += "})\n\n";
599
+ content += `export const updateable_${table} = z.object({
600
+ `;
601
+ for (const desc of describes) {
602
+ const fieldName = isCamelCase ? camelCase(desc.Field) : desc.Field;
603
+ const fieldType = getType("updateable", desc, config, destination);
604
+ content += ` ${fieldName}: ${fieldType},
605
+ `;
606
+ }
607
+ content += "})\n\n";
608
+ content += `export const selectable_${table} = z.object({
609
+ `;
610
+ for (const desc of describes) {
611
+ const fieldName = isCamelCase ? camelCase(desc.Field) : desc.Field;
612
+ const fieldType = getType("selectable", desc, config, destination);
613
+ content += ` ${fieldName}: ${fieldType},
614
+ `;
615
+ }
616
+ content += "})\n\n";
617
+ content += `export type ${camelCase(`${table}Type`, { pascalCase: true })} = z.infer<typeof ${table}>
618
+ `;
619
+ content += `export type Insertable${camelCase(`${table}Type`, { pascalCase: true })} = z.infer<typeof insertable_${table}>
620
+ `;
621
+ content += `export type Updateable${camelCase(`${table}Type`, { pascalCase: true })} = z.infer<typeof updateable_${table}>
622
+ `;
623
+ content += `export type Selectable${camelCase(`${table}Type`, { pascalCase: true })} = z.infer<typeof selectable_${table}>
624
+ `;
625
+ return content;
626
+ }
533
627
 
534
- export const updateable_${table} = z.object({`;
535
- for (const desc of describes) {
536
- const field = isCamelCase ? camelCase(desc.Field) : desc.Field;
537
- const type = getType("updateable", desc, config, destination, table);
538
- if (type) {
539
- content = `${content}
540
- ${field}: ${type},`;
541
- }
542
- }
543
- content = `${content}
544
- })
628
+ function createDatabaseConnection(config) {
629
+ const { origin } = config;
630
+ switch (origin.type) {
631
+ case "mysql":
632
+ return knex({
633
+ client: "mysql2",
634
+ connection: {
635
+ host: origin.host,
636
+ port: origin.port,
637
+ user: origin.user,
638
+ password: origin.password,
639
+ database: origin.database,
640
+ ssl: origin.ssl
641
+ }
642
+ });
643
+ case "postgres":
644
+ return knex({
645
+ client: "pg",
646
+ connection: {
647
+ host: origin.host,
648
+ port: origin.port,
649
+ user: origin.user,
650
+ password: origin.password,
651
+ database: origin.database,
652
+ ssl: origin.ssl
653
+ },
654
+ searchPath: origin.schema ? [origin.schema] : ["public"]
655
+ });
656
+ case "sqlite":
657
+ return knex({
658
+ client: "sqlite3",
659
+ connection: {
660
+ filename: origin.path
661
+ },
662
+ useNullAsDefault: true
663
+ });
664
+ default:
665
+ throw new Error(`Unsupported database type: ${origin.type}`);
666
+ }
667
+ }
668
+ async function extractTables(db, config) {
669
+ const { origin } = config;
670
+ switch (origin.type) {
671
+ case "mysql":
672
+ const mysqlTables = await db.raw(`
673
+ SELECT table_name
674
+ FROM information_schema.tables
675
+ WHERE table_schema = ? AND table_type = 'BASE TABLE'
676
+ `, [origin.database]);
677
+ return mysqlTables[0].map((row) => row.table_name);
678
+ case "postgres":
679
+ const schema = origin.schema || "public";
680
+ const postgresTables = await db.raw(`
681
+ SELECT table_name
682
+ FROM information_schema.tables
683
+ WHERE table_schema = ? AND table_type = 'BASE TABLE'
684
+ `, [schema]);
685
+ return postgresTables.rows.map((row) => row.table_name);
686
+ case "sqlite":
687
+ const sqliteTables = await db.raw(`
688
+ SELECT name
689
+ FROM sqlite_master
690
+ WHERE type = 'table' AND name NOT LIKE 'sqlite_%'
691
+ `);
692
+ return sqliteTables.map((row) => row.name);
693
+ default:
694
+ return [];
695
+ }
696
+ }
697
+ async function extractViews(db, config) {
698
+ const { origin } = config;
699
+ switch (origin.type) {
700
+ case "mysql":
701
+ const mysqlViews = await db.raw(`
702
+ SELECT table_name
703
+ FROM information_schema.tables
704
+ WHERE table_schema = ? AND table_type = 'VIEW'
705
+ `, [origin.database]);
706
+ return mysqlViews[0].map((row) => row.table_name);
707
+ case "postgres":
708
+ const schema = origin.schema || "public";
709
+ const postgresViews = await db.raw(`
710
+ SELECT table_name
711
+ FROM information_schema.tables
712
+ WHERE table_schema = ? AND table_type = 'VIEW'
713
+ `, [schema]);
714
+ return postgresViews.rows.map((row) => row.table_name);
715
+ case "sqlite":
716
+ const sqliteViews = await db.raw(`
717
+ SELECT name
718
+ FROM sqlite_master
719
+ WHERE type = 'view'
720
+ `);
721
+ return sqliteViews.map((row) => row.name);
722
+ default:
723
+ return [];
724
+ }
725
+ }
726
+ async function extractColumnDescriptions(db, config, tableName) {
727
+ const { origin } = config;
728
+ switch (origin.type) {
729
+ case "mysql":
730
+ const mysqlColumns = await db.raw(`
731
+ SELECT
732
+ column_name as \`Field\`,
733
+ column_default as \`Default\`,
734
+ extra as \`Extra\`,
735
+ is_nullable as \`Null\`,
736
+ column_type as \`Type\`,
737
+ column_comment as \`Comment\`
738
+ FROM information_schema.columns
739
+ WHERE table_schema = ? AND table_name = ?
740
+ ORDER BY ordinal_position
741
+ `, [origin.database, tableName]);
742
+ return mysqlColumns[0].map((row) => ({
743
+ Field: row.Field,
744
+ Default: row.Default,
745
+ Extra: row.Extra || "",
746
+ Null: row.Null,
747
+ Type: row.Type,
748
+ Comment: row.Comment || ""
749
+ }));
750
+ case "postgres":
751
+ const schema = origin.schema || "public";
752
+ const postgresColumns = await db.raw(`
753
+ SELECT
754
+ column_name as "Field",
755
+ column_default as "Default",
756
+ '' as "Extra",
757
+ is_nullable as "Null",
758
+ data_type as "Type",
759
+ '' as "Comment"
760
+ FROM information_schema.columns
761
+ WHERE table_schema = ? AND table_name = ?
762
+ ORDER BY ordinal_position
763
+ `, [schema, tableName]);
764
+ return postgresColumns.rows.map((row) => ({
765
+ Field: row.Field,
766
+ Default: row.Default,
767
+ Extra: row.Extra || "",
768
+ Null: row.Null,
769
+ Type: row.Type,
770
+ Comment: row.Comment || ""
771
+ }));
772
+ case "sqlite":
773
+ const sqliteColumns = await db.raw(`PRAGMA table_info(${tableName})`);
774
+ return sqliteColumns.map((row) => ({
775
+ Field: row.name,
776
+ Default: row.dflt_value,
777
+ Extra: row.pk ? "PRIMARY KEY" : "",
778
+ Null: row.notnull ? "NO" : "YES",
779
+ Type: row.type,
780
+ Comment: ""
781
+ }));
782
+ default:
783
+ return [];
784
+ }
785
+ }
545
786
 
546
- export const selectable_${table} = z.object({`;
547
- for (const desc of describes) {
548
- const field = isCamelCase ? camelCase(desc.Field) : desc.Field;
549
- const type = getType("selectable", desc, config, destination, table);
550
- if (type) {
551
- content = `${content}
552
- ${field}: ${type},`;
553
- }
787
+ function extractPrismaEntities(config) {
788
+ if (config.origin.type !== "prisma") {
789
+ return { tables: [], views: [], enumDeclarations: {} };
790
+ }
791
+ const schemaContent = readFileSync(config.origin.path, "utf-8");
792
+ const schema = createPrismaSchemaBuilder(schemaContent);
793
+ const prismaModels = schema.findAllByType("model", {});
794
+ const tables = prismaModels.filter((m) => m !== null).map((model) => model.name);
795
+ const prismaViews = schema.findAllByType("view", {});
796
+ const views = prismaViews.filter((v) => v !== null).map((view) => view.name);
797
+ const enumDeclarations = {};
798
+ const prismaEnums = schema.findAllByType("enum", {});
799
+ for (const prismaEnum of prismaEnums) {
800
+ if (prismaEnum && "name" in prismaEnum && "enumerators" in prismaEnum) {
801
+ const enumName = prismaEnum.name;
802
+ const enumerators = prismaEnum.enumerators;
803
+ enumDeclarations[enumName] = enumerators.map((e) => e.name);
554
804
  }
555
- content = `${content}
556
- })
557
-
558
- export type ${camelCase(`${table}Type`, {
559
- pascalCase: true
560
- })} = z.infer<typeof ${table}>
561
- export type Insertable${camelCase(`${table}Type`, {
562
- pascalCase: true
563
- })} = z.infer<typeof insertable_${table}>
564
- export type Updateable${camelCase(`${table}Type`, {
565
- pascalCase: true
566
- })} = z.infer<typeof updateable_${table}>
567
- export type Selectable${camelCase(`${table}Type`, {
568
- pascalCase: true
569
- })} = z.infer<typeof selectable_${table}>
570
- `;
571
805
  }
572
- return content;
806
+ return { tables, views, enumDeclarations };
573
807
  }
574
- const defaultKyselyHeader = "import { ColumnType, Selectable, Insertable, Updateable } from 'kysely';\n\n";
575
- const defaultZodHeader = (version) => "import { z } from 'zod" + (version === 3 ? "" : "/v4") + "';\n\n";
576
- async function generate(config) {
577
- let tables = [];
578
- let prismaTables = [];
579
- let schema = null;
580
- let db = null;
581
- const kyselyTableContents = {};
582
- if (config.destinations.length === 0) {
583
- throw new Error("Empty destinations object.");
808
+ function extractPrismaColumnDescriptions(config, entityName, enumDeclarations) {
809
+ if (config.origin.type !== "prisma") {
810
+ return [];
584
811
  }
585
- const dryRunOutput = {};
586
- if (config.origin.type === "mysql") {
587
- db = knex({
588
- client: "mysql2",
589
- connection: {
590
- host: config.origin.host,
591
- port: config.origin.port,
592
- user: config.origin.user,
593
- password: config.origin.password,
594
- database: config.origin.database,
595
- ssl: config.origin.ssl
596
- }
597
- });
598
- } else if (config.origin.type === "postgres") {
599
- db = knex({
600
- client: "pg",
601
- connection: {
602
- host: config.origin.host,
603
- port: config.origin.port,
604
- user: config.origin.user,
605
- password: config.origin.password,
606
- database: config.origin.database,
607
- ssl: config.origin.ssl
608
- }
609
- });
610
- } else if (config.origin.type === "sqlite") {
611
- db = knex({
612
- client: "sqlite3",
613
- connection: {
614
- filename: config.origin.path
615
- },
616
- useNullAsDefault: true
617
- });
812
+ const schemaContent = readFileSync(config.origin.path, "utf-8");
813
+ const schema = createPrismaSchemaBuilder(schemaContent);
814
+ let entity = schema.findByType("model", { name: entityName });
815
+ if (!entity) {
816
+ entity = schema.findByType("view", { name: entityName });
618
817
  }
619
- const isCamelCase = config.camelCase && config.camelCase === true;
620
- if (config.origin.type === "prisma") {
621
- const schemaContents = readFileSync(config.origin.path).toString();
622
- schema = createPrismaSchemaBuilder(schemaContents);
623
- prismaTables = schema.findAllByType("model", {});
624
- tables = prismaTables.filter((t) => t !== null).map((table) => table.name);
625
- } else if (config.origin.type === "mysql" && db) {
626
- const t = await db.raw(
627
- "SELECT table_name as table_name FROM information_schema.tables WHERE table_schema = ?",
628
- [config.origin.database]
629
- );
630
- tables = t[0].map((row) => row.table_name).sort();
631
- } else if (config.origin.type === "postgres" && db) {
632
- const schema2 = config.origin.schema || "public";
633
- const t = await db.raw(
634
- "SELECT table_name FROM information_schema.tables WHERE table_schema = ? AND table_type = ?",
635
- [schema2, "BASE TABLE"]
636
- );
637
- tables = t.rows.map((row) => row.table_name).sort();
638
- } else if (config.origin.type === "sqlite" && db) {
639
- const t = await db.raw(
640
- "SELECT name as table_name FROM sqlite_master WHERE type = 'table' AND name NOT LIKE 'sqlite_%'"
641
- );
642
- tables = t.map((row) => row.table_name).sort();
818
+ if (!entity || !("properties" in entity)) {
819
+ return [];
643
820
  }
644
- const dests = [];
645
- const includedTables = config.tables;
646
- if (includedTables?.length)
647
- tables = tables.filter((table) => includedTables.includes(table));
648
- const allIgnoredTables = config.ignore;
649
- const ignoredTablesRegex = allIgnoredTables?.filter((ignoreString) => {
650
- return ignoreString.startsWith("/") && ignoreString.endsWith("/");
651
- });
652
- const ignoredTableNames = allIgnoredTables?.filter(
653
- (table) => !ignoredTablesRegex?.includes(table)
821
+ const fields = entity.properties.filter(
822
+ (p) => p.type === "field" && p.array !== true && !p.attributes?.find((a) => a.name === "relation")
654
823
  );
655
- if (ignoredTableNames?.length)
656
- tables = tables.filter((table) => !ignoredTableNames.includes(table));
657
- if (ignoredTablesRegex?.length) {
658
- tables = tables.filter((table) => {
659
- let useTable = true;
660
- for (const text of ignoredTablesRegex) {
661
- const pattern = text.substring(1, text.length - 1);
662
- if (table.match(pattern) !== null) useTable = false;
663
- }
664
- return useTable;
665
- });
666
- }
667
- let describes = [];
668
- for (let table of tables.sort((a, b) => a.localeCompare(b))) {
669
- if (config.origin.type === "mysql" && db) {
670
- const d = await db.raw(`SHOW FULL COLUMNS FROM ${table}`);
671
- describes = d[0];
672
- } else if (config.origin.type === "postgres" && db) {
673
- const schema2 = config.origin.schema || "public";
674
- const d = await db.raw(
675
- `
676
- SELECT
677
- column_name as "Field",
678
- column_default as "Default",
679
- CASE WHEN is_nullable = 'YES' THEN 'YES' ELSE 'NO' END as "Null",
680
- data_type as "Type",
681
- CASE
682
- WHEN column_default LIKE 'nextval(%' THEN 'auto_increment'
683
- WHEN column_default IS NOT NULL AND (
684
- column_default LIKE 'now()%' OR
685
- column_default LIKE 'uuid_generate_v4()%' OR
686
- column_default LIKE 'gen_random_uuid()%' OR
687
- column_default LIKE 'current_timestamp%' OR
688
- column_default LIKE 'current_date%' OR
689
- column_default LIKE 'current_time%' OR
690
- column_default LIKE '(%' OR
691
- column_default LIKE 'array[%' OR
692
- column_default LIKE 'json_build_%'
693
- ) THEN 'DEFAULT_GENERATED'
694
- ELSE ''
695
- END as "Extra",
696
- col_description(('"'||$1||'"."'||$2||'"')::regclass::oid, ordinal_position) as "Comment"
697
- FROM
698
- information_schema.columns
699
- WHERE
700
- table_schema = $1 AND table_name = $2
701
- ORDER BY
702
- ordinal_position
703
- `,
704
- [schema2, table]
705
- );
706
- for (const column of d.rows) {
707
- if (column.Type === "USER-DEFINED") {
708
- const enumValues = await db.raw(
709
- `
710
- SELECT
711
- e.enumlabel
712
- FROM
713
- pg_type t
714
- JOIN pg_enum e ON t.oid = e.enumtypid
715
- JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace
716
- WHERE
717
- t.typname = (
718
- SELECT udt_name
719
- FROM information_schema.columns
720
- WHERE table_schema = $1
721
- AND table_name = $2
722
- AND column_name = $3
723
- )
724
- ORDER BY
725
- e.enumlabel
726
- `,
727
- [schema2, table, column.Field]
728
- );
729
- column.EnumOptions = enumValues.rows.map(
730
- (row) => row.enumlabel
731
- );
732
- }
733
- }
734
- describes = d.rows;
735
- } else if (config.origin.type === "sqlite" && db) {
736
- const d = await db.raw(`PRAGMA table_info(${table})`);
737
- describes = d.map(
738
- (row) => {
739
- let extra = "";
740
- if (row.dflt_value !== null) {
741
- if (row.name === "rowid" || row.type.toLowerCase() === "integer primary key") {
742
- extra = "auto_increment";
743
- } else if (row.dflt_value.includes("CURRENT_TIMESTAMP") || row.dflt_value.includes("CURRENT_DATE") || row.dflt_value.includes("CURRENT_TIME") || row.dflt_value.includes("DATETIME") || row.dflt_value.includes("strftime") || row.dflt_value.includes("random()") || row.dflt_value.includes("(") || row.dflt_value.includes("uuid") || row.dflt_value.includes("json_")) {
744
- extra = "DEFAULT_GENERATED";
824
+ return fields.map((field) => {
825
+ let defaultGenerated = false;
826
+ let defaultValue = null;
827
+ if (field.attributes) {
828
+ for (const attr of field.attributes) {
829
+ if (attr.name === "default") {
830
+ if (attr.args && attr.args.length > 0) {
831
+ const arg = attr.args[0];
832
+ if (typeof arg === "object" && "value" in arg) {
833
+ if (arg.value === "autoincrement()" || arg.value === "cuid()" || arg.value === "uuid()") {
834
+ defaultGenerated = true;
835
+ } else {
836
+ defaultValue = String(arg.value);
837
+ }
745
838
  }
746
839
  }
747
- return {
748
- Field: row.name,
749
- Default: row.dflt_value,
750
- Null: row.notnull === 0 ? "YES" : "NO",
751
- Type: row.type.toLowerCase(),
752
- Extra: extra,
753
- Comment: ""
754
- };
755
- }
756
- );
757
- } else {
758
- const prismaTable = prismaTables.find((t) => t?.name === table);
759
- let enumOptions;
760
- describes = prismaTable.properties.filter(
761
- (p) => p.type === "field" && p.array !== true && !p.attributes?.find((a) => a.name === "relation")
762
- ).map((field) => {
763
- let defaultGenerated = false;
764
- const defaultValueField = field.attributes ? field.attributes.find((a) => a.name === "default") : null;
765
- const defaultValue = defaultValueField?.args?.[0].value;
766
- if (typeof defaultValue === "object" && // @ts-ignore
767
- defaultValue?.type === "function") {
768
- defaultGenerated = true;
769
840
  }
770
- const parsedDefaultValue = defaultValue !== void 0 && typeof defaultValue !== "object" ? defaultValue.toString().replace(/"/g, "") : null;
771
- let fieldType = field.fieldType.toString();
772
- if (!prismaValidTypes.includes(fieldType) && schema) {
773
- enumOptions = schema.findAllByType("enum", {
774
- name: fieldType
775
- })[0]?.enumerators.filter(
776
- (e) => e.type === "enumerator"
777
- ).map((e) => {
778
- const attrs = e.attributes?.find((a) => a.name === "map");
779
- return attrs?.args ? attrs.args[0].value.toString().replace(/"/g, "") : e.name;
780
- });
781
- fieldType = "Enum";
782
- }
783
- return {
784
- Field: field.name,
785
- Default: parsedDefaultValue,
786
- EnumOptions: enumOptions,
787
- Extra: defaultGenerated ? "DEFAULT_GENERATED" : "",
788
- Type: fieldType,
789
- Null: field.optional ? "YES" : "NO",
790
- Comment: field.comment ?? ""
791
- };
792
- });
793
- }
794
- if (isCamelCase) table = camelCase(table);
795
- if (!config.destinations || config.destinations.length === 0) {
796
- throw new Error("No destinations specified");
797
- }
798
- const kyselyDestinations = config.destinations.filter(
799
- (d) => d.type === "kysely"
800
- );
801
- const nonKyselyDestinations = config.destinations.filter(
802
- (d) => d.type !== "kysely"
803
- );
804
- for (const destination of nonKyselyDestinations) {
805
- const content = generateContent({
806
- table,
807
- describes: describes.sort((a, b) => a.Field.localeCompare(b.Field)),
808
- config,
809
- destination,
810
- isCamelCase: isCamelCase === true,
811
- enumDeclarations,
812
- defaultZodHeader
813
- });
814
- const suffix = destination.suffix || "";
815
- const folder = destination.folder || ".";
816
- const file = suffix !== "" ? `${table}.${suffix}.ts` : `${table}.ts`;
817
- if (config.dryRun) {
818
- const absolutePath = path.resolve(path.join(folder, file));
819
- dryRunOutput[absolutePath] = content;
820
- } else {
821
- const dest = path.join(folder, file);
822
- dests.push(dest);
823
- if (!config.silent) console.log("Created:", dest);
824
- fs.outputFileSync(dest, content);
825
841
  }
826
842
  }
827
- for (const destination of kyselyDestinations) {
828
- const content = generateContent({
829
- table,
830
- describes: describes.sort((a, b) => a.Field.localeCompare(b.Field)),
831
- config,
832
- destination,
833
- isCamelCase: isCamelCase === true,
834
- enumDeclarations,
835
- defaultZodHeader
836
- });
837
- const outFile = destination.outFile || "db.ts";
838
- if (!kyselyTableContents[outFile]) {
839
- kyselyTableContents[outFile] = [];
840
- }
841
- kyselyTableContents[outFile].push({
842
- table,
843
- content
844
- });
845
- if (config.dryRun) {
846
- const tempKey = path.resolve(`${table}.kysely.temp`);
847
- dryRunOutput[tempKey] = content;
848
- }
849
- }
850
- }
851
- if (db) await db.destroy();
852
- for (const [outFile, tableContents] of Object.entries(kyselyTableContents)) {
853
- if (tableContents.length === 0) continue;
854
- const kyselyDestination = config.destinations.find(
855
- (d) => d.type === "kysely"
856
- );
857
- const header = kyselyDestination?.header || defaultKyselyHeader;
858
- const schemaName = kyselyDestination?.schemaName || "DB";
859
- let consolidatedContent = `${header}
843
+ const isOptional = field.optional === true;
844
+ let enumOptions;
845
+ const fieldType = String(field.fieldType);
846
+ if (enumDeclarations[fieldType]) {
847
+ enumOptions = enumDeclarations[fieldType];
848
+ }
849
+ return {
850
+ Field: field.name,
851
+ Default: defaultValue,
852
+ Extra: defaultGenerated ? "auto_increment" : "",
853
+ Null: isOptional ? "YES" : "NO",
854
+ Type: fieldType,
855
+ Comment: "",
856
+ // Prisma doesn't have column comments in the same way
857
+ EnumOptions: enumOptions
858
+ };
859
+ });
860
+ }
860
861
 
861
- // JSON type definitions
862
+ const defaultKyselyHeader = "import { Generated, Insertable, Selectable, Updateable, ColumnType } from 'kysely';\n\n";
863
+ const defaultZodHeader = (version) => "import { z } from 'zod" + (version === 3 ? "" : "/v4") + "';\n\n";
864
+ const kyselyJsonTypes = `// JSON type definitions
862
865
  export type Json = ColumnType<JsonValue, string, string>;
863
866
 
864
867
  export type JsonArray = JsonValue[];
@@ -878,65 +881,140 @@ export type Generated<T> = T extends ColumnType<infer S, infer I, infer U>
878
881
  export type Decimal = ColumnType<string, number | string, number | string>
879
882
 
880
883
  export type BigInt = ColumnType<string, number | string, number | string>
884
+
881
885
  `;
882
- consolidatedContent += "// Table Interfaces\n";
883
- for (const { content } of tableContents) {
884
- consolidatedContent += `${content}
885
- `;
886
+
887
+ async function generate(config) {
888
+ let tables = [];
889
+ let views = [];
890
+ let enumDeclarations = {};
891
+ let db = null;
892
+ try {
893
+ if (config.origin.type === "prisma") {
894
+ const prismaEntities = extractPrismaEntities(config);
895
+ tables = prismaEntities.tables;
896
+ views = prismaEntities.views;
897
+ enumDeclarations = prismaEntities.enumDeclarations;
898
+ } else {
899
+ db = createDatabaseConnection(config);
900
+ tables = await extractTables(db, config);
901
+ views = await extractViews(db, config);
902
+ }
903
+ tables = filterTables(tables, config.tables, config.ignore);
904
+ if (!config.includeViews) {
905
+ views = [];
906
+ } else {
907
+ views = filterViews(views, config.views, config.ignoreViews);
908
+ }
909
+ const allEntities = createEntityList(tables, views);
910
+ const results = {};
911
+ const isCamelCase = config.camelCase === true;
912
+ const nonKyselyDestinations = config.destinations.filter((d) => d.type !== "kysely");
913
+ for (const entity of allEntities) {
914
+ const { name: entityName, type: entityType } = entity;
915
+ let describes;
916
+ if (config.origin.type === "prisma") {
917
+ describes = extractPrismaColumnDescriptions(config, entityName, enumDeclarations);
918
+ } else {
919
+ describes = await extractColumnDescriptions(db, config, entityName);
920
+ }
921
+ if (describes.length === 0) continue;
922
+ for (const destination of nonKyselyDestinations) {
923
+ const content = entityType === "view" ? generateViewContent({
924
+ view: entityName,
925
+ describes: describes.sort((a, b) => a.Field.localeCompare(b.Field)),
926
+ config,
927
+ destination,
928
+ isCamelCase,
929
+ enumDeclarations,
930
+ defaultZodHeader
931
+ }) : generateContent({
932
+ table: entityName,
933
+ describes: describes.sort((a, b) => a.Field.localeCompare(b.Field)),
934
+ config,
935
+ destination,
936
+ isCamelCase,
937
+ enumDeclarations,
938
+ defaultZodHeader
939
+ });
940
+ const suffix = destination.suffix || destination.type;
941
+ const folder = destination.folder || ".";
942
+ const fileName = `${entityName}.${suffix}.ts`;
943
+ const filePath = path.join(folder, fileName);
944
+ results[filePath] = (destination.header || "") + content;
945
+ }
886
946
  }
887
- consolidatedContent += `
947
+ const kyselyDestinations = config.destinations.filter((d) => d.type === "kysely");
948
+ for (const kyselyDestination of kyselyDestinations) {
949
+ const header = kyselyDestination.header || defaultKyselyHeader;
950
+ const schemaName = kyselyDestination.schemaName || "DB";
951
+ let consolidatedContent = `${header}
952
+ ${kyselyJsonTypes}`;
953
+ const tableContents = [];
954
+ for (const entity of allEntities) {
955
+ const { name: entityName, type: entityType } = entity;
956
+ let describes;
957
+ if (config.origin.type === "prisma") {
958
+ describes = extractPrismaColumnDescriptions(config, entityName, enumDeclarations);
959
+ } else {
960
+ describes = await extractColumnDescriptions(db, config, entityName);
961
+ }
962
+ if (describes.length === 0) continue;
963
+ const content = entityType === "view" ? generateViewContent({
964
+ view: entityName,
965
+ describes: describes.sort((a, b) => a.Field.localeCompare(b.Field)),
966
+ config,
967
+ destination: kyselyDestination,
968
+ isCamelCase,
969
+ enumDeclarations,
970
+ defaultZodHeader
971
+ }) : generateContent({
972
+ table: entityName,
973
+ describes: describes.sort((a, b) => a.Field.localeCompare(b.Field)),
974
+ config,
975
+ destination: kyselyDestination,
976
+ isCamelCase,
977
+ enumDeclarations,
978
+ defaultZodHeader
979
+ });
980
+ tableContents.push({ table: entityName, content });
981
+ consolidatedContent += content + "\n";
982
+ }
983
+ consolidatedContent += `
888
984
  // Database Interface
889
985
  export interface ${schemaName} {
890
986
  `;
891
- const sortedTableEntries = tableContents.map(({ table }) => {
892
- const pascalTable = camelCase(table, { pascalCase: true });
893
- const tableKey = isCamelCase ? camelCase(table) : table;
894
- return { tableKey, pascalTable };
895
- }).sort((a, b) => a.tableKey.localeCompare(b.tableKey));
896
- for (const { tableKey, pascalTable } of sortedTableEntries) {
897
- consolidatedContent += ` ${tableKey}: ${pascalTable};
987
+ const sortedTableEntries = tableContents.map(({ table, content }) => {
988
+ const isView = content.includes("(view");
989
+ const pascalTable = camelCase(table, { pascalCase: true }) + (isView ? "View" : "");
990
+ const tableKey = isCamelCase ? camelCase(table) : table;
991
+ return { tableKey, pascalTable, isView };
992
+ }).sort((a, b) => a.tableKey.localeCompare(b.tableKey));
993
+ for (const { tableKey, pascalTable } of sortedTableEntries) {
994
+ consolidatedContent += ` ${tableKey}: ${pascalTable};
898
995
  `;
996
+ }
997
+ consolidatedContent += "}\n";
998
+ const outputFile = kyselyDestination.outFile || path.join(kyselyDestination.folder || ".", "db.ts");
999
+ results[outputFile] = consolidatedContent;
899
1000
  }
900
- consolidatedContent += "}\n";
901
1001
  if (config.dryRun) {
902
- const absolutePath = path.resolve(outFile);
903
- dryRunOutput[absolutePath] = consolidatedContent;
904
- for (const key of Object.keys(dryRunOutput)) {
905
- if (key.endsWith(".kysely.temp")) {
906
- delete dryRunOutput[key];
907
- }
1002
+ return results;
1003
+ }
1004
+ for (const [filePath, content] of Object.entries(results)) {
1005
+ const fullPath = path.resolve(filePath);
1006
+ await fs.ensureDir(path.dirname(fullPath));
1007
+ await fs.writeFile(fullPath, content);
1008
+ if (!config.silent) {
1009
+ console.log(`Created: ${filePath}`);
908
1010
  }
909
- } else {
910
- const dest = path.resolve(outFile);
911
- dests.push(dest);
912
- if (!config.silent) console.log("Created:", dest);
913
- fs.outputFileSync(dest, consolidatedContent);
914
1011
  }
915
- }
916
- if (!config.dryRun) {
917
- const result2 = {};
918
- for (const dest of dests) {
919
- const absolutePath = path.resolve(dest);
920
- const content = fs.readFileSync(dest, "utf8");
921
- result2[absolutePath] = content;
1012
+ return results;
1013
+ } finally {
1014
+ if (db) {
1015
+ await db.destroy();
922
1016
  }
923
- return result2;
924
- }
925
- const result = {};
926
- for (const [key, content] of Object.entries(dryRunOutput)) {
927
- const absolutePath = path.resolve(key);
928
- result[absolutePath] = content;
929
1017
  }
930
- return result;
931
1018
  }
932
- export {
933
- defaultKyselyHeader,
934
- defaultZodHeader,
935
- extractKyselyExpression,
936
- extractTSExpression,
937
- extractTypeExpression,
938
- extractZodExpression,
939
- generate,
940
- generateContent,
941
- getType
942
- };
1019
+
1020
+ export { defaultKyselyHeader, defaultZodHeader, extractKyselyExpression, extractTSExpression, extractTypeExpression, extractZodExpression, generate, generateContent, generateViewContent, getType };