true-pg 0.5.0 → 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/README.md +17 -17
- package/lib/bin.js +16 -16
- package/lib/config.d.ts +6 -6
- package/lib/config.js +8 -8
- package/lib/imports.d.ts +35 -0
- package/lib/imports.js +102 -0
- package/lib/index.js +70 -59
- package/lib/kysely/index.js +77 -90
- package/lib/types.d.ts +29 -74
- package/lib/types.js +0 -165
- package/lib/util.d.ts +2 -0
- package/lib/util.js +13 -0
- package/lib/zod/index.js +91 -93
- package/package.json +3 -2
package/README.md
CHANGED
@@ -15,7 +15,7 @@ bun add true-pg
|
|
15
15
|
## Quickstart
|
16
16
|
|
17
17
|
```bash
|
18
|
-
npx true-pg --all-
|
18
|
+
npx true-pg --all-generators --uri postgres://user:password@localhost:5432/database --out ./models
|
19
19
|
```
|
20
20
|
|
21
21
|
This will generate a `models` directory with the following structure:
|
@@ -68,8 +68,8 @@ Options:
|
|
68
68
|
- `-c, --config [path]` - Path to config file (JSON)
|
69
69
|
- `-u, --uri [uri]` - Database URI (Postgres only!)
|
70
70
|
- `-o, --out [path]` - Path to output directory (defaults to "models")
|
71
|
-
- `-
|
72
|
-
- `-A, --all-
|
71
|
+
- `-g, --generator [generator]` - Generator to use (e.g. `kysely`, `zod`). Can be specified multiple times.
|
72
|
+
- `-A, --all-generators` - Enable all built-in generators
|
73
73
|
|
74
74
|
You can configure true-pg either through command-line arguments or a config file.
|
75
75
|
|
@@ -89,7 +89,7 @@ import { config } from "true-pg";
|
|
89
89
|
export default config({
|
90
90
|
uri: "postgres://user:password@localhost:5432/database",
|
91
91
|
out: "src/models",
|
92
|
-
|
92
|
+
generators: ["kysely", "zod"],
|
93
93
|
defaultSchema: "public",
|
94
94
|
});
|
95
95
|
```
|
@@ -101,7 +101,7 @@ export default config({
|
|
101
101
|
| `uri` | PostgreSQL connection URI | Required, or config |
|
102
102
|
| `config` | Knex connection config object | Required, or uri |
|
103
103
|
| `out` | Output directory for generated files | `"models"` |
|
104
|
-
| `
|
104
|
+
| `generators` | Generators to use (e.g. `kysely`, `zod`) | `"kysely"` |
|
105
105
|
| `defaultSchema` | Default schema to use (Kysely schema will be unprefixed) | `"public"` |
|
106
106
|
|
107
107
|
## Customising Code Generation
|
@@ -118,28 +118,28 @@ You can create a custom generator to control how code is generated:
|
|
118
118
|
import { createGenerator, generate } from "true-pg";
|
119
119
|
|
120
120
|
const generator = createGenerator(opts => ({
|
121
|
-
|
122
|
-
|
123
|
-
formatType: type => `${type}Interface`,
|
124
|
-
table: (
|
121
|
+
formatSchemaName: name => `${name}Schema`,
|
122
|
+
formatSchemaMemberName: type => `${type.name}Type`,
|
123
|
+
formatType: (ctx, type) => `${type}Interface`,
|
124
|
+
table: (ctx, table) => {
|
125
125
|
// Custom table type generation
|
126
126
|
},
|
127
|
-
enum: (
|
127
|
+
enum: (ctx, en) => {
|
128
128
|
// Custom enum type generation
|
129
129
|
},
|
130
|
-
composite: (
|
130
|
+
composite: (ctx, composite) => {
|
131
131
|
// Custom composite type generation
|
132
132
|
},
|
133
|
-
function: (
|
133
|
+
function: (ctx, func) => {
|
134
134
|
// Custom function type generation
|
135
135
|
},
|
136
|
-
schemaKindIndex: (schema, kind) => {
|
136
|
+
schemaKindIndex: (ctx, schema, kind) => {
|
137
137
|
// Custom schema kind index generation
|
138
138
|
},
|
139
|
-
schemaIndex: schema => {
|
139
|
+
schemaIndex: (ctx, schema) => {
|
140
140
|
// Custom schema index generation
|
141
141
|
},
|
142
|
-
fullIndex: schemas => {
|
142
|
+
fullIndex: (ctx, schemas) => {
|
143
143
|
// Custom full index generation
|
144
144
|
},
|
145
145
|
}));
|
@@ -147,14 +147,14 @@ const generator = createGenerator(opts => ({
|
|
147
147
|
await generate(
|
148
148
|
{
|
149
149
|
uri: "postgres://user:password@localhost:5432/database",
|
150
|
-
|
150
|
+
generators: [], // empty array to disable generators
|
151
151
|
out: "src/models",
|
152
152
|
},
|
153
153
|
[generator],
|
154
154
|
);
|
155
155
|
```
|
156
156
|
|
157
|
-
Filenames will be created using the `format*` methods of the FIRST generator passed to `generate` or via the `--
|
157
|
+
Filenames will be created using the `format*` methods of the FIRST generator passed to `generate` or via the `--generator` CLI option.
|
158
158
|
|
159
159
|
## Schema Generator Interface
|
160
160
|
|
package/lib/bin.js
CHANGED
@@ -1,18 +1,18 @@
|
|
1
1
|
#!/usr/bin/env node
|
2
2
|
import mri from "mri";
|
3
3
|
import { generate } from "./index.js";
|
4
|
-
import {
|
4
|
+
import { generators } from "./config.js";
|
5
5
|
const args = process.argv.slice(2);
|
6
6
|
const opts = mri(args, {
|
7
|
-
boolean: ["help", "all-
|
8
|
-
string: ["config", "uri", "out", "
|
7
|
+
boolean: ["help", "all-generators"],
|
8
|
+
string: ["config", "uri", "out", "generator"],
|
9
9
|
alias: {
|
10
10
|
h: "help",
|
11
11
|
c: "config",
|
12
12
|
u: "uri",
|
13
13
|
o: "out",
|
14
|
-
a: "
|
15
|
-
A: "all-
|
14
|
+
a: "generator",
|
15
|
+
A: "all-generators",
|
16
16
|
},
|
17
17
|
});
|
18
18
|
import { cosmiconfig } from "cosmiconfig";
|
@@ -31,27 +31,27 @@ if (help) {
|
|
31
31
|
log(" -h, --help Show help");
|
32
32
|
log(" -u, --uri [uri] Database URI (Postgres only!)");
|
33
33
|
log(" -o, --out [path] Path to output directory");
|
34
|
-
log(" -
|
35
|
-
log(" -A, --all-
|
34
|
+
log(" -g, --generator [generator] Output generator to use (default: 'kysely')");
|
35
|
+
log(" -A, --all-generators Output all generators");
|
36
36
|
log(" -c, --config [path] Path to config file");
|
37
37
|
log(" Defaults to '.truepgrc.json' or '.config/.truepgrc.json'");
|
38
38
|
log("Example:");
|
39
|
-
log(" true-pg -u postgres://user:pass@localhost:5432/my-database -o models -
|
39
|
+
log(" true-pg -u postgres://user:pass@localhost:5432/my-database -o models -g kysely -g zod");
|
40
40
|
log();
|
41
41
|
if (opts.help)
|
42
42
|
process.exit(0);
|
43
43
|
else
|
44
44
|
process.exit(1);
|
45
45
|
}
|
46
|
-
if (opts["all-
|
47
|
-
opts.
|
48
|
-
if (!(opts.
|
49
|
-
console.warn('No
|
50
|
-
// allow single
|
51
|
-
if (typeof opts.
|
52
|
-
opts.
|
46
|
+
if (opts["all-generators"])
|
47
|
+
opts.generator = Object.keys(generators);
|
48
|
+
if (!(opts.generator || config.generators))
|
49
|
+
console.warn('No generators specified, using default: ["kysely"]');
|
50
|
+
// allow single generator or comma-separated list of generators
|
51
|
+
if (typeof opts.generator === "string")
|
52
|
+
opts.generator = opts.generator.split(",");
|
53
53
|
// CLI args take precedence over config file
|
54
54
|
config.uri = opts.uri ?? config.uri;
|
55
55
|
config.out = opts.out ?? config.out;
|
56
|
-
config.
|
56
|
+
config.generators = opts.generator ?? config.generators ?? ["kysely"];
|
57
57
|
await generate(config);
|
package/lib/config.d.ts
CHANGED
@@ -4,8 +4,8 @@ export type ExtractorConfig = Exclude<ConstructorParameters<typeof Extractor>[0]
|
|
4
4
|
export interface BaseConfig {
|
5
5
|
/** The output directory for the generated models. Default: "models" */
|
6
6
|
out?: string;
|
7
|
-
/**
|
8
|
-
|
7
|
+
/** Generators to enable. Currently supported generators are "kysely" and "zod". Default: ["kysely"] */
|
8
|
+
generators?: ("kysely" | "zod")[];
|
9
9
|
/** The default schema to use for the generated models. These will be unprefixed in the final `Database` interface. Default: "public" */
|
10
10
|
defaultSchema?: string;
|
11
11
|
}
|
@@ -22,27 +22,27 @@ export interface ConfigConfig extends BaseConfig {
|
|
22
22
|
config: ExtractorConfig["config"];
|
23
23
|
}
|
24
24
|
export type TruePGConfig = Deunionise<PgConfig | UriConfig | ConfigConfig>;
|
25
|
-
export declare const
|
25
|
+
export declare const generators: {
|
26
26
|
kysely: import("./types.ts").createGenerator;
|
27
27
|
zod: import("./types.ts").createGenerator;
|
28
28
|
};
|
29
29
|
export declare function config(opts: TruePGConfig): {
|
30
30
|
out: string;
|
31
|
-
|
31
|
+
generators: ("kysely" | "zod")[];
|
32
32
|
defaultSchema: string;
|
33
33
|
pg: ExtractorConfig["pg"];
|
34
34
|
uri?: undefined;
|
35
35
|
config?: undefined;
|
36
36
|
} | {
|
37
37
|
out: string;
|
38
|
-
|
38
|
+
generators: ("kysely" | "zod")[];
|
39
39
|
defaultSchema: string;
|
40
40
|
uri: ExtractorConfig["uri"];
|
41
41
|
pg?: undefined;
|
42
42
|
config?: undefined;
|
43
43
|
} | {
|
44
44
|
out: string;
|
45
|
-
|
45
|
+
generators: ("kysely" | "zod")[];
|
46
46
|
defaultSchema: string;
|
47
47
|
config: ExtractorConfig["config"];
|
48
48
|
pg?: undefined;
|
package/lib/config.js
CHANGED
@@ -2,19 +2,19 @@ import { normalize as normalise } from "node:path";
|
|
2
2
|
import {} from "./util.js";
|
3
3
|
import { Kysely } from "./kysely/index.js";
|
4
4
|
import { Zod } from "./zod/index.js";
|
5
|
-
export const
|
5
|
+
export const generators = {
|
6
6
|
kysely: Kysely,
|
7
7
|
zod: Zod,
|
8
8
|
};
|
9
|
-
const
|
9
|
+
const availableGenerators = Object.keys(generators);
|
10
10
|
export function config(opts) {
|
11
11
|
const out = normalise(opts.out || "./models");
|
12
|
-
const
|
12
|
+
const generators = opts.generators || ["kysely"];
|
13
13
|
const defaultSchema = opts.defaultSchema || "public";
|
14
|
-
for (const
|
15
|
-
if (!
|
16
|
-
console.error('Requested
|
17
|
-
console.error("Available
|
14
|
+
for (const generator of opts.generators ?? []) {
|
15
|
+
if (!availableGenerators.includes(generator)) {
|
16
|
+
console.error('Requested generator "%s" not found.', generator);
|
17
|
+
console.error("Available generators: %s", availableGenerators.join(", "));
|
18
18
|
console.error("See documentation for more information.");
|
19
19
|
process.exit(1);
|
20
20
|
}
|
@@ -26,7 +26,7 @@ export function config(opts) {
|
|
26
26
|
return {
|
27
27
|
...opts,
|
28
28
|
out,
|
29
|
-
|
29
|
+
generators,
|
30
30
|
defaultSchema,
|
31
31
|
};
|
32
32
|
}
|
package/lib/imports.d.ts
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
import type { Canonical } from "./extractor/index.ts";
|
2
|
+
import type { FunctionReturnType } from "./extractor/index.ts";
|
3
|
+
import type { FolderStructure } from "./types.ts";
|
4
|
+
export interface ImportIdentifier {
|
5
|
+
name: string;
|
6
|
+
alias?: string;
|
7
|
+
typeOnly?: boolean;
|
8
|
+
}
|
9
|
+
export declare class Import {
|
10
|
+
from: string | ((files: FolderStructure) => string);
|
11
|
+
namedImports?: (string | ImportIdentifier)[];
|
12
|
+
star?: string;
|
13
|
+
default?: string;
|
14
|
+
typeOnly?: boolean;
|
15
|
+
constructor(args: {
|
16
|
+
from: string | ((files: FolderStructure) => string);
|
17
|
+
namedImports?: (string | ImportIdentifier)[];
|
18
|
+
star?: string;
|
19
|
+
default?: string;
|
20
|
+
typeOnly?: boolean;
|
21
|
+
});
|
22
|
+
static fromInternal(opts: {
|
23
|
+
source: string;
|
24
|
+
type: Canonical | FunctionReturnType.ExistingTable;
|
25
|
+
withName?: string;
|
26
|
+
typeOnly?: boolean;
|
27
|
+
}): Import;
|
28
|
+
}
|
29
|
+
export declare class ImportList {
|
30
|
+
imports: Import[];
|
31
|
+
constructor(imports?: Import[]);
|
32
|
+
static merge(lists: ImportList[]): ImportList;
|
33
|
+
add(item: Import): void;
|
34
|
+
stringify(files: FolderStructure): string;
|
35
|
+
}
|
package/lib/imports.js
ADDED
@@ -0,0 +1,102 @@
|
|
1
|
+
import { dirname, relative } from "node:path";
|
2
|
+
import { eq } from "./util.js";
|
3
|
+
export class Import {
|
4
|
+
from;
|
5
|
+
namedImports;
|
6
|
+
star;
|
7
|
+
default;
|
8
|
+
typeOnly;
|
9
|
+
constructor(args) {
|
10
|
+
this.from = args.from;
|
11
|
+
this.namedImports = args.namedImports;
|
12
|
+
this.star = args.star;
|
13
|
+
this.default = args.default;
|
14
|
+
this.typeOnly = args.typeOnly ?? false;
|
15
|
+
}
|
16
|
+
static fromInternal(opts) {
|
17
|
+
const t = opts.type;
|
18
|
+
return new Import({
|
19
|
+
from: files => {
|
20
|
+
const schema = files.children[t.schema];
|
21
|
+
const kind = schema.children[`${t.kind}s`];
|
22
|
+
const type = kind.children[t.name];
|
23
|
+
const path = `${files.name}/${schema.name}/${kind.kind}/${type.name}.ts`;
|
24
|
+
return relative(dirname(opts.source), path);
|
25
|
+
},
|
26
|
+
namedImports: [opts.withName ?? t.name],
|
27
|
+
typeOnly: opts.typeOnly,
|
28
|
+
});
|
29
|
+
}
|
30
|
+
}
|
31
|
+
export class ImportList {
|
32
|
+
imports;
|
33
|
+
constructor(imports = []) {
|
34
|
+
this.imports = imports;
|
35
|
+
}
|
36
|
+
static merge(lists) {
|
37
|
+
return new ImportList(lists.flatMap(l => l.imports));
|
38
|
+
}
|
39
|
+
add(item) {
|
40
|
+
this.imports.push(item);
|
41
|
+
}
|
42
|
+
stringify(files) {
|
43
|
+
const modulegroups = {};
|
44
|
+
for (const item of this.imports) {
|
45
|
+
const from = typeof item.from === "function" ? item.from(files) : item.from;
|
46
|
+
const group = modulegroups[from];
|
47
|
+
if (group)
|
48
|
+
group.push(item);
|
49
|
+
else
|
50
|
+
modulegroups[from] = [item];
|
51
|
+
}
|
52
|
+
const imports = [];
|
53
|
+
const modules = Object.keys(modulegroups).sort((a, b) => {
|
54
|
+
const dotA = a.startsWith(".");
|
55
|
+
const dotB = b.startsWith(".");
|
56
|
+
// we could do localeCompare instead of 0, but 0 maintains order of fields for imports
|
57
|
+
return dotA === dotB ? 0 : dotA ? 1 : -1;
|
58
|
+
});
|
59
|
+
let broke = false;
|
60
|
+
for (const from of modules) {
|
61
|
+
if (!broke && from.startsWith(".")) {
|
62
|
+
imports.push("");
|
63
|
+
broke = true;
|
64
|
+
}
|
65
|
+
const items = modulegroups[from];
|
66
|
+
// unique named imports from this module
|
67
|
+
const namedImports = items
|
68
|
+
.flatMap(s => s.namedImports?.map(i => (typeof i === "string" ? { name: i, typeOnly: s.typeOnly } : i)) ?? [])
|
69
|
+
.filter((imp, index, arr) => {
|
70
|
+
if (arr.findIndex(i => eq(i, imp)) !== index)
|
71
|
+
return false;
|
72
|
+
return true;
|
73
|
+
});
|
74
|
+
const allTypeOnly = namedImports.every(i => i.typeOnly);
|
75
|
+
const namedImportPart = namedImports
|
76
|
+
.map(i => (!allTypeOnly && i.typeOnly ? "type " : "") + i.name)
|
77
|
+
.join(", ");
|
78
|
+
const namedImportLine = namedImportPart
|
79
|
+
? `import ${allTypeOnly ? "type " : ""}{ ${namedImportPart} } from "${from}";`
|
80
|
+
: undefined;
|
81
|
+
// all star imports from this module
|
82
|
+
const stars = items.filter(i => i.star).filter((i, index, arr) => arr.findIndex(j => eq(j, i)) === index);
|
83
|
+
const starImportLines = stars.map(i => `import ${i.typeOnly ? "type " : ""}* as ${i.star} from "${from}";`);
|
84
|
+
// all default imports from this module
|
85
|
+
const defaults = items
|
86
|
+
.filter(i => i.default)
|
87
|
+
.filter((i, index, arr) => arr.findIndex(j => eq(j, i)) === index);
|
88
|
+
const defaultImportLines = defaults.map(i => `import ${i.typeOnly ? "type " : ""}${i.default} from "${from}";`);
|
89
|
+
const sideEffectImports = items.find(i => !i.default && !i.star && !i.namedImports?.length);
|
90
|
+
const sideEffectImportLine = sideEffectImports ? `import "${sideEffectImports.from}";` : undefined;
|
91
|
+
if (sideEffectImportLine)
|
92
|
+
imports.push(sideEffectImportLine);
|
93
|
+
if (namedImportLine)
|
94
|
+
imports.push(namedImportLine);
|
95
|
+
if (starImportLines.length)
|
96
|
+
imports.push(...starImportLines);
|
97
|
+
if (defaultImportLines.length)
|
98
|
+
imports.push(...defaultImportLines);
|
99
|
+
}
|
100
|
+
return imports.join("\n");
|
101
|
+
}
|
102
|
+
}
|
package/lib/index.js
CHANGED
@@ -1,14 +1,30 @@
|
|
1
1
|
import { Extractor, FunctionReturnTypeKind } from "./extractor/index.js";
|
2
2
|
import { rm, mkdir, writeFile } from "node:fs/promises";
|
3
|
-
import {
|
3
|
+
import { allowed_kind_names } from "./types.js";
|
4
|
+
import { ImportList } from "./imports.js";
|
4
5
|
import { existsSync } from "node:fs";
|
5
|
-
import { join } from "./util.js";
|
6
|
+
import { join, parens } from "./util.js";
|
6
7
|
import { join as joinpath } from "node:path";
|
7
|
-
import { config,
|
8
|
+
import { config, generators as builtin_generators } from "./config.js";
|
8
9
|
export { config };
|
9
|
-
const
|
10
|
-
|
11
|
-
|
10
|
+
const NO_COLOR = Boolean(process.env.NO_COLOR || process.env.CI);
|
11
|
+
const red = (str) => (NO_COLOR ? str : `\x1b[31m${str}\x1b[0m`);
|
12
|
+
const green = (str) => (NO_COLOR ? str : `\x1b[32m${str}\x1b[0m`);
|
13
|
+
const yellow = (str) => (NO_COLOR ? str : `\x1b[33m${str}\x1b[0m`);
|
14
|
+
const blue = (str) => (NO_COLOR ? str : `\x1b[34m${str}\x1b[0m`);
|
15
|
+
const bold = (str) => (NO_COLOR ? str : `\x1b[1m${str}\x1b[0m`);
|
16
|
+
const underline = (str) => (NO_COLOR ? str : `\x1b[4m${str}\x1b[0m`);
|
17
|
+
const THRESHOLD1 = 800;
|
18
|
+
const THRESHOLD2 = 1500;
|
19
|
+
const time = (start, addParens = true) => {
|
20
|
+
const diff = performance.now() - start;
|
21
|
+
const diffstr = diff.toFixed(2) + "ms";
|
22
|
+
const str = addParens ? parens(diffstr) : diffstr;
|
23
|
+
if (diff < THRESHOLD1)
|
24
|
+
return green(str);
|
25
|
+
if (diff < THRESHOLD2)
|
26
|
+
return yellow(str);
|
27
|
+
return red(str);
|
12
28
|
};
|
13
29
|
const filter_overloaded_functions = (functions) => {
|
14
30
|
const counts = functions.reduce((acc, func) => {
|
@@ -61,11 +77,15 @@ const filter_unsupported_functions = (functions) => {
|
|
61
77
|
const unsupported = filtered.filter(func => !filtered.includes(func));
|
62
78
|
return [filtered, unsupported];
|
63
79
|
};
|
64
|
-
const write = (filename, file) => writeFile(filename, file + "\n");
|
65
80
|
const multifile = async (generators, schemas, opts) => {
|
66
|
-
const { out } = opts;
|
67
|
-
|
68
|
-
const
|
81
|
+
const { out, defaultSchema } = opts;
|
82
|
+
let count = 0;
|
83
|
+
const write = async (filename, file) => {
|
84
|
+
await writeFile(filename, file + "\n");
|
85
|
+
count++;
|
86
|
+
};
|
87
|
+
const warnings = new Set();
|
88
|
+
const gens = generators.map(g => g({ defaultSchema, warnings }));
|
69
89
|
const def_gen = gens[0];
|
70
90
|
const files = {
|
71
91
|
name: out,
|
@@ -83,7 +103,7 @@ const multifile = async (generators, schemas, opts) => {
|
|
83
103
|
children: Object.fromEntries(schema[kind].map(item => [
|
84
104
|
item.name,
|
85
105
|
{
|
86
|
-
name: def_gen.
|
106
|
+
name: def_gen.formatSchemaMemberName(item),
|
87
107
|
type: "type",
|
88
108
|
},
|
89
109
|
])),
|
@@ -101,14 +121,14 @@ const multifile = async (generators, schemas, opts) => {
|
|
101
121
|
{
|
102
122
|
const skipped = unsupported_functions.map(f => ` - ${f.name}`);
|
103
123
|
if (skipped.length) {
|
104
|
-
warnings.
|
124
|
+
warnings.add(`Skipping ${skipped.length} functions not representable in JavaScript (safe to ignore):\n` +
|
105
125
|
skipped.join("\n"));
|
106
126
|
}
|
107
127
|
}
|
108
128
|
{
|
109
129
|
const skipped = overloaded_functions.map(f => ` - ${f}`);
|
110
130
|
if (skipped.length) {
|
111
|
-
warnings.
|
131
|
+
warnings.add(`Skipping ${skipped.length} overloaded functions (not supported):\n` + skipped.join("\n"));
|
112
132
|
}
|
113
133
|
}
|
114
134
|
let createIndex = false;
|
@@ -120,69 +140,60 @@ const multifile = async (generators, schemas, opts) => {
|
|
120
140
|
console.log(" Creating %s:\n", kind);
|
121
141
|
for (const [i, item] of schema[kind].entries()) {
|
122
142
|
const index = "[" + (i + 1 + "]").padEnd(3, " ");
|
123
|
-
const filename = joinpath(schemaDir, kind, def_gen.
|
143
|
+
const filename = joinpath(schemaDir, kind, def_gen.formatSchemaMemberName(item) + ".ts");
|
124
144
|
const exists = await existsSync(filename);
|
125
145
|
if (exists) {
|
126
|
-
warnings.
|
146
|
+
warnings.add(`Skipping ${item.kind} "${item.name}": formatted name clashes. Wanted to create ${filename}`);
|
127
147
|
continue;
|
128
148
|
}
|
129
149
|
const start = performance.now();
|
130
150
|
let file = "";
|
131
|
-
const imports = new
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
// prettier-ignore
|
137
|
-
if (item.kind === "materializedView")
|
138
|
-
file += join(gens.map(gen => gen.materializedView(imports, item)));
|
139
|
-
if (item.kind === "enum")
|
140
|
-
file += join(gens.map(gen => gen.enum(imports, item)));
|
141
|
-
if (item.kind === "composite")
|
142
|
-
file += join(gens.map(gen => gen.composite(imports, item)));
|
143
|
-
if (item.kind === "domain")
|
144
|
-
file += join(gens.map(gen => gen.domain(imports, item)));
|
145
|
-
if (item.kind === "range")
|
146
|
-
file += join(gens.map(gen => gen.range(imports, item)));
|
147
|
-
if (item.kind === "function")
|
148
|
-
file += join(gens.map(gen => gen.function(imports, item)));
|
149
|
-
const parts = [];
|
150
|
-
parts.push(imports.stringify(filename, files));
|
151
|
-
parts.push(file);
|
152
|
-
file = join(parts);
|
151
|
+
const imports = new ImportList();
|
152
|
+
file += join(gens.map(gen => gen[item.kind]({ source: filename, imports },
|
153
|
+
// @ts-expect-error TypeScript cannot fathom the fact that item is related to item.kind
|
154
|
+
item)));
|
155
|
+
file = join([imports.stringify(files), file]);
|
153
156
|
await write(filename, file);
|
154
|
-
console.log(" %s %s
|
157
|
+
console.log(" %s %s %s", index, filename, time(start));
|
155
158
|
}
|
156
159
|
{
|
157
160
|
const start = performance.now();
|
158
|
-
const
|
159
|
-
const
|
160
|
-
|
161
|
-
const
|
162
|
-
|
161
|
+
const imports = new ImportList();
|
162
|
+
const fileName = joinpath(schemaDir, kind, "index.ts");
|
163
|
+
const kindIndex = join(gens.map(gen => gen.schemaKindIndex({ source: fileName, imports }, schema, kind, def_gen)));
|
164
|
+
const file = join([imports.stringify(files), kindIndex]);
|
165
|
+
await write(fileName, file);
|
166
|
+
console.log(" ✅ Created %s index: %s %s\n", kind, fileName, time(start));
|
163
167
|
}
|
164
168
|
}
|
165
169
|
if (!createIndex)
|
166
170
|
continue;
|
167
171
|
{
|
168
172
|
const start = performance.now();
|
169
|
-
const
|
170
|
-
const
|
171
|
-
|
172
|
-
|
173
|
+
const imports = new ImportList();
|
174
|
+
const fileName = joinpath(schemaDir, "index.ts");
|
175
|
+
const index = join(gens.map(gen => gen.schemaIndex({ source: fileName, imports }, schema, def_gen)));
|
176
|
+
const file = join([imports.stringify(files), index]);
|
177
|
+
await write(fileName, file);
|
178
|
+
console.log(" Created schema index: %s %s\n", fileName, time(start));
|
173
179
|
}
|
174
180
|
}
|
175
181
|
{
|
176
182
|
const start = performance.now();
|
177
|
-
const
|
178
|
-
const
|
179
|
-
|
180
|
-
|
183
|
+
const imports = new ImportList();
|
184
|
+
const fileName = joinpath(out, "index.ts");
|
185
|
+
const fullIndex = join(gens.map(gen => gen.fullIndex({ source: fileName, imports }, Object.values(schemas))));
|
186
|
+
const file = join([imports.stringify(files), fullIndex]);
|
187
|
+
await write(fileName, file);
|
188
|
+
console.log("Created full index: %s %s", fileName, time(start));
|
181
189
|
}
|
182
|
-
if (warnings.
|
190
|
+
if (warnings.size > 0) {
|
183
191
|
console.log("\nWarnings generated:");
|
184
|
-
|
192
|
+
for (const warning of warnings) {
|
193
|
+
console.log("* " + warning);
|
194
|
+
}
|
185
195
|
}
|
196
|
+
return count;
|
186
197
|
};
|
187
198
|
export async function generate(opts, generators) {
|
188
199
|
const validated = config(opts);
|
@@ -190,13 +201,13 @@ export async function generate(opts, generators) {
|
|
190
201
|
const extractor = new Extractor(opts);
|
191
202
|
const start = performance.now();
|
192
203
|
const schemas = await extractor.extractSchemas();
|
193
|
-
|
194
|
-
console.
|
195
|
-
|
196
|
-
generators = validated.adapters.map(adapter => adapters[adapter]).concat(generators ?? []);
|
204
|
+
console.log("Extracted schemas %s\n", time(start));
|
205
|
+
console.info("Generators enabled: %s\n", validated.generators.join(", "));
|
206
|
+
generators = validated.generators.map(generator => builtin_generators[generator]).concat(generators ?? []);
|
197
207
|
console.log("Clearing directory and generating schemas at '%s'\n", out);
|
198
208
|
await rm(out, { recursive: true, force: true });
|
199
209
|
await mkdir(out, { recursive: true });
|
200
|
-
await multifile(generators, schemas, validated);
|
201
|
-
console.log("Completed in
|
210
|
+
const count = await multifile(generators, schemas, validated);
|
211
|
+
console.log("Completed in %s, %s generated.", time(start, false), bold(underline(blue(count + " files"))));
|
212
|
+
console.log();
|
202
213
|
}
|