true-pg 0.4.5 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/zod/index.js CHANGED
@@ -1,33 +1,18 @@
1
1
  import { FunctionReturnTypeKind, } from "../extractor/index.js";
2
- import { allowed_kind_names, createGenerator, Nodes } from "../types.js";
2
+ import { allowed_kind_names, createGenerator } from "../types.js";
3
+ import { Import } from "../imports.js";
3
4
  import { builtins } from "./builtins.js";
4
- import { join, quote, quoteI } from "../util.js";
5
- const to_snake_case = (str) => str
6
- .replace(/^[^a-zA-Z]+/, "") // remove leading non-alphabetic characters
7
- .replace(/[^a-zA-Z0-9]+/g, "_") // replace non-alphanumeric characters with underscores
8
- .replace(/([A-Z])/g, "_$1") // insert underscores before uppercase letters
9
- .toLowerCase();
10
- // TODO: create an insert and update zod interface for each type
5
+ import { to_snake_case, join, quote, quoteI } from "../util.js";
6
+ // TODO: create an insert and update zod interface for each type?
11
7
  export const Zod = createGenerator(opts => {
12
8
  const defaultSchema = opts?.defaultSchema ?? "public";
13
- const zod = (imports, name) => imports.add(new Nodes.ExternalImport({
14
- name: name ?? "z",
15
- module: "zod",
9
+ const zod = (ctx, name) => ctx.imports.add(new Import({
10
+ from: "zod",
11
+ namedImports: name ? [name] : undefined,
16
12
  typeOnly: false,
17
- star: !name,
13
+ star: name ? undefined : "z",
18
14
  }));
19
- const add = (imports, type) => {
20
- if (type.schema === "pg_catalog")
21
- zod(imports, "z");
22
- else
23
- imports.add(new Nodes.InternalImport({
24
- name: generator.formatType(type),
25
- canonical_type: type,
26
- typeOnly: false,
27
- star: false,
28
- }));
29
- };
30
- const column = (imports,
15
+ const column = (ctx,
31
16
  /** Information about the column */
32
17
  col) => {
33
18
  // don't create a property for always generated columns
@@ -35,114 +20,133 @@ export const Zod = createGenerator(opts => {
35
20
  return "";
36
21
  let out = col.comment ? `/** ${col.comment} */\n\t` : "";
37
22
  out += quoteI(col.name);
38
- let type = generator.formatType(col.type);
39
- add(imports, col.type);
40
- if (col.type.dimensions > 0)
41
- type += ".array()".repeat(col.type.dimensions);
42
- if (col.isNullable || col.generated === "BY DEFAULT" || col.defaultValue)
43
- type += `.nullable().optional()`;
23
+ const nullable = col.isNullable || col.generated === "BY DEFAULT" || col.defaultValue;
24
+ let type = generator.formatType(ctx, col.type, { nullable });
25
+ if (nullable)
26
+ type += `.optional()`;
44
27
  out += `: ${type}`;
45
28
  return `\t${out},\n`;
46
29
  };
47
- const composite_attribute = (imports, attr) => {
30
+ const composite_attribute = (ctx, attr) => {
48
31
  let out = quoteI(attr.name);
49
- out += `: ${generator.formatType(attr.type)}`;
50
- add(imports, attr.type);
51
- if (attr.type.dimensions > 0)
52
- out += ".array()".repeat(attr.type.dimensions);
32
+ out += `: ${generator.formatType(ctx, attr.type, { nullable: attr.isNullable })}`;
53
33
  if (attr.isNullable)
54
- out += ".nullable().optional()";
34
+ out += ".optional()";
55
35
  return out;
56
36
  };
57
37
  const generator = {
58
- formatSchema(name) {
38
+ formatSchemaName(name) {
59
39
  return to_snake_case(name) + "_validators";
60
40
  },
61
- formatSchemaType(type) {
41
+ formatSchemaMemberName(type) {
62
42
  return to_snake_case(type.name);
63
43
  },
64
- formatType(type) {
44
+ formatType(ctx, type, attr) {
45
+ let base;
65
46
  if (type.kind === FunctionReturnTypeKind.ExistingTable) {
66
- return to_snake_case(type.name);
47
+ base = to_snake_case(type.name);
67
48
  }
68
49
  else if (type.schema === "pg_catalog") {
69
50
  const name = type.canonical_name;
70
51
  const format = builtins[name];
71
52
  if (format)
72
- return format;
73
- opts?.warnings?.push(`(zod) Unknown builtin type: ${name}. Pass customBuiltinMap to map this type. Defaulting to "z.unknown()".`);
74
- return "z.unknown()";
53
+ base = format;
54
+ else {
55
+ opts?.warnings?.add(`(zod) Unknown builtin type: ${name}. Pass 'zod.builtinMap' to map this type. Defaulting to "z.unknown()".`);
56
+ base = "z.unknown()";
57
+ }
75
58
  }
76
- return to_snake_case(type.name);
59
+ else
60
+ base = to_snake_case(type.name);
61
+ if (type.schema === "pg_catalog") {
62
+ ctx.imports.add(new Import({
63
+ from: "zod",
64
+ namedImports: ["z"],
65
+ }));
66
+ }
67
+ else {
68
+ // before adding modifiers, add the import
69
+ ctx.imports.add(Import.fromInternal({
70
+ source: ctx.source,
71
+ type,
72
+ withName: base,
73
+ typeOnly: false,
74
+ }));
75
+ }
76
+ if ("dimensions" in type)
77
+ base += ".array()".repeat(type.dimensions);
78
+ if (attr?.nullable)
79
+ base += ".nullable()";
80
+ return base;
77
81
  },
78
- table(imports, table) {
82
+ table(ctx, table) {
79
83
  let out = "";
80
84
  if (table.comment)
81
85
  out += `/** ${table.comment} */\n`;
82
- out += `export const ${this.formatSchemaType(table)} = z.object({\n`;
83
- zod(imports, "z");
86
+ out += `export const ${this.formatSchemaMemberName(table)} = z.object({\n`;
87
+ zod(ctx, "z");
84
88
  for (const col of table.columns)
85
- out += column(imports, col);
89
+ out += column(ctx, col);
86
90
  out += "});";
87
91
  return out;
88
92
  },
89
- view(imports, view) {
93
+ view(ctx, view) {
90
94
  let out = "";
91
95
  if (view.comment)
92
96
  out += `/** ${view.comment} */\n`;
93
- zod(imports, "z");
94
- out += `export const ${this.formatSchemaType(view)} = z.object({\n`;
97
+ zod(ctx, "z");
98
+ out += `export const ${this.formatSchemaMemberName(view)} = z.object({\n`;
95
99
  for (const col of view.columns)
96
- out += column(imports, col);
100
+ out += column(ctx, col);
97
101
  out += "});";
98
102
  return out;
99
103
  },
100
- materializedView(imports, materializedView) {
104
+ materializedView(ctx, materializedView) {
101
105
  let out = "";
102
106
  if (materializedView.comment)
103
107
  out += `/** ${materializedView.comment} */\n`;
104
- zod(imports, "z");
105
- out += `export const ${this.formatSchemaType(materializedView)} = z.object({\n`;
108
+ zod(ctx, "z");
109
+ out += `export const ${this.formatSchemaMemberName(materializedView)} = z.object({\n`;
106
110
  for (const col of materializedView.columns)
107
- out += column(imports, col);
111
+ out += column(ctx, col);
108
112
  out += "});";
109
113
  return out;
110
114
  },
111
- enum(imports, en) {
115
+ enum(ctx, en) {
112
116
  let out = "";
113
117
  if (en.comment)
114
118
  out += `/** ${en.comment} */\n`;
115
- out += `export const ${this.formatSchemaType(en)} = z.union([\n`;
119
+ out += `export const ${this.formatSchemaMemberName(en)} = z.union([\n`;
116
120
  out += en.values.map(v => `\tz.literal("${v}")`).join(",\n");
117
121
  out += "\n]);";
118
- zod(imports, "z");
122
+ zod(ctx, "z");
119
123
  return out;
120
124
  },
121
- composite(imports, type) {
125
+ composite(ctx, type) {
122
126
  let out = "";
123
127
  if (type.comment)
124
128
  out += `/** ${type.comment} */\n`;
125
- out += `export const ${this.formatSchemaType(type)} = z.object({\n`;
126
- const props = type.canonical.attributes.map(c => composite_attribute(imports, c)).map(t => `\t${t},`);
129
+ out += `export const ${this.formatSchemaMemberName(type)} = z.object({\n`;
130
+ zod(ctx, "z");
131
+ const props = type.canonical.attributes.map(c => composite_attribute(ctx, c)).map(t => `\t${t},`);
127
132
  out += props.join("\n");
128
133
  out += "\n});";
129
134
  return out;
130
135
  },
131
- domain(imports, type) {
136
+ domain(ctx, type) {
132
137
  let out = "";
133
- out += `export const ${this.formatSchemaType(type)} = ${this.formatType(type.canonical.domain_base_type)};`;
134
- zod(imports, "z");
138
+ out += `export const ${this.formatSchemaMemberName(type)} = ${this.formatType(ctx, type.canonical.domain_base_type)};`;
135
139
  return out;
136
140
  },
137
- range(imports, type) {
141
+ range(ctx, type) {
138
142
  let out = "";
139
- out += `export const ${this.formatSchemaType(type)} = z.string();`;
140
- zod(imports, "z");
143
+ out += `export const ${this.formatSchemaMemberName(type)} = z.string();`;
144
+ zod(ctx, "z");
141
145
  return out;
142
146
  },
143
- function(imports, type) {
147
+ function(ctx, type) {
144
148
  let out = "export const ";
145
- out += this.formatSchemaType(type);
149
+ out += this.formatSchemaMemberName(type);
146
150
  out += " = {\n";
147
151
  out += `\tparameters: z.tuple([`;
148
152
  // Get the input parameters (those that appear in function signature)
@@ -154,12 +158,9 @@ export const Zod = createGenerator(opts => {
154
158
  out += "\n";
155
159
  for (const param of inputParams) {
156
160
  // TODO: update imports for non-primitive types based on typeInfo.kind
157
- out += "\t\t" + this.formatType(param.type);
158
- add(imports, param.type);
159
- if (param.type.dimensions > 0)
160
- out += ".array()".repeat(param.type.dimensions);
161
+ out += "\t\t" + this.formatType(ctx, param.type, { nullable: param.hasDefault });
161
162
  if (param.hasDefault)
162
- out += ".nullable().optional()";
163
+ out += ".optional()";
163
164
  out += `, // ${param.name}\n`;
164
165
  }
165
166
  out += "\t])";
@@ -167,10 +168,8 @@ export const Zod = createGenerator(opts => {
167
168
  const variadic = type.parameters.find(p => p.mode === "VARIADIC");
168
169
  if (variadic) {
169
170
  out += ".rest(";
170
- out += this.formatType(variadic.type);
171
171
  // reduce by 1 because it's already a rest parameter
172
- if (variadic.type.dimensions > 1)
173
- out += ".array()".repeat(variadic.type.dimensions - 1);
172
+ out += this.formatType(ctx, { ...variadic.type, dimensions: variadic.type.dimensions - 1 });
174
173
  out += ")" + ", // " + variadic.name + "\n";
175
174
  }
176
175
  else
@@ -181,52 +180,46 @@ export const Zod = createGenerator(opts => {
181
180
  out += "z.void()/* RETURNS TABLE with no columns */";
182
181
  else {
183
182
  out += "z.object({\n";
183
+ zod(ctx, "z");
184
184
  for (const col of type.returnType.columns) {
185
185
  out += `\t\t"${col.name}": `;
186
- out += this.formatType(col.type);
187
- add(imports, col.type);
188
- if (col.type.dimensions > 0)
189
- out += ".array()".repeat(col.type.dimensions);
186
+ out += this.formatType(ctx, col.type); // ignore nullability of inline table columns
190
187
  out += `,\n`;
191
188
  }
192
189
  out += "\t})";
193
190
  }
194
191
  }
195
192
  else if (type.returnType.kind === FunctionReturnTypeKind.ExistingTable) {
196
- out += this.formatType(type.returnType);
197
- add(imports, type.returnType);
193
+ out += this.formatType(ctx, type.returnType);
198
194
  }
199
195
  else {
200
- out += this.formatType(type.returnType.type);
201
- add(imports, type.returnType.type);
202
- if (type.returnType.type.dimensions > 0)
203
- out += ".array()".repeat(type.returnType.type.dimensions);
196
+ out += this.formatType(ctx, type.returnType.type);
204
197
  }
205
198
  // Add additional array brackets if it returns a set
206
199
  if (type.returnType.isSet)
207
200
  out += ".array()";
208
201
  out += ",\n};";
209
- zod(imports, "z");
210
202
  return out;
211
203
  },
212
- schemaKindIndex(schema, kind, main_generator) {
204
+ schemaKindIndex(ctx, schema, kind, main_generator) {
213
205
  const imports = schema[kind];
214
206
  if (imports.length === 0)
215
207
  return "";
216
208
  const generator = main_generator ?? this;
217
209
  return imports
218
210
  .map(each => {
219
- const name = this.formatSchemaType(each);
220
- const file = generator.formatSchemaType(each);
211
+ const name = this.formatSchemaMemberName(each);
212
+ const file = generator.formatSchemaMemberName(each);
221
213
  return `export { ${name} } from "./${file}.ts";`;
222
214
  })
223
215
  .join("\n");
224
216
  },
225
- schemaIndex(schema, main_generator) {
217
+ schemaIndex(ctx, schema, main_generator) {
226
218
  const actual_kinds = allowed_kind_names.filter(kind => schema[kind].length);
219
+ // we could in theory use the imports from GeneratorContext here, but this works fine
227
220
  let out = actual_kinds.map(kind => `import * as zod_${kind} from "./${kind}/index.ts";`).join("\n");
228
221
  out += "\n\n";
229
- out += `export const ${this.formatSchema(schema.name)} = {\n`;
222
+ out += `export const ${this.formatSchemaName(schema.name)} = {\n`;
230
223
  for (const kind of actual_kinds) {
231
224
  const items = schema[kind];
232
225
  if (items.length === 0)
@@ -234,7 +227,7 @@ export const Zod = createGenerator(opts => {
234
227
  out += `\t${kind}: {\n`;
235
228
  const formatted = items
236
229
  .map(each => {
237
- const formatted = this.formatSchemaType(each);
230
+ const formatted = this.formatSchemaMemberName(each);
238
231
  return { ...each, formatted };
239
232
  })
240
233
  .filter(x => x !== undefined);
@@ -249,11 +242,11 @@ export const Zod = createGenerator(opts => {
249
242
  out += "}";
250
243
  return out;
251
244
  },
252
- fullIndex(schemas, main_generator) {
245
+ fullIndex(ctx, schemas, main_generator) {
253
246
  const generator = main_generator ?? this;
254
247
  const parts = [];
255
248
  parts.push(schemas
256
- .map(s => `import { ${generator.formatSchema(s.name)} } from "./${s.name}/index.ts";`)
249
+ .map(s => `import { ${generator.formatSchemaName(s.name)} } from "./${s.name}/index.ts";`)
257
250
  .join("\n"));
258
251
  {
259
252
  let validator = `export const Validators = {\n`;
@@ -263,7 +256,7 @@ export const Zod = createGenerator(opts => {
263
256
  const seen = new Set();
264
257
  const formatted = current
265
258
  .map(each => {
266
- const formatted = generator.formatSchemaType(each);
259
+ const formatted = generator.formatSchemaMemberName(each);
267
260
  // skip clashing names
268
261
  if (seen.has(formatted))
269
262
  return;
@@ -283,7 +276,7 @@ export const Zod = createGenerator(opts => {
283
276
  else
284
277
  qualified = t.name;
285
278
  qualified = quoteI(qualified);
286
- return `\t${qualified}: ${this.formatSchema(schema.name)}[${quote(t.kind + "s")}][${quote(t.name)}],`;
279
+ return `\t${qualified}: ${this.formatSchemaName(schema.name)}[${quote(t.kind + "s")}][${quote(t.name)}],`;
287
280
  }), "\n");
288
281
  return out;
289
282
  }));
@@ -292,7 +285,7 @@ export const Zod = createGenerator(opts => {
292
285
  validator += "\n};";
293
286
  parts.push(validator);
294
287
  }
295
- parts.push(schemas.map(s => `export type { ${this.formatSchema(s.name)} };`).join("\n"));
288
+ parts.push(schemas.map(s => `export type { ${this.formatSchemaName(s.name)} };`).join("\n"));
296
289
  return join(parts);
297
290
  },
298
291
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "true-pg",
3
- "version": "0.4.5",
3
+ "version": "0.5.1",
4
4
  "type": "module",
5
5
  "module": "lib/index.js",
6
6
  "main": "lib/index.js",
@@ -22,7 +22,8 @@
22
22
  },
23
23
  "devDependencies": {
24
24
  "@types/bun": "latest",
25
- "@types/pg": "^8.11.13"
25
+ "@types/pg": "^8.11.13",
26
+ "kysely-pglite": "^0.6.1"
26
27
  },
27
28
  "peerDependencies": {
28
29
  "kysely": "^0.27",