@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.js
CHANGED
|
@@ -30,19 +30,20 @@ import {
|
|
|
30
30
|
nestjsOutDir,
|
|
31
31
|
nextOutDir,
|
|
32
32
|
openApiFetchOutDir,
|
|
33
|
+
pothosOutDir,
|
|
33
34
|
seedOutDir,
|
|
34
35
|
tableAliases,
|
|
35
36
|
tableFilterWarnings,
|
|
36
37
|
tanstackStartOutDir,
|
|
37
38
|
trpcOutDir,
|
|
38
39
|
tsRestOutDir
|
|
39
|
-
} from "./chunk-
|
|
40
|
+
} from "./chunk-VG6SD2GP.js";
|
|
40
41
|
|
|
41
42
|
// src/cli.ts
|
|
42
43
|
import { qualifiedTableName as qualifiedTableName2, SchemaAnalyzer as SchemaAnalyzer2 } from "@drzl/analyzer";
|
|
43
44
|
import chokidar from "chokidar";
|
|
44
45
|
import { Command } from "commander";
|
|
45
|
-
import * as
|
|
46
|
+
import * as path8 from "path";
|
|
46
47
|
|
|
47
48
|
// src/output.ts
|
|
48
49
|
import { Chalk } from "chalk";
|
|
@@ -531,6 +532,17 @@ function fastCheckOptions(g, cfg) {
|
|
|
531
532
|
};
|
|
532
533
|
}
|
|
533
534
|
|
|
535
|
+
// src/pothos-options.ts
|
|
536
|
+
function pothosOptions(g, cfg) {
|
|
537
|
+
return {
|
|
538
|
+
outputDir: pothosOutDir(g, cfg),
|
|
539
|
+
naming: g.naming,
|
|
540
|
+
outputHeader: g.outputHeader,
|
|
541
|
+
format: g.format,
|
|
542
|
+
importExtension: g.importExtension
|
|
543
|
+
};
|
|
544
|
+
}
|
|
545
|
+
|
|
534
546
|
// src/h3-options.ts
|
|
535
547
|
var VALIDATOR_DEFAULT_DIRS6 = {
|
|
536
548
|
zod: "src/validators/zod",
|
|
@@ -893,6 +905,14 @@ var GENERATORS = [
|
|
|
893
905
|
outputDir: (g, cfg) => fastCheckOutDir(g, cfg),
|
|
894
906
|
options: (g, cfg) => fastCheckOptions(g, cfg)
|
|
895
907
|
},
|
|
908
|
+
{
|
|
909
|
+
kind: "pothos",
|
|
910
|
+
specifier: "@drzl/generator-pothos",
|
|
911
|
+
load: () => import("@drzl/generator-pothos"),
|
|
912
|
+
construct: (m, analysis) => new m.PothosGenerator(analysis),
|
|
913
|
+
outputDir: (g, cfg) => pothosOutDir(g, cfg),
|
|
914
|
+
options: (g, cfg) => pothosOptions(g, cfg)
|
|
915
|
+
},
|
|
896
916
|
{
|
|
897
917
|
kind: "service",
|
|
898
918
|
specifier: "@drzl/generator-service",
|
|
@@ -1136,14 +1156,109 @@ import { parseCheck as parseCheck2 } from "@drzl/validation-core";
|
|
|
1136
1156
|
|
|
1137
1157
|
// src/doctor.ts
|
|
1138
1158
|
import { lengthMeasure, parseCheck } from "@drzl/validation-core";
|
|
1159
|
+
|
|
1160
|
+
// src/auth-tables.ts
|
|
1161
|
+
var SIGNATURES = [
|
|
1162
|
+
{
|
|
1163
|
+
model: "account",
|
|
1164
|
+
// `providerId` beside `accountId` is the distinctive pair: it is a link to an external identity
|
|
1165
|
+
// provider, which an application table has no reason to model this way.
|
|
1166
|
+
required: ["accountId", "providerId", "userId"],
|
|
1167
|
+
supporting: ["accessToken", "refreshToken", "idToken", "password", "scope"],
|
|
1168
|
+
conventionalName: "account",
|
|
1169
|
+
secrets: ["accessToken", "refreshToken", "idToken", "password"]
|
|
1170
|
+
},
|
|
1171
|
+
{
|
|
1172
|
+
model: "session",
|
|
1173
|
+
required: ["token", "expiresAt", "userId"],
|
|
1174
|
+
supporting: ["ipAddress", "userAgent"],
|
|
1175
|
+
conventionalName: "session",
|
|
1176
|
+
// A session token is a bearer credential: whoever reads it is that user until it expires.
|
|
1177
|
+
secrets: ["token"]
|
|
1178
|
+
},
|
|
1179
|
+
{
|
|
1180
|
+
model: "verification",
|
|
1181
|
+
// `identifier` and `value` are deliberately generic names, which is why `expiresAt` is required
|
|
1182
|
+
// too: the three together are a short-lived token store and little else.
|
|
1183
|
+
required: ["identifier", "value", "expiresAt"],
|
|
1184
|
+
supporting: [],
|
|
1185
|
+
conventionalName: "verification",
|
|
1186
|
+
secrets: ["value"]
|
|
1187
|
+
},
|
|
1188
|
+
{
|
|
1189
|
+
model: "user",
|
|
1190
|
+
required: ["email", "emailVerified"],
|
|
1191
|
+
supporting: ["name", "image"],
|
|
1192
|
+
conventionalName: "user",
|
|
1193
|
+
secrets: []
|
|
1194
|
+
}
|
|
1195
|
+
];
|
|
1196
|
+
function columnKeys(table) {
|
|
1197
|
+
const out = /* @__PURE__ */ new Map();
|
|
1198
|
+
for (const c of table.columns) out.set(normalise(c.name), c);
|
|
1199
|
+
return out;
|
|
1200
|
+
}
|
|
1201
|
+
function normalise(name) {
|
|
1202
|
+
return name.replace(/[_-]/g, "").toLowerCase();
|
|
1203
|
+
}
|
|
1204
|
+
function has(keys, name) {
|
|
1205
|
+
return keys.has(normalise(name));
|
|
1206
|
+
}
|
|
1207
|
+
function actualName(table, wanted) {
|
|
1208
|
+
return table.columns.find((c) => normalise(c.name) === normalise(wanted))?.name;
|
|
1209
|
+
}
|
|
1210
|
+
function matchOne(table, sig) {
|
|
1211
|
+
const keys = columnKeys(table);
|
|
1212
|
+
const missing = sig.required.filter((r) => !has(keys, r));
|
|
1213
|
+
if (missing.length) return void 0;
|
|
1214
|
+
const supporting = sig.supporting.filter((s) => has(keys, s));
|
|
1215
|
+
const nameMatches = normalise(table.name) === normalise(sig.conventionalName) || normalise(table.name) === `${normalise(sig.conventionalName)}s`;
|
|
1216
|
+
const confidence = supporting.length > 0 || nameMatches ? "strong" : "likely";
|
|
1217
|
+
const matched = [...sig.required, ...supporting].map((c) => actualName(table, c)).filter((c) => Boolean(c));
|
|
1218
|
+
const secrets = sig.secrets.map((c) => actualName(table, c)).filter((c) => Boolean(c));
|
|
1219
|
+
return { table: table.name, model: sig.model, confidence, matched, secrets };
|
|
1220
|
+
}
|
|
1221
|
+
function detectAuthTables(analysis) {
|
|
1222
|
+
const found = [];
|
|
1223
|
+
for (const table of analysis.tables) {
|
|
1224
|
+
for (const sig of SIGNATURES) {
|
|
1225
|
+
const m = matchOne(table, sig);
|
|
1226
|
+
if (m) {
|
|
1227
|
+
found.push(m);
|
|
1228
|
+
break;
|
|
1229
|
+
}
|
|
1230
|
+
}
|
|
1231
|
+
}
|
|
1232
|
+
const hasNonUser = found.some((f) => f.model !== "user");
|
|
1233
|
+
return hasNonUser ? found : found.filter((f) => f.model !== "user");
|
|
1234
|
+
}
|
|
1235
|
+
function authTablesWithSecrets(matches) {
|
|
1236
|
+
return matches.filter((m) => m.secrets.length > 0);
|
|
1237
|
+
}
|
|
1238
|
+
function excludeSuggestion(matches) {
|
|
1239
|
+
const names = [...new Set(matches.map((m) => m.table))].sort();
|
|
1240
|
+
return `exclude: [${names.map((n) => `'${n}'`).join(", ")}]`;
|
|
1241
|
+
}
|
|
1242
|
+
function authTableWarnings(analysis) {
|
|
1243
|
+
const risky = authTablesWithSecrets(detectAuthTables(analysis));
|
|
1244
|
+
if (!risky.length) return [];
|
|
1245
|
+
const lines = risky.map(
|
|
1246
|
+
(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"}.`
|
|
1247
|
+
);
|
|
1248
|
+
return [
|
|
1249
|
+
`drzl generate: ${lines.join(" ")} Keep ${risky.length === 1 ? "it" : "them"} out with ${excludeSuggestion(risky)}, or leave it if the route is deliberate.`
|
|
1250
|
+
];
|
|
1251
|
+
}
|
|
1252
|
+
|
|
1253
|
+
// src/doctor.ts
|
|
1139
1254
|
import { Chalk as Chalk2 } from "chalk";
|
|
1140
1255
|
var PLAIN = new Chalk2({ level: 0 });
|
|
1141
1256
|
var HANDLED_CODES = /* @__PURE__ */ new Set(["DRZL_ANL_UNKNOWN_COLUMN"]);
|
|
1142
|
-
function splitPath(
|
|
1143
|
-
if (!
|
|
1144
|
-
const dot =
|
|
1145
|
-
if (dot <= 0) return { table:
|
|
1146
|
-
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) };
|
|
1147
1262
|
}
|
|
1148
1263
|
function namedColumns(parsed) {
|
|
1149
1264
|
const out = [];
|
|
@@ -1308,6 +1423,18 @@ function buildDoctorReport(analysis, schemaPath) {
|
|
|
1308
1423
|
hint: i.hint
|
|
1309
1424
|
});
|
|
1310
1425
|
}
|
|
1426
|
+
for (const match of detectAuthTables(analysis)) {
|
|
1427
|
+
const secrets = match.secrets.length ? ` It holds ${match.secrets.map((c) => `\`${c}\``).join(", ")}, which a generated read route would return to whoever calls it.` : "";
|
|
1428
|
+
findings.push({
|
|
1429
|
+
kind: "auth-table",
|
|
1430
|
+
// A warning rather than an error: this is a real leak and it is also a guess about someone
|
|
1431
|
+
// else's schema, and a doctor that failed the build on a guess would be switched off.
|
|
1432
|
+
level: "warn",
|
|
1433
|
+
table: match.table,
|
|
1434
|
+
message: `"${match.table}" matches the shape of an authentication library's ${match.model} table (${match.matched.join(", ")}).${secrets}`,
|
|
1435
|
+
hint: `Keep it out of every generator with ${excludeSuggestion([match])}.`
|
|
1436
|
+
});
|
|
1437
|
+
}
|
|
1311
1438
|
for (const i of analysis.issues) {
|
|
1312
1439
|
if (i.code !== "DRZL_ANL_UNKNOWN_COLUMN") continue;
|
|
1313
1440
|
findings.push({
|
|
@@ -1346,6 +1473,11 @@ var SECTIONS = [
|
|
|
1346
1473
|
title: "Columns DRZL cannot type",
|
|
1347
1474
|
why: "These get a validator that accepts any value."
|
|
1348
1475
|
},
|
|
1476
|
+
{
|
|
1477
|
+
kinds: ["auth-table"],
|
|
1478
|
+
title: "Tables that look like an authentication library's",
|
|
1479
|
+
why: "Every generator loops over every table it finds, so these get routes too."
|
|
1480
|
+
},
|
|
1349
1481
|
{
|
|
1350
1482
|
kinds: ["check-declined", "check-unknown-column", "check-not-scalar", "check-uncountable"],
|
|
1351
1483
|
title: "CHECK constraints DRZL does not enforce",
|
|
@@ -1678,11 +1810,11 @@ function issueTouches(issue, table) {
|
|
|
1678
1810
|
return dot > 0 && own.includes(issue.path.slice(0, dot));
|
|
1679
1811
|
}
|
|
1680
1812
|
function issueColumn(issue, table) {
|
|
1681
|
-
const
|
|
1813
|
+
const path9 = issue.path ?? "";
|
|
1682
1814
|
const names = namesOf(table);
|
|
1683
1815
|
for (const prefix of [names.qualified, names.tsName, names.name, table.name]) {
|
|
1684
|
-
if (
|
|
1685
|
-
const rest =
|
|
1816
|
+
if (path9.startsWith(`${prefix}.`)) {
|
|
1817
|
+
const rest = path9.slice(prefix.length + 1);
|
|
1686
1818
|
if (table.columns.some((c) => c.name === rest)) return rest;
|
|
1687
1819
|
}
|
|
1688
1820
|
}
|
|
@@ -2841,6 +2973,79 @@ function displayPath(file, cwd = process.cwd()) {
|
|
|
2841
2973
|
return rel && !rel.startsWith("..") ? rel : file;
|
|
2842
2974
|
}
|
|
2843
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
|
+
|
|
2844
3049
|
// src/unified-diff.ts
|
|
2845
3050
|
var DEFAULT_DIFF_LIMITS = {
|
|
2846
3051
|
maxLines: 4e3,
|
|
@@ -3083,8 +3288,8 @@ function createRebuildScheduler(options) {
|
|
|
3083
3288
|
|
|
3084
3289
|
// src/init.ts
|
|
3085
3290
|
import { SchemaAnalyzer } from "@drzl/analyzer";
|
|
3086
|
-
import * as
|
|
3087
|
-
import * as
|
|
3291
|
+
import * as fs5 from "fs";
|
|
3292
|
+
import * as path5 from "path";
|
|
3088
3293
|
var INIT_GENERATOR_CHOICES = [
|
|
3089
3294
|
{ kind: "zod", packageName: "@drzl/generator-zod", label: "Zod validators" },
|
|
3090
3295
|
{ kind: "valibot", packageName: "@drzl/generator-valibot", label: "Valibot validators" },
|
|
@@ -3155,7 +3360,7 @@ async function detectSchema(cwd) {
|
|
|
3155
3360
|
kitFiles = null;
|
|
3156
3361
|
}
|
|
3157
3362
|
if (kitFiles && kitPath) {
|
|
3158
|
-
const rel =
|
|
3363
|
+
const rel = path5.relative(cwd, kitPath) || path5.basename(kitPath);
|
|
3159
3364
|
const report = await classifySchemaCandidate(kitFiles);
|
|
3160
3365
|
if (report.verdict === "confirmed" || report.verdict === "unverified") {
|
|
3161
3366
|
notes.push(
|
|
@@ -3171,10 +3376,10 @@ async function detectSchema(cwd) {
|
|
|
3171
3376
|
}
|
|
3172
3377
|
notes.push(`${rel} names schema files that declare no Drizzle tables; looking elsewhere.`);
|
|
3173
3378
|
}
|
|
3174
|
-
const present = schemaCandidates().filter((c) =>
|
|
3379
|
+
const present = schemaCandidates().filter((c) => fs5.existsSync(path5.resolve(cwd, c)));
|
|
3175
3380
|
const unverified = [];
|
|
3176
3381
|
for (const file of present) {
|
|
3177
|
-
const report = await classifySchemaCandidate(
|
|
3382
|
+
const report = await classifySchemaCandidate(path5.resolve(cwd, file));
|
|
3178
3383
|
if (report.verdict === "confirmed") {
|
|
3179
3384
|
notes.push(
|
|
3180
3385
|
`Schema found at ${file} (${report.tables} table${report.tables === 1 ? "" : "s"})`
|
|
@@ -3276,7 +3481,7 @@ async function promptForPlan(args) {
|
|
|
3276
3481
|
if (answer === null) endedEarly = true;
|
|
3277
3482
|
else if (answer.trim()) {
|
|
3278
3483
|
const typed = answer.trim();
|
|
3279
|
-
const report = await classifySchemaCandidate(
|
|
3484
|
+
const report = await classifySchemaCandidate(path5.resolve(cwd, typed));
|
|
3280
3485
|
if (report.verdict === "confirmed") {
|
|
3281
3486
|
write(` ${typed}: ${report.tables} table${report.tables === 1 ? "" : "s"}`);
|
|
3282
3487
|
} else if (report.verdict === "unverified") {
|
|
@@ -3338,8 +3543,8 @@ function parseGeneratorsFlag(value) {
|
|
|
3338
3543
|
return parts;
|
|
3339
3544
|
}
|
|
3340
3545
|
async function runInit(args) {
|
|
3341
|
-
const target =
|
|
3342
|
-
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)));
|
|
3343
3548
|
if (existing) {
|
|
3344
3549
|
args.error(
|
|
3345
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.`
|
|
@@ -3378,8 +3583,8 @@ async function runInit(args) {
|
|
|
3378
3583
|
generators: fromFlag ?? [DEFAULT_GENERATOR_KIND]
|
|
3379
3584
|
};
|
|
3380
3585
|
if (args.schemaFlag) {
|
|
3381
|
-
const full =
|
|
3382
|
-
if (!
|
|
3586
|
+
const full = path5.resolve(args.cwd, args.schemaFlag);
|
|
3587
|
+
if (!fs5.existsSync(full)) {
|
|
3383
3588
|
args.log(`--schema ${args.schemaFlag} is not there yet. Writing it anyway.`);
|
|
3384
3589
|
} else if ((await classifySchemaCandidate(full)).verdict === "rejected") {
|
|
3385
3590
|
args.log(`--schema ${args.schemaFlag} declares no Drizzle tables. Writing it anyway.`);
|
|
@@ -3387,7 +3592,7 @@ async function runInit(args) {
|
|
|
3387
3592
|
}
|
|
3388
3593
|
}
|
|
3389
3594
|
try {
|
|
3390
|
-
|
|
3595
|
+
fs5.writeFileSync(target, renderInitConfig(plan), { flag: "wx" });
|
|
3391
3596
|
} catch (e) {
|
|
3392
3597
|
if (e?.code === "EEXIST") {
|
|
3393
3598
|
args.error(
|
|
@@ -3411,9 +3616,9 @@ async function runInit(args) {
|
|
|
3411
3616
|
|
|
3412
3617
|
// src/sponsor.ts
|
|
3413
3618
|
import { existsSync as existsSync3, mkdirSync, readFileSync, writeFileSync as writeFileSync2 } from "fs";
|
|
3414
|
-
import
|
|
3415
|
-
var CACHE_DIR =
|
|
3416
|
-
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");
|
|
3417
3622
|
var DEFAULT_INTERVAL_MS = 1e3 * 60 * 15;
|
|
3418
3623
|
var shownThisProcess = false;
|
|
3419
3624
|
var tips = [
|
|
@@ -3482,11 +3687,11 @@ function writeCache(payload) {
|
|
|
3482
3687
|
|
|
3483
3688
|
// src/version.ts
|
|
3484
3689
|
import { readFileSync as readFileSync2 } from "fs";
|
|
3485
|
-
import * as
|
|
3690
|
+
import * as path7 from "path";
|
|
3486
3691
|
import { fileURLToPath } from "url";
|
|
3487
3692
|
var PACKAGE_NAME = "@drzl/cli";
|
|
3488
3693
|
function moduleDir() {
|
|
3489
|
-
return
|
|
3694
|
+
return path7.dirname(fileURLToPath(import.meta.url));
|
|
3490
3695
|
}
|
|
3491
3696
|
function readVersionFrom(manifestPath) {
|
|
3492
3697
|
let raw;
|
|
@@ -3509,7 +3714,7 @@ function readVersionFrom(manifestPath) {
|
|
|
3509
3714
|
return manifest.version;
|
|
3510
3715
|
}
|
|
3511
3716
|
function readCliVersion() {
|
|
3512
|
-
return readVersionFrom(
|
|
3717
|
+
return readVersionFrom(path7.join(moduleDir(), "..", "package.json"));
|
|
3513
3718
|
}
|
|
3514
3719
|
var CLI_VERSION = readCliVersion();
|
|
3515
3720
|
|
|
@@ -3598,8 +3803,8 @@ withOutputFlags(
|
|
|
3598
3803
|
const errors = res.issues.some((i) => i.level === "error");
|
|
3599
3804
|
const code = unreadable ? EXIT_FAILED : errors ? EXIT_FINDINGS : EXIT_OK;
|
|
3600
3805
|
if (opts.out && !opts.json) {
|
|
3601
|
-
const
|
|
3602
|
-
await
|
|
3806
|
+
const fs6 = await import("fs/promises");
|
|
3807
|
+
await fs6.writeFile(opts.out, JSON.stringify(res, null, 2), "utf8");
|
|
3603
3808
|
spinner.succeed(`Analysis written to ${opts.out} in ${ms}ms`);
|
|
3604
3809
|
} else {
|
|
3605
3810
|
spinner.succeed(`Analyzed in ${ms}ms`);
|
|
@@ -3736,7 +3941,7 @@ async function explainSchemaSource(opts, out) {
|
|
|
3736
3941
|
schema: source.schema,
|
|
3737
3942
|
label: describeSchemaTarget(source.schema),
|
|
3738
3943
|
config: cfg,
|
|
3739
|
-
...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)}` } : {}
|
|
3740
3945
|
};
|
|
3741
3946
|
}
|
|
3742
3947
|
const detected = await detectSchema(process.cwd());
|
|
@@ -3846,7 +4051,11 @@ withOutputFlags(
|
|
|
3846
4051
|
).option(
|
|
3847
4052
|
"--check",
|
|
3848
4053
|
"regenerate and fail if the result differs from what is on disk, without changing it"
|
|
3849
|
-
).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
|
+
)
|
|
3850
4059
|
).action(async (opts) => {
|
|
3851
4060
|
const out = outputFor(opts);
|
|
3852
4061
|
const planning = !!opts.check || !!opts.dryRun;
|
|
@@ -3896,7 +4105,7 @@ withOutputFlags(
|
|
|
3896
4105
|
const n = source.schema.length;
|
|
3897
4106
|
out.note(
|
|
3898
4107
|
out.errStyle.gray(
|
|
3899
|
-
`Schema from ${
|
|
4108
|
+
`Schema from ${path8.relative(process.cwd(), source.drizzleKitConfigPath)} (${n} file${n === 1 ? "" : "s"})`
|
|
3900
4109
|
)
|
|
3901
4110
|
);
|
|
3902
4111
|
}
|
|
@@ -3925,6 +4134,7 @@ withOutputFlags(
|
|
|
3925
4134
|
analysis.tables = filterTables(narrowed.tables, cfg);
|
|
3926
4135
|
for (const w of [...narrowed.warnings, ...filterWarnings]) warn(w);
|
|
3927
4136
|
for (const w of wideColumnWarning(analysis.issues)) warn(w);
|
|
4137
|
+
for (const w of authTableWarnings(analysis)) warn(w);
|
|
3928
4138
|
const empty = nothingToGenerate({
|
|
3929
4139
|
schema: source.schema,
|
|
3930
4140
|
analyzed: narrowed.tables,
|
|
@@ -3994,6 +4204,19 @@ withOutputFlags(
|
|
|
3994
4204
|
files: e.files,
|
|
3995
4205
|
changes: e.changes.map((c) => ({ file: displayPath(c.file), status: c.status }))
|
|
3996
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
|
+
}
|
|
3997
4220
|
if (planning) {
|
|
3998
4221
|
const wrote = await verifyNothingWasWritten(outputDirs, existing);
|
|
3999
4222
|
if (wrote.length) {
|
|
@@ -4003,6 +4226,14 @@ withOutputFlags(
|
|
|
4003
4226
|
process.exit(EXIT_FAILED);
|
|
4004
4227
|
}
|
|
4005
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
|
+
}
|
|
4006
4237
|
if (opts.check) {
|
|
4007
4238
|
const drift = pendingChanges(plan);
|
|
4008
4239
|
const upToDate = drift.length === 0;
|
|
@@ -4244,10 +4475,10 @@ program.command("watch").description("Watch schema and regenerate on changes").o
|
|
|
4244
4475
|
return;
|
|
4245
4476
|
}
|
|
4246
4477
|
let cfg = loaded;
|
|
4247
|
-
const abs = (p) =>
|
|
4478
|
+
const abs = (p) => path8.resolve(process.cwd(), p);
|
|
4248
4479
|
const isInside = (child, parent) => {
|
|
4249
|
-
const rel =
|
|
4250
|
-
return !!rel && !rel.startsWith("..") && !
|
|
4480
|
+
const rel = path8.relative(parent, child);
|
|
4481
|
+
return !!rel && !rel.startsWith("..") && !path8.isAbsolute(rel);
|
|
4251
4482
|
};
|
|
4252
4483
|
let source;
|
|
4253
4484
|
try {
|
|
@@ -4283,7 +4514,7 @@ program.command("watch").description("Watch schema and regenerate on changes").o
|
|
|
4283
4514
|
if (full === dir || isInside(full, dir)) return true;
|
|
4284
4515
|
}
|
|
4285
4516
|
if (stats?.isDirectory()) return false;
|
|
4286
|
-
const ext =
|
|
4517
|
+
const ext = path8.extname(full);
|
|
4287
4518
|
if (!ext) return false;
|
|
4288
4519
|
return !WATCHED_EXTENSIONS.has(ext);
|
|
4289
4520
|
};
|
|
@@ -4446,7 +4677,7 @@ program.command("watch").description("Watch schema and regenerate on changes").o
|
|
|
4446
4677
|
} else {
|
|
4447
4678
|
out.note(
|
|
4448
4679
|
out.errStyle.gray(
|
|
4449
|
-
"Watching:\n " + Array.from(currentTargets).map((p) =>
|
|
4680
|
+
"Watching:\n " + Array.from(currentTargets).map((p) => path8.relative(process.cwd(), p)).join("\n ")
|
|
4450
4681
|
)
|
|
4451
4682
|
);
|
|
4452
4683
|
}
|