@drzl/cli 4.0.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-VSEU5SZ6.js → chunk-UXA4IRLP.js} +42 -1
- package/dist/chunk-UXA4IRLP.js.map +1 -0
- package/dist/cli.cjs +145 -22
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +102 -18
- package/dist/cli.js.map +1 -1
- package/dist/config.cjs +42 -0
- package/dist/config.cjs.map +1 -1
- package/dist/config.d.cts +18 -1
- package/dist/config.d.ts +18 -1
- package/dist/config.js +3 -1
- package/package.json +7 -7
- package/dist/chunk-VSEU5SZ6.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(),
|
|
@@ -99,6 +111,24 @@ var AnalyzerSchema = z.object({
|
|
|
99
111
|
var ConfigSchema = z.object({
|
|
100
112
|
schema: z.string(),
|
|
101
113
|
outDir: z.string().default("src/api"),
|
|
114
|
+
/**
|
|
115
|
+
* Which tables to generate for, matched against the database table name.
|
|
116
|
+
*
|
|
117
|
+
* There was no way to say this, and every generator loops over every table it finds, so
|
|
118
|
+
* DRZL emitted unauthenticated CRUD over whatever shared the schema file. That is noise for
|
|
119
|
+
* a migrations table and a genuine leak for an auth one: Better Auth puts `user`, `session`,
|
|
120
|
+
* `account` and `verification` alongside your own tables, and `account` holds
|
|
121
|
+
* `accessToken`, `refreshToken`, `idToken` and `password`.
|
|
122
|
+
*
|
|
123
|
+
* Deliberately name-based and explicit rather than detecting any particular library. Auth
|
|
124
|
+
* table names are all renameable, so a built-in list would miss renamed tables and, worse,
|
|
125
|
+
* silently skip an ordinary table that happened to be called `user`, which is usually the
|
|
126
|
+
* application's main entity.
|
|
127
|
+
*
|
|
128
|
+
* `exclude` wins over `include`. Patterns support `*`, matching within a name.
|
|
129
|
+
*/
|
|
130
|
+
include: z.array(z.string()).optional(),
|
|
131
|
+
exclude: z.array(z.string()).optional(),
|
|
102
132
|
/**
|
|
103
133
|
* How every relative specifier drzl invents spells its extension, for every generator.
|
|
104
134
|
* A generator may override it. Defaults to `js`, which is the only form that resolves
|
|
@@ -270,6 +300,16 @@ function resolveTemplateDirsSync(cfg, cwd = process.cwd()) {
|
|
|
270
300
|
}
|
|
271
301
|
return Array.from(new Set(results));
|
|
272
302
|
}
|
|
303
|
+
function filterTables(tables, opts) {
|
|
304
|
+
const toRegExp = (pattern) => new RegExp(
|
|
305
|
+
"^" + pattern.split("*").map((part) => part.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join(".*") + "$"
|
|
306
|
+
);
|
|
307
|
+
const matches = (patterns, name) => patterns.some((p) => toRegExp(p).test(name));
|
|
308
|
+
let out = tables;
|
|
309
|
+
if (opts.include?.length) out = out.filter((t) => matches(opts.include, t.name));
|
|
310
|
+
if (opts.exclude?.length) out = out.filter((t) => !matches(opts.exclude, t.name));
|
|
311
|
+
return out;
|
|
312
|
+
}
|
|
273
313
|
function computeWatchTargets(cfg, cwd = process.cwd()) {
|
|
274
314
|
const abs = (p) => path.resolve(cwd, p);
|
|
275
315
|
const schemaAbs = abs(cfg.schema);
|
|
@@ -296,6 +336,7 @@ export {
|
|
|
296
336
|
loadConfig,
|
|
297
337
|
computeGeneratorOutputDirs,
|
|
298
338
|
resolveTemplateDirsSync,
|
|
339
|
+
filterTables,
|
|
299
340
|
computeWatchTargets
|
|
300
341
|
};
|
|
301
|
-
//# 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(),
|
|
@@ -126,6 +138,24 @@ var AnalyzerSchema = import_zod.z.object({
|
|
|
126
138
|
var ConfigSchema = import_zod.z.object({
|
|
127
139
|
schema: import_zod.z.string(),
|
|
128
140
|
outDir: import_zod.z.string().default("src/api"),
|
|
141
|
+
/**
|
|
142
|
+
* Which tables to generate for, matched against the database table name.
|
|
143
|
+
*
|
|
144
|
+
* There was no way to say this, and every generator loops over every table it finds, so
|
|
145
|
+
* DRZL emitted unauthenticated CRUD over whatever shared the schema file. That is noise for
|
|
146
|
+
* a migrations table and a genuine leak for an auth one: Better Auth puts `user`, `session`,
|
|
147
|
+
* `account` and `verification` alongside your own tables, and `account` holds
|
|
148
|
+
* `accessToken`, `refreshToken`, `idToken` and `password`.
|
|
149
|
+
*
|
|
150
|
+
* Deliberately name-based and explicit rather than detecting any particular library. Auth
|
|
151
|
+
* table names are all renameable, so a built-in list would miss renamed tables and, worse,
|
|
152
|
+
* silently skip an ordinary table that happened to be called `user`, which is usually the
|
|
153
|
+
* application's main entity.
|
|
154
|
+
*
|
|
155
|
+
* `exclude` wins over `include`. Patterns support `*`, matching within a name.
|
|
156
|
+
*/
|
|
157
|
+
include: import_zod.z.array(import_zod.z.string()).optional(),
|
|
158
|
+
exclude: import_zod.z.array(import_zod.z.string()).optional(),
|
|
129
159
|
/**
|
|
130
160
|
* How every relative specifier drzl invents spells its extension, for every generator.
|
|
131
161
|
* A generator may override it. Defaults to `js`, which is the only form that resolves
|
|
@@ -294,6 +324,16 @@ function resolveTemplateDirsSync(cfg, cwd = process.cwd()) {
|
|
|
294
324
|
}
|
|
295
325
|
return Array.from(new Set(results));
|
|
296
326
|
}
|
|
327
|
+
function filterTables(tables, opts) {
|
|
328
|
+
const toRegExp = (pattern) => new RegExp(
|
|
329
|
+
"^" + pattern.split("*").map((part) => part.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join(".*") + "$"
|
|
330
|
+
);
|
|
331
|
+
const matches = (patterns, name) => patterns.some((p) => toRegExp(p).test(name));
|
|
332
|
+
let out = tables;
|
|
333
|
+
if (opts.include?.length) out = out.filter((t) => matches(opts.include, t.name));
|
|
334
|
+
if (opts.exclude?.length) out = out.filter((t) => !matches(opts.exclude, t.name));
|
|
335
|
+
return out;
|
|
336
|
+
}
|
|
297
337
|
function computeWatchTargets(cfg, cwd = process.cwd()) {
|
|
298
338
|
const abs = (p) => path.resolve(cwd, p);
|
|
299
339
|
const schemaAbs = abs(cfg.schema);
|
|
@@ -308,12 +348,63 @@ function computeWatchTargets(cfg, cwd = process.cwd()) {
|
|
|
308
348
|
return [...targets];
|
|
309
349
|
}
|
|
310
350
|
|
|
311
|
-
// src/
|
|
312
|
-
var import_chalk = __toESM(require("chalk"), 1);
|
|
351
|
+
// src/drift.ts
|
|
313
352
|
var import_node_fs = require("fs");
|
|
314
353
|
var import_node_path = __toESM(require("path"), 1);
|
|
315
|
-
|
|
316
|
-
|
|
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");
|
|
317
408
|
var DEFAULT_INTERVAL_MS = 1e3 * 60 * 15;
|
|
318
409
|
var shownThisProcess = false;
|
|
319
410
|
var tips = [
|
|
@@ -334,7 +425,7 @@ function maybeShowSponsorMessage({
|
|
|
334
425
|
const hideRequested = hideViaEnv === "1" || hideViaEnv === "true";
|
|
335
426
|
if (hideRequested || process.env.CI && !force || shownThisProcess && !force) return;
|
|
336
427
|
try {
|
|
337
|
-
(0,
|
|
428
|
+
(0, import_node_fs2.mkdirSync)(CACHE_DIR, { recursive: true });
|
|
338
429
|
const payload = readCache();
|
|
339
430
|
payload.runs += 1;
|
|
340
431
|
const now = Date.now();
|
|
@@ -361,11 +452,11 @@ ${green("Pro tip:")} ${tip}
|
|
|
361
452
|
}
|
|
362
453
|
}
|
|
363
454
|
function readCache() {
|
|
364
|
-
if (!(0,
|
|
455
|
+
if (!(0, import_node_fs2.existsSync)(CACHE_FILE)) {
|
|
365
456
|
return { runs: 0 };
|
|
366
457
|
}
|
|
367
458
|
try {
|
|
368
|
-
const data = JSON.parse((0,
|
|
459
|
+
const data = JSON.parse((0, import_node_fs2.readFileSync)(CACHE_FILE, "utf8"));
|
|
369
460
|
if (typeof data.runs !== "number") return { runs: 0 };
|
|
370
461
|
return data;
|
|
371
462
|
} catch {
|
|
@@ -373,7 +464,7 @@ function readCache() {
|
|
|
373
464
|
}
|
|
374
465
|
}
|
|
375
466
|
function writeCache(payload) {
|
|
376
|
-
(0,
|
|
467
|
+
(0, import_node_fs2.writeFileSync)(CACHE_FILE, JSON.stringify(payload, null, 2), "utf8");
|
|
377
468
|
}
|
|
378
469
|
|
|
379
470
|
// src/cli.ts
|
|
@@ -400,8 +491,8 @@ program.command("analyze").argument("<schema>", "path to drizzle schema (TS)").o
|
|
|
400
491
|
if (opts.json) {
|
|
401
492
|
console.log(json);
|
|
402
493
|
} else if (opts.out) {
|
|
403
|
-
const
|
|
404
|
-
await
|
|
494
|
+
const fs3 = await import("fs/promises");
|
|
495
|
+
await fs3.writeFile(opts.out, json, "utf8");
|
|
405
496
|
spinner?.succeed(import_chalk2.default.green(`Analysis written to ${opts.out} in ${ms}ms`));
|
|
406
497
|
} else {
|
|
407
498
|
spinner?.succeed(import_chalk2.default.green(`Analyzed in ${ms}ms`));
|
|
@@ -421,7 +512,10 @@ program.command("analyze").argument("<schema>", "path to drizzle schema (TS)").o
|
|
|
421
512
|
process.exit(1);
|
|
422
513
|
}
|
|
423
514
|
});
|
|
424
|
-
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) => {
|
|
425
519
|
try {
|
|
426
520
|
const cfg = await loadConfig(opts.config);
|
|
427
521
|
if (!cfg) {
|
|
@@ -439,7 +533,10 @@ program.command("generate").description("Run configured generators (drzl.config.
|
|
|
439
533
|
validateConstraints: cfg.analyzer.validateConstraints,
|
|
440
534
|
includeHeuristicRelations: cfg.analyzer.includeHeuristicRelations
|
|
441
535
|
});
|
|
536
|
+
analysis.tables = filterTables(analysis.tables, cfg);
|
|
442
537
|
spinner.succeed(`Analysis complete in ${Date.now() - t0}ms`);
|
|
538
|
+
const driftDirs = computeGeneratorOutputDirs(cfg);
|
|
539
|
+
const driftBefore = opts.check ? await snapshotAll(driftDirs) : null;
|
|
443
540
|
const progress = new import_cli_progress.default.SingleBar(
|
|
444
541
|
{ hideCursor: true },
|
|
445
542
|
import_cli_progress.default.Presets.shades_classic
|
|
@@ -504,7 +601,11 @@ program.command("generate").description("Run configured generators (drzl.config.
|
|
|
504
601
|
schemaSuffix: g.schemaSuffix,
|
|
505
602
|
fileSuffix: g.fileSuffix,
|
|
506
603
|
importExtension: g.importExtension,
|
|
507
|
-
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
|
|
508
609
|
});
|
|
509
610
|
progress.stop();
|
|
510
611
|
(0, import_ora.default)().succeed(import_chalk2.default.green(`Generated (zod): ${files.length} files`));
|
|
@@ -572,6 +673,27 @@ program.command("generate").description("Run configured generators (drzl.config.
|
|
|
572
673
|
}
|
|
573
674
|
}
|
|
574
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
|
+
}
|
|
575
697
|
if (cfg.generators.length) {
|
|
576
698
|
maybeShowSponsorMessage({ reason: "generate" });
|
|
577
699
|
}
|
|
@@ -611,10 +733,10 @@ program.command("watch").description("Watch schema and regenerate on changes").o
|
|
|
611
733
|
process.exit(2);
|
|
612
734
|
return;
|
|
613
735
|
}
|
|
614
|
-
const abs = (p) =>
|
|
736
|
+
const abs = (p) => path4.resolve(process.cwd(), p);
|
|
615
737
|
const isInside = (child, parent) => {
|
|
616
|
-
const rel =
|
|
617
|
-
return !!rel && !rel.startsWith("..") && !
|
|
738
|
+
const rel = path4.relative(parent, child);
|
|
739
|
+
return !!rel && !rel.startsWith("..") && !path4.isAbsolute(rel);
|
|
618
740
|
};
|
|
619
741
|
const ignoredOutDirs = new Set(computeGeneratorOutputDirs(cfg).map(abs));
|
|
620
742
|
const currentTargets = new Set(computeWatchTargets(cfg).map(abs));
|
|
@@ -639,7 +761,7 @@ program.command("watch").description("Watch schema and regenerate on changes").o
|
|
|
639
761
|
if (full === dir || isInside(full, dir)) return true;
|
|
640
762
|
}
|
|
641
763
|
if (stats?.isDirectory()) return false;
|
|
642
|
-
const ext =
|
|
764
|
+
const ext = path4.extname(full);
|
|
643
765
|
if (!ext) return false;
|
|
644
766
|
return !WATCHED_EXTENSIONS.has(ext);
|
|
645
767
|
};
|
|
@@ -687,6 +809,7 @@ program.command("watch").description("Watch schema and regenerate on changes").o
|
|
|
687
809
|
validateConstraints: cfg.analyzer.validateConstraints,
|
|
688
810
|
includeHeuristicRelations: cfg.analyzer.includeHeuristicRelations
|
|
689
811
|
});
|
|
812
|
+
analysis.tables = filterTables(analysis.tables, cfg);
|
|
690
813
|
if (opts.pipeline === "analyze") {
|
|
691
814
|
if (opts.json) {
|
|
692
815
|
console.log(
|
|
@@ -872,7 +995,7 @@ program.command("watch").description("Watch schema and regenerate on changes").o
|
|
|
872
995
|
} else {
|
|
873
996
|
console.log(
|
|
874
997
|
import_chalk2.default.gray(
|
|
875
|
-
"Watching:\n " + Array.from(currentTargets).map((p) =>
|
|
998
|
+
"Watching:\n " + Array.from(currentTargets).map((p) => path4.relative(process.cwd(), p)).join("\n ")
|
|
876
999
|
)
|
|
877
1000
|
);
|
|
878
1001
|
}
|
|
@@ -880,9 +1003,9 @@ program.command("watch").description("Watch schema and regenerate on changes").o
|
|
|
880
1003
|
await run();
|
|
881
1004
|
});
|
|
882
1005
|
program.command("init").description("Scaffold a drzl.config.ts").option("-y, --yes", "accept defaults").action(async (_opts) => {
|
|
883
|
-
const
|
|
884
|
-
const
|
|
885
|
-
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");
|
|
886
1009
|
const template = `export default {
|
|
887
1010
|
schema: 'src/db/schema.ts',
|
|
888
1011
|
outDir: 'src/api',
|
|
@@ -893,7 +1016,7 @@ program.command("init").description("Scaffold a drzl.config.ts").option("-y, --y
|
|
|
893
1016
|
} as const
|
|
894
1017
|
`;
|
|
895
1018
|
try {
|
|
896
|
-
await
|
|
1019
|
+
await fs3.writeFile(target, template, { flag: "wx" });
|
|
897
1020
|
console.log(import_chalk2.default.green(`Created ${target}`));
|
|
898
1021
|
} catch (e) {
|
|
899
1022
|
console.error(import_chalk2.default.red("Init failed:"), e?.message ?? e);
|