mutano 1.0.7 → 1.1.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.
Files changed (3) hide show
  1. package/README.md +150 -8
  2. package/dist/main.js +30 -55
  3. package/package.json +11 -9
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # mutano
2
2
 
3
- Converts Prisma/MySQL schemas to Zod interfaces
3
+ Converts Prisma/MySQL/PostgreSQL/SQLite schemas to Zod interfaces
4
4
 
5
5
  ## Installation
6
6
 
@@ -17,7 +17,7 @@ Create user table:
17
17
  ```sql
18
18
  CREATE TABLE `user` (
19
19
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
20
- `name` varchar(255) NOT NULL COMMENT '@zod(z.string().min(10).max(255))', -- this will override the type
20
+ `name` varchar(255) NOT NULL COMMENT '@zod(z.string().min(10).max(255))', -- this will override the type
21
21
  `username` varchar(255) NOT NULL,
22
22
  `password` varchar(255) NOT NULL,
23
23
  `profile_picture` varchar(255) DEFAULT NULL,
@@ -27,17 +27,59 @@ CREATE TABLE `user` (
27
27
  ```
28
28
  Use the mutano API:
29
29
 
30
+ ### MySQL Example
31
+
30
32
  ```typescript
31
33
  import { generate } from 'mutano'
32
34
 
33
35
  await generate({
34
- origin: {
36
+ origin: {
35
37
  type: 'mysql',
36
38
  host: '127.0.0.1',
37
39
  port: 3306,
38
40
  user: 'root',
39
41
  password: 'secret',
40
- database: 'myapp',
42
+ database: 'myapp',
43
+ overrideTypes: {
44
+ json: 'z.record(z.string())'
45
+ }
46
+ },
47
+ })
48
+ ```
49
+
50
+ ### PostgreSQL Example
51
+
52
+ ```typescript
53
+ import { generate } from 'mutano'
54
+
55
+ await generate({
56
+ origin: {
57
+ type: 'postgres',
58
+ host: '127.0.0.1',
59
+ port: 5432,
60
+ user: 'postgres',
61
+ password: 'secret',
62
+ database: 'myapp',
63
+ schema: 'public', // optional, defaults to 'public'
64
+ overrideTypes: {
65
+ jsonb: 'z.record(z.string())'
66
+ }
67
+ },
68
+ })
69
+ ```
70
+
71
+ ### SQLite Example
72
+
73
+ ```typescript
74
+ import { generate } from 'mutano'
75
+
76
+ await generate({
77
+ origin: {
78
+ type: 'sqlite',
79
+ path: './myapp.db',
80
+ overrideTypes: {
81
+ json: 'z.record(z.string())'
82
+ }
41
83
  },
42
84
  })
43
85
  ```
@@ -98,14 +140,42 @@ export type SelectableUserType = z.infer<typeof selectable_user>
98
140
  "user": "root",
99
141
  "password": "secret",
100
142
  "database": "myapp",
143
+ "overrideTypes": {
144
+ "json": "z.record(z.string())"
145
+ },
101
146
  "ssl": {
102
147
  "ca": "path/to/ca.pem",
103
148
  "cert": "path/to/cert.pem",
104
149
  "key": "path/to/key.pem"
105
150
  },
106
151
  } | {
107
- "type": "prisma"
108
- "path": "path/to/schema.prisma"
152
+ "type": "postgres",
153
+ "host": "127.0.0.1",
154
+ "port": 5432,
155
+ "user": "postgres",
156
+ "password": "secret",
157
+ "database": "myapp",
158
+ "schema": "public",
159
+ "overrideTypes": {
160
+ "jsonb": "z.record(z.string())"
161
+ },
162
+ "ssl": {
163
+ "ca": "path/to/ca.pem",
164
+ "cert": "path/to/cert.pem",
165
+ "key": "path/to/key.pem"
166
+ },
167
+ } | {
168
+ "type": "sqlite",
169
+ "path": "path/to/database.db",
170
+ "overrideTypes": {
171
+ "json": "z.record(z.string())"
172
+ }
173
+ } | {
174
+ "type": "prisma",
175
+ "path": "path/to/schema.prisma",
176
+ "overrideTypes": {
177
+ "Json": "z.record(z.string())"
178
+ }
109
179
  },
110
180
  "tables": ["user", "log"],
111
181
  "ignore": ["log", "/^temp/"],
@@ -136,5 +206,77 @@ export type SelectableUserType = z.infer<typeof selectable_user>
136
206
  | useDateType | Use a specialized Zod type for date-like fields instead of string
137
207
  | useTrim | Use `z.string().trim()` instead of `z.string()` |
138
208
  | silent | Don't log anything to the console |
139
- | overrideTypes | Override zod types for specific field types |
140
- | zodCommentTypes | Use @zod comment to override entire type |
209
+ | magicComments | Use @zod comment to override entire type (unsupported by SQLite) |
210
+
211
+ ## overrideTypes
212
+
213
+ You can override the default Zod type for a specific column type. This is specific to each database type and is placed inside the origin object. Each database type has its own set of valid types that can be overridden:
214
+
215
+ ### MySQL overrideTypes
216
+
217
+ ```json
218
+ {
219
+ "origin": {
220
+ "type": "mysql",
221
+ "host": "127.0.0.1",
222
+ "port": 3306,
223
+ "user": "root",
224
+ "password": "secret",
225
+ "database": "myapp",
226
+ "overrideTypes": {
227
+ "json": "z.record(z.string())",
228
+ "text": "z.string().max(1000)"
229
+ }
230
+ }
231
+ }
232
+ ```
233
+
234
+ ### PostgreSQL overrideTypes
235
+
236
+ ```json
237
+ {
238
+ "origin": {
239
+ "type": "postgres",
240
+ "host": "127.0.0.1",
241
+ "port": 5432,
242
+ "user": "postgres",
243
+ "password": "secret",
244
+ "database": "myapp",
245
+ "schema": "public",
246
+ "overrideTypes": {
247
+ "jsonb": "z.record(z.string())",
248
+ "uuid": "z.string().uuid()"
249
+ }
250
+ }
251
+ }
252
+ ```
253
+
254
+ ### SQLite overrideTypes
255
+
256
+ ```json
257
+ {
258
+ "origin": {
259
+ "type": "sqlite",
260
+ "path": "./myapp.db",
261
+ "overrideTypes": {
262
+ "json": "z.record(z.string())",
263
+ "text": "z.string().max(1000)"
264
+ }
265
+ }
266
+ }
267
+ ```
268
+
269
+ ### Prisma overrideTypes
270
+
271
+ ```json
272
+ {
273
+ "origin": {
274
+ "type": "prisma",
275
+ "path": "./schema.prisma",
276
+ "overrideTypes": {
277
+ "Json": "z.record(z.string())",
278
+ "String": "z.string().min(1)"
279
+ }
280
+ }
281
+ }
282
+ ```
package/dist/main.js CHANGED
@@ -8,8 +8,7 @@ import fs from "fs-extra";
8
8
  import knex from "knex";
9
9
  function extractZodExpression(comment) {
10
10
  const zodStart = comment.indexOf("@zod(");
11
- if (zodStart === -1)
12
- return null;
11
+ if (zodStart === -1) return null;
13
12
  let openParens = 0;
14
13
  let position = zodStart + 5;
15
14
  while (position < comment.length) {
@@ -93,80 +92,59 @@ function getType(op, desc, config) {
93
92
  const typeOverride = zodOverrideType ?? config.overrideTypes?.[type];
94
93
  const generateDateLikeField = () => {
95
94
  const field = typeOverride ? [typeOverride] : dateField;
96
- if (isNull && !typeOverride)
97
- field.push(nullable);
98
- else if (hasDefaultValue)
99
- field.push(optional);
100
- if (hasDefaultValue && !isGenerated)
101
- field.push(`default('${Default}')`);
102
- if (isUpdateableFormat)
95
+ if (isNull && !typeOverride) field.push(nullable);
96
+ else if (hasDefaultValue || !hasDefaultValue && isGenerated)
103
97
  field.push(optional);
98
+ if (hasDefaultValue && !isGenerated) field.push(`default('${Default}')`);
99
+ if (isUpdateableFormat) field.push(optional);
104
100
  return field.join(".");
105
101
  };
106
102
  const generateStringLikeField = () => {
107
103
  const field = typeOverride ? [typeOverride] : string;
108
- if (isNull && !typeOverride)
109
- field.push(nullable);
110
- else if (hasDefaultValue)
111
- field.push(optional);
112
- else if (isRequiredString && !typeOverride)
113
- field.push(min1);
114
- if (hasDefaultValue && !isGenerated)
115
- field.push(`default('${Default}')`);
116
- if (isUpdateableFormat)
104
+ if (isNull && !typeOverride) field.push(nullable);
105
+ else if (hasDefaultValue || !hasDefaultValue && isGenerated)
117
106
  field.push(optional);
107
+ else if (isRequiredString && !typeOverride) field.push(min1);
108
+ if (hasDefaultValue && !isGenerated) field.push(`default('${Default}')`);
109
+ if (isUpdateableFormat) field.push(optional);
118
110
  return field.join(".");
119
111
  };
120
112
  const generateBooleanLikeField = () => {
121
113
  const field = typeOverride ? [typeOverride] : boolean;
122
- if (isNull && !typeOverride)
123
- field.push(nullable);
124
- else if (hasDefaultValue)
114
+ if (isNull && !typeOverride) field.push(nullable);
115
+ else if (hasDefaultValue || !hasDefaultValue && isGenerated)
125
116
  field.push(optional);
126
117
  if (hasDefaultValue && !isGenerated)
127
118
  field.push(`default(${Boolean(+Default)})`);
128
- if (isUpdateableFormat)
129
- field.push(optional);
119
+ if (isUpdateableFormat) field.push(optional);
130
120
  return field.join(".");
131
121
  };
132
122
  const generateNumberLikeField = () => {
133
123
  const unsigned = Type.endsWith(" unsigned");
134
124
  const field = typeOverride ? [typeOverride] : number;
135
- if (unsigned && !typeOverride)
136
- field.push(nonnegative);
137
- if (isNull && !typeOverride)
138
- field.push(nullable);
139
- else if (hasDefaultValue)
140
- field.push(optional);
141
- if (hasDefaultValue && !isGenerated)
142
- field.push(`default(${Default})`);
143
- if (isUpdateableFormat)
125
+ if (unsigned && !typeOverride) field.push(nonnegative);
126
+ if (isNull && !typeOverride) field.push(nullable);
127
+ else if (hasDefaultValue || !hasDefaultValue && isGenerated)
144
128
  field.push(optional);
129
+ if (hasDefaultValue && !isGenerated) field.push(`default(${Default})`);
130
+ if (isUpdateableFormat) field.push(optional);
145
131
  return field.join(".");
146
132
  };
147
133
  const generateEnumLikeField = () => {
148
134
  const value = schemaType === "mysql" ? Type.replace("enum(", "").replace(")", "").replace(/,/g, ",") : EnumOptions?.map((e) => `'${e}'`).join(",");
149
135
  const field = [`z.enum([${value}])`];
150
- if (isNull)
151
- field.push(nullable);
152
- else if (hasDefaultValue)
153
- field.push(optional);
154
- if (hasDefaultValue && !isGenerated)
155
- field.push(`default('${Default}')`);
156
- if (isUpdateableFormat)
136
+ if (isNull) field.push(nullable);
137
+ else if (hasDefaultValue || !hasDefaultValue && isGenerated)
157
138
  field.push(optional);
139
+ if (hasDefaultValue && !isGenerated) field.push(`default('${Default}')`);
140
+ if (isUpdateableFormat) field.push(optional);
158
141
  return field.join(".");
159
142
  };
160
- if (dateTypes[schemaType].includes(type))
161
- return generateDateLikeField();
162
- if (stringTypes[schemaType].includes(type))
163
- return generateStringLikeField();
164
- if (numberTypes[schemaType].includes(type))
165
- return generateNumberLikeField();
166
- if (booleanTypes[schemaType].includes(type))
167
- return generateBooleanLikeField();
168
- if (enumTypes[schemaType].includes(type))
169
- return generateEnumLikeField();
143
+ if (dateTypes[schemaType].includes(type)) return generateDateLikeField();
144
+ if (stringTypes[schemaType].includes(type)) return generateStringLikeField();
145
+ if (numberTypes[schemaType].includes(type)) return generateNumberLikeField();
146
+ if (booleanTypes[schemaType].includes(type)) return generateBooleanLikeField();
147
+ if (enumTypes[schemaType].includes(type)) return generateEnumLikeField();
170
148
  throw new Error(`Unsupported column type: ${type}`);
171
149
  }
172
150
  async function generate(config) {
@@ -215,8 +193,7 @@ async function generate(config) {
215
193
  let useTable = true;
216
194
  for (const text of ignoredTablesRegex) {
217
195
  const pattern = text.substring(1, text.length - 1);
218
- if (table.match(pattern) !== null)
219
- useTable = false;
196
+ if (table.match(pattern) !== null) useTable = false;
220
197
  }
221
198
  return useTable;
222
199
  });
@@ -263,8 +240,7 @@ async function generate(config) {
263
240
  };
264
241
  });
265
242
  }
266
- if (isCamelCase)
267
- table = camelCase(table);
243
+ if (isCamelCase) table = camelCase(table);
268
244
  let content = `import { z } from 'zod'
269
245
 
270
246
  export const ${table} = z.object({`;
@@ -332,8 +308,7 @@ export type Selectable${camelCase(`${table}Type`, {
332
308
  const file = config.suffix && config.suffix !== "" ? `${table}.${config.suffix}.ts` : `${table}.ts`;
333
309
  const dest = path.join(dir, file);
334
310
  dests.push(dest);
335
- if (!config.silent)
336
- console.log("Created:", dest);
311
+ if (!config.silent) console.log("Created:", dest);
337
312
  fs.outputFileSync(dest, content);
338
313
  }
339
314
  if (config.origin.type === "mysql") {
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "mutano",
3
3
  "type": "module",
4
- "version": "1.0.7",
5
- "description": "Converts Prisma/MySQL schemas to Zod interfaces",
4
+ "version": "1.1.0",
5
+ "description": "Converts Prisma/MySQL/PostgreSQL/SQLite schemas to Zod interfaces",
6
6
  "author": "Alisson Cavalcante Agiani <thelinuxlich@gmail.com>",
7
7
  "license": "MIT",
8
8
  "repository": "git@github.com:thelinuxlich/mutano.git",
@@ -16,14 +16,16 @@
16
16
  "dependencies": {
17
17
  "@mrleebo/prisma-ast": "^0.12.1",
18
18
  "camelcase": "^8.0.0",
19
- "fs-extra": "^11.1.1",
20
- "knex": "^3.0.1",
21
- "mysql2": "^3.6.3"
19
+ "fs-extra": "^11.3.0",
20
+ "knex": "^3.1.0",
21
+ "mysql2": "^3.14.1",
22
+ "pg": "^8.15.6",
23
+ "sqlite3": "^5.1.7"
22
24
  },
23
25
  "devDependencies": {
24
- "@types/fs-extra": "^11.0.3",
25
- "esbuild": "^0.19.5",
26
- "typescript": "^5.2.2",
27
- "vitest": "^0.34.6"
26
+ "@types/fs-extra": "^11.0.4",
27
+ "esbuild": "^0.25.3",
28
+ "typescript": "^5.8.3",
29
+ "vitest": "^3.1.2"
28
30
  }
29
31
  }