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/README.md CHANGED
@@ -15,7 +15,7 @@ bun add true-pg
15
15
  ## Quickstart
16
16
 
17
17
  ```bash
18
- npx true-pg --all-adapters --uri postgres://user:password@localhost:5432/database --out ./models
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
- - `-a, --adapter [adapter]` - Adapter to use (e.g. `kysely`, `zod`). Can be specified multiple times.
72
- - `-A, --all-adapters` - Enable all built-in adapters
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
- adapters: ["kysely", "zod"],
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
- | `adapters` | Adapters to use (e.g. `kysely`, `zod`) | `"kysely"` |
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
- formatSchema: name => `${name}Schema`,
122
- formatSchemaType: type => `${type}Type`,
123
- formatType: type => `${type}Interface`,
124
- table: (imports, 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: (imports, en) => {
127
+ enum: (ctx, en) => {
128
128
  // Custom enum type generation
129
129
  },
130
- composite: (imports, composite) => {
130
+ composite: (ctx, composite) => {
131
131
  // Custom composite type generation
132
132
  },
133
- function: (imports, func) => {
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
- adapters: [], // empty array to disable adapters
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 `--adapter` CLI option.
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 { adapters } from "./config.js";
4
+ import { generators } from "./config.js";
5
5
  const args = process.argv.slice(2);
6
6
  const opts = mri(args, {
7
- boolean: ["help", "all-adapters"],
8
- string: ["config", "uri", "out", "adapter"],
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: "adapter",
15
- A: "all-adapters",
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(" -a, --adapter [adapter] Output adapter to use (default: 'kysely')");
35
- log(" -A, --all-adapters Output all adapters");
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 -a kysely -a zod");
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-adapters"])
47
- opts.adapter = Object.keys(adapters);
48
- if (!(opts.adapter || config.adapters))
49
- console.warn('No adapters specified, using default: ["kysely"]');
50
- // allow single adapter or comma-separated list of adapters
51
- if (typeof opts.adapter === "string")
52
- opts.adapter = opts.adapter.split(",");
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.adapters = opts.adapter ?? config.adapters ?? ["kysely"];
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
- /** Adapters to enable. Currently supported adapters are "kysely" and "zod". Default: ["kysely"] */
8
- adapters?: ("kysely" | "zod")[];
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 adapters: {
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
- adapters: ("kysely" | "zod")[];
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
- adapters: ("kysely" | "zod")[];
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
- adapters: ("kysely" | "zod")[];
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 adapters = {
5
+ export const generators = {
6
6
  kysely: Kysely,
7
7
  zod: Zod,
8
8
  };
9
- const availableAdapters = Object.keys(adapters);
9
+ const availableGenerators = Object.keys(generators);
10
10
  export function config(opts) {
11
11
  const out = normalise(opts.out || "./models");
12
- const adapters = opts.adapters || ["kysely"];
12
+ const generators = opts.generators || ["kysely"];
13
13
  const defaultSchema = opts.defaultSchema || "public";
14
- for (const adapter of opts.adapters ?? []) {
15
- if (!availableAdapters.includes(adapter)) {
16
- console.error('Requested adapter "%s" not found.', adapter);
17
- console.error("Available adapters: %s", availableAdapters.join(", "));
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
- adapters,
29
+ generators,
30
30
  defaultSchema,
31
31
  };
32
32
  }
@@ -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 { Nodes, allowed_kind_names } from "./types.js";
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, adapters } from "./config.js";
8
+ import { config, generators as builtin_generators } from "./config.js";
8
9
  export { config };
9
- const time = (start) => {
10
- const end = performance.now();
11
- return (end - start).toFixed(2);
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
- const warnings = [];
68
- const gens = generators.map(g => g({ ...opts, warnings }));
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.formatSchemaType(item),
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.push(`Skipping ${skipped.length} functions not representable in JavaScript (safe to ignore):\n` +
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.push(`Skipping ${skipped.length} overloaded functions (not supported):\n` + skipped.join("\n"));
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.formatSchemaType(item) + ".ts");
143
+ const filename = joinpath(schemaDir, kind, def_gen.formatSchemaMemberName(item) + ".ts");
124
144
  const exists = await existsSync(filename);
125
145
  if (exists) {
126
- warnings.push(`Skipping ${item.kind} "${item.name}": formatted name clashes. Wanted to create ${filename}`);
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 Nodes.ImportList([]);
132
- if (item.kind === "table")
133
- file += join(gens.map(gen => gen.table(imports, item)));
134
- if (item.kind === "view")
135
- file += join(gens.map(gen => gen.view(imports, item)));
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 \x1b[32m(%sms)\x1B[0m", index, filename, time(start));
157
+ console.log(" %s %s %s", index, filename, time(start));
155
158
  }
156
159
  {
157
160
  const start = performance.now();
158
- const kindIndex = join(gens.map(gen => gen.schemaKindIndex(schema, kind, def_gen)));
159
- const kindIndexFilename = joinpath(schemaDir, kind, "index.ts");
160
- await write(kindIndexFilename, kindIndex);
161
- const end = performance.now();
162
- console.log(" ✅ Created %s index: %s \x1b[32m(%sms)\x1B[0m\n", kind, kindIndexFilename, (end - start).toFixed(2));
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 index = join(gens.map(gen => gen.schemaIndex(schema, def_gen)));
170
- const indexFilename = joinpath(schemaDir, "index.ts");
171
- await write(indexFilename, index);
172
- console.log(" Created schema index: %s \x1b[32m(%sms)\x1B[0m\n", indexFilename, time(start));
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 fullIndex = join(gens.map(gen => gen.fullIndex(Object.values(schemas))));
178
- const fullIndexFilename = joinpath(out, "index.ts");
179
- await write(fullIndexFilename, fullIndex);
180
- console.log("Created full index: %s \x1b[32m(%sms)\x1B[0m", fullIndexFilename, time(start));
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.length > 0) {
190
+ if (warnings.size > 0) {
183
191
  console.log("\nWarnings generated:");
184
- console.log(warnings.map(warning => "* " + warning).join("\n"));
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
- const end = performance.now();
194
- console.log("Extracted schemas \x1b[32m(%sms)\x1b[0m\n", (end - start).toFixed(2));
195
- console.info("Adapters enabled: %s\n", validated.adapters.join(", "));
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 \x1b[32m%sms\x1b[0m", time(start));
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
  }