@drzl/cli 4.38.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/cli.cjs +141 -43
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +134 -36
- package/dist/cli.js.map +1 -1
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -43,7 +43,7 @@ import {
|
|
|
43
43
|
import { qualifiedTableName as qualifiedTableName2, SchemaAnalyzer as SchemaAnalyzer2 } from "@drzl/analyzer";
|
|
44
44
|
import chokidar from "chokidar";
|
|
45
45
|
import { Command } from "commander";
|
|
46
|
-
import * as
|
|
46
|
+
import * as path8 from "path";
|
|
47
47
|
|
|
48
48
|
// src/output.ts
|
|
49
49
|
import { Chalk } from "chalk";
|
|
@@ -1254,11 +1254,11 @@ function authTableWarnings(analysis) {
|
|
|
1254
1254
|
import { Chalk as Chalk2 } from "chalk";
|
|
1255
1255
|
var PLAIN = new Chalk2({ level: 0 });
|
|
1256
1256
|
var HANDLED_CODES = /* @__PURE__ */ new Set(["DRZL_ANL_UNKNOWN_COLUMN"]);
|
|
1257
|
-
function splitPath(
|
|
1258
|
-
if (!
|
|
1259
|
-
const dot =
|
|
1260
|
-
if (dot <= 0) return { table:
|
|
1261
|
-
return { table:
|
|
1257
|
+
function splitPath(path9) {
|
|
1258
|
+
if (!path9) return {};
|
|
1259
|
+
const dot = path9.lastIndexOf(".");
|
|
1260
|
+
if (dot <= 0) return { table: path9 };
|
|
1261
|
+
return { table: path9.slice(0, dot), column: path9.slice(dot + 1) };
|
|
1262
1262
|
}
|
|
1263
1263
|
function namedColumns(parsed) {
|
|
1264
1264
|
const out = [];
|
|
@@ -1810,11 +1810,11 @@ function issueTouches(issue, table) {
|
|
|
1810
1810
|
return dot > 0 && own.includes(issue.path.slice(0, dot));
|
|
1811
1811
|
}
|
|
1812
1812
|
function issueColumn(issue, table) {
|
|
1813
|
-
const
|
|
1813
|
+
const path9 = issue.path ?? "";
|
|
1814
1814
|
const names = namesOf(table);
|
|
1815
1815
|
for (const prefix of [names.qualified, names.tsName, names.name, table.name]) {
|
|
1816
|
-
if (
|
|
1817
|
-
const rest =
|
|
1816
|
+
if (path9.startsWith(`${prefix}.`)) {
|
|
1817
|
+
const rest = path9.slice(prefix.length + 1);
|
|
1818
1818
|
if (table.columns.some((c) => c.name === rest)) return rest;
|
|
1819
1819
|
}
|
|
1820
1820
|
}
|
|
@@ -2973,6 +2973,79 @@ function displayPath(file, cwd = process.cwd()) {
|
|
|
2973
2973
|
return rel && !rel.startsWith("..") ? rel : file;
|
|
2974
2974
|
}
|
|
2975
2975
|
|
|
2976
|
+
// src/manifest.ts
|
|
2977
|
+
import path4 from "path";
|
|
2978
|
+
import { promises as fs4 } from "fs";
|
|
2979
|
+
var MANIFEST_VERSION = 1;
|
|
2980
|
+
var MANIFEST_PATH = path4.join(".drzl", "manifest.json");
|
|
2981
|
+
function toPosix(p) {
|
|
2982
|
+
return p.split(path4.sep).join("/");
|
|
2983
|
+
}
|
|
2984
|
+
function manifestEntries(files, root) {
|
|
2985
|
+
const rel = files.map((f) => toPosix(path4.relative(root, f)));
|
|
2986
|
+
return [...new Set(rel)].sort();
|
|
2987
|
+
}
|
|
2988
|
+
async function readManifest(root) {
|
|
2989
|
+
try {
|
|
2990
|
+
const raw = await fs4.readFile(path4.join(root, MANIFEST_PATH), "utf8");
|
|
2991
|
+
const parsed = JSON.parse(raw);
|
|
2992
|
+
if (!parsed || typeof parsed !== "object" || parsed.version !== MANIFEST_VERSION || !Array.isArray(parsed.files) || !parsed.files.every((f) => typeof f === "string")) {
|
|
2993
|
+
return void 0;
|
|
2994
|
+
}
|
|
2995
|
+
return { version: MANIFEST_VERSION, files: parsed.files };
|
|
2996
|
+
} catch {
|
|
2997
|
+
return void 0;
|
|
2998
|
+
}
|
|
2999
|
+
}
|
|
3000
|
+
async function writeManifest(root, files) {
|
|
3001
|
+
const manifest = { version: MANIFEST_VERSION, files: manifestEntries(files, root) };
|
|
3002
|
+
const target = path4.join(root, MANIFEST_PATH);
|
|
3003
|
+
await fs4.mkdir(path4.dirname(target), { recursive: true });
|
|
3004
|
+
await fs4.writeFile(target, `${JSON.stringify(manifest, null, 2)}
|
|
3005
|
+
`, "utf8");
|
|
3006
|
+
}
|
|
3007
|
+
async function staleFiles(previous, writtenNow, root) {
|
|
3008
|
+
if (!previous) return [];
|
|
3009
|
+
const current = new Set(manifestEntries(writtenNow, root));
|
|
3010
|
+
const gone = previous.files.filter((f) => !current.has(f));
|
|
3011
|
+
const out = [];
|
|
3012
|
+
for (const file of gone) {
|
|
3013
|
+
let present = true;
|
|
3014
|
+
try {
|
|
3015
|
+
await fs4.access(path4.join(root, file));
|
|
3016
|
+
} catch {
|
|
3017
|
+
present = false;
|
|
3018
|
+
}
|
|
3019
|
+
out.push({ file, present });
|
|
3020
|
+
}
|
|
3021
|
+
return out;
|
|
3022
|
+
}
|
|
3023
|
+
async function pruneStale(root, stale) {
|
|
3024
|
+
const deleted = [];
|
|
3025
|
+
for (const entry of stale) {
|
|
3026
|
+
if (!entry.present) continue;
|
|
3027
|
+
const target = path4.resolve(root, entry.file);
|
|
3028
|
+
const inside = target === root || target.startsWith(root + path4.sep);
|
|
3029
|
+
if (!inside) continue;
|
|
3030
|
+
try {
|
|
3031
|
+
await fs4.rm(target);
|
|
3032
|
+
deleted.push(entry.file);
|
|
3033
|
+
} catch {
|
|
3034
|
+
}
|
|
3035
|
+
}
|
|
3036
|
+
return deleted;
|
|
3037
|
+
}
|
|
3038
|
+
function staleWarning(stale) {
|
|
3039
|
+
const present = stale.filter((s) => s.present);
|
|
3040
|
+
if (!present.length) return void 0;
|
|
3041
|
+
const list = present.map((s) => s.file).join(", ");
|
|
3042
|
+
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.`;
|
|
3043
|
+
}
|
|
3044
|
+
function nextManifestFiles(writtenNow, stale, root) {
|
|
3045
|
+
const kept = stale.filter((s) => s.present).map((s) => path4.resolve(root, s.file));
|
|
3046
|
+
return manifestEntries([...writtenNow, ...kept], root);
|
|
3047
|
+
}
|
|
3048
|
+
|
|
2976
3049
|
// src/unified-diff.ts
|
|
2977
3050
|
var DEFAULT_DIFF_LIMITS = {
|
|
2978
3051
|
maxLines: 4e3,
|
|
@@ -3215,8 +3288,8 @@ function createRebuildScheduler(options) {
|
|
|
3215
3288
|
|
|
3216
3289
|
// src/init.ts
|
|
3217
3290
|
import { SchemaAnalyzer } from "@drzl/analyzer";
|
|
3218
|
-
import * as
|
|
3219
|
-
import * as
|
|
3291
|
+
import * as fs5 from "fs";
|
|
3292
|
+
import * as path5 from "path";
|
|
3220
3293
|
var INIT_GENERATOR_CHOICES = [
|
|
3221
3294
|
{ kind: "zod", packageName: "@drzl/generator-zod", label: "Zod validators" },
|
|
3222
3295
|
{ kind: "valibot", packageName: "@drzl/generator-valibot", label: "Valibot validators" },
|
|
@@ -3287,7 +3360,7 @@ async function detectSchema(cwd) {
|
|
|
3287
3360
|
kitFiles = null;
|
|
3288
3361
|
}
|
|
3289
3362
|
if (kitFiles && kitPath) {
|
|
3290
|
-
const rel =
|
|
3363
|
+
const rel = path5.relative(cwd, kitPath) || path5.basename(kitPath);
|
|
3291
3364
|
const report = await classifySchemaCandidate(kitFiles);
|
|
3292
3365
|
if (report.verdict === "confirmed" || report.verdict === "unverified") {
|
|
3293
3366
|
notes.push(
|
|
@@ -3303,10 +3376,10 @@ async function detectSchema(cwd) {
|
|
|
3303
3376
|
}
|
|
3304
3377
|
notes.push(`${rel} names schema files that declare no Drizzle tables; looking elsewhere.`);
|
|
3305
3378
|
}
|
|
3306
|
-
const present = schemaCandidates().filter((c) =>
|
|
3379
|
+
const present = schemaCandidates().filter((c) => fs5.existsSync(path5.resolve(cwd, c)));
|
|
3307
3380
|
const unverified = [];
|
|
3308
3381
|
for (const file of present) {
|
|
3309
|
-
const report = await classifySchemaCandidate(
|
|
3382
|
+
const report = await classifySchemaCandidate(path5.resolve(cwd, file));
|
|
3310
3383
|
if (report.verdict === "confirmed") {
|
|
3311
3384
|
notes.push(
|
|
3312
3385
|
`Schema found at ${file} (${report.tables} table${report.tables === 1 ? "" : "s"})`
|
|
@@ -3408,7 +3481,7 @@ async function promptForPlan(args) {
|
|
|
3408
3481
|
if (answer === null) endedEarly = true;
|
|
3409
3482
|
else if (answer.trim()) {
|
|
3410
3483
|
const typed = answer.trim();
|
|
3411
|
-
const report = await classifySchemaCandidate(
|
|
3484
|
+
const report = await classifySchemaCandidate(path5.resolve(cwd, typed));
|
|
3412
3485
|
if (report.verdict === "confirmed") {
|
|
3413
3486
|
write(` ${typed}: ${report.tables} table${report.tables === 1 ? "" : "s"}`);
|
|
3414
3487
|
} else if (report.verdict === "unverified") {
|
|
@@ -3470,8 +3543,8 @@ function parseGeneratorsFlag(value) {
|
|
|
3470
3543
|
return parts;
|
|
3471
3544
|
}
|
|
3472
3545
|
async function runInit(args) {
|
|
3473
|
-
const target =
|
|
3474
|
-
const existing = CONFIG_FILE_NAMES.find((name) =>
|
|
3546
|
+
const target = path5.resolve(args.cwd, "drzl.config.ts");
|
|
3547
|
+
const existing = CONFIG_FILE_NAMES.find((name) => fs5.existsSync(path5.resolve(args.cwd, name)));
|
|
3475
3548
|
if (existing) {
|
|
3476
3549
|
args.error(
|
|
3477
3550
|
`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.`
|
|
@@ -3510,8 +3583,8 @@ async function runInit(args) {
|
|
|
3510
3583
|
generators: fromFlag ?? [DEFAULT_GENERATOR_KIND]
|
|
3511
3584
|
};
|
|
3512
3585
|
if (args.schemaFlag) {
|
|
3513
|
-
const full =
|
|
3514
|
-
if (!
|
|
3586
|
+
const full = path5.resolve(args.cwd, args.schemaFlag);
|
|
3587
|
+
if (!fs5.existsSync(full)) {
|
|
3515
3588
|
args.log(`--schema ${args.schemaFlag} is not there yet. Writing it anyway.`);
|
|
3516
3589
|
} else if ((await classifySchemaCandidate(full)).verdict === "rejected") {
|
|
3517
3590
|
args.log(`--schema ${args.schemaFlag} declares no Drizzle tables. Writing it anyway.`);
|
|
@@ -3519,7 +3592,7 @@ async function runInit(args) {
|
|
|
3519
3592
|
}
|
|
3520
3593
|
}
|
|
3521
3594
|
try {
|
|
3522
|
-
|
|
3595
|
+
fs5.writeFileSync(target, renderInitConfig(plan), { flag: "wx" });
|
|
3523
3596
|
} catch (e) {
|
|
3524
3597
|
if (e?.code === "EEXIST") {
|
|
3525
3598
|
args.error(
|
|
@@ -3543,9 +3616,9 @@ async function runInit(args) {
|
|
|
3543
3616
|
|
|
3544
3617
|
// src/sponsor.ts
|
|
3545
3618
|
import { existsSync as existsSync3, mkdirSync, readFileSync, writeFileSync as writeFileSync2 } from "fs";
|
|
3546
|
-
import
|
|
3547
|
-
var CACHE_DIR =
|
|
3548
|
-
var CACHE_FILE =
|
|
3619
|
+
import path6 from "path";
|
|
3620
|
+
var CACHE_DIR = path6.join(process.cwd(), "node_modules", ".cache", "@drzl");
|
|
3621
|
+
var CACHE_FILE = path6.join(CACHE_DIR, "sponsor-message.json");
|
|
3549
3622
|
var DEFAULT_INTERVAL_MS = 1e3 * 60 * 15;
|
|
3550
3623
|
var shownThisProcess = false;
|
|
3551
3624
|
var tips = [
|
|
@@ -3614,11 +3687,11 @@ function writeCache(payload) {
|
|
|
3614
3687
|
|
|
3615
3688
|
// src/version.ts
|
|
3616
3689
|
import { readFileSync as readFileSync2 } from "fs";
|
|
3617
|
-
import * as
|
|
3690
|
+
import * as path7 from "path";
|
|
3618
3691
|
import { fileURLToPath } from "url";
|
|
3619
3692
|
var PACKAGE_NAME = "@drzl/cli";
|
|
3620
3693
|
function moduleDir() {
|
|
3621
|
-
return
|
|
3694
|
+
return path7.dirname(fileURLToPath(import.meta.url));
|
|
3622
3695
|
}
|
|
3623
3696
|
function readVersionFrom(manifestPath) {
|
|
3624
3697
|
let raw;
|
|
@@ -3641,7 +3714,7 @@ function readVersionFrom(manifestPath) {
|
|
|
3641
3714
|
return manifest.version;
|
|
3642
3715
|
}
|
|
3643
3716
|
function readCliVersion() {
|
|
3644
|
-
return readVersionFrom(
|
|
3717
|
+
return readVersionFrom(path7.join(moduleDir(), "..", "package.json"));
|
|
3645
3718
|
}
|
|
3646
3719
|
var CLI_VERSION = readCliVersion();
|
|
3647
3720
|
|
|
@@ -3730,8 +3803,8 @@ withOutputFlags(
|
|
|
3730
3803
|
const errors = res.issues.some((i) => i.level === "error");
|
|
3731
3804
|
const code = unreadable ? EXIT_FAILED : errors ? EXIT_FINDINGS : EXIT_OK;
|
|
3732
3805
|
if (opts.out && !opts.json) {
|
|
3733
|
-
const
|
|
3734
|
-
await
|
|
3806
|
+
const fs6 = await import("fs/promises");
|
|
3807
|
+
await fs6.writeFile(opts.out, JSON.stringify(res, null, 2), "utf8");
|
|
3735
3808
|
spinner.succeed(`Analysis written to ${opts.out} in ${ms}ms`);
|
|
3736
3809
|
} else {
|
|
3737
3810
|
spinner.succeed(`Analyzed in ${ms}ms`);
|
|
@@ -3868,7 +3941,7 @@ async function explainSchemaSource(opts, out) {
|
|
|
3868
3941
|
schema: source.schema,
|
|
3869
3942
|
label: describeSchemaTarget(source.schema),
|
|
3870
3943
|
config: cfg,
|
|
3871
|
-
...source.source === "drizzle-kit" && source.drizzleKitConfigPath ? { note: `Schema from ${
|
|
3944
|
+
...source.source === "drizzle-kit" && source.drizzleKitConfigPath ? { note: `Schema from ${path8.relative(process.cwd(), source.drizzleKitConfigPath)}` } : {}
|
|
3872
3945
|
};
|
|
3873
3946
|
}
|
|
3874
3947
|
const detected = await detectSchema(process.cwd());
|
|
@@ -3978,7 +4051,11 @@ withOutputFlags(
|
|
|
3978
4051
|
).option(
|
|
3979
4052
|
"--check",
|
|
3980
4053
|
"regenerate and fail if the result differs from what is on disk, without changing it"
|
|
3981
|
-
).option("--dry-run", "report what would be written, and write nothing", false)
|
|
4054
|
+
).option("--dry-run", "report what would be written, and write nothing", false).option(
|
|
4055
|
+
"--prune",
|
|
4056
|
+
"delete files a previous run wrote that this one did not, and nothing else",
|
|
4057
|
+
false
|
|
4058
|
+
)
|
|
3982
4059
|
).action(async (opts) => {
|
|
3983
4060
|
const out = outputFor(opts);
|
|
3984
4061
|
const planning = !!opts.check || !!opts.dryRun;
|
|
@@ -4028,7 +4105,7 @@ withOutputFlags(
|
|
|
4028
4105
|
const n = source.schema.length;
|
|
4029
4106
|
out.note(
|
|
4030
4107
|
out.errStyle.gray(
|
|
4031
|
-
`Schema from ${
|
|
4108
|
+
`Schema from ${path8.relative(process.cwd(), source.drizzleKitConfigPath)} (${n} file${n === 1 ? "" : "s"})`
|
|
4032
4109
|
)
|
|
4033
4110
|
);
|
|
4034
4111
|
}
|
|
@@ -4127,6 +4204,19 @@ withOutputFlags(
|
|
|
4127
4204
|
files: e.files,
|
|
4128
4205
|
changes: e.changes.map((c) => ({ file: displayPath(c.file), status: c.status }))
|
|
4129
4206
|
}));
|
|
4207
|
+
const previousManifest = await readManifest(process.cwd());
|
|
4208
|
+
const writtenNow = plan.files.map((f) => f.file);
|
|
4209
|
+
const stale = await staleFiles(previousManifest, writtenNow, process.cwd());
|
|
4210
|
+
let stillStale = stale;
|
|
4211
|
+
if (opts.prune && !planning) {
|
|
4212
|
+
const removed = await pruneStale(process.cwd(), stale);
|
|
4213
|
+
for (const file of removed) out.warn(`drzl generate: removed stale ${file}`);
|
|
4214
|
+
const gone = new Set(removed);
|
|
4215
|
+
stillStale = stale.filter((s) => !gone.has(s.file));
|
|
4216
|
+
} else {
|
|
4217
|
+
const warning = staleWarning(stale);
|
|
4218
|
+
if (warning) warn(warning);
|
|
4219
|
+
}
|
|
4130
4220
|
if (planning) {
|
|
4131
4221
|
const wrote = await verifyNothingWasWritten(outputDirs, existing);
|
|
4132
4222
|
if (wrote.length) {
|
|
@@ -4136,6 +4226,14 @@ withOutputFlags(
|
|
|
4136
4226
|
process.exit(EXIT_FAILED);
|
|
4137
4227
|
}
|
|
4138
4228
|
}
|
|
4229
|
+
if (!planning) {
|
|
4230
|
+
await writeManifest(
|
|
4231
|
+
process.cwd(),
|
|
4232
|
+
nextManifestFiles(writtenNow, stillStale, process.cwd()).map(
|
|
4233
|
+
(f) => path8.resolve(process.cwd(), f)
|
|
4234
|
+
)
|
|
4235
|
+
);
|
|
4236
|
+
}
|
|
4139
4237
|
if (opts.check) {
|
|
4140
4238
|
const drift = pendingChanges(plan);
|
|
4141
4239
|
const upToDate = drift.length === 0;
|
|
@@ -4377,10 +4475,10 @@ program.command("watch").description("Watch schema and regenerate on changes").o
|
|
|
4377
4475
|
return;
|
|
4378
4476
|
}
|
|
4379
4477
|
let cfg = loaded;
|
|
4380
|
-
const abs = (p) =>
|
|
4478
|
+
const abs = (p) => path8.resolve(process.cwd(), p);
|
|
4381
4479
|
const isInside = (child, parent) => {
|
|
4382
|
-
const rel =
|
|
4383
|
-
return !!rel && !rel.startsWith("..") && !
|
|
4480
|
+
const rel = path8.relative(parent, child);
|
|
4481
|
+
return !!rel && !rel.startsWith("..") && !path8.isAbsolute(rel);
|
|
4384
4482
|
};
|
|
4385
4483
|
let source;
|
|
4386
4484
|
try {
|
|
@@ -4416,7 +4514,7 @@ program.command("watch").description("Watch schema and regenerate on changes").o
|
|
|
4416
4514
|
if (full === dir || isInside(full, dir)) return true;
|
|
4417
4515
|
}
|
|
4418
4516
|
if (stats?.isDirectory()) return false;
|
|
4419
|
-
const ext =
|
|
4517
|
+
const ext = path8.extname(full);
|
|
4420
4518
|
if (!ext) return false;
|
|
4421
4519
|
return !WATCHED_EXTENSIONS.has(ext);
|
|
4422
4520
|
};
|
|
@@ -4579,7 +4677,7 @@ program.command("watch").description("Watch schema and regenerate on changes").o
|
|
|
4579
4677
|
} else {
|
|
4580
4678
|
out.note(
|
|
4581
4679
|
out.errStyle.gray(
|
|
4582
|
-
"Watching:\n " + Array.from(currentTargets).map((p) =>
|
|
4680
|
+
"Watching:\n " + Array.from(currentTargets).map((p) => path8.relative(process.cwd(), p)).join("\n ")
|
|
4583
4681
|
)
|
|
4584
4682
|
);
|
|
4585
4683
|
}
|