@drzl/cli 4.1.0 → 4.2.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-PP33LGZD.js → chunk-UXA4IRLP.js} +13 -1
- package/dist/chunk-UXA4IRLP.js.map +1 -0
- package/dist/cli.cjs +115 -22
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +99 -18
- package/dist/cli.js.map +1 -1
- package/dist/config.cjs +12 -0
- package/dist/config.cjs.map +1 -1
- package/dist/config.d.cts +2 -0
- package/dist/config.d.ts +2 -0
- package/dist/config.js +1 -1
- package/package.json +5 -5
- package/dist/chunk-PP33LGZD.js.map +0 -1
|
@@ -53,6 +53,18 @@ var GeneratorSchema = z.object({
|
|
|
53
53
|
importExtension: ImportExtensionSchema.optional(),
|
|
54
54
|
template: z.string().optional(),
|
|
55
55
|
includeRelations: z.boolean().optional(),
|
|
56
|
+
/**
|
|
57
|
+
* Type `json` and `jsonb` columns from the schema rather than leaving them wide.
|
|
58
|
+
*
|
|
59
|
+
* `.$type<T>()` is a compile-time cast, so no runtime-derived validator can see it and
|
|
60
|
+
* `drizzle-orm/zod` types every json column as its generic `Json`. A generator can reference
|
|
61
|
+
* `typeof <table>.$inferSelect['<column>']` instead, which is the declared type resolved by
|
|
62
|
+
* TypeScript itself, so generics, unions and imported interfaces all work.
|
|
63
|
+
*
|
|
64
|
+
* Off by default because it makes the generated file import your schema module, as a
|
|
65
|
+
* type-only import that disappears at build time.
|
|
66
|
+
*/
|
|
67
|
+
typedJson: z.boolean().optional(),
|
|
56
68
|
naming: NamingSchema.optional(),
|
|
57
69
|
outputHeader: z.object({
|
|
58
70
|
enabled: z.boolean().default(true).optional(),
|
|
@@ -327,4 +339,4 @@ export {
|
|
|
327
339
|
filterTables,
|
|
328
340
|
computeWatchTargets
|
|
329
341
|
};
|
|
330
|
-
//# sourceMappingURL=chunk-
|
|
342
|
+
//# sourceMappingURL=chunk-UXA4IRLP.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/config.ts"],"sourcesContent":["import type { AffixOptions } from '@drzl/validation-core';\nimport {\n AFFIX_PROBE_TABLE,\n DEFAULT_IMPORT_EXTENSION,\n IMPORT_EXTENSIONS,\n NAME_MODES,\n resolveAffix,\n schemaName,\n validateAffix,\n} from '@drzl/validation-core';\nimport * as fs from 'node:fs';\nimport { createRequire } from 'node:module';\nimport * as path from 'node:path';\nimport { z } from 'zod';\n\nexport const NamingSchema = z\n .object({\n routerSuffix: z.string().default('Router'),\n procedureCase: z.enum(['camel', 'kebab', 'snake']).default('camel'),\n })\n .partial();\n\n/** One affix for every mode, or a per-mode map. Keys match drzl's internal mode names. */\nconst AffixValueSchema = z.union(\n [\n z.string(),\n z\n .object({\n insert: z.string().optional(),\n update: z.string().optional(),\n select: z.string().optional(),\n })\n .strict(),\n ],\n {\n error:\n 'Expected a string to use for every mode, or an object with any of the keys \"insert\", ' +\n '\"update\" and \"select\". Those keys are lowercase, matching the mode names drzl uses ' +\n 'everywhere else.',\n }\n);\n\nconst AffixPartSchema = z\n .object({\n prefix: AffixValueSchema.optional(),\n suffix: AffixValueSchema.optional(),\n })\n .strict();\n\nexport const AffixSchema = z\n .object({\n /**\n * `preserve` (default) keeps today's output: the Drizzle export name goes into the\n * identifier verbatim, so `export const users` yields `InsertusersSchema`. `pascal`\n * upper-camels it first, yielding `InsertUsersSchema`.\n */\n tableCase: z.enum(['preserve', 'pascal']).optional(),\n schema: AffixPartSchema.optional(),\n type: AffixPartSchema.optional(),\n })\n .strict();\n\n/**\n * How every relative specifier drzl invents spells its extension.\n *\n * The generated files land in the consumer's own source tree, so the consumer's\n * `moduleResolution` decides which forms resolve. `js` is the only one that resolves under\n * all of `bundler`, `node10`, `node16` and `nodenext` with no compiler flag, so it is the\n * default. See the `ImportExtension` docs in `@drzl/validation-core` for the measured grid.\n */\nexport const ImportExtensionSchema = z.enum(IMPORT_EXTENSIONS);\n\nexport const GeneratorSchema = z.object({\n kind: z.enum(['orpc', 'service', 'zod', 'valibot', 'arktype']),\n /**\n * Overrides the top-level `importExtension` for this generator alone, for a project whose\n * generated directories are compiled by different tsconfigs.\n */\n importExtension: ImportExtensionSchema.optional(),\n template: z.string().optional(),\n includeRelations: z.boolean().optional(),\n /**\n * Type `json` and `jsonb` columns from the schema rather than leaving them wide.\n *\n * `.$type<T>()` is a compile-time cast, so no runtime-derived validator can see it and\n * `drizzle-orm/zod` types every json column as its generic `Json`. A generator can reference\n * `typeof <table>.$inferSelect['<column>']` instead, which is the declared type resolved by\n * TypeScript itself, so generics, unions and imported interfaces all work.\n *\n * Off by default because it makes the generated file import your schema module, as a\n * type-only import that disappears at build time.\n */\n typedJson: z.boolean().optional(),\n naming: NamingSchema.optional(),\n outputHeader: z\n .object({\n enabled: z.boolean().default(true).optional(),\n text: z.string().optional(),\n })\n .optional(),\n format: z\n .object({\n enabled: z.boolean().default(true).optional(),\n engine: z.enum(['auto', 'prettier', 'biome']).default('auto').optional(),\n configPath: z.string().optional(),\n })\n .optional(),\n // service generator specific options\n path: z.string().optional(),\n dataAccess: z.enum(['stub', 'drizzle']).default('stub').optional(),\n dbImportPath: z.string().optional(),\n schemaImportPath: z.string().optional(),\n // zod/valibot/arktype generator specific options\n schemaSuffix: z.string().optional(),\n fileSuffix: z.string().optional(),\n /**\n * Prefixes, suffixes and table casing for generated identifiers (zod/valibot/arktype).\n * Omitting it reproduces the output of every previous release exactly.\n */\n affix: AffixSchema.optional(),\n // orpc validation sharing\n validation: z\n .object({\n useShared: z.boolean().default(false).optional(),\n library: z.enum(['zod', 'valibot', 'arktype']).default('zod').optional(),\n importPath: z.string().optional(),\n schemaSuffix: z.string().optional(),\n /**\n * How the validation generator named its exports. Usually left unset: the CLI copies\n * it from the sibling generator whose `kind` matches `library`.\n */\n affix: AffixSchema.optional(),\n })\n .optional(),\n // template options\n templateOptions: z.record(z.string(), z.any()).optional(),\n});\n\nexport const AnalyzerSchema = z.object({\n includeRelations: z.boolean().default(true),\n validateConstraints: z.boolean().default(true),\n includeHeuristicRelations: z.boolean().default(false),\n});\n\nexport const ConfigSchema = z\n .object({\n schema: z.string(),\n outDir: z.string().default('src/api'),\n /**\n * Which tables to generate for, matched against the database table name.\n *\n * There was no way to say this, and every generator loops over every table it finds, so\n * DRZL emitted unauthenticated CRUD over whatever shared the schema file. That is noise for\n * a migrations table and a genuine leak for an auth one: Better Auth puts `user`, `session`,\n * `account` and `verification` alongside your own tables, and `account` holds\n * `accessToken`, `refreshToken`, `idToken` and `password`.\n *\n * Deliberately name-based and explicit rather than detecting any particular library. Auth\n * table names are all renameable, so a built-in list would miss renamed tables and, worse,\n * silently skip an ordinary table that happened to be called `user`, which is usually the\n * application's main entity.\n *\n * `exclude` wins over `include`. Patterns support `*`, matching within a name.\n */\n include: z.array(z.string()).optional(),\n exclude: z.array(z.string()).optional(),\n /**\n * How every relative specifier drzl invents spells its extension, for every generator.\n * A generator may override it. Defaults to `js`, which is the only form that resolves\n * under every `moduleResolution` without a compiler flag.\n */\n importExtension: ImportExtensionSchema.default(DEFAULT_IMPORT_EXTENSION),\n analyzer: AnalyzerSchema.default({\n includeRelations: true,\n validateConstraints: true,\n includeHeuristicRelations: false,\n }),\n generators: z\n .array(GeneratorSchema)\n .min(1)\n .default([{ kind: 'orpc' } as any]),\n })\n // Reject an affix before anything is written, rather than emitting a file that cannot\n // compile. Only `affix` is inspected; the legacy flat `schemaSuffix` is left alone so\n // configs that parse today keep parsing.\n .superRefine((cfg, ctx) => {\n cfg.generators.forEach((g, i) => {\n const report = (base: (string | number)[], affix?: AffixOptions, schemaSuffix?: string) => {\n for (const issue of validateAffix(affix, schemaSuffix)) {\n ctx.addIssue({\n code: 'custom',\n path: ['generators', i, ...base, ...issue.path],\n message: issue.message,\n });\n }\n };\n report(['affix'], g.affix as AffixOptions | undefined, g.schemaSuffix);\n report(\n ['validation', 'affix'],\n g.validation?.affix as AffixOptions | undefined,\n g.validation?.schemaSuffix\n );\n });\n });\n\n// ✨ Separate input vs output types\nexport type DrzlConfigInput = z.input<typeof ConfigSchema>;\nexport type DrzlConfig = z.output<typeof ConfigSchema>;\n\nexport function defineConfig<T extends DrzlConfigInput>(cfg: T): T {\n return cfg;\n}\n\ntype GeneratorConfig = DrzlConfig['generators'][number];\n\nfunction sharedSchemaNames(opts: { affix?: AffixOptions; schemaSuffix?: string }): string[] {\n const resolved = resolveAffix(opts);\n return NAME_MODES.map((mode) => schemaName(mode, AFFIX_PROBE_TABLE, resolved));\n}\n\n/**\n * Fill in cross-generator defaults and refuse configs whose generators would disagree.\n *\n * An oRPC router that imports shared schemas has to spell the exact names the validation\n * generator exported. Both sides used to be configured independently, so they could silently\n * drift into a router that does not compile. When an oRPC generator uses shared validation\n * and exactly one sibling generator produces that library, its `affix` is copied across.\n *\n * Deliberately conservative about the pre-existing flat `schemaSuffix`: a disagreement there\n * is only reported, never repaired, because repairing it would change the bytes an existing\n * config emits.\n *\n * `importExtension` is pushed down here too. A consumer compiles the whole generated tree\n * with one tsconfig, so the setting that has to hold is the same for every generator, and\n * every call site downstream can then read it off the generator without knowing about the\n * top-level default.\n */\nexport function resolveConfig(cfg: DrzlConfig): { config: DrzlConfig; warnings: string[] } {\n const warnings: string[] = [];\n const generators: GeneratorConfig[] = cfg.generators.map((g) => ({\n ...g,\n importExtension: g.importExtension ?? cfg.importExtension,\n }));\n\n for (const g of generators) {\n if (g.kind !== 'orpc') continue;\n const v = g.validation;\n if (!v?.useShared) continue;\n\n const library = v.library ?? 'zod';\n const siblings = generators.filter((s) => s.kind === library);\n // Zero siblings means the user points at a barrel drzl does not generate; more than one\n // means there is no single source of truth. Either way, leave the config alone.\n if (siblings.length !== 1) continue;\n const sibling = siblings[0];\n\n const theirs = sharedSchemaNames({\n affix: sibling.affix as AffixOptions | undefined,\n schemaSuffix: sibling.schemaSuffix,\n });\n\n if (!v.affix) {\n if (sibling.affix) {\n // Bake the sibling's fully resolved naming in, so its own schemaSuffix fallback\n // travels with it and cannot be re-interpreted on the oRPC side.\n g.validation = {\n ...v,\n affix: resolveAffix({\n affix: sibling.affix as AffixOptions,\n schemaSuffix: sibling.schemaSuffix,\n }),\n };\n continue;\n }\n const mine = sharedSchemaNames({ schemaSuffix: v.schemaSuffix });\n if (mine.join(',') !== theirs.join(',')) {\n warnings.push(\n `drzl config: the \"orpc\" generator's validation.schemaSuffix ` +\n `(${JSON.stringify(v.schemaSuffix ?? 'Schema')}) does not match the \"${library}\" ` +\n `generator's schemaSuffix (${JSON.stringify(sibling.schemaSuffix ?? 'Schema')}). ` +\n `The router will import ${mine.join(', ')} but the \"${library}\" generator exports ` +\n `${theirs.join(', ')}, so the generated router will not compile. Set both to the ` +\n `same value, or move to \"affix\", which is inherited automatically.`\n );\n }\n continue;\n }\n\n const mine = sharedSchemaNames({\n affix: v.affix as AffixOptions,\n schemaSuffix: v.schemaSuffix,\n });\n if (mine.join(',') !== theirs.join(',')) {\n throw new Error(\n `drzl config: the \"orpc\" generator imports shared ${library} schemas, but its ` +\n `validation.affix disagrees with the \"${library}\" generator's own naming. The router ` +\n `would import ${mine.join(', ')} while the \"${library}\" generator exports ` +\n `${theirs.join(', ')}. Make them match, or drop validation.affix and let it be ` +\n `inherited from the \"${library}\" generator.`\n );\n }\n }\n\n return { config: { ...cfg, generators }, warnings };\n}\n\n/**\n * Parse, then resolve cross-generator defaults. Both `generate` and `watch` go through\n * loadConfig, so putting the resolution here is what keeps the two duplicated generator\n * dispatch blocks in cli.ts from needing the logic twice.\n */\nfunction finalize(raw: unknown): DrzlConfig {\n const { config, warnings } = resolveConfig(ConfigSchema.parse(raw));\n for (const w of warnings) console.warn(w);\n return config;\n}\n\nexport async function loadConfig(customPath?: string): Promise<DrzlConfig | null> {\n const fsp = await import('node:fs/promises');\n\n const candidates = customPath\n ? [customPath]\n : [\n 'drzl.config.ts',\n 'drzl.config.mjs',\n 'drzl.config.js',\n 'drzl.config.cjs',\n 'drzl.config.json',\n ];\n\n for (const c of candidates) {\n const p = path.resolve(process.cwd(), c);\n try {\n await fsp.access(p);\n } catch {\n continue;\n }\n\n const ext = path.extname(p).toLowerCase();\n\n // JSON: read directly\n if (ext === '.json') {\n const raw = JSON.parse(await fsp.readFile(p, 'utf8'));\n return finalize(raw);\n }\n\n // Everything else (TS/JS/MJS/CJS) -> Jiti with cache-busting\n const { createJiti } = await import('jiti');\n const stat = await fsp.stat(p);\n\n // Passing __filename is safe in CJS; fallback to cwd if not defined.\n const base =\n typeof __filename !== 'undefined' ? __filename : path.join(process.cwd(), 'index.js');\n\n const jiti = createJiti(base, {\n moduleCache: false, // re-evaluate each time\n fsCache: true, // keep transform cache\n cacheVersion: String(stat.mtimeMs), // bump on edit\n interopDefault: true,\n tryNative: false, // <-- prevent native import of .ts\n // debug: true,\n }) as any;\n\n const mod = await jiti.import(p);\n const raw = mod?.default ?? mod;\n return finalize(raw);\n }\n\n return null;\n}\n\n/** Absolute output dirs for all generators (to ignore in watcher). */\nexport function computeGeneratorOutputDirs(cfg: DrzlConfig, cwd = process.cwd()): string[] {\n const abs = (p: string) => path.resolve(cwd, p);\n const dirs = new Set<string>();\n dirs.add(abs(cfg.outDir)); // orpc\n for (const g of cfg.generators) {\n if (g.kind === 'service') dirs.add(abs(g.path ?? 'src/services'));\n if (g.kind === 'zod') dirs.add(abs(g.path ?? 'src/validators/zod'));\n if (g.kind === 'valibot') dirs.add(abs(g.path ?? 'src/validators/valibot'));\n if (g.kind === 'arktype') dirs.add(abs(g.path ?? 'src/validators/arktype'));\n }\n return [...dirs];\n}\n\n/** Resolve custom template directories (local path or installed package). */\nexport function resolveTemplateDirsSync(cfg: DrzlConfig, cwd = process.cwd()): string[] {\n const results: string[] = [];\n const req = createRequire(\n typeof __filename !== 'undefined' ? __filename : path.join(process.cwd(), 'index.js')\n );\n\n for (const g of cfg.generators) {\n const t = g.template;\n if (!t || t === 'standard' || t === 'minimal') continue;\n\n // Try package resolution relative to cwd\n let pkgDir: string | null = null;\n try {\n const pkg = req.resolve(`${t}/package.json`, { paths: [cwd] as any });\n pkgDir = path.dirname(pkg);\n } catch {}\n\n if (pkgDir) {\n results.push(pkgDir);\n continue;\n }\n\n // Local path-like template\n if (/[./\\\\]/.test(t)) {\n const abs = path.resolve(cwd, t);\n if (fs.existsSync(abs)) results.push(abs);\n }\n }\n\n return Array.from(new Set(results));\n}\n\n/** Build watch targets (exclude output dirs; watcher will ignore those). */\n/**\n * Narrow an analysis's tables to the ones the config asked for.\n *\n * Matching is on the database table name, anchored, with `*` as the only metacharacter. Anchored\n * matters: `user` must not also drop `users`, and a substring match would. `exclude` is applied\n * after `include`, so the safer direction wins when both name the same table.\n */\nexport function filterTables<T extends { name: string }>(\n tables: T[],\n opts: { include?: string[]; exclude?: string[] }\n): T[] {\n const toRegExp = (pattern: string) =>\n new RegExp(\n '^' +\n pattern\n .split('*')\n .map((part) => part.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&'))\n .join('.*') +\n '$'\n );\n\n const matches = (patterns: string[], name: string) =>\n patterns.some((p) => toRegExp(p).test(name));\n\n let out = tables;\n if (opts.include?.length) out = out.filter((t) => matches(opts.include!, t.name));\n if (opts.exclude?.length) out = out.filter((t) => !matches(opts.exclude!, t.name));\n return out;\n}\n\nexport function computeWatchTargets(cfg: DrzlConfig, cwd = process.cwd()): string[] {\n const abs = (p: string) => path.resolve(cwd, p);\n const schemaAbs = abs(cfg.schema);\n // The schema's directory, not a glob under it. Chokidar removed glob support in v4 and treats\n // `<dir>/**/*.{ts,tsx,js}` as a literal path, so it watched a directory named `**` that does\n // not exist: no event ever fired and `drzl watch` did its initial build and then sat inert.\n // A directory is watched recursively by chokidar itself, and the extension filtering that the\n // glob was doing now happens on the event instead.\n const targets = new Set<string>([\n path.dirname(schemaAbs),\n abs('drzl.config.ts'),\n abs('drzl.config.js'),\n abs('drzl.config.mjs'),\n abs('drzl.config.cjs'),\n ]);\n for (const t of resolveTemplateDirsSync(cfg, cwd)) targets.add(t);\n return [...targets];\n}\n"],"mappings":"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","names":["mine","raw"]}
|
package/dist/cli.cjs
CHANGED
|
@@ -30,7 +30,7 @@ var import_chalk2 = __toESM(require("chalk"), 1);
|
|
|
30
30
|
var import_chokidar = __toESM(require("chokidar"), 1);
|
|
31
31
|
var import_cli_progress = __toESM(require("cli-progress"), 1);
|
|
32
32
|
var import_commander = require("commander");
|
|
33
|
-
var
|
|
33
|
+
var path4 = __toESM(require("path"), 1);
|
|
34
34
|
var import_ora = __toESM(require("ora"), 1);
|
|
35
35
|
|
|
36
36
|
// src/config.ts
|
|
@@ -80,6 +80,18 @@ var GeneratorSchema = import_zod.z.object({
|
|
|
80
80
|
importExtension: ImportExtensionSchema.optional(),
|
|
81
81
|
template: import_zod.z.string().optional(),
|
|
82
82
|
includeRelations: import_zod.z.boolean().optional(),
|
|
83
|
+
/**
|
|
84
|
+
* Type `json` and `jsonb` columns from the schema rather than leaving them wide.
|
|
85
|
+
*
|
|
86
|
+
* `.$type<T>()` is a compile-time cast, so no runtime-derived validator can see it and
|
|
87
|
+
* `drizzle-orm/zod` types every json column as its generic `Json`. A generator can reference
|
|
88
|
+
* `typeof <table>.$inferSelect['<column>']` instead, which is the declared type resolved by
|
|
89
|
+
* TypeScript itself, so generics, unions and imported interfaces all work.
|
|
90
|
+
*
|
|
91
|
+
* Off by default because it makes the generated file import your schema module, as a
|
|
92
|
+
* type-only import that disappears at build time.
|
|
93
|
+
*/
|
|
94
|
+
typedJson: import_zod.z.boolean().optional(),
|
|
83
95
|
naming: NamingSchema.optional(),
|
|
84
96
|
outputHeader: import_zod.z.object({
|
|
85
97
|
enabled: import_zod.z.boolean().default(true).optional(),
|
|
@@ -336,12 +348,63 @@ function computeWatchTargets(cfg, cwd = process.cwd()) {
|
|
|
336
348
|
return [...targets];
|
|
337
349
|
}
|
|
338
350
|
|
|
339
|
-
// src/
|
|
340
|
-
var import_chalk = __toESM(require("chalk"), 1);
|
|
351
|
+
// src/drift.ts
|
|
341
352
|
var import_node_fs = require("fs");
|
|
342
353
|
var import_node_path = __toESM(require("path"), 1);
|
|
343
|
-
|
|
344
|
-
|
|
354
|
+
async function snapshotDir(dir) {
|
|
355
|
+
const out = /* @__PURE__ */ new Map();
|
|
356
|
+
async function walk(current) {
|
|
357
|
+
let entries;
|
|
358
|
+
try {
|
|
359
|
+
entries = await import_node_fs.promises.readdir(current, { withFileTypes: true });
|
|
360
|
+
} catch {
|
|
361
|
+
return;
|
|
362
|
+
}
|
|
363
|
+
for (const e of entries) {
|
|
364
|
+
const full = import_node_path.default.join(current, e.name);
|
|
365
|
+
if (e.isDirectory()) await walk(full);
|
|
366
|
+
else out.set(import_node_path.default.relative(dir, full), await import_node_fs.promises.readFile(full, "utf8"));
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
await walk(dir);
|
|
370
|
+
return out;
|
|
371
|
+
}
|
|
372
|
+
async function snapshotAll(dirs) {
|
|
373
|
+
const all = /* @__PURE__ */ new Map();
|
|
374
|
+
for (const dir of dirs) {
|
|
375
|
+
for (const [rel, content] of await snapshotDir(dir)) {
|
|
376
|
+
all.set(import_node_path.default.join(dir, rel), content);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
return all;
|
|
380
|
+
}
|
|
381
|
+
function diffSnapshots(before, after) {
|
|
382
|
+
const out = [];
|
|
383
|
+
for (const [file, content] of after) {
|
|
384
|
+
if (!before.has(file)) out.push({ file, status: "added" });
|
|
385
|
+
else if (before.get(file) !== content) out.push({ file, status: "changed" });
|
|
386
|
+
}
|
|
387
|
+
for (const file of before.keys()) {
|
|
388
|
+
if (!after.has(file)) out.push({ file, status: "removed" });
|
|
389
|
+
}
|
|
390
|
+
return out.sort((a, b) => a.file.localeCompare(b.file));
|
|
391
|
+
}
|
|
392
|
+
async function restoreSnapshot(before, after) {
|
|
393
|
+
for (const [file, content] of before) {
|
|
394
|
+
await import_node_fs.promises.mkdir(import_node_path.default.dirname(file), { recursive: true });
|
|
395
|
+
await import_node_fs.promises.writeFile(file, content, "utf8");
|
|
396
|
+
}
|
|
397
|
+
for (const file of after.keys()) {
|
|
398
|
+
if (!before.has(file)) await import_node_fs.promises.rm(file, { force: true });
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
// src/sponsor.ts
|
|
403
|
+
var import_chalk = __toESM(require("chalk"), 1);
|
|
404
|
+
var import_node_fs2 = require("fs");
|
|
405
|
+
var import_node_path2 = __toESM(require("path"), 1);
|
|
406
|
+
var CACHE_DIR = import_node_path2.default.join(process.cwd(), "node_modules", ".cache", "@drzl");
|
|
407
|
+
var CACHE_FILE = import_node_path2.default.join(CACHE_DIR, "sponsor-message.json");
|
|
345
408
|
var DEFAULT_INTERVAL_MS = 1e3 * 60 * 15;
|
|
346
409
|
var shownThisProcess = false;
|
|
347
410
|
var tips = [
|
|
@@ -362,7 +425,7 @@ function maybeShowSponsorMessage({
|
|
|
362
425
|
const hideRequested = hideViaEnv === "1" || hideViaEnv === "true";
|
|
363
426
|
if (hideRequested || process.env.CI && !force || shownThisProcess && !force) return;
|
|
364
427
|
try {
|
|
365
|
-
(0,
|
|
428
|
+
(0, import_node_fs2.mkdirSync)(CACHE_DIR, { recursive: true });
|
|
366
429
|
const payload = readCache();
|
|
367
430
|
payload.runs += 1;
|
|
368
431
|
const now = Date.now();
|
|
@@ -389,11 +452,11 @@ ${green("Pro tip:")} ${tip}
|
|
|
389
452
|
}
|
|
390
453
|
}
|
|
391
454
|
function readCache() {
|
|
392
|
-
if (!(0,
|
|
455
|
+
if (!(0, import_node_fs2.existsSync)(CACHE_FILE)) {
|
|
393
456
|
return { runs: 0 };
|
|
394
457
|
}
|
|
395
458
|
try {
|
|
396
|
-
const data = JSON.parse((0,
|
|
459
|
+
const data = JSON.parse((0, import_node_fs2.readFileSync)(CACHE_FILE, "utf8"));
|
|
397
460
|
if (typeof data.runs !== "number") return { runs: 0 };
|
|
398
461
|
return data;
|
|
399
462
|
} catch {
|
|
@@ -401,7 +464,7 @@ function readCache() {
|
|
|
401
464
|
}
|
|
402
465
|
}
|
|
403
466
|
function writeCache(payload) {
|
|
404
|
-
(0,
|
|
467
|
+
(0, import_node_fs2.writeFileSync)(CACHE_FILE, JSON.stringify(payload, null, 2), "utf8");
|
|
405
468
|
}
|
|
406
469
|
|
|
407
470
|
// src/cli.ts
|
|
@@ -428,8 +491,8 @@ program.command("analyze").argument("<schema>", "path to drizzle schema (TS)").o
|
|
|
428
491
|
if (opts.json) {
|
|
429
492
|
console.log(json);
|
|
430
493
|
} else if (opts.out) {
|
|
431
|
-
const
|
|
432
|
-
await
|
|
494
|
+
const fs3 = await import("fs/promises");
|
|
495
|
+
await fs3.writeFile(opts.out, json, "utf8");
|
|
433
496
|
spinner?.succeed(import_chalk2.default.green(`Analysis written to ${opts.out} in ${ms}ms`));
|
|
434
497
|
} else {
|
|
435
498
|
spinner?.succeed(import_chalk2.default.green(`Analyzed in ${ms}ms`));
|
|
@@ -449,7 +512,10 @@ program.command("analyze").argument("<schema>", "path to drizzle schema (TS)").o
|
|
|
449
512
|
process.exit(1);
|
|
450
513
|
}
|
|
451
514
|
});
|
|
452
|
-
program.command("generate").description("Run configured generators (drzl.config.*)").option("-c, --config <path>", "path to drzl.config").
|
|
515
|
+
program.command("generate").description("Run configured generators (drzl.config.*)").option("-c, --config <path>", "path to drzl.config").option(
|
|
516
|
+
"--check",
|
|
517
|
+
"regenerate and fail if the result differs from what is on disk, without changing it"
|
|
518
|
+
).action(async (opts) => {
|
|
453
519
|
try {
|
|
454
520
|
const cfg = await loadConfig(opts.config);
|
|
455
521
|
if (!cfg) {
|
|
@@ -469,6 +535,8 @@ program.command("generate").description("Run configured generators (drzl.config.
|
|
|
469
535
|
});
|
|
470
536
|
analysis.tables = filterTables(analysis.tables, cfg);
|
|
471
537
|
spinner.succeed(`Analysis complete in ${Date.now() - t0}ms`);
|
|
538
|
+
const driftDirs = computeGeneratorOutputDirs(cfg);
|
|
539
|
+
const driftBefore = opts.check ? await snapshotAll(driftDirs) : null;
|
|
472
540
|
const progress = new import_cli_progress.default.SingleBar(
|
|
473
541
|
{ hideCursor: true },
|
|
474
542
|
import_cli_progress.default.Presets.shades_classic
|
|
@@ -533,7 +601,11 @@ program.command("generate").description("Run configured generators (drzl.config.
|
|
|
533
601
|
schemaSuffix: g.schemaSuffix,
|
|
534
602
|
fileSuffix: g.fileSuffix,
|
|
535
603
|
importExtension: g.importExtension,
|
|
536
|
-
affix: g.affix
|
|
604
|
+
affix: g.affix,
|
|
605
|
+
// Needed by typedJson, which imports the schema back to reference the type
|
|
606
|
+
// Drizzle inferred for a json column.
|
|
607
|
+
schemaPath: cfg.schema,
|
|
608
|
+
typedJson: g.typedJson
|
|
537
609
|
});
|
|
538
610
|
progress.stop();
|
|
539
611
|
(0, import_ora.default)().succeed(import_chalk2.default.green(`Generated (zod): ${files.length} files`));
|
|
@@ -601,6 +673,27 @@ program.command("generate").description("Run configured generators (drzl.config.
|
|
|
601
673
|
}
|
|
602
674
|
}
|
|
603
675
|
}
|
|
676
|
+
if (driftBefore) {
|
|
677
|
+
const after = await snapshotAll(driftDirs);
|
|
678
|
+
const drift = diffSnapshots(driftBefore, after);
|
|
679
|
+
await restoreSnapshot(driftBefore, after);
|
|
680
|
+
if (drift.length) {
|
|
681
|
+
console.error(
|
|
682
|
+
import_chalk2.default.red(`
|
|
683
|
+
Generated output is out of date (${drift.length} file(s)):`)
|
|
684
|
+
);
|
|
685
|
+
for (const d of drift) {
|
|
686
|
+
const mark = d.status === "added" ? "+" : d.status === "removed" ? "-" : "~";
|
|
687
|
+
console.error(` ${mark} ${import_chalk2.default.yellow(d.status.padEnd(8))} ${path4.relative(process.cwd(), d.file)}`);
|
|
688
|
+
}
|
|
689
|
+
console.error(
|
|
690
|
+
import_chalk2.default.dim("\nRun `drzl generate` and commit the result. Nothing was written by this check.")
|
|
691
|
+
);
|
|
692
|
+
process.exit(1);
|
|
693
|
+
}
|
|
694
|
+
console.log(import_chalk2.default.green("Generated output is up to date."));
|
|
695
|
+
return;
|
|
696
|
+
}
|
|
604
697
|
if (cfg.generators.length) {
|
|
605
698
|
maybeShowSponsorMessage({ reason: "generate" });
|
|
606
699
|
}
|
|
@@ -640,10 +733,10 @@ program.command("watch").description("Watch schema and regenerate on changes").o
|
|
|
640
733
|
process.exit(2);
|
|
641
734
|
return;
|
|
642
735
|
}
|
|
643
|
-
const abs = (p) =>
|
|
736
|
+
const abs = (p) => path4.resolve(process.cwd(), p);
|
|
644
737
|
const isInside = (child, parent) => {
|
|
645
|
-
const rel =
|
|
646
|
-
return !!rel && !rel.startsWith("..") && !
|
|
738
|
+
const rel = path4.relative(parent, child);
|
|
739
|
+
return !!rel && !rel.startsWith("..") && !path4.isAbsolute(rel);
|
|
647
740
|
};
|
|
648
741
|
const ignoredOutDirs = new Set(computeGeneratorOutputDirs(cfg).map(abs));
|
|
649
742
|
const currentTargets = new Set(computeWatchTargets(cfg).map(abs));
|
|
@@ -668,7 +761,7 @@ program.command("watch").description("Watch schema and regenerate on changes").o
|
|
|
668
761
|
if (full === dir || isInside(full, dir)) return true;
|
|
669
762
|
}
|
|
670
763
|
if (stats?.isDirectory()) return false;
|
|
671
|
-
const ext =
|
|
764
|
+
const ext = path4.extname(full);
|
|
672
765
|
if (!ext) return false;
|
|
673
766
|
return !WATCHED_EXTENSIONS.has(ext);
|
|
674
767
|
};
|
|
@@ -902,7 +995,7 @@ program.command("watch").description("Watch schema and regenerate on changes").o
|
|
|
902
995
|
} else {
|
|
903
996
|
console.log(
|
|
904
997
|
import_chalk2.default.gray(
|
|
905
|
-
"Watching:\n " + Array.from(currentTargets).map((p) =>
|
|
998
|
+
"Watching:\n " + Array.from(currentTargets).map((p) => path4.relative(process.cwd(), p)).join("\n ")
|
|
906
999
|
)
|
|
907
1000
|
);
|
|
908
1001
|
}
|
|
@@ -910,9 +1003,9 @@ program.command("watch").description("Watch schema and regenerate on changes").o
|
|
|
910
1003
|
await run();
|
|
911
1004
|
});
|
|
912
1005
|
program.command("init").description("Scaffold a drzl.config.ts").option("-y, --yes", "accept defaults").action(async (_opts) => {
|
|
913
|
-
const
|
|
914
|
-
const
|
|
915
|
-
const target =
|
|
1006
|
+
const fs3 = await import("fs/promises");
|
|
1007
|
+
const path5 = await import("path");
|
|
1008
|
+
const target = path5.resolve(process.cwd(), "drzl.config.ts");
|
|
916
1009
|
const template = `export default {
|
|
917
1010
|
schema: 'src/db/schema.ts',
|
|
918
1011
|
outDir: 'src/api',
|
|
@@ -923,7 +1016,7 @@ program.command("init").description("Scaffold a drzl.config.ts").option("-y, --y
|
|
|
923
1016
|
} as const
|
|
924
1017
|
`;
|
|
925
1018
|
try {
|
|
926
|
-
await
|
|
1019
|
+
await fs3.writeFile(target, template, { flag: "wx" });
|
|
927
1020
|
console.log(import_chalk2.default.green(`Created ${target}`));
|
|
928
1021
|
} catch (e) {
|
|
929
1022
|
console.error(import_chalk2.default.red("Init failed:"), e?.message ?? e);
|