@drzl/cli 4.1.0 → 4.3.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-CHVAKQVU.js} +15 -2
- package/dist/chunk-CHVAKQVU.js.map +1 -0
- package/dist/cli.cjs +143 -23
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +125 -18
- package/dist/cli.js.map +1 -1
- package/dist/config.cjs +14 -1
- package/dist/config.cjs.map +1 -1
- package/dist/config.d.cts +4 -0
- package/dist/config.d.ts +4 -0
- package/dist/config.js +1 -1
- package/package.json +6 -5
- package/dist/chunk-PP33LGZD.js.map +0 -1
|
@@ -45,7 +45,7 @@ var AffixSchema = z.object({
|
|
|
45
45
|
}).strict();
|
|
46
46
|
var ImportExtensionSchema = z.enum(IMPORT_EXTENSIONS);
|
|
47
47
|
var GeneratorSchema = z.object({
|
|
48
|
-
kind: z.enum(["orpc", "service", "zod", "valibot", "arktype"]),
|
|
48
|
+
kind: z.enum(["orpc", "service", "zod", "valibot", "arktype", "typebox"]),
|
|
49
49
|
/**
|
|
50
50
|
* Overrides the top-level `importExtension` for this generator alone, for a project whose
|
|
51
51
|
* generated directories are compiled by different tsconfigs.
|
|
@@ -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(),
|
|
@@ -260,6 +272,7 @@ function computeGeneratorOutputDirs(cfg, cwd = process.cwd()) {
|
|
|
260
272
|
if (g.kind === "zod") dirs.add(abs(g.path ?? "src/validators/zod"));
|
|
261
273
|
if (g.kind === "valibot") dirs.add(abs(g.path ?? "src/validators/valibot"));
|
|
262
274
|
if (g.kind === "arktype") dirs.add(abs(g.path ?? "src/validators/arktype"));
|
|
275
|
+
if (g.kind === "typebox") dirs.add(abs(g.path ?? "src/validators/typebox"));
|
|
263
276
|
}
|
|
264
277
|
return [...dirs];
|
|
265
278
|
}
|
|
@@ -327,4 +340,4 @@ export {
|
|
|
327
340
|
filterTables,
|
|
328
341
|
computeWatchTargets
|
|
329
342
|
};
|
|
330
|
-
//# sourceMappingURL=chunk-
|
|
343
|
+
//# sourceMappingURL=chunk-CHVAKQVU.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', 'typebox']),\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 if (g.kind === 'typebox') dirs.add(abs(g.path ?? 'src/validators/typebox'));\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
|
|
@@ -72,7 +72,7 @@ var AffixSchema = import_zod.z.object({
|
|
|
72
72
|
}).strict();
|
|
73
73
|
var ImportExtensionSchema = import_zod.z.enum(import_validation_core.IMPORT_EXTENSIONS);
|
|
74
74
|
var GeneratorSchema = import_zod.z.object({
|
|
75
|
-
kind: import_zod.z.enum(["orpc", "service", "zod", "valibot", "arktype"]),
|
|
75
|
+
kind: import_zod.z.enum(["orpc", "service", "zod", "valibot", "arktype", "typebox"]),
|
|
76
76
|
/**
|
|
77
77
|
* Overrides the top-level `importExtension` for this generator alone, for a project whose
|
|
78
78
|
* generated directories are compiled by different tsconfigs.
|
|
@@ -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(),
|
|
@@ -284,6 +296,7 @@ function computeGeneratorOutputDirs(cfg, cwd = process.cwd()) {
|
|
|
284
296
|
if (g.kind === "zod") dirs.add(abs(g.path ?? "src/validators/zod"));
|
|
285
297
|
if (g.kind === "valibot") dirs.add(abs(g.path ?? "src/validators/valibot"));
|
|
286
298
|
if (g.kind === "arktype") dirs.add(abs(g.path ?? "src/validators/arktype"));
|
|
299
|
+
if (g.kind === "typebox") dirs.add(abs(g.path ?? "src/validators/typebox"));
|
|
287
300
|
}
|
|
288
301
|
return [...dirs];
|
|
289
302
|
}
|
|
@@ -336,12 +349,63 @@ function computeWatchTargets(cfg, cwd = process.cwd()) {
|
|
|
336
349
|
return [...targets];
|
|
337
350
|
}
|
|
338
351
|
|
|
339
|
-
// src/
|
|
340
|
-
var import_chalk = __toESM(require("chalk"), 1);
|
|
352
|
+
// src/drift.ts
|
|
341
353
|
var import_node_fs = require("fs");
|
|
342
354
|
var import_node_path = __toESM(require("path"), 1);
|
|
343
|
-
|
|
344
|
-
|
|
355
|
+
async function snapshotDir(dir) {
|
|
356
|
+
const out = /* @__PURE__ */ new Map();
|
|
357
|
+
async function walk(current) {
|
|
358
|
+
let entries;
|
|
359
|
+
try {
|
|
360
|
+
entries = await import_node_fs.promises.readdir(current, { withFileTypes: true });
|
|
361
|
+
} catch {
|
|
362
|
+
return;
|
|
363
|
+
}
|
|
364
|
+
for (const e of entries) {
|
|
365
|
+
const full = import_node_path.default.join(current, e.name);
|
|
366
|
+
if (e.isDirectory()) await walk(full);
|
|
367
|
+
else out.set(import_node_path.default.relative(dir, full), await import_node_fs.promises.readFile(full, "utf8"));
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
await walk(dir);
|
|
371
|
+
return out;
|
|
372
|
+
}
|
|
373
|
+
async function snapshotAll(dirs) {
|
|
374
|
+
const all = /* @__PURE__ */ new Map();
|
|
375
|
+
for (const dir of dirs) {
|
|
376
|
+
for (const [rel, content] of await snapshotDir(dir)) {
|
|
377
|
+
all.set(import_node_path.default.join(dir, rel), content);
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
return all;
|
|
381
|
+
}
|
|
382
|
+
function diffSnapshots(before, after) {
|
|
383
|
+
const out = [];
|
|
384
|
+
for (const [file, content] of after) {
|
|
385
|
+
if (!before.has(file)) out.push({ file, status: "added" });
|
|
386
|
+
else if (before.get(file) !== content) out.push({ file, status: "changed" });
|
|
387
|
+
}
|
|
388
|
+
for (const file of before.keys()) {
|
|
389
|
+
if (!after.has(file)) out.push({ file, status: "removed" });
|
|
390
|
+
}
|
|
391
|
+
return out.sort((a, b) => a.file.localeCompare(b.file));
|
|
392
|
+
}
|
|
393
|
+
async function restoreSnapshot(before, after) {
|
|
394
|
+
for (const [file, content] of before) {
|
|
395
|
+
await import_node_fs.promises.mkdir(import_node_path.default.dirname(file), { recursive: true });
|
|
396
|
+
await import_node_fs.promises.writeFile(file, content, "utf8");
|
|
397
|
+
}
|
|
398
|
+
for (const file of after.keys()) {
|
|
399
|
+
if (!before.has(file)) await import_node_fs.promises.rm(file, { force: true });
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
// src/sponsor.ts
|
|
404
|
+
var import_chalk = __toESM(require("chalk"), 1);
|
|
405
|
+
var import_node_fs2 = require("fs");
|
|
406
|
+
var import_node_path2 = __toESM(require("path"), 1);
|
|
407
|
+
var CACHE_DIR = import_node_path2.default.join(process.cwd(), "node_modules", ".cache", "@drzl");
|
|
408
|
+
var CACHE_FILE = import_node_path2.default.join(CACHE_DIR, "sponsor-message.json");
|
|
345
409
|
var DEFAULT_INTERVAL_MS = 1e3 * 60 * 15;
|
|
346
410
|
var shownThisProcess = false;
|
|
347
411
|
var tips = [
|
|
@@ -362,7 +426,7 @@ function maybeShowSponsorMessage({
|
|
|
362
426
|
const hideRequested = hideViaEnv === "1" || hideViaEnv === "true";
|
|
363
427
|
if (hideRequested || process.env.CI && !force || shownThisProcess && !force) return;
|
|
364
428
|
try {
|
|
365
|
-
(0,
|
|
429
|
+
(0, import_node_fs2.mkdirSync)(CACHE_DIR, { recursive: true });
|
|
366
430
|
const payload = readCache();
|
|
367
431
|
payload.runs += 1;
|
|
368
432
|
const now = Date.now();
|
|
@@ -389,11 +453,11 @@ ${green("Pro tip:")} ${tip}
|
|
|
389
453
|
}
|
|
390
454
|
}
|
|
391
455
|
function readCache() {
|
|
392
|
-
if (!(0,
|
|
456
|
+
if (!(0, import_node_fs2.existsSync)(CACHE_FILE)) {
|
|
393
457
|
return { runs: 0 };
|
|
394
458
|
}
|
|
395
459
|
try {
|
|
396
|
-
const data = JSON.parse((0,
|
|
460
|
+
const data = JSON.parse((0, import_node_fs2.readFileSync)(CACHE_FILE, "utf8"));
|
|
397
461
|
if (typeof data.runs !== "number") return { runs: 0 };
|
|
398
462
|
return data;
|
|
399
463
|
} catch {
|
|
@@ -401,7 +465,7 @@ function readCache() {
|
|
|
401
465
|
}
|
|
402
466
|
}
|
|
403
467
|
function writeCache(payload) {
|
|
404
|
-
(0,
|
|
468
|
+
(0, import_node_fs2.writeFileSync)(CACHE_FILE, JSON.stringify(payload, null, 2), "utf8");
|
|
405
469
|
}
|
|
406
470
|
|
|
407
471
|
// src/cli.ts
|
|
@@ -428,8 +492,8 @@ program.command("analyze").argument("<schema>", "path to drizzle schema (TS)").o
|
|
|
428
492
|
if (opts.json) {
|
|
429
493
|
console.log(json);
|
|
430
494
|
} else if (opts.out) {
|
|
431
|
-
const
|
|
432
|
-
await
|
|
495
|
+
const fs3 = await import("fs/promises");
|
|
496
|
+
await fs3.writeFile(opts.out, json, "utf8");
|
|
433
497
|
spinner?.succeed(import_chalk2.default.green(`Analysis written to ${opts.out} in ${ms}ms`));
|
|
434
498
|
} else {
|
|
435
499
|
spinner?.succeed(import_chalk2.default.green(`Analyzed in ${ms}ms`));
|
|
@@ -449,7 +513,10 @@ program.command("analyze").argument("<schema>", "path to drizzle schema (TS)").o
|
|
|
449
513
|
process.exit(1);
|
|
450
514
|
}
|
|
451
515
|
});
|
|
452
|
-
program.command("generate").description("Run configured generators (drzl.config.*)").option("-c, --config <path>", "path to drzl.config").
|
|
516
|
+
program.command("generate").description("Run configured generators (drzl.config.*)").option("-c, --config <path>", "path to drzl.config").option(
|
|
517
|
+
"--check",
|
|
518
|
+
"regenerate and fail if the result differs from what is on disk, without changing it"
|
|
519
|
+
).action(async (opts) => {
|
|
453
520
|
try {
|
|
454
521
|
const cfg = await loadConfig(opts.config);
|
|
455
522
|
if (!cfg) {
|
|
@@ -469,6 +536,8 @@ program.command("generate").description("Run configured generators (drzl.config.
|
|
|
469
536
|
});
|
|
470
537
|
analysis.tables = filterTables(analysis.tables, cfg);
|
|
471
538
|
spinner.succeed(`Analysis complete in ${Date.now() - t0}ms`);
|
|
539
|
+
const driftDirs = computeGeneratorOutputDirs(cfg);
|
|
540
|
+
const driftBefore = opts.check ? await snapshotAll(driftDirs) : null;
|
|
472
541
|
const progress = new import_cli_progress.default.SingleBar(
|
|
473
542
|
{ hideCursor: true },
|
|
474
543
|
import_cli_progress.default.Presets.shades_classic
|
|
@@ -533,7 +602,11 @@ program.command("generate").description("Run configured generators (drzl.config.
|
|
|
533
602
|
schemaSuffix: g.schemaSuffix,
|
|
534
603
|
fileSuffix: g.fileSuffix,
|
|
535
604
|
importExtension: g.importExtension,
|
|
536
|
-
affix: g.affix
|
|
605
|
+
affix: g.affix,
|
|
606
|
+
// Needed by typedJson, which imports the schema back to reference the type
|
|
607
|
+
// Drizzle inferred for a json column.
|
|
608
|
+
schemaPath: cfg.schema,
|
|
609
|
+
typedJson: g.typedJson
|
|
537
610
|
});
|
|
538
611
|
progress.stop();
|
|
539
612
|
(0, import_ora.default)().succeed(import_chalk2.default.green(`Generated (zod): ${files.length} files`));
|
|
@@ -599,8 +672,55 @@ program.command("generate").description("Run configured generators (drzl.config.
|
|
|
599
672
|
console.error(import_chalk2.default.gray("Error details:"), e?.message ?? e);
|
|
600
673
|
process.exit(1);
|
|
601
674
|
}
|
|
675
|
+
} else if (g.kind === "typebox") {
|
|
676
|
+
try {
|
|
677
|
+
const { TypeBoxGenerator } = await import("@drzl/generator-typebox");
|
|
678
|
+
const gen = new TypeBoxGenerator(analysis);
|
|
679
|
+
const target = g.path ?? "src/validators/typebox";
|
|
680
|
+
const files = await gen.generate({
|
|
681
|
+
outDir: target,
|
|
682
|
+
outputHeader: g.outputHeader,
|
|
683
|
+
format: g.format,
|
|
684
|
+
schemaSuffix: g.schemaSuffix,
|
|
685
|
+
fileSuffix: g.fileSuffix,
|
|
686
|
+
importExtension: g.importExtension,
|
|
687
|
+
affix: g.affix
|
|
688
|
+
});
|
|
689
|
+
progress.stop();
|
|
690
|
+
(0, import_ora.default)().succeed(import_chalk2.default.green(`Generated (typebox): ${files.length} files`));
|
|
691
|
+
files.forEach((f) => console.log(" -", import_chalk2.default.cyan(f)));
|
|
692
|
+
} catch (e) {
|
|
693
|
+
progress.stop();
|
|
694
|
+
console.error(
|
|
695
|
+
import_chalk2.default.red("ArkType generator missing."),
|
|
696
|
+
import_chalk2.default.yellow("\nInstall with: npm install @drzl/generator-typebox")
|
|
697
|
+
);
|
|
698
|
+
console.error(import_chalk2.default.gray("Error details:"), e?.message ?? e);
|
|
699
|
+
process.exit(1);
|
|
700
|
+
}
|
|
602
701
|
}
|
|
603
702
|
}
|
|
703
|
+
if (driftBefore) {
|
|
704
|
+
const after = await snapshotAll(driftDirs);
|
|
705
|
+
const drift = diffSnapshots(driftBefore, after);
|
|
706
|
+
await restoreSnapshot(driftBefore, after);
|
|
707
|
+
if (drift.length) {
|
|
708
|
+
console.error(
|
|
709
|
+
import_chalk2.default.red(`
|
|
710
|
+
Generated output is out of date (${drift.length} file(s)):`)
|
|
711
|
+
);
|
|
712
|
+
for (const d of drift) {
|
|
713
|
+
const mark = d.status === "added" ? "+" : d.status === "removed" ? "-" : "~";
|
|
714
|
+
console.error(` ${mark} ${import_chalk2.default.yellow(d.status.padEnd(8))} ${path4.relative(process.cwd(), d.file)}`);
|
|
715
|
+
}
|
|
716
|
+
console.error(
|
|
717
|
+
import_chalk2.default.dim("\nRun `drzl generate` and commit the result. Nothing was written by this check.")
|
|
718
|
+
);
|
|
719
|
+
process.exit(1);
|
|
720
|
+
}
|
|
721
|
+
console.log(import_chalk2.default.green("Generated output is up to date."));
|
|
722
|
+
return;
|
|
723
|
+
}
|
|
604
724
|
if (cfg.generators.length) {
|
|
605
725
|
maybeShowSponsorMessage({ reason: "generate" });
|
|
606
726
|
}
|
|
@@ -640,10 +760,10 @@ program.command("watch").description("Watch schema and regenerate on changes").o
|
|
|
640
760
|
process.exit(2);
|
|
641
761
|
return;
|
|
642
762
|
}
|
|
643
|
-
const abs = (p) =>
|
|
763
|
+
const abs = (p) => path4.resolve(process.cwd(), p);
|
|
644
764
|
const isInside = (child, parent) => {
|
|
645
|
-
const rel =
|
|
646
|
-
return !!rel && !rel.startsWith("..") && !
|
|
765
|
+
const rel = path4.relative(parent, child);
|
|
766
|
+
return !!rel && !rel.startsWith("..") && !path4.isAbsolute(rel);
|
|
647
767
|
};
|
|
648
768
|
const ignoredOutDirs = new Set(computeGeneratorOutputDirs(cfg).map(abs));
|
|
649
769
|
const currentTargets = new Set(computeWatchTargets(cfg).map(abs));
|
|
@@ -668,7 +788,7 @@ program.command("watch").description("Watch schema and regenerate on changes").o
|
|
|
668
788
|
if (full === dir || isInside(full, dir)) return true;
|
|
669
789
|
}
|
|
670
790
|
if (stats?.isDirectory()) return false;
|
|
671
|
-
const ext =
|
|
791
|
+
const ext = path4.extname(full);
|
|
672
792
|
if (!ext) return false;
|
|
673
793
|
return !WATCHED_EXTENSIONS.has(ext);
|
|
674
794
|
};
|
|
@@ -902,7 +1022,7 @@ program.command("watch").description("Watch schema and regenerate on changes").o
|
|
|
902
1022
|
} else {
|
|
903
1023
|
console.log(
|
|
904
1024
|
import_chalk2.default.gray(
|
|
905
|
-
"Watching:\n " + Array.from(currentTargets).map((p) =>
|
|
1025
|
+
"Watching:\n " + Array.from(currentTargets).map((p) => path4.relative(process.cwd(), p)).join("\n ")
|
|
906
1026
|
)
|
|
907
1027
|
);
|
|
908
1028
|
}
|
|
@@ -910,9 +1030,9 @@ program.command("watch").description("Watch schema and regenerate on changes").o
|
|
|
910
1030
|
await run();
|
|
911
1031
|
});
|
|
912
1032
|
program.command("init").description("Scaffold a drzl.config.ts").option("-y, --yes", "accept defaults").action(async (_opts) => {
|
|
913
|
-
const
|
|
914
|
-
const
|
|
915
|
-
const target =
|
|
1033
|
+
const fs3 = await import("fs/promises");
|
|
1034
|
+
const path5 = await import("path");
|
|
1035
|
+
const target = path5.resolve(process.cwd(), "drzl.config.ts");
|
|
916
1036
|
const template = `export default {
|
|
917
1037
|
schema: 'src/db/schema.ts',
|
|
918
1038
|
outDir: 'src/api',
|
|
@@ -923,7 +1043,7 @@ program.command("init").description("Scaffold a drzl.config.ts").option("-y, --y
|
|
|
923
1043
|
} as const
|
|
924
1044
|
`;
|
|
925
1045
|
try {
|
|
926
|
-
await
|
|
1046
|
+
await fs3.writeFile(target, template, { flag: "wx" });
|
|
927
1047
|
console.log(import_chalk2.default.green(`Created ${target}`));
|
|
928
1048
|
} catch (e) {
|
|
929
1049
|
console.error(import_chalk2.default.red("Init failed:"), e?.message ?? e);
|