@drzl/cli 4.16.0 → 4.18.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.
- package/dist/{chunk-K4J4XIFO.js → chunk-I4KMDCQR.js} +30 -1
- package/dist/chunk-I4KMDCQR.js.map +1 -0
- package/dist/cli.cjs +741 -133
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +403 -99
- package/dist/cli.js.map +1 -1
- package/dist/config.cjs +29 -0
- package/dist/config.cjs.map +1 -1
- package/dist/config.d.cts +38 -2
- package/dist/config.d.ts +38 -2
- package/dist/config.js +1 -1
- package/dist/dist-TRJLPIWT.js +603 -0
- package/dist/dist-TRJLPIWT.js.map +1 -0
- package/package.json +5 -5
- package/dist/chunk-K4J4XIFO.js.map +0 -1
- package/dist/dist-UE55LTXV.js +0 -327
- package/dist/dist-UE55LTXV.js.map +0 -1
package/dist/cli.js
CHANGED
|
@@ -5,37 +5,18 @@ import {
|
|
|
5
5
|
filterTables,
|
|
6
6
|
loadConfig,
|
|
7
7
|
trpcOutDir
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-I4KMDCQR.js";
|
|
9
9
|
|
|
10
10
|
// src/cli.ts
|
|
11
11
|
import { SchemaAnalyzer } from "@drzl/analyzer";
|
|
12
12
|
import { ORPCGenerator } from "@drzl/generator-orpc";
|
|
13
|
-
import
|
|
13
|
+
import chalk3 from "chalk";
|
|
14
14
|
import chokidar from "chokidar";
|
|
15
15
|
import cliProgress from "cli-progress";
|
|
16
16
|
import { Command } from "commander";
|
|
17
17
|
import * as path4 from "path";
|
|
18
18
|
import ora from "ora";
|
|
19
19
|
|
|
20
|
-
// src/trpc-options.ts
|
|
21
|
-
function trpcOptions(g, cfg, servicesDir) {
|
|
22
|
-
return {
|
|
23
|
-
outputDir: trpcOutDir(g, cfg),
|
|
24
|
-
template: g.template,
|
|
25
|
-
includeRelations: g.includeRelations,
|
|
26
|
-
naming: g.naming,
|
|
27
|
-
outputHeader: g.outputHeader,
|
|
28
|
-
format: g.format,
|
|
29
|
-
importExtension: g.importExtension,
|
|
30
|
-
validation: g.validation,
|
|
31
|
-
databaseInjection: g.databaseInjection,
|
|
32
|
-
// Where the service generator is actually writing, so `template: 'service'` emits an import
|
|
33
|
-
// of a module that exists. The generator defaults this to `src/services`, which is right only
|
|
34
|
-
// by coincidence for a config that puts them elsewhere.
|
|
35
|
-
servicesDir
|
|
36
|
-
};
|
|
37
|
-
}
|
|
38
|
-
|
|
39
20
|
// src/validation-options.ts
|
|
40
21
|
function validationOptions(g, cfg, outDir, caps = {}) {
|
|
41
22
|
return {
|
|
@@ -62,6 +43,297 @@ function validationOptions(g, cfg, outDir, caps = {}) {
|
|
|
62
43
|
};
|
|
63
44
|
}
|
|
64
45
|
|
|
46
|
+
// src/json-schema-options.ts
|
|
47
|
+
function jsonSchemaOptions(g, cfg, outDir) {
|
|
48
|
+
return {
|
|
49
|
+
// JSON Schema is data, so nothing it emits references a type from the schema module.
|
|
50
|
+
...validationOptions(g, cfg, outDir, { schemaTypes: false }),
|
|
51
|
+
target: g.target,
|
|
52
|
+
components: g.components,
|
|
53
|
+
document: g.document,
|
|
54
|
+
// Read only while emitting a document, where it adds `/users/{id}/posts`. The per-table
|
|
55
|
+
// schemas are flat whatever it says.
|
|
56
|
+
includeRelations: g.includeRelations
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// src/trpc-options.ts
|
|
61
|
+
function trpcOptions(g, cfg, servicesDir) {
|
|
62
|
+
return {
|
|
63
|
+
outputDir: trpcOutDir(g, cfg),
|
|
64
|
+
template: g.template,
|
|
65
|
+
includeRelations: g.includeRelations,
|
|
66
|
+
naming: g.naming,
|
|
67
|
+
outputHeader: g.outputHeader,
|
|
68
|
+
format: g.format,
|
|
69
|
+
importExtension: g.importExtension,
|
|
70
|
+
validation: g.validation,
|
|
71
|
+
databaseInjection: g.databaseInjection,
|
|
72
|
+
// Where the service generator is actually writing, so `template: 'service'` emits an import
|
|
73
|
+
// of a module that exists. The generator defaults this to `src/services`, which is right only
|
|
74
|
+
// by coincidence for a config that puts them elsewhere.
|
|
75
|
+
servicesDir
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// src/doctor.ts
|
|
80
|
+
import { parseCheck } from "@drzl/validation-core";
|
|
81
|
+
import chalk from "chalk";
|
|
82
|
+
var HANDLED_CODES = /* @__PURE__ */ new Set(["DRZL_ANL_UNKNOWN_COLUMN"]);
|
|
83
|
+
function splitPath(path5) {
|
|
84
|
+
if (!path5) return {};
|
|
85
|
+
const dot = path5.lastIndexOf(".");
|
|
86
|
+
if (dot <= 0) return { table: path5 };
|
|
87
|
+
return { table: path5.slice(0, dot), column: path5.slice(dot + 1) };
|
|
88
|
+
}
|
|
89
|
+
function namedColumns(parsed) {
|
|
90
|
+
const out = [];
|
|
91
|
+
for (const c of parsed.checks) out.push({ column: c.column, scalar: true });
|
|
92
|
+
for (const s of parsed.sets ?? []) out.push({ column: s.column, scalar: true });
|
|
93
|
+
for (const l of parsed.lengths ?? []) out.push({ column: l.column, scalar: false });
|
|
94
|
+
for (const c of parsed.cardinalities ?? []) out.push({ column: c.column, scalar: false });
|
|
95
|
+
for (const r of parsed.rows ?? []) {
|
|
96
|
+
out.push({ column: r.left, scalar: false });
|
|
97
|
+
out.push({ column: r.right, scalar: false });
|
|
98
|
+
}
|
|
99
|
+
return out;
|
|
100
|
+
}
|
|
101
|
+
function describeShape(c) {
|
|
102
|
+
if (c.arrayDimensions) return "an array";
|
|
103
|
+
switch (c.shape?.kind) {
|
|
104
|
+
case "json":
|
|
105
|
+
return "a JSON";
|
|
106
|
+
case "buffer":
|
|
107
|
+
return "a binary";
|
|
108
|
+
case "tuple":
|
|
109
|
+
case "numberObject":
|
|
110
|
+
return "a structured";
|
|
111
|
+
case "numberVector":
|
|
112
|
+
return "a vector";
|
|
113
|
+
case "bitstring":
|
|
114
|
+
return "a bit-string";
|
|
115
|
+
case "byteString":
|
|
116
|
+
return "a byte-string";
|
|
117
|
+
case "custom":
|
|
118
|
+
return "a customType";
|
|
119
|
+
default:
|
|
120
|
+
return "a structured";
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
function checkFindings(table) {
|
|
124
|
+
const out = [];
|
|
125
|
+
const byName = new Map(table.columns.map((c) => [c.name, c]));
|
|
126
|
+
for (const k of table.checks ?? []) {
|
|
127
|
+
const label = k.name ? `"${k.name}"` : "an unnamed constraint";
|
|
128
|
+
const raw = k.expression ?? "";
|
|
129
|
+
const expr = raw.trim() ? raw : "(empty)";
|
|
130
|
+
const parsed = parseCheck(raw, k.name);
|
|
131
|
+
if (!parsed.ok) {
|
|
132
|
+
out.push({
|
|
133
|
+
kind: "check-declined",
|
|
134
|
+
level: "warn",
|
|
135
|
+
table: table.tsName,
|
|
136
|
+
constraint: k.name,
|
|
137
|
+
message: `CHECK ${label} on "${table.tsName}" is not translated: ${parsed.reason}. Expression: ${expr}`,
|
|
138
|
+
hint: "Only constraints whose meaning is unambiguous are translated, because a validator enforcing a guess rejects rows the database accepts. Your database still enforces this one; nothing DRZL emits does."
|
|
139
|
+
});
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
const seen = /* @__PURE__ */ new Set();
|
|
143
|
+
for (const { column, scalar } of namedColumns(parsed)) {
|
|
144
|
+
if (seen.has(column)) continue;
|
|
145
|
+
seen.add(column);
|
|
146
|
+
const col = byName.get(column);
|
|
147
|
+
if (!col) {
|
|
148
|
+
out.push({
|
|
149
|
+
kind: "check-unknown-column",
|
|
150
|
+
level: "warn",
|
|
151
|
+
table: table.tsName,
|
|
152
|
+
column,
|
|
153
|
+
constraint: k.name,
|
|
154
|
+
message: `CHECK ${label} on "${table.tsName}" names "${column}", which is not a column of that table, so nothing enforces it. Expression: ${expr}`,
|
|
155
|
+
hint: "A constraint is attached to the field it names. Check the spelling, or move a constraint spanning two tables out of the schema."
|
|
156
|
+
});
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
if (scalar && (col.arrayDimensions || col.shape)) {
|
|
160
|
+
out.push({
|
|
161
|
+
kind: "check-not-scalar",
|
|
162
|
+
level: "warn",
|
|
163
|
+
table: table.tsName,
|
|
164
|
+
column,
|
|
165
|
+
constraint: k.name,
|
|
166
|
+
message: `CHECK ${label} on "${table.tsName}" compares ${describeShape(col)} column "${column}" against a scalar literal, which does not describe it, so it is not translated. Expression: ${expr}`,
|
|
167
|
+
hint: "On an array column only cardinality(col) is read, since it is the one comparison that is about the array rather than about an element."
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
return out;
|
|
173
|
+
}
|
|
174
|
+
function primaryKeyFindings(table) {
|
|
175
|
+
if (table.readOnly) return [];
|
|
176
|
+
const pk = table.primaryKey?.columns ?? [];
|
|
177
|
+
if (!pk.length) {
|
|
178
|
+
const hasId = table.columns.some((c) => c.name === "id");
|
|
179
|
+
return [
|
|
180
|
+
{
|
|
181
|
+
kind: "no-primary-key",
|
|
182
|
+
level: "warn",
|
|
183
|
+
table: table.tsName,
|
|
184
|
+
message: hasId ? `Table "${table.tsName}" declares no primary key. The service and router generators fall back to a column named "id", which this table happens to have, so they work by coincidence.` : `Table "${table.tsName}" declares no primary key. The service and router generators fall back to a column named "id", which this table does not have, so the generated service will not compile.`,
|
|
185
|
+
hint: "Declare a primary key, or leave this table out with the config table filter."
|
|
186
|
+
}
|
|
187
|
+
];
|
|
188
|
+
}
|
|
189
|
+
if (pk.length > 1) {
|
|
190
|
+
return [
|
|
191
|
+
{
|
|
192
|
+
kind: "partial-primary-key",
|
|
193
|
+
level: "warn",
|
|
194
|
+
table: table.tsName,
|
|
195
|
+
message: `Table "${table.tsName}" has a composite primary key (${pk.join(", ")}). The service and router generators key getById, update and delete on "${pk[0]}" alone, so those operations match on part of the key.`,
|
|
196
|
+
hint: "Treat the generated service as a starting point for this table and widen the key by hand."
|
|
197
|
+
}
|
|
198
|
+
];
|
|
199
|
+
}
|
|
200
|
+
return [];
|
|
201
|
+
}
|
|
202
|
+
function buildDoctorReport(analysis, schemaPath) {
|
|
203
|
+
const findings = [];
|
|
204
|
+
const errors = analysis.issues.filter((i) => i.level === "error");
|
|
205
|
+
for (const i of errors) {
|
|
206
|
+
findings.push({
|
|
207
|
+
kind: "analyzer",
|
|
208
|
+
level: "error",
|
|
209
|
+
...splitPath(i.path),
|
|
210
|
+
message: i.message,
|
|
211
|
+
hint: i.hint
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
for (const i of analysis.issues) {
|
|
215
|
+
if (i.code !== "DRZL_ANL_UNKNOWN_COLUMN") continue;
|
|
216
|
+
findings.push({
|
|
217
|
+
kind: "unknown-column",
|
|
218
|
+
level: "warn",
|
|
219
|
+
...splitPath(i.path),
|
|
220
|
+
message: i.message,
|
|
221
|
+
hint: i.hint
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
for (const t of analysis.tables) findings.push(...checkFindings(t));
|
|
225
|
+
for (const t of analysis.tables) findings.push(...primaryKeyFindings(t));
|
|
226
|
+
for (const i of analysis.issues) {
|
|
227
|
+
if (i.level === "error" || HANDLED_CODES.has(i.code)) continue;
|
|
228
|
+
findings.push({
|
|
229
|
+
kind: "analyzer",
|
|
230
|
+
level: "warn",
|
|
231
|
+
...splitPath(i.path),
|
|
232
|
+
message: i.message,
|
|
233
|
+
hint: i.hint
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
const columns = analysis.tables.reduce((n, t) => n + t.columns.length, 0);
|
|
237
|
+
const checks = analysis.tables.reduce((n, t) => n + (t.checks?.length ?? 0), 0);
|
|
238
|
+
return {
|
|
239
|
+
schema: schemaPath,
|
|
240
|
+
dialect: analysis.dialect,
|
|
241
|
+
ok: findings.length === 0,
|
|
242
|
+
counts: { tables: analysis.tables.length, columns, checks, findings: findings.length },
|
|
243
|
+
findings
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
var SECTIONS = [
|
|
247
|
+
{
|
|
248
|
+
kinds: ["unknown-column"],
|
|
249
|
+
title: "Columns DRZL cannot type",
|
|
250
|
+
why: "These get a validator that accepts any value."
|
|
251
|
+
},
|
|
252
|
+
{
|
|
253
|
+
kinds: ["check-declined", "check-unknown-column", "check-not-scalar"],
|
|
254
|
+
title: "CHECK constraints DRZL does not enforce",
|
|
255
|
+
why: "Your database still enforces these. Nothing DRZL generates does."
|
|
256
|
+
},
|
|
257
|
+
{
|
|
258
|
+
kinds: ["no-primary-key", "partial-primary-key"],
|
|
259
|
+
title: "Primary keys the generators cannot use",
|
|
260
|
+
why: "The generated getById, update and delete are keyed on one column."
|
|
261
|
+
},
|
|
262
|
+
{
|
|
263
|
+
kinds: ["analyzer"],
|
|
264
|
+
title: "Other findings",
|
|
265
|
+
why: "Reported by the analyzer while reading the schema."
|
|
266
|
+
}
|
|
267
|
+
];
|
|
268
|
+
function wrap(text, indent, first = indent, width = 96) {
|
|
269
|
+
const lines = [];
|
|
270
|
+
let line = "";
|
|
271
|
+
for (const word of text.split(/\s+/)) {
|
|
272
|
+
if (line && `${line} ${word}`.length + indent.length > width) {
|
|
273
|
+
lines.push(line);
|
|
274
|
+
line = word;
|
|
275
|
+
} else {
|
|
276
|
+
line = line ? `${line} ${word}` : word;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
if (line) lines.push(line);
|
|
280
|
+
return lines.map((l, i) => (i === 0 ? first : indent) + l).join("\n");
|
|
281
|
+
}
|
|
282
|
+
function renderDoctorReport(report) {
|
|
283
|
+
const out = [];
|
|
284
|
+
const plural = (n, one) => `${n} ${one}${n === 1 ? "" : "s"}`;
|
|
285
|
+
out.push(chalk.bold(`DRZL doctor ${report.schema}`));
|
|
286
|
+
out.push(
|
|
287
|
+
chalk.dim(
|
|
288
|
+
`${report.dialect}, ${plural(report.counts.tables, "table")}, ${plural(report.counts.columns, "column")}, ${plural(report.counts.checks, "CHECK constraint")}`
|
|
289
|
+
)
|
|
290
|
+
);
|
|
291
|
+
out.push("");
|
|
292
|
+
if (report.ok) {
|
|
293
|
+
out.push(chalk.green("Nothing to report."));
|
|
294
|
+
out.push(chalk.dim(" Every column has a type DRZL can describe."));
|
|
295
|
+
out.push(chalk.dim(" Every CHECK constraint is translated into the generated validators."));
|
|
296
|
+
out.push(chalk.dim(" Every table has a primary key the generators can use."));
|
|
297
|
+
return out.join("\n");
|
|
298
|
+
}
|
|
299
|
+
const fatal = report.findings.filter((f) => f.level === "error");
|
|
300
|
+
if (fatal.length) {
|
|
301
|
+
out.push(chalk.red("DRZL could not read this schema"));
|
|
302
|
+
out.push(chalk.dim(" Nothing else could be checked."));
|
|
303
|
+
out.push("");
|
|
304
|
+
for (const f of fatal) {
|
|
305
|
+
out.push(wrap(f.message, " ", ` ${chalk.dim("-")} `));
|
|
306
|
+
if (f.hint) out.push(chalk.dim(wrap(f.hint, " ")));
|
|
307
|
+
}
|
|
308
|
+
out.push("");
|
|
309
|
+
}
|
|
310
|
+
for (const section of SECTIONS) {
|
|
311
|
+
const mine = report.findings.filter(
|
|
312
|
+
(f) => f.level !== "error" && section.kinds.includes(f.kind)
|
|
313
|
+
);
|
|
314
|
+
if (!mine.length) continue;
|
|
315
|
+
out.push(chalk.yellow(`${section.title} (${mine.length})`));
|
|
316
|
+
out.push(chalk.dim(` ${section.why}`));
|
|
317
|
+
out.push("");
|
|
318
|
+
const groups = /* @__PURE__ */ new Map();
|
|
319
|
+
for (const f of mine) {
|
|
320
|
+
const key = f.hint ?? "";
|
|
321
|
+
groups.set(key, [...groups.get(key) ?? [], f]);
|
|
322
|
+
}
|
|
323
|
+
for (const [hint, items] of groups) {
|
|
324
|
+
for (const f of items) out.push(wrap(f.message, " ", ` ${chalk.dim("-")} `));
|
|
325
|
+
if (hint) out.push(chalk.dim(wrap(hint, " ")));
|
|
326
|
+
out.push("");
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
out.push(
|
|
330
|
+
chalk.bold(`${plural(report.counts.findings, "finding")} in ${report.schema}.`) + chalk.dim(
|
|
331
|
+
fatal.length ? " Fix the error above and run this again." : " None of these stop DRZL generating; they are what it will not check for you."
|
|
332
|
+
)
|
|
333
|
+
);
|
|
334
|
+
return out.join("\n");
|
|
335
|
+
}
|
|
336
|
+
|
|
65
337
|
// src/drift.ts
|
|
66
338
|
import { promises as fs } from "fs";
|
|
67
339
|
import path from "path";
|
|
@@ -138,7 +410,7 @@ async function loadGenerator(specifier, load) {
|
|
|
138
410
|
}
|
|
139
411
|
|
|
140
412
|
// src/sponsor.ts
|
|
141
|
-
import
|
|
413
|
+
import chalk2 from "chalk";
|
|
142
414
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
143
415
|
import path2 from "path";
|
|
144
416
|
var CACHE_DIR = path2.join(process.cwd(), "node_modules", ".cache", "@drzl");
|
|
@@ -152,9 +424,9 @@ var tips = [
|
|
|
152
424
|
"Need JSON Schema or OpenAPI? The json-schema generator emits both, with no runtime dependency.",
|
|
153
425
|
"Use output headers to track generated files and trim noisy diffs."
|
|
154
426
|
];
|
|
155
|
-
var green = (msg) =>
|
|
156
|
-
var cyan = (msg) =>
|
|
157
|
-
var gray = (msg) =>
|
|
427
|
+
var green = (msg) => chalk2.hex("#6ee7b7")(msg);
|
|
428
|
+
var cyan = (msg) => chalk2.cyan(msg);
|
|
429
|
+
var gray = (msg) => chalk2.gray(msg);
|
|
158
430
|
function maybeShowSponsorMessage({
|
|
159
431
|
reason = "generate",
|
|
160
432
|
minIntervalMs = DEFAULT_INTERVAL_MS,
|
|
@@ -243,13 +515,13 @@ var CLI_VERSION = readCliVersion();
|
|
|
243
515
|
function reportGeneratorFailure(kind, e) {
|
|
244
516
|
if (e instanceof GeneratorNotInstalledError) {
|
|
245
517
|
console.error(
|
|
246
|
-
|
|
247
|
-
|
|
518
|
+
chalk3.red(`The ${kind} generator is not installed.`),
|
|
519
|
+
chalk3.yellow(`
|
|
248
520
|
Install with: npm install ${e.specifier}`)
|
|
249
521
|
);
|
|
250
522
|
return;
|
|
251
523
|
}
|
|
252
|
-
console.error(
|
|
524
|
+
console.error(chalk3.red(`The ${kind} generator failed:`), e?.message ?? e);
|
|
253
525
|
}
|
|
254
526
|
var program = new Command();
|
|
255
527
|
program.name("drzl").description("DRZL - Drizzle Developer Toolkit").version(CLI_VERSION);
|
|
@@ -276,9 +548,9 @@ program.command("analyze").argument("<schema>", "path to drizzle schema (TS)").o
|
|
|
276
548
|
} else if (opts.out) {
|
|
277
549
|
const fs2 = await import("fs/promises");
|
|
278
550
|
await fs2.writeFile(opts.out, json, "utf8");
|
|
279
|
-
spinner?.succeed(
|
|
551
|
+
spinner?.succeed(chalk3.green(`Analysis written to ${opts.out} in ${ms}ms`));
|
|
280
552
|
} else {
|
|
281
|
-
spinner?.succeed(
|
|
553
|
+
spinner?.succeed(chalk3.green(`Analyzed in ${ms}ms`));
|
|
282
554
|
console.log(json);
|
|
283
555
|
}
|
|
284
556
|
process.exit(res.issues.some((i) => i.level === "error") ? 2 : 0);
|
|
@@ -288,7 +560,48 @@ program.command("analyze").argument("<schema>", "path to drizzle schema (TS)").o
|
|
|
288
560
|
console.log(JSON.stringify({ event: "error", code: "DRZL_CLI_ANALYZE", message: msg }));
|
|
289
561
|
else
|
|
290
562
|
console.error(
|
|
291
|
-
|
|
563
|
+
chalk3.red("Analyze failed (DRZL_CLI_ANALYZE):"),
|
|
564
|
+
msg,
|
|
565
|
+
"\nTip: run with --json for structured output."
|
|
566
|
+
);
|
|
567
|
+
process.exit(1);
|
|
568
|
+
}
|
|
569
|
+
});
|
|
570
|
+
program.command("doctor").description("Report what DRZL cannot type or enforce in your schema, and why").argument("[schema]", "path to drizzle schema (TS); defaults to the schema in drzl.config").option("-c, --config <path>", "path to drzl.config, read when no schema argument is given").option("--json", "print the report as JSON instead of prose", false).option("--strict", "exit 2 when anything is reported", false).action(async (schema, opts) => {
|
|
571
|
+
try {
|
|
572
|
+
let target = schema;
|
|
573
|
+
if (!target) {
|
|
574
|
+
const cfg = await loadConfig(opts.config);
|
|
575
|
+
target = cfg?.schema;
|
|
576
|
+
}
|
|
577
|
+
if (!target) {
|
|
578
|
+
const msg = "No schema given. Pass a path, or run from a directory with a drzl.config.";
|
|
579
|
+
if (opts.json)
|
|
580
|
+
console.log(JSON.stringify({ event: "error", code: "DRZL_CLI_DOCTOR", message: msg }));
|
|
581
|
+
else console.error(chalk3.red("Doctor failed (DRZL_CLI_DOCTOR):"), msg);
|
|
582
|
+
process.exit(1);
|
|
583
|
+
return;
|
|
584
|
+
}
|
|
585
|
+
const analyzer = new SchemaAnalyzer(target);
|
|
586
|
+
const analysis = await analyzer.analyze({
|
|
587
|
+
includeRelations: true,
|
|
588
|
+
validateConstraints: true
|
|
589
|
+
});
|
|
590
|
+
const report = buildDoctorReport(analysis, target);
|
|
591
|
+
if (opts.json) console.log(JSON.stringify(report, null, 2));
|
|
592
|
+
else console.log(renderDoctorReport(report));
|
|
593
|
+
if (report.findings.some((f) => f.level === "error")) {
|
|
594
|
+
process.exit(1);
|
|
595
|
+
return;
|
|
596
|
+
}
|
|
597
|
+
process.exit(opts.strict && report.findings.length ? 2 : 0);
|
|
598
|
+
} catch (e) {
|
|
599
|
+
const msg = e?.message ?? String(e);
|
|
600
|
+
if (opts.json)
|
|
601
|
+
console.log(JSON.stringify({ event: "error", code: "DRZL_CLI_DOCTOR", message: msg }));
|
|
602
|
+
else
|
|
603
|
+
console.error(
|
|
604
|
+
chalk3.red("Doctor failed (DRZL_CLI_DOCTOR):"),
|
|
292
605
|
msg,
|
|
293
606
|
"\nTip: run with --json for structured output."
|
|
294
607
|
);
|
|
@@ -303,7 +616,7 @@ program.command("generate").description("Run configured generators (drzl.config.
|
|
|
303
616
|
const cfg = await loadConfig(opts.config);
|
|
304
617
|
if (!cfg) {
|
|
305
618
|
console.error(
|
|
306
|
-
|
|
619
|
+
chalk3.red("No config found (DRZL_CFG_001). Create drzl.config.ts or pass --config.")
|
|
307
620
|
);
|
|
308
621
|
process.exit(2);
|
|
309
622
|
return;
|
|
@@ -348,8 +661,8 @@ program.command("generate").description("Run configured generators (drzl.config.
|
|
|
348
661
|
onProgress: ({ index }) => progress.update(index)
|
|
349
662
|
});
|
|
350
663
|
progress.stop();
|
|
351
|
-
ora().succeed(
|
|
352
|
-
files.forEach((f) => console.log(" -",
|
|
664
|
+
ora().succeed(chalk3.green(`Generated (${g.kind}): ${files.length} files`));
|
|
665
|
+
files.forEach((f) => console.log(" -", chalk3.cyan(f)));
|
|
353
666
|
} else if (g.kind === "trpc") {
|
|
354
667
|
try {
|
|
355
668
|
const { TRPCGenerator } = await loadGenerator(
|
|
@@ -362,8 +675,8 @@ program.command("generate").description("Run configured generators (drzl.config.
|
|
|
362
675
|
onProgress: ({ index }) => progress.update(index)
|
|
363
676
|
});
|
|
364
677
|
progress.stop();
|
|
365
|
-
ora().succeed(
|
|
366
|
-
files.forEach((f) => console.log(" -",
|
|
678
|
+
ora().succeed(chalk3.green(`Generated (trpc): ${files.length} files`));
|
|
679
|
+
files.forEach((f) => console.log(" -", chalk3.cyan(f)));
|
|
367
680
|
} catch (e) {
|
|
368
681
|
progress.stop();
|
|
369
682
|
reportGeneratorFailure(g.kind, e);
|
|
@@ -392,8 +705,8 @@ program.command("generate").description("Run configured generators (drzl.config.
|
|
|
392
705
|
databaseInjection: g.databaseInjection
|
|
393
706
|
});
|
|
394
707
|
progress.stop();
|
|
395
|
-
ora().succeed(
|
|
396
|
-
files.forEach((f) => console.log(" -",
|
|
708
|
+
ora().succeed(chalk3.green(`Generated (service): ${files.length} files`));
|
|
709
|
+
files.forEach((f) => console.log(" -", chalk3.cyan(f)));
|
|
397
710
|
} catch (e) {
|
|
398
711
|
progress.stop();
|
|
399
712
|
reportGeneratorFailure(g.kind, e);
|
|
@@ -411,8 +724,8 @@ program.command("generate").description("Run configured generators (drzl.config.
|
|
|
411
724
|
validationOptions(g, cfg, target, { schemaTypes: true })
|
|
412
725
|
);
|
|
413
726
|
progress.stop();
|
|
414
|
-
ora().succeed(
|
|
415
|
-
files.forEach((f) => console.log(" -",
|
|
727
|
+
ora().succeed(chalk3.green(`Generated (zod): ${files.length} files`));
|
|
728
|
+
files.forEach((f) => console.log(" -", chalk3.cyan(f)));
|
|
416
729
|
} catch (e) {
|
|
417
730
|
progress.stop();
|
|
418
731
|
reportGeneratorFailure(g.kind, e);
|
|
@@ -430,8 +743,8 @@ program.command("generate").description("Run configured generators (drzl.config.
|
|
|
430
743
|
validationOptions(g, cfg, target, { schemaTypes: true })
|
|
431
744
|
);
|
|
432
745
|
progress.stop();
|
|
433
|
-
ora().succeed(
|
|
434
|
-
files.forEach((f) => console.log(" -",
|
|
746
|
+
ora().succeed(chalk3.green(`Generated (valibot): ${files.length} files`));
|
|
747
|
+
files.forEach((f) => console.log(" -", chalk3.cyan(f)));
|
|
435
748
|
} catch (e) {
|
|
436
749
|
progress.stop();
|
|
437
750
|
reportGeneratorFailure(g.kind, e);
|
|
@@ -449,8 +762,8 @@ program.command("generate").description("Run configured generators (drzl.config.
|
|
|
449
762
|
validationOptions(g, cfg, target, { schemaTypes: false })
|
|
450
763
|
);
|
|
451
764
|
progress.stop();
|
|
452
|
-
ora().succeed(
|
|
453
|
-
files.forEach((f) => console.log(" -",
|
|
765
|
+
ora().succeed(chalk3.green(`Generated (arktype): ${files.length} files`));
|
|
766
|
+
files.forEach((f) => console.log(" -", chalk3.cyan(f)));
|
|
454
767
|
} catch (e) {
|
|
455
768
|
progress.stop();
|
|
456
769
|
reportGeneratorFailure(g.kind, e);
|
|
@@ -460,19 +773,14 @@ program.command("generate").description("Run configured generators (drzl.config.
|
|
|
460
773
|
try {
|
|
461
774
|
const { JsonSchemaGenerator } = await loadGenerator(
|
|
462
775
|
"@drzl/generator-json-schema",
|
|
463
|
-
() => import("./dist-
|
|
776
|
+
() => import("./dist-TRJLPIWT.js")
|
|
464
777
|
);
|
|
465
778
|
const gen = new JsonSchemaGenerator(analysis);
|
|
466
779
|
const target = g.path ?? "src/validators/json-schema";
|
|
467
|
-
const files = await gen.generate(
|
|
468
|
-
// JSON Schema is data, so nothing here references a type from the schema module.
|
|
469
|
-
...validationOptions(g, cfg, target, { schemaTypes: false }),
|
|
470
|
-
target: g.target,
|
|
471
|
-
components: g.components
|
|
472
|
-
});
|
|
780
|
+
const files = await gen.generate(jsonSchemaOptions(g, cfg, target));
|
|
473
781
|
progress.stop();
|
|
474
|
-
ora().succeed(
|
|
475
|
-
files.forEach((f) => console.log(" -",
|
|
782
|
+
ora().succeed(chalk3.green(`Generated (json-schema): ${files.length} files`));
|
|
783
|
+
files.forEach((f) => console.log(" -", chalk3.cyan(f)));
|
|
476
784
|
} catch (e) {
|
|
477
785
|
progress.stop();
|
|
478
786
|
reportGeneratorFailure(g.kind, e);
|
|
@@ -490,8 +798,8 @@ program.command("generate").description("Run configured generators (drzl.config.
|
|
|
490
798
|
validationOptions(g, cfg, target, { schemaTypes: true })
|
|
491
799
|
);
|
|
492
800
|
progress.stop();
|
|
493
|
-
ora().succeed(
|
|
494
|
-
files.forEach((f) => console.log(" -",
|
|
801
|
+
ora().succeed(chalk3.green(`Generated (typebox): ${files.length} files`));
|
|
802
|
+
files.forEach((f) => console.log(" -", chalk3.cyan(f)));
|
|
495
803
|
} catch (e) {
|
|
496
804
|
progress.stop();
|
|
497
805
|
reportGeneratorFailure(g.kind, e);
|
|
@@ -504,22 +812,22 @@ program.command("generate").description("Run configured generators (drzl.config.
|
|
|
504
812
|
const drift = diffSnapshots(driftBefore, after);
|
|
505
813
|
await restoreSnapshot(driftBefore, after);
|
|
506
814
|
if (drift.length) {
|
|
507
|
-
console.error(
|
|
815
|
+
console.error(chalk3.red(`
|
|
508
816
|
Generated output is out of date (${drift.length} file(s)):`));
|
|
509
817
|
for (const d of drift) {
|
|
510
818
|
const mark = d.status === "added" ? "+" : d.status === "removed" ? "-" : "~";
|
|
511
819
|
console.error(
|
|
512
|
-
` ${mark} ${
|
|
820
|
+
` ${mark} ${chalk3.yellow(d.status.padEnd(8))} ${path4.relative(process.cwd(), d.file)}`
|
|
513
821
|
);
|
|
514
822
|
}
|
|
515
823
|
console.error(
|
|
516
|
-
|
|
824
|
+
chalk3.dim(
|
|
517
825
|
"\nRun `drzl generate` and commit the result. Nothing was written by this check."
|
|
518
826
|
)
|
|
519
827
|
);
|
|
520
828
|
process.exit(1);
|
|
521
829
|
}
|
|
522
|
-
console.log(
|
|
830
|
+
console.log(chalk3.green("Generated output is up to date."));
|
|
523
831
|
return;
|
|
524
832
|
}
|
|
525
833
|
if (cfg.generators.length) {
|
|
@@ -527,7 +835,7 @@ Generated output is out of date (${drift.length} file(s)):`));
|
|
|
527
835
|
}
|
|
528
836
|
} catch (e) {
|
|
529
837
|
console.error(
|
|
530
|
-
|
|
838
|
+
chalk3.red("Generate failed (DRZL_GEN_001):"),
|
|
531
839
|
e?.message ?? e,
|
|
532
840
|
"\nTip: check your drzl.config.ts and template path."
|
|
533
841
|
);
|
|
@@ -547,10 +855,10 @@ program.command("generate:orpc").argument("<schema>", "path to drizzle schema (T
|
|
|
547
855
|
template: opts.template,
|
|
548
856
|
includeRelations: !!opts.includeRelations
|
|
549
857
|
});
|
|
550
|
-
console.log(
|
|
858
|
+
console.log(chalk3.green(`Generated:`), files.map((f) => chalk3.cyan(f)).join(", "));
|
|
551
859
|
maybeShowSponsorMessage({ reason: "generate:orpc" });
|
|
552
860
|
} catch (e) {
|
|
553
|
-
console.error(
|
|
861
|
+
console.error(chalk3.red("Generate orpc failed:"), e?.message ?? e);
|
|
554
862
|
process.exit(1);
|
|
555
863
|
}
|
|
556
864
|
});
|
|
@@ -574,7 +882,7 @@ program.command("generate:trpc").argument("<schema>", "path to drizzle schema (T
|
|
|
574
882
|
// cannot become the branch that forgets it.
|
|
575
883
|
servicesDir: opts.servicesDir
|
|
576
884
|
});
|
|
577
|
-
console.log(
|
|
885
|
+
console.log(chalk3.green(`Generated:`), files.map((f) => chalk3.cyan(f)).join(", "));
|
|
578
886
|
maybeShowSponsorMessage({ reason: "generate:trpc" });
|
|
579
887
|
} catch (e) {
|
|
580
888
|
reportGeneratorFailure("trpc", e);
|
|
@@ -584,7 +892,7 @@ program.command("generate:trpc").argument("<schema>", "path to drizzle schema (T
|
|
|
584
892
|
program.command("watch").description("Watch schema and regenerate on changes").option("-c, --config <path>", "path to drzl.config").option("--pipeline <name>", "all | analyze | generate-orpc | generate-trpc", "all").option("--debounce <ms>", "debounce ms", "200").option("--json", "emit JSON logs", false).option("--poll", "force polling (helps WSL/Docker/remote FS)", false).action(async (opts) => {
|
|
585
893
|
let cfg = await loadConfig(opts.config);
|
|
586
894
|
if (!cfg) {
|
|
587
|
-
console.error(
|
|
895
|
+
console.error(chalk3.red("No config found. Create drzl.config.ts or pass --config."));
|
|
588
896
|
process.exit(2);
|
|
589
897
|
return;
|
|
590
898
|
}
|
|
@@ -676,7 +984,7 @@ program.command("watch").description("Watch schema and regenerate on changes").o
|
|
|
676
984
|
})
|
|
677
985
|
);
|
|
678
986
|
} else {
|
|
679
|
-
console.log(
|
|
987
|
+
console.log(chalk3.green("Analyze complete."));
|
|
680
988
|
}
|
|
681
989
|
return;
|
|
682
990
|
}
|
|
@@ -706,8 +1014,8 @@ program.command("watch").description("Watch schema and regenerate on changes").o
|
|
|
706
1014
|
servicesDir
|
|
707
1015
|
});
|
|
708
1016
|
opts.json ? console.log(JSON.stringify({ event: "generate_complete", kind: g.kind, files })) : console.log(
|
|
709
|
-
|
|
710
|
-
files.map((f) =>
|
|
1017
|
+
chalk3.green(`Generated (${g.kind}):`),
|
|
1018
|
+
files.map((f) => chalk3.cyan(f)).join(", ")
|
|
711
1019
|
);
|
|
712
1020
|
newFiles.push(...files);
|
|
713
1021
|
} else if (g.kind === "trpc") {
|
|
@@ -719,8 +1027,8 @@ program.command("watch").description("Watch schema and regenerate on changes").o
|
|
|
719
1027
|
const gen = new TRPCGenerator(analysis);
|
|
720
1028
|
const { files } = await gen.generate(trpcOptions(g, cfg, servicesDir));
|
|
721
1029
|
opts.json ? console.log(JSON.stringify({ event: "generate_complete", kind: g.kind, files })) : console.log(
|
|
722
|
-
|
|
723
|
-
files.map((f) =>
|
|
1030
|
+
chalk3.green(`Generated (trpc): ${files.length} files`),
|
|
1031
|
+
files.map((f) => chalk3.cyan(f)).join(", ")
|
|
724
1032
|
);
|
|
725
1033
|
newFiles.push(...files);
|
|
726
1034
|
} catch (e) {
|
|
@@ -746,8 +1054,8 @@ program.command("watch").description("Watch schema and regenerate on changes").o
|
|
|
746
1054
|
databaseInjection: g.databaseInjection
|
|
747
1055
|
});
|
|
748
1056
|
opts.json ? console.log(JSON.stringify({ event: "generate_complete", kind: g.kind, files })) : console.log(
|
|
749
|
-
|
|
750
|
-
files.map((f) =>
|
|
1057
|
+
chalk3.green(`Generated (service): ${files.length} files`),
|
|
1058
|
+
files.map((f) => chalk3.cyan(f)).join(", ")
|
|
751
1059
|
);
|
|
752
1060
|
newFiles.push(...files);
|
|
753
1061
|
} catch (e) {
|
|
@@ -766,8 +1074,8 @@ program.command("watch").description("Watch schema and regenerate on changes").o
|
|
|
766
1074
|
validationOptions(g, cfg, target, { schemaTypes: true })
|
|
767
1075
|
);
|
|
768
1076
|
opts.json ? console.log(JSON.stringify({ event: "generate_complete", kind: g.kind, files })) : console.log(
|
|
769
|
-
|
|
770
|
-
files.map((f) =>
|
|
1077
|
+
chalk3.green(`Generated (zod): ${files.length} files`),
|
|
1078
|
+
files.map((f) => chalk3.cyan(f)).join(", ")
|
|
771
1079
|
);
|
|
772
1080
|
newFiles.push(...files);
|
|
773
1081
|
} catch (e) {
|
|
@@ -786,8 +1094,8 @@ program.command("watch").description("Watch schema and regenerate on changes").o
|
|
|
786
1094
|
validationOptions(g, cfg, target, { schemaTypes: true })
|
|
787
1095
|
);
|
|
788
1096
|
opts.json ? console.log(JSON.stringify({ event: "generate_complete", kind: g.kind, files })) : console.log(
|
|
789
|
-
|
|
790
|
-
files.map((f) =>
|
|
1097
|
+
chalk3.green(`Generated (valibot): ${files.length} files`),
|
|
1098
|
+
files.map((f) => chalk3.cyan(f)).join(", ")
|
|
791
1099
|
);
|
|
792
1100
|
newFiles.push(...files);
|
|
793
1101
|
} catch (e) {
|
|
@@ -806,8 +1114,8 @@ program.command("watch").description("Watch schema and regenerate on changes").o
|
|
|
806
1114
|
validationOptions(g, cfg, target, { schemaTypes: false })
|
|
807
1115
|
);
|
|
808
1116
|
opts.json ? console.log(JSON.stringify({ event: "generate_complete", kind: g.kind, files })) : console.log(
|
|
809
|
-
|
|
810
|
-
files.map((f) =>
|
|
1117
|
+
chalk3.green(`Generated (arktype): ${files.length} files`),
|
|
1118
|
+
files.map((f) => chalk3.cyan(f)).join(", ")
|
|
811
1119
|
);
|
|
812
1120
|
newFiles.push(...files);
|
|
813
1121
|
} catch (e) {
|
|
@@ -826,8 +1134,8 @@ program.command("watch").description("Watch schema and regenerate on changes").o
|
|
|
826
1134
|
validationOptions(g, cfg, target, { schemaTypes: true })
|
|
827
1135
|
);
|
|
828
1136
|
opts.json ? console.log(JSON.stringify({ event: "generate_complete", kind: g.kind, files })) : console.log(
|
|
829
|
-
|
|
830
|
-
files.map((f) =>
|
|
1137
|
+
chalk3.green(`Generated (typebox): ${files.length} files`),
|
|
1138
|
+
files.map((f) => chalk3.cyan(f)).join(", ")
|
|
831
1139
|
);
|
|
832
1140
|
newFiles.push(...files);
|
|
833
1141
|
} catch (e) {
|
|
@@ -838,19 +1146,14 @@ program.command("watch").description("Watch schema and regenerate on changes").o
|
|
|
838
1146
|
try {
|
|
839
1147
|
const { JsonSchemaGenerator } = await loadGenerator(
|
|
840
1148
|
"@drzl/generator-json-schema",
|
|
841
|
-
() => import("./dist-
|
|
1149
|
+
() => import("./dist-TRJLPIWT.js")
|
|
842
1150
|
);
|
|
843
1151
|
const gen = new JsonSchemaGenerator(analysis);
|
|
844
1152
|
const target = g.path ?? "src/validators/json-schema";
|
|
845
|
-
const files = await gen.generate(
|
|
846
|
-
// JSON Schema is data, so nothing here references a type from the schema module.
|
|
847
|
-
...validationOptions(g, cfg, target, { schemaTypes: false }),
|
|
848
|
-
target: g.target,
|
|
849
|
-
components: g.components
|
|
850
|
-
});
|
|
1153
|
+
const files = await gen.generate(jsonSchemaOptions(g, cfg, target));
|
|
851
1154
|
opts.json ? console.log(JSON.stringify({ event: "generate_complete", kind: g.kind, files })) : console.log(
|
|
852
|
-
|
|
853
|
-
files.map((f) =>
|
|
1155
|
+
chalk3.green(`Generated (json-schema): ${files.length} files`),
|
|
1156
|
+
files.map((f) => chalk3.cyan(f)).join(", ")
|
|
854
1157
|
);
|
|
855
1158
|
newFiles.push(...files);
|
|
856
1159
|
} catch (e) {
|
|
@@ -862,8 +1165,8 @@ program.command("watch").description("Watch schema and regenerate on changes").o
|
|
|
862
1165
|
const added = newFiles.filter((f) => !lastFiles.includes(f));
|
|
863
1166
|
const removed = lastFiles.filter((f) => !newFiles.includes(f));
|
|
864
1167
|
opts.json ? console.log(JSON.stringify({ event: "diff", added, removed })) : (() => {
|
|
865
|
-
if (added.length) console.log(
|
|
866
|
-
if (removed.length) console.log(
|
|
1168
|
+
if (added.length) console.log(chalk3.blue(`Added: ${added.join(", ")}`));
|
|
1169
|
+
if (removed.length) console.log(chalk3.yellow(`Removed: ${removed.join(", ")}`));
|
|
867
1170
|
})();
|
|
868
1171
|
if (newFiles.length && !opts.json) {
|
|
869
1172
|
const reason = opts.pipeline && opts.pipeline !== "all" ? `watch:${opts.pipeline}` : "watch";
|
|
@@ -871,7 +1174,7 @@ program.command("watch").description("Watch schema and regenerate on changes").o
|
|
|
871
1174
|
}
|
|
872
1175
|
lastFiles = newFiles;
|
|
873
1176
|
} catch (e) {
|
|
874
|
-
opts.json ? console.log(JSON.stringify({ event: "error", message: String(e?.message ?? e) })) : console.error(
|
|
1177
|
+
opts.json ? console.log(JSON.stringify({ event: "error", message: String(e?.message ?? e) })) : console.error(chalk3.red("Watch pipeline failed:"), e?.message ?? e);
|
|
875
1178
|
}
|
|
876
1179
|
};
|
|
877
1180
|
const debounced = Number(opts.debounce) || 200;
|
|
@@ -896,12 +1199,12 @@ program.command("watch").description("Watch schema and regenerate on changes").o
|
|
|
896
1199
|
);
|
|
897
1200
|
} else {
|
|
898
1201
|
console.log(
|
|
899
|
-
|
|
1202
|
+
chalk3.gray(
|
|
900
1203
|
"Watching:\n " + Array.from(currentTargets).map((p) => path4.relative(process.cwd(), p)).join("\n ")
|
|
901
1204
|
)
|
|
902
1205
|
);
|
|
903
1206
|
}
|
|
904
|
-
watcher.on("add", (p) => trigger(p)).on("change", (p) => trigger(p)).on("unlink", (p) => trigger(p)).on("error", (err) => console.error(
|
|
1207
|
+
watcher.on("add", (p) => trigger(p)).on("change", (p) => trigger(p)).on("unlink", (p) => trigger(p)).on("error", (err) => console.error(chalk3.red("Watcher error:"), err));
|
|
905
1208
|
await run();
|
|
906
1209
|
});
|
|
907
1210
|
program.command("init").description("Scaffold a drzl.config.ts").option("-y, --yes", "accept defaults").action(async (_opts) => {
|
|
@@ -921,9 +1224,9 @@ program.command("init").description("Scaffold a drzl.config.ts").option("-y, --y
|
|
|
921
1224
|
`;
|
|
922
1225
|
try {
|
|
923
1226
|
await fs2.writeFile(target, template, { flag: "wx" });
|
|
924
|
-
console.log(
|
|
1227
|
+
console.log(chalk3.green(`Created ${target}`));
|
|
925
1228
|
} catch (e) {
|
|
926
|
-
console.error(
|
|
1229
|
+
console.error(chalk3.red("Init failed:"), e?.message ?? e);
|
|
927
1230
|
process.exit(1);
|
|
928
1231
|
}
|
|
929
1232
|
});
|
|
@@ -931,13 +1234,14 @@ function reportWideColumns(issues) {
|
|
|
931
1234
|
const wide = issues.filter((i) => i.code === "DRZL_ANL_UNKNOWN_COLUMN");
|
|
932
1235
|
if (!wide.length) return;
|
|
933
1236
|
console.warn(
|
|
934
|
-
|
|
1237
|
+
chalk3.yellow(`
|
|
935
1238
|
${wide.length} column${wide.length === 1 ? "" : "s"} could not be typed:`)
|
|
936
1239
|
);
|
|
937
|
-
for (const i of wide.slice(0, 10)) console.warn(
|
|
938
|
-
if (wide.length > 10) console.warn(
|
|
1240
|
+
for (const i of wide.slice(0, 10)) console.warn(chalk3.gray(` - ${i.message}`));
|
|
1241
|
+
if (wide.length > 10) console.warn(chalk3.gray(` ... and ${wide.length - 10} more`));
|
|
939
1242
|
const hints = [...new Set(wide.map((i) => i.hint).filter(Boolean))];
|
|
940
|
-
for (const h of hints) console.warn(
|
|
1243
|
+
for (const h of hints) console.warn(chalk3.gray(` ${h}`));
|
|
1244
|
+
console.warn(chalk3.gray(" Run `drzl doctor` for the full report."));
|
|
941
1245
|
}
|
|
942
1246
|
program.parseAsync(process.argv);
|
|
943
1247
|
//# sourceMappingURL=cli.js.map
|