mutano 2.0.0 → 2.2.0
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 +12 -140
- package/dist/main.d.ts +2 -4
- package/dist/main.js +121 -42
- package/package.json +6 -4
package/README.md
CHANGED
|
@@ -9,6 +9,7 @@ Converts Prisma/MySQL/PostgreSQL/SQLite schemas to Zod schemas, TypeScript inter
|
|
|
9
9
|
- Handles nullable, default, auto-increment and enum fields
|
|
10
10
|
- Supports custom type overrides via configuration or database comments
|
|
11
11
|
- Intelligently handles field nullability based on operation type (table, insertable, updateable, selectable)
|
|
12
|
+
- All fields in updateable schemas are automatically made optional
|
|
12
13
|
|
|
13
14
|
## Installation
|
|
14
15
|
|
|
@@ -57,7 +58,7 @@ await generate({
|
|
|
57
58
|
type: 'zod',
|
|
58
59
|
useDateType: true,
|
|
59
60
|
useTrim: false,
|
|
60
|
-
nullish: false,
|
|
61
|
+
nullish: false, // When true, nullable fields use nullish() instead of nullable()
|
|
61
62
|
folder: './generated',
|
|
62
63
|
suffix: 'schema'
|
|
63
64
|
}]
|
|
@@ -303,16 +304,16 @@ export const insertable_user = z.object({
|
|
|
303
304
|
})
|
|
304
305
|
|
|
305
306
|
export const updateable_user = z.object({
|
|
306
|
-
name: z.string().min(10).max(255),
|
|
307
|
-
username: z.string(),
|
|
308
|
-
password: z.string(),
|
|
309
|
-
profile_picture: z.string().nullable(),
|
|
310
|
-
role: z.enum(['admin', 'user']),
|
|
307
|
+
name: z.string().min(10).max(255).optional(),
|
|
308
|
+
username: z.string().optional(),
|
|
309
|
+
password: z.string().optional(),
|
|
310
|
+
profile_picture: z.string().nullable().optional(),
|
|
311
|
+
role: z.enum(['admin', 'user']).optional(),
|
|
311
312
|
})
|
|
312
313
|
|
|
313
314
|
export const selectable_user = z.object({
|
|
314
315
|
id: z.number().nonnegative(),
|
|
315
|
-
name: z.string()
|
|
316
|
+
name: z.string(),
|
|
316
317
|
username: z.string(),
|
|
317
318
|
password: z.string(),
|
|
318
319
|
profile_picture: z.string().nullable(),
|
|
@@ -372,92 +373,6 @@ export interface SelectableUser {
|
|
|
372
373
|
}
|
|
373
374
|
```
|
|
374
375
|
|
|
375
|
-
### TypeScript Interface Output Example (Enum Type for Enums)
|
|
376
|
-
|
|
377
|
-
```typescript
|
|
378
|
-
// TypeScript interfaces for user
|
|
379
|
-
|
|
380
|
-
// Enum declarations
|
|
381
|
-
enum RoleEnum {
|
|
382
|
-
admin = 'admin',
|
|
383
|
-
user = 'user'
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
export interface User {
|
|
387
|
-
id: number;
|
|
388
|
-
name: string;
|
|
389
|
-
username: string;
|
|
390
|
-
password: string;
|
|
391
|
-
profile_picture: string | null;
|
|
392
|
-
role: RoleEnum;
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
export interface InsertableUser {
|
|
396
|
-
name: string | null; // Optional because it has a default value
|
|
397
|
-
username: string;
|
|
398
|
-
password: string;
|
|
399
|
-
profile_picture: string | null;
|
|
400
|
-
role: RoleEnum;
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
export interface UpdateableUser {
|
|
404
|
-
name: string | null; // Optional for updates
|
|
405
|
-
username: string | null; // Optional for updates
|
|
406
|
-
password: string | null; // Optional for updates
|
|
407
|
-
profile_picture: string | null;
|
|
408
|
-
role: RoleEnum | null; // Optional for updates
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
export interface SelectableUser {
|
|
412
|
-
id: number;
|
|
413
|
-
name: string;
|
|
414
|
-
username: string;
|
|
415
|
-
password: string;
|
|
416
|
-
profile_picture: string | null;
|
|
417
|
-
role: RoleEnum;
|
|
418
|
-
}
|
|
419
|
-
```
|
|
420
|
-
|
|
421
|
-
### TypeScript Type Alias Output Example
|
|
422
|
-
|
|
423
|
-
```typescript
|
|
424
|
-
// TypeScript types for user
|
|
425
|
-
|
|
426
|
-
export type User = {
|
|
427
|
-
id: number;
|
|
428
|
-
name: string;
|
|
429
|
-
username: string;
|
|
430
|
-
password: string;
|
|
431
|
-
profile_picture: string | null;
|
|
432
|
-
role: 'admin' | 'user';
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
export type InsertableUser = {
|
|
436
|
-
name: string | null; // Optional because it has a default value
|
|
437
|
-
username: string;
|
|
438
|
-
password: string;
|
|
439
|
-
profile_picture: string | null;
|
|
440
|
-
role: 'admin' | 'user';
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
export type UpdateableUser = {
|
|
444
|
-
name: string | null; // Optional for updates
|
|
445
|
-
username: string | null; // Optional for updates
|
|
446
|
-
password: string | null; // Optional for updates
|
|
447
|
-
profile_picture: string | null;
|
|
448
|
-
role: 'admin' | 'user' | null; // Optional for updates
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
export type SelectableUser = {
|
|
452
|
-
id: number;
|
|
453
|
-
name: string;
|
|
454
|
-
username: string;
|
|
455
|
-
password: string;
|
|
456
|
-
profile_picture: string | null;
|
|
457
|
-
role: 'admin' | 'user';
|
|
458
|
-
}
|
|
459
|
-
```
|
|
460
|
-
|
|
461
376
|
### Kysely Type Definitions Output Example
|
|
462
377
|
|
|
463
378
|
```typescript
|
|
@@ -500,49 +415,6 @@ export type NewUser = Insertable<UserTable>;
|
|
|
500
415
|
export type UserUpdate = Updateable<UserTable>;
|
|
501
416
|
```
|
|
502
417
|
|
|
503
|
-
### Kysely Type Definitions Output Example with Custom Schema Name
|
|
504
|
-
|
|
505
|
-
```typescript
|
|
506
|
-
import { Generated, ColumnType, Selectable, Insertable, Updateable } from 'kysely';
|
|
507
|
-
import { CustomTypes } from './types';
|
|
508
|
-
|
|
509
|
-
// JSON type definitions
|
|
510
|
-
export type Json = ColumnType<JsonValue, string, string>;
|
|
511
|
-
|
|
512
|
-
export type JsonArray = JsonValue[];
|
|
513
|
-
|
|
514
|
-
export type JsonObject = {
|
|
515
|
-
[x: string]: JsonValue | undefined;
|
|
516
|
-
};
|
|
517
|
-
|
|
518
|
-
export type JsonPrimitive = boolean | number | string | null;
|
|
519
|
-
|
|
520
|
-
export type JsonValue = JsonArray | JsonObject | JsonPrimitive;
|
|
521
|
-
|
|
522
|
-
// Kysely type definitions for user
|
|
523
|
-
|
|
524
|
-
// This interface defines the structure of the 'user' table
|
|
525
|
-
export interface UserTable {
|
|
526
|
-
id: Generated<number>;
|
|
527
|
-
name: string;
|
|
528
|
-
username: string;
|
|
529
|
-
password: string;
|
|
530
|
-
profile_picture: string | null;
|
|
531
|
-
metadata: Json;
|
|
532
|
-
role: 'admin' | 'user';
|
|
533
|
-
}
|
|
534
|
-
|
|
535
|
-
// Define the database interface
|
|
536
|
-
export interface Database {
|
|
537
|
-
user: UserTable;
|
|
538
|
-
}
|
|
539
|
-
|
|
540
|
-
// Use these types for inserting, selecting and updating the table
|
|
541
|
-
export type User = Selectable<UserTable>;
|
|
542
|
-
export type NewUser = Insertable<UserTable>;
|
|
543
|
-
export type UserUpdate = Updateable<UserTable>;
|
|
544
|
-
```
|
|
545
|
-
|
|
546
418
|
## Config
|
|
547
419
|
|
|
548
420
|
```json
|
|
@@ -596,8 +468,8 @@ export type UserUpdate = Updateable<UserTable>;
|
|
|
596
468
|
"type": "zod",
|
|
597
469
|
"useDateType": true,
|
|
598
470
|
"useTrim": false,
|
|
599
|
-
"nullish": false,
|
|
600
|
-
"requiredString": false,
|
|
471
|
+
"nullish": false, // When true, nullable fields use nullish() instead of nullable()
|
|
472
|
+
"requiredString": false, // When true, adds min(1) validation to non-nullable string fields
|
|
601
473
|
"header": "import { z } from 'zod';\nimport { CustomValidator } from './validators';",
|
|
602
474
|
"folder": "@zod",
|
|
603
475
|
"suffix": "table"
|
|
@@ -633,8 +505,8 @@ export type UserUpdate = Updateable<UserTable>;
|
|
|
633
505
|
| destinations[].type | The type of output to generate: "zod", "ts", or "kysely" |
|
|
634
506
|
| destinations[].useDateType | (Zod only) Use a specialized Zod type for date-like fields instead of string |
|
|
635
507
|
| destinations[].useTrim | (Zod only) Use `z.string().trim()` instead of `z.string()` |
|
|
636
|
-
| destinations[].nullish | (Zod only)
|
|
637
|
-
| destinations[].requiredString | (Zod only) Add `min(1)` for string
|
|
508
|
+
| destinations[].nullish | (Zod only) Use `nullish()` instead of `nullable()` for nullable fields. In updateable schemas, fields that were already nullable will become nullish |
|
|
509
|
+
| destinations[].requiredString | (Zod only) Add `min(1)` for non-nullable string fields |
|
|
638
510
|
| destinations[].enumType | (TypeScript only) How to represent enum types: "union" (default) or "enum" |
|
|
639
511
|
| destinations[].modelType | (TypeScript only) How to represent models: "interface" (default) or "type" |
|
|
640
512
|
| destinations[].schemaName | (Kysely only) Name of the database interface (default: "DB") |
|
package/dist/main.d.ts
CHANGED
|
@@ -7,9 +7,8 @@ export interface GenerateContentParams {
|
|
|
7
7
|
isCamelCase: boolean;
|
|
8
8
|
enumDeclarations: Record<string, string[]>;
|
|
9
9
|
defaultZodHeader: string;
|
|
10
|
-
defaultKyselyHeader: string;
|
|
11
10
|
}
|
|
12
|
-
export declare function generateContent({ table, describes, config, destination, isCamelCase, enumDeclarations, defaultZodHeader,
|
|
11
|
+
export declare function generateContent({ table, describes, config, destination, isCamelCase, enumDeclarations, defaultZodHeader, }: GenerateContentParams): string;
|
|
13
12
|
export declare const defaultKyselyHeader = "import { Generated, ColumnType, Selectable, Insertable, Updateable } from 'kysely';\n\n";
|
|
14
13
|
export declare const defaultZodHeader = "import { z } from 'zod';\n\n";
|
|
15
14
|
export declare function generate(config: Config): Promise<string[] | Record<string, string>>;
|
|
@@ -46,8 +45,7 @@ export type Destination = {
|
|
|
46
45
|
type: 'kysely';
|
|
47
46
|
header?: string;
|
|
48
47
|
schemaName?: string;
|
|
49
|
-
|
|
50
|
-
suffix?: string;
|
|
48
|
+
outFile?: string;
|
|
51
49
|
};
|
|
52
50
|
export interface Config {
|
|
53
51
|
origin: {
|
package/dist/main.js
CHANGED
|
@@ -164,6 +164,7 @@ function getType(op, desc, config, destination, tableName) {
|
|
|
164
164
|
const { Default, Extra, Null, Type, Comment, EnumOptions } = desc;
|
|
165
165
|
const isZodDestination = destination.type === "zod";
|
|
166
166
|
const isTsDestination = destination.type === "ts";
|
|
167
|
+
const isKyselyDestination = destination.type === "kysely";
|
|
167
168
|
const isNullish = isZodDestination && destination.type === "zod" && destination.nullish === true;
|
|
168
169
|
const isTrim = isZodDestination && destination.type === "zod" && destination.useTrim === true && op !== "selectable";
|
|
169
170
|
const isUseDateType = isZodDestination && destination.type === "zod" && destination.useDateType === true;
|
|
@@ -174,11 +175,11 @@ function getType(op, desc, config, destination, tableName) {
|
|
|
174
175
|
return;
|
|
175
176
|
const isRequiredString = destination.type === "zod" && destination.requiredString === true && op !== "selectable";
|
|
176
177
|
const type = schemaType === "mysql" ? Type.split("(")[0].split(" ")[0] : Type;
|
|
177
|
-
if (isTsDestination) {
|
|
178
|
+
if (isTsDestination || isKyselyDestination) {
|
|
178
179
|
const tsOverrideType = config.magicComments ? extractTSExpression(Comment) : null;
|
|
179
180
|
const shouldBeNullable = isNull || ["insertable", "updateable"].includes(op) && (hasDefaultValue || isGenerated) || op === "updateable" && !isNull && !hasDefaultValue;
|
|
180
181
|
if (tsOverrideType) {
|
|
181
|
-
return shouldBeNullable ? `${tsOverrideType} | null` : tsOverrideType;
|
|
182
|
+
return shouldBeNullable ? tsOverrideType.includes("| null") ? tsOverrideType : `${tsOverrideType} | null` : tsOverrideType;
|
|
182
183
|
}
|
|
183
184
|
if (dateTypes[schemaType].includes(type)) {
|
|
184
185
|
return shouldBeNullable ? "Date | null" : "Date";
|
|
@@ -198,10 +199,10 @@ function getType(op, desc, config, destination, tableName) {
|
|
|
198
199
|
if (schemaType === "mysql") {
|
|
199
200
|
const matches = Type.match(enumRegex);
|
|
200
201
|
if (matches?.[1]) {
|
|
201
|
-
enumValues = matches[1].split(",").map((v) => v.trim());
|
|
202
|
+
enumValues = matches[1].split(",").map((v) => v.trim()).sort();
|
|
202
203
|
}
|
|
203
204
|
} else if (EnumOptions && EnumOptions.length > 0) {
|
|
204
|
-
enumValues = EnumOptions.map((e) => `'${e}'`);
|
|
205
|
+
enumValues = EnumOptions.map((e) => `'${e}'`).sort();
|
|
205
206
|
}
|
|
206
207
|
if (enumValues.length === 0) {
|
|
207
208
|
return isNull ? "string | null" : "string";
|
|
@@ -302,7 +303,16 @@ function getType(op, desc, config, destination, tableName) {
|
|
|
302
303
|
return field.join(".");
|
|
303
304
|
};
|
|
304
305
|
const generateEnumLikeField = () => {
|
|
305
|
-
|
|
306
|
+
let enumValues = [];
|
|
307
|
+
if (schemaType === "mysql") {
|
|
308
|
+
const matches = Type.match(enumRegex);
|
|
309
|
+
if (matches?.[1]) {
|
|
310
|
+
enumValues = matches[1].split(",").map((v) => v.trim()).sort();
|
|
311
|
+
}
|
|
312
|
+
} else if (EnumOptions && EnumOptions.length > 0) {
|
|
313
|
+
enumValues = [...EnumOptions].sort().map((e) => `'${e}'`);
|
|
314
|
+
}
|
|
315
|
+
const value = enumValues.join(",");
|
|
306
316
|
const field = [`z.enum([${value}])`];
|
|
307
317
|
if (isNull) field.push(nullable);
|
|
308
318
|
else if (hasDefaultValue || !hasDefaultValue && isGenerated)
|
|
@@ -326,35 +336,16 @@ function generateContent({
|
|
|
326
336
|
destination,
|
|
327
337
|
isCamelCase,
|
|
328
338
|
enumDeclarations: enumDeclarations2,
|
|
329
|
-
defaultZodHeader: defaultZodHeader2
|
|
330
|
-
defaultKyselyHeader: defaultKyselyHeader2
|
|
339
|
+
defaultZodHeader: defaultZodHeader2
|
|
331
340
|
}) {
|
|
332
341
|
let content = "";
|
|
342
|
+
const schemaType = config.origin.type;
|
|
333
343
|
if (destination.type === "kysely") {
|
|
334
|
-
const header = destination.header;
|
|
335
|
-
const schemaName = destination.schemaName || "DB";
|
|
336
|
-
content = header ? `${header}
|
|
337
|
-
|
|
338
|
-
` : defaultKyselyHeader2;
|
|
339
|
-
content += `// JSON type definitions
|
|
340
|
-
export type Json = ColumnType<JsonValue, string, string>;
|
|
341
|
-
|
|
342
|
-
export type JsonArray = JsonValue[];
|
|
343
|
-
|
|
344
|
-
export type JsonObject = {
|
|
345
|
-
[x: string]: JsonValue | undefined;
|
|
346
|
-
};
|
|
347
|
-
|
|
348
|
-
export type JsonPrimitive = boolean | number | string | null;
|
|
349
|
-
|
|
350
|
-
export type JsonValue = JsonArray | JsonObject | JsonPrimitive;
|
|
351
|
-
|
|
352
|
-
`;
|
|
353
344
|
content += `// Kysely type definitions for ${table}
|
|
354
345
|
`;
|
|
355
346
|
content += `
|
|
356
347
|
// This interface defines the structure of the '${table}' table
|
|
357
|
-
export interface ${camelCase(table, { pascalCase: true })}
|
|
348
|
+
export interface ${camelCase(table, { pascalCase: true })} {`;
|
|
358
349
|
for (const desc of describes) {
|
|
359
350
|
const field = isCamelCase ? camelCase(desc.Field) : desc.Field;
|
|
360
351
|
const type = getType("table", desc, config, destination, table);
|
|
@@ -364,9 +355,13 @@ export interface ${camelCase(table, { pascalCase: true })}Table {`;
|
|
|
364
355
|
const isDefaultGenerated = desc.Extra.toLowerCase().includes("default_generated");
|
|
365
356
|
const isNullable = desc.Null === "YES";
|
|
366
357
|
const isJsonField = desc.Type.toLowerCase().includes("json");
|
|
358
|
+
const hasDefaultValue = desc.Default !== null;
|
|
359
|
+
const isEnum = schemaType !== "sqlite" && enumTypes[schemaType].includes(
|
|
360
|
+
schemaType === "mysql" ? desc.Type.split("(")[0].split(" ")[0] : desc.Type
|
|
361
|
+
);
|
|
367
362
|
if (isJsonField) {
|
|
368
363
|
kyselyType = "Json";
|
|
369
|
-
} else if (isAutoIncrement || isDefaultGenerated) {
|
|
364
|
+
} else if (isAutoIncrement || isDefaultGenerated || isEnum && hasDefaultValue) {
|
|
370
365
|
kyselyType = `Generated<${kyselyType}>`;
|
|
371
366
|
}
|
|
372
367
|
if (isNullable && !isJsonField) {
|
|
@@ -381,15 +376,10 @@ export interface ${camelCase(table, { pascalCase: true })}Table {`;
|
|
|
381
376
|
content = `${content}
|
|
382
377
|
}
|
|
383
378
|
|
|
384
|
-
//
|
|
385
|
-
export
|
|
386
|
-
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
// Use these types for inserting, selecting and updating the table
|
|
390
|
-
export type ${camelCase(table, { pascalCase: true })} = Selectable<${camelCase(table, { pascalCase: true })}Table>;
|
|
391
|
-
export type New${camelCase(table, { pascalCase: true })} = Insertable<${camelCase(table, { pascalCase: true })}Table>;
|
|
392
|
-
export type ${camelCase(table, { pascalCase: true })}Update = Updateable<${camelCase(table, { pascalCase: true })}Table>;
|
|
379
|
+
// Helper types for ${table}
|
|
380
|
+
export type Selectable${camelCase(table, { pascalCase: true })} = Selectable<${camelCase(table, { pascalCase: true })}>;
|
|
381
|
+
export type Insertable${camelCase(table, { pascalCase: true })} = Insertable<${camelCase(table, { pascalCase: true })}>;
|
|
382
|
+
export type Updateable${camelCase(table, { pascalCase: true })} = Updateable<${camelCase(table, { pascalCase: true })}>;
|
|
393
383
|
`;
|
|
394
384
|
} else if (destination.type === "ts") {
|
|
395
385
|
const modelType = destination.modelType || "interface";
|
|
@@ -552,6 +542,10 @@ async function generate(config) {
|
|
|
552
542
|
let prismaTables = [];
|
|
553
543
|
let schema = null;
|
|
554
544
|
let db = null;
|
|
545
|
+
const kyselyTableContents = {};
|
|
546
|
+
if (config.destinations.length === 0) {
|
|
547
|
+
throw new Error("Empty destinations object.");
|
|
548
|
+
}
|
|
555
549
|
const dryRunOutput = {};
|
|
556
550
|
if (config.origin.type === "mysql") {
|
|
557
551
|
db = knex({
|
|
@@ -692,7 +686,7 @@ async function generate(config) {
|
|
|
692
686
|
AND column_name = $3
|
|
693
687
|
)
|
|
694
688
|
ORDER BY
|
|
695
|
-
e.
|
|
689
|
+
e.enumlabel
|
|
696
690
|
`,
|
|
697
691
|
[schema2, table, column.Field]
|
|
698
692
|
);
|
|
@@ -765,16 +759,21 @@ async function generate(config) {
|
|
|
765
759
|
if (!config.destinations || config.destinations.length === 0) {
|
|
766
760
|
throw new Error("No destinations specified");
|
|
767
761
|
}
|
|
768
|
-
|
|
762
|
+
const kyselyDestinations = config.destinations.filter(
|
|
763
|
+
(d) => d.type === "kysely"
|
|
764
|
+
);
|
|
765
|
+
const nonKyselyDestinations = config.destinations.filter(
|
|
766
|
+
(d) => d.type !== "kysely"
|
|
767
|
+
);
|
|
768
|
+
for (const destination of nonKyselyDestinations) {
|
|
769
769
|
const content = generateContent({
|
|
770
770
|
table,
|
|
771
|
-
describes,
|
|
771
|
+
describes: describes.sort((a, b) => a.Field.localeCompare(b.Field)),
|
|
772
772
|
config,
|
|
773
773
|
destination,
|
|
774
774
|
isCamelCase: isCamelCase === true,
|
|
775
775
|
enumDeclarations,
|
|
776
|
-
defaultZodHeader
|
|
777
|
-
defaultKyselyHeader
|
|
776
|
+
defaultZodHeader
|
|
778
777
|
});
|
|
779
778
|
const suffix = destination.suffix || "";
|
|
780
779
|
const folder = destination.folder || ".";
|
|
@@ -788,8 +787,88 @@ async function generate(config) {
|
|
|
788
787
|
fs.outputFileSync(dest, content);
|
|
789
788
|
}
|
|
790
789
|
}
|
|
790
|
+
for (const destination of kyselyDestinations) {
|
|
791
|
+
const content = generateContent({
|
|
792
|
+
table,
|
|
793
|
+
describes: describes.sort((a, b) => a.Field.localeCompare(b.Field)),
|
|
794
|
+
config,
|
|
795
|
+
destination,
|
|
796
|
+
isCamelCase: isCamelCase === true,
|
|
797
|
+
enumDeclarations,
|
|
798
|
+
defaultZodHeader
|
|
799
|
+
});
|
|
800
|
+
const outFile = destination.outFile || "db.ts";
|
|
801
|
+
if (!kyselyTableContents[outFile]) {
|
|
802
|
+
kyselyTableContents[outFile] = [];
|
|
803
|
+
}
|
|
804
|
+
kyselyTableContents[outFile].push({
|
|
805
|
+
table,
|
|
806
|
+
content
|
|
807
|
+
});
|
|
808
|
+
if (config.dryRun) {
|
|
809
|
+
const tempKey = `${table}.kysely.temp`;
|
|
810
|
+
dryRunOutput[tempKey] = content;
|
|
811
|
+
}
|
|
812
|
+
}
|
|
791
813
|
}
|
|
792
814
|
if (db) await db.destroy();
|
|
815
|
+
for (const [outFile, tableContents] of Object.entries(kyselyTableContents)) {
|
|
816
|
+
if (tableContents.length === 0) continue;
|
|
817
|
+
const kyselyDestination = config.destinations.find(
|
|
818
|
+
(d) => d.type === "kysely"
|
|
819
|
+
);
|
|
820
|
+
const header = kyselyDestination?.header || defaultKyselyHeader;
|
|
821
|
+
const schemaName = kyselyDestination?.schemaName || "DB";
|
|
822
|
+
let consolidatedContent = `${header}
|
|
823
|
+
|
|
824
|
+
// JSON type definitions
|
|
825
|
+
export type Json = ColumnType<JsonValue, string, string>;
|
|
826
|
+
|
|
827
|
+
export type JsonArray = JsonValue[];
|
|
828
|
+
|
|
829
|
+
export type JsonObject = {
|
|
830
|
+
[x: string]: JsonValue | undefined;
|
|
831
|
+
};
|
|
832
|
+
|
|
833
|
+
export type JsonPrimitive = boolean | number | string | null;
|
|
834
|
+
|
|
835
|
+
export type JsonValue = JsonArray | JsonObject | JsonPrimitive;
|
|
836
|
+
|
|
837
|
+
`;
|
|
838
|
+
consolidatedContent += "// Table Interfaces\n";
|
|
839
|
+
for (const { content } of tableContents) {
|
|
840
|
+
consolidatedContent += `${content}
|
|
841
|
+
`;
|
|
842
|
+
}
|
|
843
|
+
consolidatedContent += `
|
|
844
|
+
// Database Interface
|
|
845
|
+
export interface ${schemaName} {
|
|
846
|
+
`;
|
|
847
|
+
const sortedTableEntries = tableContents.map(({ table }) => {
|
|
848
|
+
const pascalTable = camelCase(table, { pascalCase: true });
|
|
849
|
+
const tableKey = isCamelCase ? camelCase(table) : table;
|
|
850
|
+
return { tableKey, pascalTable };
|
|
851
|
+
}).sort((a, b) => a.tableKey.localeCompare(b.tableKey));
|
|
852
|
+
for (const { tableKey, pascalTable } of sortedTableEntries) {
|
|
853
|
+
consolidatedContent += ` ${tableKey}: ${pascalTable};
|
|
854
|
+
`;
|
|
855
|
+
}
|
|
856
|
+
consolidatedContent += "}\n";
|
|
857
|
+
if (config.dryRun) {
|
|
858
|
+
const fileName = path.basename(outFile);
|
|
859
|
+
dryRunOutput[fileName] = consolidatedContent;
|
|
860
|
+
for (const key of Object.keys(dryRunOutput)) {
|
|
861
|
+
if (key.endsWith(".kysely.temp")) {
|
|
862
|
+
delete dryRunOutput[key];
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
} else {
|
|
866
|
+
const dest = path.resolve(outFile);
|
|
867
|
+
dests.push(dest);
|
|
868
|
+
if (!config.silent) console.log("Created:", dest);
|
|
869
|
+
fs.outputFileSync(dest, consolidatedContent);
|
|
870
|
+
}
|
|
871
|
+
}
|
|
793
872
|
return config.dryRun ? dryRunOutput : dests;
|
|
794
873
|
}
|
|
795
874
|
export {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mutano",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "2.
|
|
4
|
+
"version": "2.2.0",
|
|
5
5
|
"description": "Converts Prisma/MySQL/PostgreSQL/SQLite schemas to Zod/TS/Kysely interfaces",
|
|
6
6
|
"author": "Alisson Cavalcante Agiani <thelinuxlich@gmail.com>",
|
|
7
7
|
"license": "MIT",
|
|
@@ -19,13 +19,15 @@
|
|
|
19
19
|
"fs-extra": "^11.3.0",
|
|
20
20
|
"knex": "^3.1.0",
|
|
21
21
|
"mysql2": "^3.14.1",
|
|
22
|
-
"pg": "^8.
|
|
22
|
+
"pg": "^8.16.0",
|
|
23
23
|
"sqlite3": "^5.1.7"
|
|
24
24
|
},
|
|
25
25
|
"devDependencies": {
|
|
26
26
|
"@types/fs-extra": "^11.0.4",
|
|
27
|
-
"esbuild": "^0.25.
|
|
27
|
+
"esbuild": "^0.25.4",
|
|
28
|
+
"ts-node": "^10.9.2",
|
|
29
|
+
"tsx": "^4.19.4",
|
|
28
30
|
"typescript": "^5.8.3",
|
|
29
|
-
"vitest": "^3.1.
|
|
31
|
+
"vitest": "^3.1.3"
|
|
30
32
|
}
|
|
31
33
|
}
|