@drzl/cli 4.37.0 → 4.39.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-EQ4OSVMJ.js → chunk-VG6SD2GP.js} +7 -1
- package/dist/chunk-VG6SD2GP.js.map +1 -0
- package/dist/cli.cjs +278 -43
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +268 -37
- package/dist/cli.js.map +1 -1
- package/dist/config.cjs +7 -0
- package/dist/config.cjs.map +1 -1
- package/dist/config.d.cts +16 -1
- package/dist/config.d.ts +16 -1
- package/dist/config.js +3 -1
- package/dist/drzl.config.schema.json +1 -0
- package/package.json +4 -3
- package/dist/chunk-EQ4OSVMJ.js.map +0 -1
package/dist/cli.cjs
CHANGED
|
@@ -28,7 +28,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
28
28
|
var import_analyzer3 = require("@drzl/analyzer");
|
|
29
29
|
var import_chokidar = __toESM(require("chokidar"), 1);
|
|
30
30
|
var import_commander = require("commander");
|
|
31
|
-
var
|
|
31
|
+
var path9 = __toESM(require("path"), 1);
|
|
32
32
|
|
|
33
33
|
// src/output.ts
|
|
34
34
|
var import_chalk = require("chalk");
|
|
@@ -519,6 +519,7 @@ var GeneratorKindSchema = import_zod.z.enum([
|
|
|
519
519
|
"forms",
|
|
520
520
|
"seed",
|
|
521
521
|
"fast-check",
|
|
522
|
+
"pothos",
|
|
522
523
|
"service",
|
|
523
524
|
"zod",
|
|
524
525
|
"valibot",
|
|
@@ -1098,6 +1099,9 @@ function seedOutDir(g, cfg) {
|
|
|
1098
1099
|
function fastCheckOutDir(g, cfg) {
|
|
1099
1100
|
return g.path ?? cfg.outDir;
|
|
1100
1101
|
}
|
|
1102
|
+
function pothosOutDir(g, cfg) {
|
|
1103
|
+
return g.path ?? cfg.outDir;
|
|
1104
|
+
}
|
|
1101
1105
|
function sharedSchemaNames(opts) {
|
|
1102
1106
|
const resolved = (0, import_validation_core.resolveAffix)(opts);
|
|
1103
1107
|
return import_validation_core.NAME_MODES.map((mode) => (0, import_validation_core.schemaName)(mode, import_validation_core.AFFIX_PROBE_TABLE, resolved));
|
|
@@ -1418,6 +1422,7 @@ function computeGeneratorOutputDirs(cfg, cwd = process.cwd()) {
|
|
|
1418
1422
|
if (g.kind === "forms") dirs.add(abs(formsOutDir(g, cfg)));
|
|
1419
1423
|
if (g.kind === "seed") dirs.add(abs(seedOutDir(g, cfg)));
|
|
1420
1424
|
if (g.kind === "fast-check") dirs.add(abs(fastCheckOutDir(g, cfg)));
|
|
1425
|
+
if (g.kind === "pothos") dirs.add(abs(pothosOutDir(g, cfg)));
|
|
1421
1426
|
if (g.kind === "service") dirs.add(abs(g.path ?? "src/services"));
|
|
1422
1427
|
if (g.kind === "zod") dirs.add(abs(g.path ?? "src/validators/zod"));
|
|
1423
1428
|
if (g.kind === "valibot") dirs.add(abs(g.path ?? "src/validators/valibot"));
|
|
@@ -1785,6 +1790,17 @@ function fastCheckOptions(g, cfg) {
|
|
|
1785
1790
|
};
|
|
1786
1791
|
}
|
|
1787
1792
|
|
|
1793
|
+
// src/pothos-options.ts
|
|
1794
|
+
function pothosOptions(g, cfg) {
|
|
1795
|
+
return {
|
|
1796
|
+
outputDir: pothosOutDir(g, cfg),
|
|
1797
|
+
naming: g.naming,
|
|
1798
|
+
outputHeader: g.outputHeader,
|
|
1799
|
+
format: g.format,
|
|
1800
|
+
importExtension: g.importExtension
|
|
1801
|
+
};
|
|
1802
|
+
}
|
|
1803
|
+
|
|
1788
1804
|
// src/h3-options.ts
|
|
1789
1805
|
var VALIDATOR_DEFAULT_DIRS6 = {
|
|
1790
1806
|
zod: "src/validators/zod",
|
|
@@ -2147,6 +2163,14 @@ var GENERATORS = [
|
|
|
2147
2163
|
outputDir: (g, cfg) => fastCheckOutDir(g, cfg),
|
|
2148
2164
|
options: (g, cfg) => fastCheckOptions(g, cfg)
|
|
2149
2165
|
},
|
|
2166
|
+
{
|
|
2167
|
+
kind: "pothos",
|
|
2168
|
+
specifier: "@drzl/generator-pothos",
|
|
2169
|
+
load: () => import("@drzl/generator-pothos"),
|
|
2170
|
+
construct: (m, analysis) => new m.PothosGenerator(analysis),
|
|
2171
|
+
outputDir: (g, cfg) => pothosOutDir(g, cfg),
|
|
2172
|
+
options: (g, cfg) => pothosOptions(g, cfg)
|
|
2173
|
+
},
|
|
2150
2174
|
{
|
|
2151
2175
|
kind: "service",
|
|
2152
2176
|
specifier: "@drzl/generator-service",
|
|
@@ -2390,14 +2414,109 @@ var import_validation_core3 = require("@drzl/validation-core");
|
|
|
2390
2414
|
|
|
2391
2415
|
// src/doctor.ts
|
|
2392
2416
|
var import_validation_core2 = require("@drzl/validation-core");
|
|
2417
|
+
|
|
2418
|
+
// src/auth-tables.ts
|
|
2419
|
+
var SIGNATURES = [
|
|
2420
|
+
{
|
|
2421
|
+
model: "account",
|
|
2422
|
+
// `providerId` beside `accountId` is the distinctive pair: it is a link to an external identity
|
|
2423
|
+
// provider, which an application table has no reason to model this way.
|
|
2424
|
+
required: ["accountId", "providerId", "userId"],
|
|
2425
|
+
supporting: ["accessToken", "refreshToken", "idToken", "password", "scope"],
|
|
2426
|
+
conventionalName: "account",
|
|
2427
|
+
secrets: ["accessToken", "refreshToken", "idToken", "password"]
|
|
2428
|
+
},
|
|
2429
|
+
{
|
|
2430
|
+
model: "session",
|
|
2431
|
+
required: ["token", "expiresAt", "userId"],
|
|
2432
|
+
supporting: ["ipAddress", "userAgent"],
|
|
2433
|
+
conventionalName: "session",
|
|
2434
|
+
// A session token is a bearer credential: whoever reads it is that user until it expires.
|
|
2435
|
+
secrets: ["token"]
|
|
2436
|
+
},
|
|
2437
|
+
{
|
|
2438
|
+
model: "verification",
|
|
2439
|
+
// `identifier` and `value` are deliberately generic names, which is why `expiresAt` is required
|
|
2440
|
+
// too: the three together are a short-lived token store and little else.
|
|
2441
|
+
required: ["identifier", "value", "expiresAt"],
|
|
2442
|
+
supporting: [],
|
|
2443
|
+
conventionalName: "verification",
|
|
2444
|
+
secrets: ["value"]
|
|
2445
|
+
},
|
|
2446
|
+
{
|
|
2447
|
+
model: "user",
|
|
2448
|
+
required: ["email", "emailVerified"],
|
|
2449
|
+
supporting: ["name", "image"],
|
|
2450
|
+
conventionalName: "user",
|
|
2451
|
+
secrets: []
|
|
2452
|
+
}
|
|
2453
|
+
];
|
|
2454
|
+
function columnKeys(table) {
|
|
2455
|
+
const out = /* @__PURE__ */ new Map();
|
|
2456
|
+
for (const c of table.columns) out.set(normalise(c.name), c);
|
|
2457
|
+
return out;
|
|
2458
|
+
}
|
|
2459
|
+
function normalise(name) {
|
|
2460
|
+
return name.replace(/[_-]/g, "").toLowerCase();
|
|
2461
|
+
}
|
|
2462
|
+
function has(keys, name) {
|
|
2463
|
+
return keys.has(normalise(name));
|
|
2464
|
+
}
|
|
2465
|
+
function actualName(table, wanted) {
|
|
2466
|
+
return table.columns.find((c) => normalise(c.name) === normalise(wanted))?.name;
|
|
2467
|
+
}
|
|
2468
|
+
function matchOne(table, sig) {
|
|
2469
|
+
const keys = columnKeys(table);
|
|
2470
|
+
const missing = sig.required.filter((r) => !has(keys, r));
|
|
2471
|
+
if (missing.length) return void 0;
|
|
2472
|
+
const supporting = sig.supporting.filter((s) => has(keys, s));
|
|
2473
|
+
const nameMatches = normalise(table.name) === normalise(sig.conventionalName) || normalise(table.name) === `${normalise(sig.conventionalName)}s`;
|
|
2474
|
+
const confidence = supporting.length > 0 || nameMatches ? "strong" : "likely";
|
|
2475
|
+
const matched = [...sig.required, ...supporting].map((c) => actualName(table, c)).filter((c) => Boolean(c));
|
|
2476
|
+
const secrets = sig.secrets.map((c) => actualName(table, c)).filter((c) => Boolean(c));
|
|
2477
|
+
return { table: table.name, model: sig.model, confidence, matched, secrets };
|
|
2478
|
+
}
|
|
2479
|
+
function detectAuthTables(analysis) {
|
|
2480
|
+
const found = [];
|
|
2481
|
+
for (const table of analysis.tables) {
|
|
2482
|
+
for (const sig of SIGNATURES) {
|
|
2483
|
+
const m = matchOne(table, sig);
|
|
2484
|
+
if (m) {
|
|
2485
|
+
found.push(m);
|
|
2486
|
+
break;
|
|
2487
|
+
}
|
|
2488
|
+
}
|
|
2489
|
+
}
|
|
2490
|
+
const hasNonUser = found.some((f) => f.model !== "user");
|
|
2491
|
+
return hasNonUser ? found : found.filter((f) => f.model !== "user");
|
|
2492
|
+
}
|
|
2493
|
+
function authTablesWithSecrets(matches) {
|
|
2494
|
+
return matches.filter((m) => m.secrets.length > 0);
|
|
2495
|
+
}
|
|
2496
|
+
function excludeSuggestion(matches) {
|
|
2497
|
+
const names = [...new Set(matches.map((m) => m.table))].sort();
|
|
2498
|
+
return `exclude: [${names.map((n) => `'${n}'`).join(", ")}]`;
|
|
2499
|
+
}
|
|
2500
|
+
function authTableWarnings(analysis) {
|
|
2501
|
+
const risky = authTablesWithSecrets(detectAuthTables(analysis));
|
|
2502
|
+
if (!risky.length) return [];
|
|
2503
|
+
const lines = risky.map(
|
|
2504
|
+
(m) => `"${m.table}" looks like an authentication library's ${m.model} table and holds ${m.secrets.map((c) => `"${c}"`).join(", ")}. Generating for it publishes ${m.secrets.length === 1 ? "that column" : "those columns"}.`
|
|
2505
|
+
);
|
|
2506
|
+
return [
|
|
2507
|
+
`drzl generate: ${lines.join(" ")} Keep ${risky.length === 1 ? "it" : "them"} out with ${excludeSuggestion(risky)}, or leave it if the route is deliberate.`
|
|
2508
|
+
];
|
|
2509
|
+
}
|
|
2510
|
+
|
|
2511
|
+
// src/doctor.ts
|
|
2393
2512
|
var import_chalk2 = require("chalk");
|
|
2394
2513
|
var PLAIN = new import_chalk2.Chalk({ level: 0 });
|
|
2395
2514
|
var HANDLED_CODES = /* @__PURE__ */ new Set(["DRZL_ANL_UNKNOWN_COLUMN"]);
|
|
2396
|
-
function splitPath(
|
|
2397
|
-
if (!
|
|
2398
|
-
const dot =
|
|
2399
|
-
if (dot <= 0) return { table:
|
|
2400
|
-
return { table:
|
|
2515
|
+
function splitPath(path10) {
|
|
2516
|
+
if (!path10) return {};
|
|
2517
|
+
const dot = path10.lastIndexOf(".");
|
|
2518
|
+
if (dot <= 0) return { table: path10 };
|
|
2519
|
+
return { table: path10.slice(0, dot), column: path10.slice(dot + 1) };
|
|
2401
2520
|
}
|
|
2402
2521
|
function namedColumns(parsed) {
|
|
2403
2522
|
const out = [];
|
|
@@ -2562,6 +2681,18 @@ function buildDoctorReport(analysis, schemaPath) {
|
|
|
2562
2681
|
hint: i.hint
|
|
2563
2682
|
});
|
|
2564
2683
|
}
|
|
2684
|
+
for (const match of detectAuthTables(analysis)) {
|
|
2685
|
+
const secrets = match.secrets.length ? ` It holds ${match.secrets.map((c) => `\`${c}\``).join(", ")}, which a generated read route would return to whoever calls it.` : "";
|
|
2686
|
+
findings.push({
|
|
2687
|
+
kind: "auth-table",
|
|
2688
|
+
// A warning rather than an error: this is a real leak and it is also a guess about someone
|
|
2689
|
+
// else's schema, and a doctor that failed the build on a guess would be switched off.
|
|
2690
|
+
level: "warn",
|
|
2691
|
+
table: match.table,
|
|
2692
|
+
message: `"${match.table}" matches the shape of an authentication library's ${match.model} table (${match.matched.join(", ")}).${secrets}`,
|
|
2693
|
+
hint: `Keep it out of every generator with ${excludeSuggestion([match])}.`
|
|
2694
|
+
});
|
|
2695
|
+
}
|
|
2565
2696
|
for (const i of analysis.issues) {
|
|
2566
2697
|
if (i.code !== "DRZL_ANL_UNKNOWN_COLUMN") continue;
|
|
2567
2698
|
findings.push({
|
|
@@ -2600,6 +2731,11 @@ var SECTIONS = [
|
|
|
2600
2731
|
title: "Columns DRZL cannot type",
|
|
2601
2732
|
why: "These get a validator that accepts any value."
|
|
2602
2733
|
},
|
|
2734
|
+
{
|
|
2735
|
+
kinds: ["auth-table"],
|
|
2736
|
+
title: "Tables that look like an authentication library's",
|
|
2737
|
+
why: "Every generator loops over every table it finds, so these get routes too."
|
|
2738
|
+
},
|
|
2603
2739
|
{
|
|
2604
2740
|
kinds: ["check-declined", "check-unknown-column", "check-not-scalar", "check-uncountable"],
|
|
2605
2741
|
title: "CHECK constraints DRZL does not enforce",
|
|
@@ -2932,11 +3068,11 @@ function issueTouches(issue, table) {
|
|
|
2932
3068
|
return dot > 0 && own.includes(issue.path.slice(0, dot));
|
|
2933
3069
|
}
|
|
2934
3070
|
function issueColumn(issue, table) {
|
|
2935
|
-
const
|
|
3071
|
+
const path10 = issue.path ?? "";
|
|
2936
3072
|
const names = namesOf(table);
|
|
2937
3073
|
for (const prefix of [names.qualified, names.tsName, names.name, table.name]) {
|
|
2938
|
-
if (
|
|
2939
|
-
const rest =
|
|
3074
|
+
if (path10.startsWith(`${prefix}.`)) {
|
|
3075
|
+
const rest = path10.slice(prefix.length + 1);
|
|
2940
3076
|
if (table.columns.some((c) => c.name === rest)) return rest;
|
|
2941
3077
|
}
|
|
2942
3078
|
}
|
|
@@ -4095,6 +4231,79 @@ function displayPath(file, cwd = process.cwd()) {
|
|
|
4095
4231
|
return rel && !rel.startsWith("..") ? rel : file;
|
|
4096
4232
|
}
|
|
4097
4233
|
|
|
4234
|
+
// src/manifest.ts
|
|
4235
|
+
var import_node_path3 = __toESM(require("path"), 1);
|
|
4236
|
+
var import_node_fs3 = require("fs");
|
|
4237
|
+
var MANIFEST_VERSION = 1;
|
|
4238
|
+
var MANIFEST_PATH = import_node_path3.default.join(".drzl", "manifest.json");
|
|
4239
|
+
function toPosix(p) {
|
|
4240
|
+
return p.split(import_node_path3.default.sep).join("/");
|
|
4241
|
+
}
|
|
4242
|
+
function manifestEntries(files, root) {
|
|
4243
|
+
const rel = files.map((f) => toPosix(import_node_path3.default.relative(root, f)));
|
|
4244
|
+
return [...new Set(rel)].sort();
|
|
4245
|
+
}
|
|
4246
|
+
async function readManifest(root) {
|
|
4247
|
+
try {
|
|
4248
|
+
const raw = await import_node_fs3.promises.readFile(import_node_path3.default.join(root, MANIFEST_PATH), "utf8");
|
|
4249
|
+
const parsed = JSON.parse(raw);
|
|
4250
|
+
if (!parsed || typeof parsed !== "object" || parsed.version !== MANIFEST_VERSION || !Array.isArray(parsed.files) || !parsed.files.every((f) => typeof f === "string")) {
|
|
4251
|
+
return void 0;
|
|
4252
|
+
}
|
|
4253
|
+
return { version: MANIFEST_VERSION, files: parsed.files };
|
|
4254
|
+
} catch {
|
|
4255
|
+
return void 0;
|
|
4256
|
+
}
|
|
4257
|
+
}
|
|
4258
|
+
async function writeManifest(root, files) {
|
|
4259
|
+
const manifest = { version: MANIFEST_VERSION, files: manifestEntries(files, root) };
|
|
4260
|
+
const target = import_node_path3.default.join(root, MANIFEST_PATH);
|
|
4261
|
+
await import_node_fs3.promises.mkdir(import_node_path3.default.dirname(target), { recursive: true });
|
|
4262
|
+
await import_node_fs3.promises.writeFile(target, `${JSON.stringify(manifest, null, 2)}
|
|
4263
|
+
`, "utf8");
|
|
4264
|
+
}
|
|
4265
|
+
async function staleFiles(previous, writtenNow, root) {
|
|
4266
|
+
if (!previous) return [];
|
|
4267
|
+
const current = new Set(manifestEntries(writtenNow, root));
|
|
4268
|
+
const gone = previous.files.filter((f) => !current.has(f));
|
|
4269
|
+
const out = [];
|
|
4270
|
+
for (const file of gone) {
|
|
4271
|
+
let present = true;
|
|
4272
|
+
try {
|
|
4273
|
+
await import_node_fs3.promises.access(import_node_path3.default.join(root, file));
|
|
4274
|
+
} catch {
|
|
4275
|
+
present = false;
|
|
4276
|
+
}
|
|
4277
|
+
out.push({ file, present });
|
|
4278
|
+
}
|
|
4279
|
+
return out;
|
|
4280
|
+
}
|
|
4281
|
+
async function pruneStale(root, stale) {
|
|
4282
|
+
const deleted = [];
|
|
4283
|
+
for (const entry of stale) {
|
|
4284
|
+
if (!entry.present) continue;
|
|
4285
|
+
const target = import_node_path3.default.resolve(root, entry.file);
|
|
4286
|
+
const inside = target === root || target.startsWith(root + import_node_path3.default.sep);
|
|
4287
|
+
if (!inside) continue;
|
|
4288
|
+
try {
|
|
4289
|
+
await import_node_fs3.promises.rm(target);
|
|
4290
|
+
deleted.push(entry.file);
|
|
4291
|
+
} catch {
|
|
4292
|
+
}
|
|
4293
|
+
}
|
|
4294
|
+
return deleted;
|
|
4295
|
+
}
|
|
4296
|
+
function staleWarning(stale) {
|
|
4297
|
+
const present = stale.filter((s) => s.present);
|
|
4298
|
+
if (!present.length) return void 0;
|
|
4299
|
+
const list = present.map((s) => s.file).join(", ");
|
|
4300
|
+
return `drzl generate: ${present.length} file(s) written by a previous run were not written by this one, and are still on disk: ${list}. That usually means a table was renamed or removed. Delete them with \`drzl generate --prune\`, which removes only files a previous run recorded writing.`;
|
|
4301
|
+
}
|
|
4302
|
+
function nextManifestFiles(writtenNow, stale, root) {
|
|
4303
|
+
const kept = stale.filter((s) => s.present).map((s) => import_node_path3.default.resolve(root, s.file));
|
|
4304
|
+
return manifestEntries([...writtenNow, ...kept], root);
|
|
4305
|
+
}
|
|
4306
|
+
|
|
4098
4307
|
// src/unified-diff.ts
|
|
4099
4308
|
var DEFAULT_DIFF_LIMITS = {
|
|
4100
4309
|
maxLines: 4e3,
|
|
@@ -4337,8 +4546,8 @@ function createRebuildScheduler(options) {
|
|
|
4337
4546
|
|
|
4338
4547
|
// src/init.ts
|
|
4339
4548
|
var import_analyzer2 = require("@drzl/analyzer");
|
|
4340
|
-
var
|
|
4341
|
-
var
|
|
4549
|
+
var fs6 = __toESM(require("fs"), 1);
|
|
4550
|
+
var path6 = __toESM(require("path"), 1);
|
|
4342
4551
|
var INIT_GENERATOR_CHOICES = [
|
|
4343
4552
|
{ kind: "zod", packageName: "@drzl/generator-zod", label: "Zod validators" },
|
|
4344
4553
|
{ kind: "valibot", packageName: "@drzl/generator-valibot", label: "Valibot validators" },
|
|
@@ -4409,7 +4618,7 @@ async function detectSchema(cwd) {
|
|
|
4409
4618
|
kitFiles = null;
|
|
4410
4619
|
}
|
|
4411
4620
|
if (kitFiles && kitPath) {
|
|
4412
|
-
const rel =
|
|
4621
|
+
const rel = path6.relative(cwd, kitPath) || path6.basename(kitPath);
|
|
4413
4622
|
const report = await classifySchemaCandidate(kitFiles);
|
|
4414
4623
|
if (report.verdict === "confirmed" || report.verdict === "unverified") {
|
|
4415
4624
|
notes.push(
|
|
@@ -4425,10 +4634,10 @@ async function detectSchema(cwd) {
|
|
|
4425
4634
|
}
|
|
4426
4635
|
notes.push(`${rel} names schema files that declare no Drizzle tables; looking elsewhere.`);
|
|
4427
4636
|
}
|
|
4428
|
-
const present = schemaCandidates().filter((c) =>
|
|
4637
|
+
const present = schemaCandidates().filter((c) => fs6.existsSync(path6.resolve(cwd, c)));
|
|
4429
4638
|
const unverified = [];
|
|
4430
4639
|
for (const file of present) {
|
|
4431
|
-
const report = await classifySchemaCandidate(
|
|
4640
|
+
const report = await classifySchemaCandidate(path6.resolve(cwd, file));
|
|
4432
4641
|
if (report.verdict === "confirmed") {
|
|
4433
4642
|
notes.push(
|
|
4434
4643
|
`Schema found at ${file} (${report.tables} table${report.tables === 1 ? "" : "s"})`
|
|
@@ -4530,7 +4739,7 @@ async function promptForPlan(args) {
|
|
|
4530
4739
|
if (answer === null) endedEarly = true;
|
|
4531
4740
|
else if (answer.trim()) {
|
|
4532
4741
|
const typed = answer.trim();
|
|
4533
|
-
const report = await classifySchemaCandidate(
|
|
4742
|
+
const report = await classifySchemaCandidate(path6.resolve(cwd, typed));
|
|
4534
4743
|
if (report.verdict === "confirmed") {
|
|
4535
4744
|
write(` ${typed}: ${report.tables} table${report.tables === 1 ? "" : "s"}`);
|
|
4536
4745
|
} else if (report.verdict === "unverified") {
|
|
@@ -4592,8 +4801,8 @@ function parseGeneratorsFlag(value) {
|
|
|
4592
4801
|
return parts;
|
|
4593
4802
|
}
|
|
4594
4803
|
async function runInit(args) {
|
|
4595
|
-
const target =
|
|
4596
|
-
const existing = CONFIG_FILE_NAMES.find((name) =>
|
|
4804
|
+
const target = path6.resolve(args.cwd, "drzl.config.ts");
|
|
4805
|
+
const existing = CONFIG_FILE_NAMES.find((name) => fs6.existsSync(path6.resolve(args.cwd, name)));
|
|
4597
4806
|
if (existing) {
|
|
4598
4807
|
args.error(
|
|
4599
4808
|
`drzl init: ${existing} already exists, so nothing was written. Delete it, or edit it by hand; init never overwrites a config, and will not write one that shadows it either.`
|
|
@@ -4632,8 +4841,8 @@ async function runInit(args) {
|
|
|
4632
4841
|
generators: fromFlag ?? [DEFAULT_GENERATOR_KIND]
|
|
4633
4842
|
};
|
|
4634
4843
|
if (args.schemaFlag) {
|
|
4635
|
-
const full =
|
|
4636
|
-
if (!
|
|
4844
|
+
const full = path6.resolve(args.cwd, args.schemaFlag);
|
|
4845
|
+
if (!fs6.existsSync(full)) {
|
|
4637
4846
|
args.log(`--schema ${args.schemaFlag} is not there yet. Writing it anyway.`);
|
|
4638
4847
|
} else if ((await classifySchemaCandidate(full)).verdict === "rejected") {
|
|
4639
4848
|
args.log(`--schema ${args.schemaFlag} declares no Drizzle tables. Writing it anyway.`);
|
|
@@ -4641,7 +4850,7 @@ async function runInit(args) {
|
|
|
4641
4850
|
}
|
|
4642
4851
|
}
|
|
4643
4852
|
try {
|
|
4644
|
-
|
|
4853
|
+
fs6.writeFileSync(target, renderInitConfig(plan), { flag: "wx" });
|
|
4645
4854
|
} catch (e) {
|
|
4646
4855
|
if (e?.code === "EEXIST") {
|
|
4647
4856
|
args.error(
|
|
@@ -4664,10 +4873,10 @@ async function runInit(args) {
|
|
|
4664
4873
|
}
|
|
4665
4874
|
|
|
4666
4875
|
// src/sponsor.ts
|
|
4667
|
-
var
|
|
4668
|
-
var
|
|
4669
|
-
var CACHE_DIR =
|
|
4670
|
-
var CACHE_FILE =
|
|
4876
|
+
var import_node_fs4 = require("fs");
|
|
4877
|
+
var import_node_path4 = __toESM(require("path"), 1);
|
|
4878
|
+
var CACHE_DIR = import_node_path4.default.join(process.cwd(), "node_modules", ".cache", "@drzl");
|
|
4879
|
+
var CACHE_FILE = import_node_path4.default.join(CACHE_DIR, "sponsor-message.json");
|
|
4671
4880
|
var DEFAULT_INTERVAL_MS = 1e3 * 60 * 15;
|
|
4672
4881
|
var shownThisProcess = false;
|
|
4673
4882
|
var tips = [
|
|
@@ -4691,7 +4900,7 @@ function maybeShowSponsorMessage({
|
|
|
4691
4900
|
if (hideRequested || process.env.CI && !force || shownThisProcess && !force) return;
|
|
4692
4901
|
if (!out.wantsAsides && !force) return;
|
|
4693
4902
|
try {
|
|
4694
|
-
(0,
|
|
4903
|
+
(0, import_node_fs4.mkdirSync)(CACHE_DIR, { recursive: true });
|
|
4695
4904
|
const payload = readCache();
|
|
4696
4905
|
payload.runs += 1;
|
|
4697
4906
|
const now = Date.now();
|
|
@@ -4719,11 +4928,11 @@ ${green("Pro tip:")} ${tip}
|
|
|
4719
4928
|
}
|
|
4720
4929
|
}
|
|
4721
4930
|
function readCache() {
|
|
4722
|
-
if (!(0,
|
|
4931
|
+
if (!(0, import_node_fs4.existsSync)(CACHE_FILE)) {
|
|
4723
4932
|
return { runs: 0 };
|
|
4724
4933
|
}
|
|
4725
4934
|
try {
|
|
4726
|
-
const data = JSON.parse((0,
|
|
4935
|
+
const data = JSON.parse((0, import_node_fs4.readFileSync)(CACHE_FILE, "utf8"));
|
|
4727
4936
|
if (typeof data.runs !== "number") return { runs: 0 };
|
|
4728
4937
|
return data;
|
|
4729
4938
|
} catch {
|
|
@@ -4731,21 +4940,21 @@ function readCache() {
|
|
|
4731
4940
|
}
|
|
4732
4941
|
}
|
|
4733
4942
|
function writeCache(payload) {
|
|
4734
|
-
(0,
|
|
4943
|
+
(0, import_node_fs4.writeFileSync)(CACHE_FILE, JSON.stringify(payload, null, 2), "utf8");
|
|
4735
4944
|
}
|
|
4736
4945
|
|
|
4737
4946
|
// src/version.ts
|
|
4738
|
-
var
|
|
4739
|
-
var
|
|
4947
|
+
var import_node_fs5 = require("fs");
|
|
4948
|
+
var path8 = __toESM(require("path"), 1);
|
|
4740
4949
|
var import_node_url = require("url");
|
|
4741
4950
|
var PACKAGE_NAME = "@drzl/cli";
|
|
4742
4951
|
function moduleDir() {
|
|
4743
|
-
return
|
|
4952
|
+
return path8.dirname((0, import_node_url.fileURLToPath)(__drzlModuleUrl));
|
|
4744
4953
|
}
|
|
4745
4954
|
function readVersionFrom(manifestPath) {
|
|
4746
4955
|
let raw;
|
|
4747
4956
|
try {
|
|
4748
|
-
raw = (0,
|
|
4957
|
+
raw = (0, import_node_fs5.readFileSync)(manifestPath, "utf8");
|
|
4749
4958
|
} catch (e) {
|
|
4750
4959
|
throw new Error(
|
|
4751
4960
|
`${PACKAGE_NAME} cannot read its own version: no manifest at ${manifestPath} (${e?.message ?? String(e)}).`
|
|
@@ -4763,7 +4972,7 @@ function readVersionFrom(manifestPath) {
|
|
|
4763
4972
|
return manifest.version;
|
|
4764
4973
|
}
|
|
4765
4974
|
function readCliVersion() {
|
|
4766
|
-
return readVersionFrom(
|
|
4975
|
+
return readVersionFrom(path8.join(moduleDir(), "..", "package.json"));
|
|
4767
4976
|
}
|
|
4768
4977
|
var CLI_VERSION = readCliVersion();
|
|
4769
4978
|
|
|
@@ -4852,8 +5061,8 @@ withOutputFlags(
|
|
|
4852
5061
|
const errors = res.issues.some((i) => i.level === "error");
|
|
4853
5062
|
const code = unreadable ? EXIT_FAILED : errors ? EXIT_FINDINGS : EXIT_OK;
|
|
4854
5063
|
if (opts.out && !opts.json) {
|
|
4855
|
-
const
|
|
4856
|
-
await
|
|
5064
|
+
const fs7 = await import("fs/promises");
|
|
5065
|
+
await fs7.writeFile(opts.out, JSON.stringify(res, null, 2), "utf8");
|
|
4857
5066
|
spinner.succeed(`Analysis written to ${opts.out} in ${ms}ms`);
|
|
4858
5067
|
} else {
|
|
4859
5068
|
spinner.succeed(`Analyzed in ${ms}ms`);
|
|
@@ -4990,7 +5199,7 @@ async function explainSchemaSource(opts, out) {
|
|
|
4990
5199
|
schema: source.schema,
|
|
4991
5200
|
label: describeSchemaTarget(source.schema),
|
|
4992
5201
|
config: cfg,
|
|
4993
|
-
...source.source === "drizzle-kit" && source.drizzleKitConfigPath ? { note: `Schema from ${
|
|
5202
|
+
...source.source === "drizzle-kit" && source.drizzleKitConfigPath ? { note: `Schema from ${path9.relative(process.cwd(), source.drizzleKitConfigPath)}` } : {}
|
|
4994
5203
|
};
|
|
4995
5204
|
}
|
|
4996
5205
|
const detected = await detectSchema(process.cwd());
|
|
@@ -5100,7 +5309,11 @@ withOutputFlags(
|
|
|
5100
5309
|
).option(
|
|
5101
5310
|
"--check",
|
|
5102
5311
|
"regenerate and fail if the result differs from what is on disk, without changing it"
|
|
5103
|
-
).option("--dry-run", "report what would be written, and write nothing", false)
|
|
5312
|
+
).option("--dry-run", "report what would be written, and write nothing", false).option(
|
|
5313
|
+
"--prune",
|
|
5314
|
+
"delete files a previous run wrote that this one did not, and nothing else",
|
|
5315
|
+
false
|
|
5316
|
+
)
|
|
5104
5317
|
).action(async (opts) => {
|
|
5105
5318
|
const out = outputFor(opts);
|
|
5106
5319
|
const planning = !!opts.check || !!opts.dryRun;
|
|
@@ -5150,7 +5363,7 @@ withOutputFlags(
|
|
|
5150
5363
|
const n = source.schema.length;
|
|
5151
5364
|
out.note(
|
|
5152
5365
|
out.errStyle.gray(
|
|
5153
|
-
`Schema from ${
|
|
5366
|
+
`Schema from ${path9.relative(process.cwd(), source.drizzleKitConfigPath)} (${n} file${n === 1 ? "" : "s"})`
|
|
5154
5367
|
)
|
|
5155
5368
|
);
|
|
5156
5369
|
}
|
|
@@ -5179,6 +5392,7 @@ withOutputFlags(
|
|
|
5179
5392
|
analysis.tables = filterTables(narrowed.tables, cfg);
|
|
5180
5393
|
for (const w of [...narrowed.warnings, ...filterWarnings]) warn(w);
|
|
5181
5394
|
for (const w of wideColumnWarning(analysis.issues)) warn(w);
|
|
5395
|
+
for (const w of authTableWarnings(analysis)) warn(w);
|
|
5182
5396
|
const empty = nothingToGenerate({
|
|
5183
5397
|
schema: source.schema,
|
|
5184
5398
|
analyzed: narrowed.tables,
|
|
@@ -5248,6 +5462,19 @@ withOutputFlags(
|
|
|
5248
5462
|
files: e.files,
|
|
5249
5463
|
changes: e.changes.map((c) => ({ file: displayPath(c.file), status: c.status }))
|
|
5250
5464
|
}));
|
|
5465
|
+
const previousManifest = await readManifest(process.cwd());
|
|
5466
|
+
const writtenNow = plan.files.map((f) => f.file);
|
|
5467
|
+
const stale = await staleFiles(previousManifest, writtenNow, process.cwd());
|
|
5468
|
+
let stillStale = stale;
|
|
5469
|
+
if (opts.prune && !planning) {
|
|
5470
|
+
const removed = await pruneStale(process.cwd(), stale);
|
|
5471
|
+
for (const file of removed) out.warn(`drzl generate: removed stale ${file}`);
|
|
5472
|
+
const gone = new Set(removed);
|
|
5473
|
+
stillStale = stale.filter((s) => !gone.has(s.file));
|
|
5474
|
+
} else {
|
|
5475
|
+
const warning = staleWarning(stale);
|
|
5476
|
+
if (warning) warn(warning);
|
|
5477
|
+
}
|
|
5251
5478
|
if (planning) {
|
|
5252
5479
|
const wrote = await verifyNothingWasWritten(outputDirs, existing);
|
|
5253
5480
|
if (wrote.length) {
|
|
@@ -5257,6 +5484,14 @@ withOutputFlags(
|
|
|
5257
5484
|
process.exit(EXIT_FAILED);
|
|
5258
5485
|
}
|
|
5259
5486
|
}
|
|
5487
|
+
if (!planning) {
|
|
5488
|
+
await writeManifest(
|
|
5489
|
+
process.cwd(),
|
|
5490
|
+
nextManifestFiles(writtenNow, stillStale, process.cwd()).map(
|
|
5491
|
+
(f) => path9.resolve(process.cwd(), f)
|
|
5492
|
+
)
|
|
5493
|
+
);
|
|
5494
|
+
}
|
|
5260
5495
|
if (opts.check) {
|
|
5261
5496
|
const drift = pendingChanges(plan);
|
|
5262
5497
|
const upToDate = drift.length === 0;
|
|
@@ -5498,10 +5733,10 @@ program.command("watch").description("Watch schema and regenerate on changes").o
|
|
|
5498
5733
|
return;
|
|
5499
5734
|
}
|
|
5500
5735
|
let cfg = loaded;
|
|
5501
|
-
const abs = (p) =>
|
|
5736
|
+
const abs = (p) => path9.resolve(process.cwd(), p);
|
|
5502
5737
|
const isInside = (child, parent) => {
|
|
5503
|
-
const rel =
|
|
5504
|
-
return !!rel && !rel.startsWith("..") && !
|
|
5738
|
+
const rel = path9.relative(parent, child);
|
|
5739
|
+
return !!rel && !rel.startsWith("..") && !path9.isAbsolute(rel);
|
|
5505
5740
|
};
|
|
5506
5741
|
let source;
|
|
5507
5742
|
try {
|
|
@@ -5537,7 +5772,7 @@ program.command("watch").description("Watch schema and regenerate on changes").o
|
|
|
5537
5772
|
if (full === dir || isInside(full, dir)) return true;
|
|
5538
5773
|
}
|
|
5539
5774
|
if (stats?.isDirectory()) return false;
|
|
5540
|
-
const ext =
|
|
5775
|
+
const ext = path9.extname(full);
|
|
5541
5776
|
if (!ext) return false;
|
|
5542
5777
|
return !WATCHED_EXTENSIONS.has(ext);
|
|
5543
5778
|
};
|
|
@@ -5700,7 +5935,7 @@ program.command("watch").description("Watch schema and regenerate on changes").o
|
|
|
5700
5935
|
} else {
|
|
5701
5936
|
out.note(
|
|
5702
5937
|
out.errStyle.gray(
|
|
5703
|
-
"Watching:\n " + Array.from(currentTargets).map((p) =>
|
|
5938
|
+
"Watching:\n " + Array.from(currentTargets).map((p) => path9.relative(process.cwd(), p)).join("\n ")
|
|
5704
5939
|
)
|
|
5705
5940
|
);
|
|
5706
5941
|
}
|