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.
@@ -1,155 +1,148 @@
1
1
  import { Canonical, 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 toPascalCase = (str) => str
6
- .replace(/^[^a-zA-Z]+/, "") // remove leading non-alphabetic characters
7
- .replace(/[^a-zA-Z0-9_]+/g, "") // remove non-alphanumeric/underscore characters
8
- .replace(" ", "_") // replace spaces with underscores
9
- .replace(/_([a-z])/g, (_, letter) => letter.toUpperCase()) // capitalize letters after underscores
10
- .replace(/^([a-z])/, (_, letter) => letter.toUpperCase()); // capitalize first letter
5
+ import { toPascalCase, join, quote, quoteI } from "../util.js";
11
6
  export const Kysely = createGenerator(opts => {
12
7
  const defaultSchema = opts?.defaultSchema ?? "public";
13
- const ky = (imports, name) => imports.add(new Nodes.ExternalImport({
14
- name,
15
- module: "kysely",
16
- typeOnly: true,
17
- star: false,
18
- }));
19
- const add = (imports, type) => {
20
- if (type.schema === "pg_catalog")
21
- return;
22
- imports.add(new Nodes.InternalImport({
23
- name: generator.formatType(type),
24
- canonical_type: type,
8
+ const ky = (ctx, name) => {
9
+ ctx.imports.add(new Import({
10
+ from: "kysely",
11
+ namedImports: [name],
25
12
  typeOnly: true,
26
- star: false,
27
13
  }));
28
14
  };
29
15
  const column = (
30
16
  /** "this" */
31
- generator,
32
- /** @out Append used types to this array */
33
- imports,
17
+ generator, ctx,
34
18
  /** Information about the column */
35
19
  col) => {
36
- let base = generator.formatType(col.type);
37
- if (col.type.dimensions > 0)
38
- base += "[]".repeat(col.type.dimensions);
39
- if (col.isNullable)
40
- base += " | null";
20
+ let base = generator.formatType(ctx, col.type, { nullable: col.isNullable });
41
21
  let qualified = base;
42
22
  if (col.generated === "ALWAYS") {
43
23
  qualified = `GeneratedAlways<${qualified}>`;
44
- ky(imports, "GeneratedAlways");
24
+ ky(ctx, "GeneratedAlways");
45
25
  }
46
26
  else if (col.generated === "BY DEFAULT") {
47
27
  qualified = `Generated<${qualified}>`;
48
- ky(imports, "Generated");
28
+ ky(ctx, "Generated");
49
29
  }
50
30
  else if (col.defaultValue) {
51
31
  qualified = `Generated<${qualified}>`;
52
- ky(imports, "Generated");
32
+ ky(ctx, "Generated");
53
33
  }
54
34
  let out = col.comment ? `/** ${col.comment} */\n\t` : "";
55
35
  out += quoteI(col.name);
56
36
  // TODO: update imports for non-primitive types
57
37
  out += `: ${qualified}`;
58
- add(imports, col.type);
59
38
  return `\t${out};\n`;
60
39
  };
61
- const composite_attribute = (generator, imports, attr) => {
40
+ const composite_attribute = (ctx, generator, attr) => {
62
41
  let out = quoteI(attr.name);
63
42
  if (attr.isNullable)
64
43
  out += "?";
65
- out += `: ${generator.formatType(attr.type)}`;
66
- add(imports, attr.type);
67
- if (attr.type.dimensions > 0)
68
- out += "[]".repeat(attr.type.dimensions);
69
- if (attr.isNullable)
70
- out += " | null";
44
+ out += ": ";
45
+ out += generator.formatType(ctx, attr.type, { nullable: attr.isNullable });
71
46
  return out;
72
47
  };
73
48
  const generator = {
74
- formatSchema(name) {
49
+ formatSchemaName(name) {
75
50
  return toPascalCase(name) + "Schema";
76
51
  },
77
- formatSchemaType(type) {
52
+ formatSchemaMemberName(type) {
78
53
  return toPascalCase(type.name);
79
54
  },
80
- formatType(type) {
55
+ formatType(ctx, type, attr) {
56
+ let base;
81
57
  if (type.kind === FunctionReturnTypeKind.ExistingTable) {
82
- return toPascalCase(type.name);
58
+ base = toPascalCase(type.name);
83
59
  }
84
60
  else if (type.schema === "pg_catalog") {
85
61
  const name = type.canonical_name;
86
62
  const format = builtins[name];
87
63
  if (format)
88
- return format;
89
- opts?.warnings?.push(`(kysely) Unknown builtin type: ${name}. Pass customBuiltinMap to map this type. Defaulting to "unknown".`);
90
- return "unknown";
64
+ base = format;
65
+ else {
66
+ opts?.warnings?.add(`(kysely) Unknown builtin type: ${name}. Pass 'kysely.builtinMap' to map this type. Defaulting to "unknown".`);
67
+ base = "unknown";
68
+ }
91
69
  }
92
- return toPascalCase(type.name);
70
+ else
71
+ base = toPascalCase(type.name);
72
+ if (type.schema !== "pg_catalog") {
73
+ // before adding modifiers, add the import
74
+ ctx.imports.add(Import.fromInternal({
75
+ source: ctx.source,
76
+ type,
77
+ withName: base,
78
+ typeOnly: true,
79
+ }));
80
+ }
81
+ if ("dimensions" in type)
82
+ base += "[]".repeat(type.dimensions);
83
+ if (attr?.nullable)
84
+ base += " | null";
85
+ return base;
93
86
  },
94
- table(imports, table) {
87
+ table(ctx, table) {
95
88
  let out = "";
96
89
  if (table.comment)
97
90
  out += `/** ${table.comment} */\n`;
98
- out += `export interface ${this.formatSchemaType(table)} {\n`;
91
+ out += `export interface ${this.formatSchemaMemberName(table)} {\n`;
99
92
  for (const col of table.columns)
100
- out += column(this, imports, col);
93
+ out += column(this, ctx, col);
101
94
  out += "}";
102
95
  return out;
103
96
  },
104
- view(imports, view) {
97
+ view(ctx, view) {
105
98
  let out = "";
106
99
  if (view.comment)
107
100
  out += `/** ${view.comment} */\n`;
108
- out += `export interface ${this.formatSchemaType(view)} {\n`;
101
+ out += `export interface ${this.formatSchemaMemberName(view)} {\n`;
109
102
  for (const col of view.columns)
110
- out += column(this, imports, col);
103
+ out += column(this, ctx, col);
111
104
  out += "}";
112
105
  return out;
113
106
  },
114
- materializedView(imports, materializedView) {
107
+ materializedView(ctx, materializedView) {
115
108
  let out = "";
116
109
  if (materializedView.comment)
117
110
  out += `/** ${materializedView.comment} */\n`;
118
- out += `export interface ${this.formatSchemaType(materializedView)} {\n`;
111
+ out += `export interface ${this.formatSchemaMemberName(materializedView)} {\n`;
119
112
  for (const col of materializedView.columns)
120
- out += column(this, imports, col);
113
+ out += column(this, ctx, col);
121
114
  out += "}";
122
115
  return out;
123
116
  },
124
- enum(imports, en) {
117
+ enum(ctx, en) {
125
118
  let out = "";
126
119
  if (en.comment)
127
120
  out += `/** ${en.comment} */\n`;
128
- out += `export type ${this.formatSchemaType(en)} = ${en.values.map(v => `"${v}"`).join(" | ")};`;
121
+ out += `export type ${this.formatSchemaMemberName(en)} = ${en.values.map(v => `"${v}"`).join(" | ")};`;
129
122
  return out;
130
123
  },
131
- composite(imports, type) {
124
+ composite(ctx, type) {
132
125
  let out = "";
133
126
  if (type.comment)
134
127
  out += `/** ${type.comment} */\n`;
135
- out += `export interface ${this.formatSchemaType(type)} {\n`;
136
- const props = type.canonical.attributes.map(c => composite_attribute(this, imports, c)).map(t => `\t${t};`);
128
+ out += `export interface ${this.formatSchemaMemberName(type)} {\n`;
129
+ const props = type.canonical.attributes.map(c => composite_attribute(ctx, this, c)).map(t => `\t${t};`);
137
130
  out += props.join("\n");
138
131
  out += "\n}";
139
132
  return out;
140
133
  },
141
- domain(imports, type) {
134
+ domain(ctx, type) {
142
135
  let out = "";
143
- out += `export type ${this.formatSchemaType(type)} = ${this.formatType(type.canonical.domain_base_type)};`;
136
+ out += `export type ${this.formatSchemaMemberName(type)} = ${this.formatType(ctx, type.canonical.domain_base_type)};`;
144
137
  return out;
145
138
  },
146
- range(imports, type) {
139
+ range(ctx, type) {
147
140
  let out = "";
148
141
  // force this to be string because range has to be passed as a string to Kysely
149
- out += `export type ${this.formatSchemaType(type)} = string;`;
142
+ out += `export type ${this.formatSchemaMemberName(type)} = string;`;
150
143
  return out;
151
144
  },
152
- function(imports, type) {
145
+ function(ctx, type) {
153
146
  let out = "";
154
147
  out += "/**\n";
155
148
  if (type.comment)
@@ -158,7 +151,7 @@ export const Kysely = createGenerator(opts => {
158
151
  out += ` * @parallelSafety ${type.parallelSafety}\n`;
159
152
  out += ` * @isStrict ${type.isStrict}\n`;
160
153
  out += " */\n";
161
- out += `export interface ${this.formatSchemaType(type)} {\n\t`;
154
+ out += `export interface ${this.formatSchemaMemberName(type)} {\n\t`;
162
155
  // Get the input parameters (those that appear in function signature)
163
156
  const inputParams = type.parameters.filter(p => p.mode === "IN" || p.mode === "INOUT" || p.mode === "VARIADIC");
164
157
  if (inputParams.length === 0) {
@@ -168,10 +161,7 @@ export const Kysely = createGenerator(opts => {
168
161
  out += "(";
169
162
  out += inputParams[0].name;
170
163
  out += ": ";
171
- out += this.formatType(inputParams[0].type);
172
- add(imports, inputParams[0].type);
173
- if (inputParams[0].type.dimensions > 0)
174
- out += "[]".repeat(inputParams[0].type.dimensions);
164
+ out += this.formatType(ctx, inputParams[0].type);
175
165
  out += "): ";
176
166
  }
177
167
  else if (inputParams.length > 0) {
@@ -183,10 +173,7 @@ export const Kysely = createGenerator(opts => {
183
173
  out += `\t\t${paramName}`;
184
174
  if (param.hasDefault && !isVariadic)
185
175
  out += "?";
186
- out += `: ${this.formatType(param.type)}`;
187
- add(imports, param.type);
188
- if (param.type.dimensions > 0)
189
- out += "[]".repeat(param.type.dimensions);
176
+ out += `: ${this.formatType(ctx, param.type)}`;
190
177
  if (!isVariadic)
191
178
  out += ",";
192
179
  out += "\n";
@@ -200,24 +187,17 @@ export const Kysely = createGenerator(opts => {
200
187
  out += "{\n";
201
188
  for (const col of type.returnType.columns) {
202
189
  out += `\t\t"${col.name}": `;
203
- out += this.formatType(col.type);
204
- add(imports, col.type);
205
- if (col.type.dimensions > 0)
206
- out += "[]".repeat(col.type.dimensions);
190
+ out += this.formatType(ctx, col.type);
207
191
  out += `;\n`;
208
192
  }
209
193
  out += "\t}";
210
194
  }
211
195
  }
212
196
  else if (type.returnType.kind === FunctionReturnTypeKind.ExistingTable) {
213
- out += this.formatType(type.returnType);
214
- add(imports, type.returnType);
197
+ out += this.formatType(ctx, type.returnType);
215
198
  }
216
199
  else {
217
- out += this.formatType(type.returnType.type);
218
- add(imports, type.returnType.type);
219
- if (type.returnType.type.dimensions > 0)
220
- out += "[]".repeat(type.returnType.type.dimensions);
200
+ out += this.formatType(ctx, type.returnType.type);
221
201
  }
222
202
  // Add additional array brackets if it returns a set
223
203
  if (type.returnType.isSet) {
@@ -226,24 +206,25 @@ export const Kysely = createGenerator(opts => {
226
206
  out += ";\n}";
227
207
  return out;
228
208
  },
229
- schemaKindIndex(schema, kind, main_generator) {
209
+ schemaKindIndex(ctx, schema, kind, main_generator) {
230
210
  const generator = main_generator ?? this;
231
211
  const imports = schema[kind];
232
212
  if (imports.length === 0)
233
213
  return "";
234
214
  return imports
235
215
  .map(each => {
236
- const name = this.formatSchemaType(each);
237
- const file = generator.formatSchemaType(each);
216
+ const name = generator.formatSchemaMemberName(each);
217
+ const file = generator.formatSchemaMemberName(each);
238
218
  return `export type { ${name} } from "./${file}.ts";`;
239
219
  })
240
220
  .join("\n");
241
221
  },
242
- schemaIndex(schema) {
222
+ schemaIndex(ctx, schema) {
243
223
  const actual_kinds = allowed_kind_names.filter(kind => schema[kind].length);
224
+ // we could in theory use the imports from GeneratorContext here, but this works fine
244
225
  let out = actual_kinds.map(kind => `import type * as ${kind} from "./${kind}/index.ts";`).join("\n");
245
226
  out += "\n\n";
246
- out += `export interface ${this.formatSchema(schema.name)} {\n`;
227
+ out += `export interface ${this.formatSchemaName(schema.name)} {\n`;
247
228
  for (const kind of actual_kinds) {
248
229
  const items = schema[kind];
249
230
  if (items.length === 0)
@@ -251,7 +232,7 @@ export const Kysely = createGenerator(opts => {
251
232
  out += `\t${kind}: {\n`;
252
233
  const formatted = items
253
234
  .map(each => {
254
- const formatted = this.formatSchemaType(each);
235
+ const formatted = generator.formatSchemaMemberName(each);
255
236
  return { ...each, formatted };
256
237
  })
257
238
  .filter(x => x !== undefined);
@@ -266,10 +247,10 @@ export const Kysely = createGenerator(opts => {
266
247
  out += "}";
267
248
  return out;
268
249
  },
269
- fullIndex(schemas) {
250
+ fullIndex(ctx, schemas) {
270
251
  const parts = [];
271
252
  parts.push(schemas
272
- .map(s => `import type { ${this.formatSchema(s.name)} } from "./${s.name}/index.ts";`)
253
+ .map(s => `import type { ${generator.formatSchemaName(s.name)} } from "./${s.name}/index.ts";`)
273
254
  .join("\n"));
274
255
  {
275
256
  let iface = `export interface Database {\n`;
@@ -281,7 +262,7 @@ export const Kysely = createGenerator(opts => {
281
262
  const seen = new Set();
282
263
  const formatted = tables
283
264
  .map(each => {
284
- const formatted = this.formatSchemaType(each);
265
+ const formatted = generator.formatSchemaMemberName(each);
285
266
  // skip clashing names
286
267
  if (seen.has(formatted))
287
268
  return;
@@ -300,7 +281,7 @@ export const Kysely = createGenerator(opts => {
300
281
  else
301
282
  qualified = t.name;
302
283
  qualified = quoteI(qualified);
303
- return `\t${qualified}: ${this.formatSchema(schema.name)}[${quote(t.kind + "s")}][${quote(t.name)}];`;
284
+ return `\t${qualified}: ${generator.formatSchemaName(schema.name)}[${quote(t.kind + "s")}][${quote(t.name)}];`;
304
285
  })
305
286
  .join("\n");
306
287
  return out;
@@ -309,7 +290,7 @@ export const Kysely = createGenerator(opts => {
309
290
  iface += "\n}";
310
291
  parts.push(iface);
311
292
  }
312
- parts.push(schemas.map(s => `export type { ${this.formatSchema(s.name)} };`).join("\n"));
293
+ parts.push(schemas.map(s => `export type { ${generator.formatSchemaName(s.name)} };`).join("\n"));
313
294
  return join(parts);
314
295
  },
315
296
  };
package/lib/types.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { Canonical, type TableDetails, type ViewDetails, type MaterializedViewDetails, type EnumDetails, type CompositeTypeDetails, type DomainDetails, type RangeDetails, type FunctionDetails, type SchemaType, type Schema, type FunctionReturnType } from "./extractor/index.ts";
2
+ import type { ImportList } from "./imports.ts";
2
3
  export declare const allowed_kind_names: readonly ["tables", "views", "materializedViews", "enums", "composites", "functions", "domains", "ranges"];
3
4
  export type allowed_kind_names = (typeof allowed_kind_names)[number];
4
5
  export interface FolderStructure {
@@ -23,118 +24,72 @@ export interface FolderStructure {
23
24
  };
24
25
  };
25
26
  }
26
- export declare namespace Nodes {
27
- class ExternalImport {
28
- name: string;
29
- typeOnly: boolean;
30
- star: boolean;
31
- external: true;
32
- module: string;
33
- constructor(args: {
34
- name: string;
35
- module: string;
36
- typeOnly: boolean;
37
- star: boolean;
38
- });
39
- }
40
- class InternalImport {
41
- name: string;
42
- canonical_type: Canonical | FunctionReturnType.ExistingTable;
43
- typeOnly: boolean;
44
- star: boolean;
45
- external: false;
46
- constructor(args: {
47
- name: string;
48
- canonical_type: Canonical | FunctionReturnType.ExistingTable;
49
- typeOnly: boolean;
50
- star: boolean;
51
- });
52
- }
53
- class ImportList {
54
- imports: (ExternalImport | InternalImport)[];
55
- constructor(imports: (ExternalImport | InternalImport)[]);
56
- static merge(lists: ImportList[]): ImportList;
57
- add(item: ExternalImport | InternalImport): void;
58
- stringify(context_file: string, files: FolderStructure): string;
59
- }
60
- interface Export {
61
- name: string;
62
- kind: SchemaType["kind"];
63
- schema: string;
64
- star: boolean;
65
- }
66
- }
67
27
  export interface CreateGeneratorOpts {
68
28
  defaultSchema?: string;
69
- warnings: string[];
29
+ warnings: Set<string>;
70
30
  }
71
31
  export interface createGenerator {
72
32
  (opts?: CreateGeneratorOpts): SchemaGenerator;
73
33
  }
74
34
  export declare const createGenerator: (generatorCreator: createGenerator) => createGenerator;
35
+ export interface GeneratorContext {
36
+ /** The source file path */
37
+ source: string;
38
+ /** Append types to import */
39
+ imports: ImportList;
40
+ }
41
+ export interface FormatTypeAttributes {
42
+ nullable?: boolean;
43
+ generated?: boolean;
44
+ identity?: boolean;
45
+ }
75
46
  export interface SchemaGenerator {
76
47
  /**
77
48
  * Use this function to define a name mapping for schema names.
78
49
  * This is useful if you want to use a different name for a schema in the generated code.
79
50
  * Example: "public" -> "PublicSchema"
80
51
  */
81
- formatSchema(name: string): string;
52
+ formatSchemaName(name: string): string;
82
53
  /**
83
- * Use this function to define a name mapping for schema types.
84
- * This is useful if you want to use a different name for a type in the generated code.
54
+ * Use this function to define a name mapping for schema members.
55
+ * This is useful if you want to use a different name for a member in the generated code.
85
56
  * Example: "users" -> "UsersTable"
86
57
  */
87
- formatSchemaType(type: SchemaType): string;
58
+ formatSchemaMemberName(type: SchemaType): string;
88
59
  /**
89
60
  * Use this function to define a name mapping for type names.
90
61
  * This is useful if you want to use a different name for a type in the generated code.
91
62
  * Example: "users" -> "UsersTable"
92
63
  */
93
- formatType(type: Canonical | FunctionReturnType.ExistingTable): string;
94
- table(
95
- /** @out Append used types to this array */
96
- imports: Nodes.ImportList,
64
+ formatType(ctx: GeneratorContext, type: Canonical | FunctionReturnType.ExistingTable, attr?: FormatTypeAttributes): string;
65
+ table(ctx: GeneratorContext,
97
66
  /** Information about the table */
98
67
  table: TableDetails): string;
99
- view(
100
- /** @out Append used types to this array */
101
- imports: Nodes.ImportList,
68
+ view(ctx: GeneratorContext,
102
69
  /** Information about the view */
103
70
  view: ViewDetails): string;
104
- materializedView(
105
- /** @out Append used types to this array */
106
- imports: Nodes.ImportList,
71
+ materializedView(ctx: GeneratorContext,
107
72
  /** Information about the materialized view */
108
73
  materializedView: MaterializedViewDetails): string;
109
- enum(
110
- /** @out Append used types to this array */
111
- imports: Nodes.ImportList,
74
+ enum(ctx: GeneratorContext,
112
75
  /** Information about the enum */
113
76
  en: EnumDetails): string;
114
- composite(
115
- /** @out Append used types to this array */
116
- imports: Nodes.ImportList,
77
+ composite(ctx: GeneratorContext,
117
78
  /** Information about the composite type */
118
79
  type: CompositeTypeDetails): string;
119
- domain(
120
- /** @out Append used types to this array */
121
- imports: Nodes.ImportList,
80
+ domain(ctx: GeneratorContext,
122
81
  /** Information about the domain */
123
82
  type: DomainDetails): string;
124
- range(
125
- /** @out Append used types to this array */
126
- imports: Nodes.ImportList,
83
+ range(ctx: GeneratorContext,
127
84
  /** Information about the range */
128
85
  type: RangeDetails): string;
129
- function(
130
- /** @out Append used types to this array */
131
- imports: Nodes.ImportList,
86
+ function(ctx: GeneratorContext,
132
87
  /** Information about the function */
133
88
  type: FunctionDetails): string;
134
89
  /** create the file `$out/$schema.name/$kind/index.ts` */
135
- schemaKindIndex(schema: Schema, kind: Exclude<keyof Schema, "name">, main_generator?: SchemaGenerator): string;
90
+ schemaKindIndex(ctx: GeneratorContext, schema: Schema, kind: Exclude<keyof Schema, "name">, main_generator?: SchemaGenerator): string;
136
91
  /** create the file `$out/$schema.name/index.ts` */
137
- schemaIndex(schema: Schema, main_generator?: SchemaGenerator): string;
92
+ schemaIndex(ctx: GeneratorContext, schema: Schema, main_generator?: SchemaGenerator): string;
138
93
  /** create the file `$out/index.ts` */
139
- fullIndex(schemas: Schema[], main_generator?: SchemaGenerator): string;
94
+ fullIndex(ctx: GeneratorContext, schemas: Schema[], main_generator?: SchemaGenerator): string;
140
95
  }