@drzl/cli 1.1.0 → 2.0.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-HHT7INUJ.js +301 -0
- package/dist/chunk-HHT7INUJ.js.map +1 -0
- package/dist/cli.cjs +154 -12
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +25 -9
- package/dist/cli.js.map +1 -1
- package/dist/config.cjs +136 -4
- package/dist/config.cjs.map +1 -1
- package/dist/config.d.cts +200 -1
- package/dist/config.d.ts +200 -1
- package/dist/config.js +7 -1
- package/package.json +8 -7
- package/dist/chunk-MEPFGJIF.js +0 -164
- package/dist/chunk-MEPFGJIF.js.map +0 -1
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
// src/config.ts
|
|
2
|
+
import {
|
|
3
|
+
AFFIX_PROBE_TABLE,
|
|
4
|
+
DEFAULT_IMPORT_EXTENSION,
|
|
5
|
+
IMPORT_EXTENSIONS,
|
|
6
|
+
NAME_MODES,
|
|
7
|
+
resolveAffix,
|
|
8
|
+
schemaName,
|
|
9
|
+
validateAffix
|
|
10
|
+
} from "@drzl/validation-core";
|
|
11
|
+
import * as fs from "fs";
|
|
12
|
+
import { createRequire } from "module";
|
|
13
|
+
import * as path from "path";
|
|
14
|
+
import { z } from "zod";
|
|
15
|
+
var NamingSchema = z.object({
|
|
16
|
+
routerSuffix: z.string().default("Router"),
|
|
17
|
+
procedureCase: z.enum(["camel", "kebab", "snake"]).default("camel")
|
|
18
|
+
}).partial();
|
|
19
|
+
var AffixValueSchema = z.union(
|
|
20
|
+
[
|
|
21
|
+
z.string(),
|
|
22
|
+
z.object({
|
|
23
|
+
insert: z.string().optional(),
|
|
24
|
+
update: z.string().optional(),
|
|
25
|
+
select: z.string().optional()
|
|
26
|
+
}).strict()
|
|
27
|
+
],
|
|
28
|
+
{
|
|
29
|
+
error: 'Expected a string to use for every mode, or an object with any of the keys "insert", "update" and "select". Those keys are lowercase, matching the mode names drzl uses everywhere else.'
|
|
30
|
+
}
|
|
31
|
+
);
|
|
32
|
+
var AffixPartSchema = z.object({
|
|
33
|
+
prefix: AffixValueSchema.optional(),
|
|
34
|
+
suffix: AffixValueSchema.optional()
|
|
35
|
+
}).strict();
|
|
36
|
+
var AffixSchema = z.object({
|
|
37
|
+
/**
|
|
38
|
+
* `preserve` (default) keeps today's output: the Drizzle export name goes into the
|
|
39
|
+
* identifier verbatim, so `export const users` yields `InsertusersSchema`. `pascal`
|
|
40
|
+
* upper-camels it first, yielding `InsertUsersSchema`.
|
|
41
|
+
*/
|
|
42
|
+
tableCase: z.enum(["preserve", "pascal"]).optional(),
|
|
43
|
+
schema: AffixPartSchema.optional(),
|
|
44
|
+
type: AffixPartSchema.optional()
|
|
45
|
+
}).strict();
|
|
46
|
+
var ImportExtensionSchema = z.enum(IMPORT_EXTENSIONS);
|
|
47
|
+
var GeneratorSchema = z.object({
|
|
48
|
+
kind: z.enum(["orpc", "service", "zod", "valibot", "arktype"]),
|
|
49
|
+
/**
|
|
50
|
+
* Overrides the top-level `importExtension` for this generator alone, for a project whose
|
|
51
|
+
* generated directories are compiled by different tsconfigs.
|
|
52
|
+
*/
|
|
53
|
+
importExtension: ImportExtensionSchema.optional(),
|
|
54
|
+
template: z.string().optional(),
|
|
55
|
+
includeRelations: z.boolean().optional(),
|
|
56
|
+
naming: NamingSchema.optional(),
|
|
57
|
+
outputHeader: z.object({
|
|
58
|
+
enabled: z.boolean().default(true).optional(),
|
|
59
|
+
text: z.string().optional()
|
|
60
|
+
}).optional(),
|
|
61
|
+
format: z.object({
|
|
62
|
+
enabled: z.boolean().default(true).optional(),
|
|
63
|
+
engine: z.enum(["auto", "prettier", "biome"]).default("auto").optional(),
|
|
64
|
+
configPath: z.string().optional()
|
|
65
|
+
}).optional(),
|
|
66
|
+
// service generator specific options
|
|
67
|
+
path: z.string().optional(),
|
|
68
|
+
dataAccess: z.enum(["stub", "drizzle"]).default("stub").optional(),
|
|
69
|
+
dbImportPath: z.string().optional(),
|
|
70
|
+
schemaImportPath: z.string().optional(),
|
|
71
|
+
// zod/valibot/arktype generator specific options
|
|
72
|
+
schemaSuffix: z.string().optional(),
|
|
73
|
+
fileSuffix: z.string().optional(),
|
|
74
|
+
/**
|
|
75
|
+
* Prefixes, suffixes and table casing for generated identifiers (zod/valibot/arktype).
|
|
76
|
+
* Omitting it reproduces the output of every previous release exactly.
|
|
77
|
+
*/
|
|
78
|
+
affix: AffixSchema.optional(),
|
|
79
|
+
// orpc validation sharing
|
|
80
|
+
validation: z.object({
|
|
81
|
+
useShared: z.boolean().default(false).optional(),
|
|
82
|
+
library: z.enum(["zod", "valibot", "arktype"]).default("zod").optional(),
|
|
83
|
+
importPath: z.string().optional(),
|
|
84
|
+
schemaSuffix: z.string().optional(),
|
|
85
|
+
/**
|
|
86
|
+
* How the validation generator named its exports. Usually left unset: the CLI copies
|
|
87
|
+
* it from the sibling generator whose `kind` matches `library`.
|
|
88
|
+
*/
|
|
89
|
+
affix: AffixSchema.optional()
|
|
90
|
+
}).optional(),
|
|
91
|
+
// template options
|
|
92
|
+
templateOptions: z.record(z.string(), z.any()).optional()
|
|
93
|
+
});
|
|
94
|
+
var AnalyzerSchema = z.object({
|
|
95
|
+
includeRelations: z.boolean().default(true),
|
|
96
|
+
validateConstraints: z.boolean().default(true),
|
|
97
|
+
includeHeuristicRelations: z.boolean().default(false)
|
|
98
|
+
});
|
|
99
|
+
var ConfigSchema = z.object({
|
|
100
|
+
schema: z.string(),
|
|
101
|
+
outDir: z.string().default("src/api"),
|
|
102
|
+
/**
|
|
103
|
+
* How every relative specifier drzl invents spells its extension, for every generator.
|
|
104
|
+
* A generator may override it. Defaults to `js`, which is the only form that resolves
|
|
105
|
+
* under every `moduleResolution` without a compiler flag.
|
|
106
|
+
*/
|
|
107
|
+
importExtension: ImportExtensionSchema.default(DEFAULT_IMPORT_EXTENSION),
|
|
108
|
+
analyzer: AnalyzerSchema.default({
|
|
109
|
+
includeRelations: true,
|
|
110
|
+
validateConstraints: true,
|
|
111
|
+
includeHeuristicRelations: false
|
|
112
|
+
}),
|
|
113
|
+
generators: z.array(GeneratorSchema).min(1).default([{ kind: "orpc" }])
|
|
114
|
+
}).superRefine((cfg, ctx) => {
|
|
115
|
+
cfg.generators.forEach((g, i) => {
|
|
116
|
+
const report = (base, affix, schemaSuffix) => {
|
|
117
|
+
for (const issue of validateAffix(affix, schemaSuffix)) {
|
|
118
|
+
ctx.addIssue({
|
|
119
|
+
code: "custom",
|
|
120
|
+
path: ["generators", i, ...base, ...issue.path],
|
|
121
|
+
message: issue.message
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
report(["affix"], g.affix, g.schemaSuffix);
|
|
126
|
+
report(
|
|
127
|
+
["validation", "affix"],
|
|
128
|
+
g.validation?.affix,
|
|
129
|
+
g.validation?.schemaSuffix
|
|
130
|
+
);
|
|
131
|
+
});
|
|
132
|
+
});
|
|
133
|
+
function defineConfig(cfg) {
|
|
134
|
+
return cfg;
|
|
135
|
+
}
|
|
136
|
+
function sharedSchemaNames(opts) {
|
|
137
|
+
const resolved = resolveAffix(opts);
|
|
138
|
+
return NAME_MODES.map((mode) => schemaName(mode, AFFIX_PROBE_TABLE, resolved));
|
|
139
|
+
}
|
|
140
|
+
function resolveConfig(cfg) {
|
|
141
|
+
const warnings = [];
|
|
142
|
+
const generators = cfg.generators.map((g) => ({
|
|
143
|
+
...g,
|
|
144
|
+
importExtension: g.importExtension ?? cfg.importExtension
|
|
145
|
+
}));
|
|
146
|
+
for (const g of generators) {
|
|
147
|
+
if (g.kind !== "orpc") continue;
|
|
148
|
+
const v = g.validation;
|
|
149
|
+
if (!v?.useShared) continue;
|
|
150
|
+
const library = v.library ?? "zod";
|
|
151
|
+
const siblings = generators.filter((s) => s.kind === library);
|
|
152
|
+
if (siblings.length !== 1) continue;
|
|
153
|
+
const sibling = siblings[0];
|
|
154
|
+
const theirs = sharedSchemaNames({
|
|
155
|
+
affix: sibling.affix,
|
|
156
|
+
schemaSuffix: sibling.schemaSuffix
|
|
157
|
+
});
|
|
158
|
+
if (!v.affix) {
|
|
159
|
+
if (sibling.affix) {
|
|
160
|
+
g.validation = {
|
|
161
|
+
...v,
|
|
162
|
+
affix: resolveAffix({
|
|
163
|
+
affix: sibling.affix,
|
|
164
|
+
schemaSuffix: sibling.schemaSuffix
|
|
165
|
+
})
|
|
166
|
+
};
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
169
|
+
const mine2 = sharedSchemaNames({ schemaSuffix: v.schemaSuffix });
|
|
170
|
+
if (mine2.join(",") !== theirs.join(",")) {
|
|
171
|
+
warnings.push(
|
|
172
|
+
`drzl config: the "orpc" generator's validation.schemaSuffix (${JSON.stringify(v.schemaSuffix ?? "Schema")}) does not match the "${library}" generator's schemaSuffix (${JSON.stringify(sibling.schemaSuffix ?? "Schema")}). The router will import ${mine2.join(", ")} but the "${library}" generator exports ${theirs.join(", ")}, so the generated router will not compile. Set both to the same value, or move to "affix", which is inherited automatically.`
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
continue;
|
|
176
|
+
}
|
|
177
|
+
const mine = sharedSchemaNames({
|
|
178
|
+
affix: v.affix,
|
|
179
|
+
schemaSuffix: v.schemaSuffix
|
|
180
|
+
});
|
|
181
|
+
if (mine.join(",") !== theirs.join(",")) {
|
|
182
|
+
throw new Error(
|
|
183
|
+
`drzl config: the "orpc" generator imports shared ${library} schemas, but its validation.affix disagrees with the "${library}" generator's own naming. The router would import ${mine.join(", ")} while the "${library}" generator exports ${theirs.join(", ")}. Make them match, or drop validation.affix and let it be inherited from the "${library}" generator.`
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
return { config: { ...cfg, generators }, warnings };
|
|
188
|
+
}
|
|
189
|
+
function finalize(raw) {
|
|
190
|
+
const { config, warnings } = resolveConfig(ConfigSchema.parse(raw));
|
|
191
|
+
for (const w of warnings) console.warn(w);
|
|
192
|
+
return config;
|
|
193
|
+
}
|
|
194
|
+
async function loadConfig(customPath) {
|
|
195
|
+
const fsp = await import("fs/promises");
|
|
196
|
+
const candidates = customPath ? [customPath] : [
|
|
197
|
+
"drzl.config.ts",
|
|
198
|
+
"drzl.config.mjs",
|
|
199
|
+
"drzl.config.js",
|
|
200
|
+
"drzl.config.cjs",
|
|
201
|
+
"drzl.config.json"
|
|
202
|
+
];
|
|
203
|
+
for (const c of candidates) {
|
|
204
|
+
const p = path.resolve(process.cwd(), c);
|
|
205
|
+
try {
|
|
206
|
+
await fsp.access(p);
|
|
207
|
+
} catch {
|
|
208
|
+
continue;
|
|
209
|
+
}
|
|
210
|
+
const ext = path.extname(p).toLowerCase();
|
|
211
|
+
if (ext === ".json") {
|
|
212
|
+
const raw2 = JSON.parse(await fsp.readFile(p, "utf8"));
|
|
213
|
+
return finalize(raw2);
|
|
214
|
+
}
|
|
215
|
+
const { createJiti } = await import("jiti");
|
|
216
|
+
const stat = await fsp.stat(p);
|
|
217
|
+
const base = typeof __filename !== "undefined" ? __filename : path.join(process.cwd(), "index.js");
|
|
218
|
+
const jiti = createJiti(base, {
|
|
219
|
+
moduleCache: false,
|
|
220
|
+
// re-evaluate each time
|
|
221
|
+
fsCache: true,
|
|
222
|
+
// keep transform cache
|
|
223
|
+
cacheVersion: String(stat.mtimeMs),
|
|
224
|
+
// bump on edit
|
|
225
|
+
interopDefault: true,
|
|
226
|
+
tryNative: false
|
|
227
|
+
// <-- prevent native import of .ts
|
|
228
|
+
// debug: true,
|
|
229
|
+
});
|
|
230
|
+
const mod = await jiti.import(p);
|
|
231
|
+
const raw = mod?.default ?? mod;
|
|
232
|
+
return finalize(raw);
|
|
233
|
+
}
|
|
234
|
+
return null;
|
|
235
|
+
}
|
|
236
|
+
function computeGeneratorOutputDirs(cfg, cwd = process.cwd()) {
|
|
237
|
+
const abs = (p) => path.resolve(cwd, p);
|
|
238
|
+
const dirs = /* @__PURE__ */ new Set();
|
|
239
|
+
dirs.add(abs(cfg.outDir));
|
|
240
|
+
for (const g of cfg.generators) {
|
|
241
|
+
if (g.kind === "service") dirs.add(abs(g.path ?? "src/services"));
|
|
242
|
+
if (g.kind === "zod") dirs.add(abs(g.path ?? "src/validators/zod"));
|
|
243
|
+
if (g.kind === "valibot") dirs.add(abs(g.path ?? "src/validators/valibot"));
|
|
244
|
+
if (g.kind === "arktype") dirs.add(abs(g.path ?? "src/validators/arktype"));
|
|
245
|
+
}
|
|
246
|
+
return [...dirs];
|
|
247
|
+
}
|
|
248
|
+
function resolveTemplateDirsSync(cfg, cwd = process.cwd()) {
|
|
249
|
+
const results = [];
|
|
250
|
+
const req = createRequire(
|
|
251
|
+
typeof __filename !== "undefined" ? __filename : path.join(process.cwd(), "index.js")
|
|
252
|
+
);
|
|
253
|
+
for (const g of cfg.generators) {
|
|
254
|
+
const t = g.template;
|
|
255
|
+
if (!t || t === "standard" || t === "minimal") continue;
|
|
256
|
+
let pkgDir = null;
|
|
257
|
+
try {
|
|
258
|
+
const pkg = req.resolve(`${t}/package.json`, { paths: [cwd] });
|
|
259
|
+
pkgDir = path.dirname(pkg);
|
|
260
|
+
} catch {
|
|
261
|
+
}
|
|
262
|
+
if (pkgDir) {
|
|
263
|
+
results.push(pkgDir);
|
|
264
|
+
continue;
|
|
265
|
+
}
|
|
266
|
+
if (/[./\\]/.test(t)) {
|
|
267
|
+
const abs = path.resolve(cwd, t);
|
|
268
|
+
if (fs.existsSync(abs)) results.push(abs);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
return Array.from(new Set(results));
|
|
272
|
+
}
|
|
273
|
+
function computeWatchTargets(cfg, cwd = process.cwd()) {
|
|
274
|
+
const abs = (p) => path.resolve(cwd, p);
|
|
275
|
+
const schemaAbs = abs(cfg.schema);
|
|
276
|
+
const targets = /* @__PURE__ */ new Set([
|
|
277
|
+
path.join(path.dirname(schemaAbs), "**/*.{ts,tsx,js}"),
|
|
278
|
+
abs("drzl.config.ts"),
|
|
279
|
+
abs("drzl.config.js"),
|
|
280
|
+
abs("drzl.config.mjs"),
|
|
281
|
+
abs("drzl.config.cjs")
|
|
282
|
+
]);
|
|
283
|
+
for (const t of resolveTemplateDirsSync(cfg, cwd)) targets.add(t);
|
|
284
|
+
return [...targets];
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
export {
|
|
288
|
+
NamingSchema,
|
|
289
|
+
AffixSchema,
|
|
290
|
+
ImportExtensionSchema,
|
|
291
|
+
GeneratorSchema,
|
|
292
|
+
AnalyzerSchema,
|
|
293
|
+
ConfigSchema,
|
|
294
|
+
defineConfig,
|
|
295
|
+
resolveConfig,
|
|
296
|
+
loadConfig,
|
|
297
|
+
computeGeneratorOutputDirs,
|
|
298
|
+
resolveTemplateDirsSync,
|
|
299
|
+
computeWatchTargets
|
|
300
|
+
};
|
|
301
|
+
//# sourceMappingURL=chunk-HHT7INUJ.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 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 * 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). */\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 const targets = new Set<string>([\n path.join(path.dirname(schemaAbs), '**/*.{ts,tsx,js}'),\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
|
@@ -34,6 +34,7 @@ var path3 = __toESM(require("path"), 1);
|
|
|
34
34
|
var import_ora = __toESM(require("ora"), 1);
|
|
35
35
|
|
|
36
36
|
// src/config.ts
|
|
37
|
+
var import_validation_core = require("@drzl/validation-core");
|
|
37
38
|
var fs = __toESM(require("fs"), 1);
|
|
38
39
|
var import_node_module = require("module");
|
|
39
40
|
var path = __toESM(require("path"), 1);
|
|
@@ -42,8 +43,41 @@ var NamingSchema = import_zod.z.object({
|
|
|
42
43
|
routerSuffix: import_zod.z.string().default("Router"),
|
|
43
44
|
procedureCase: import_zod.z.enum(["camel", "kebab", "snake"]).default("camel")
|
|
44
45
|
}).partial();
|
|
46
|
+
var AffixValueSchema = import_zod.z.union(
|
|
47
|
+
[
|
|
48
|
+
import_zod.z.string(),
|
|
49
|
+
import_zod.z.object({
|
|
50
|
+
insert: import_zod.z.string().optional(),
|
|
51
|
+
update: import_zod.z.string().optional(),
|
|
52
|
+
select: import_zod.z.string().optional()
|
|
53
|
+
}).strict()
|
|
54
|
+
],
|
|
55
|
+
{
|
|
56
|
+
error: 'Expected a string to use for every mode, or an object with any of the keys "insert", "update" and "select". Those keys are lowercase, matching the mode names drzl uses everywhere else.'
|
|
57
|
+
}
|
|
58
|
+
);
|
|
59
|
+
var AffixPartSchema = import_zod.z.object({
|
|
60
|
+
prefix: AffixValueSchema.optional(),
|
|
61
|
+
suffix: AffixValueSchema.optional()
|
|
62
|
+
}).strict();
|
|
63
|
+
var AffixSchema = import_zod.z.object({
|
|
64
|
+
/**
|
|
65
|
+
* `preserve` (default) keeps today's output: the Drizzle export name goes into the
|
|
66
|
+
* identifier verbatim, so `export const users` yields `InsertusersSchema`. `pascal`
|
|
67
|
+
* upper-camels it first, yielding `InsertUsersSchema`.
|
|
68
|
+
*/
|
|
69
|
+
tableCase: import_zod.z.enum(["preserve", "pascal"]).optional(),
|
|
70
|
+
schema: AffixPartSchema.optional(),
|
|
71
|
+
type: AffixPartSchema.optional()
|
|
72
|
+
}).strict();
|
|
73
|
+
var ImportExtensionSchema = import_zod.z.enum(import_validation_core.IMPORT_EXTENSIONS);
|
|
45
74
|
var GeneratorSchema = import_zod.z.object({
|
|
46
75
|
kind: import_zod.z.enum(["orpc", "service", "zod", "valibot", "arktype"]),
|
|
76
|
+
/**
|
|
77
|
+
* Overrides the top-level `importExtension` for this generator alone, for a project whose
|
|
78
|
+
* generated directories are compiled by different tsconfigs.
|
|
79
|
+
*/
|
|
80
|
+
importExtension: ImportExtensionSchema.optional(),
|
|
47
81
|
template: import_zod.z.string().optional(),
|
|
48
82
|
includeRelations: import_zod.z.boolean().optional(),
|
|
49
83
|
naming: NamingSchema.optional(),
|
|
@@ -64,12 +98,22 @@ var GeneratorSchema = import_zod.z.object({
|
|
|
64
98
|
// zod/valibot/arktype generator specific options
|
|
65
99
|
schemaSuffix: import_zod.z.string().optional(),
|
|
66
100
|
fileSuffix: import_zod.z.string().optional(),
|
|
101
|
+
/**
|
|
102
|
+
* Prefixes, suffixes and table casing for generated identifiers (zod/valibot/arktype).
|
|
103
|
+
* Omitting it reproduces the output of every previous release exactly.
|
|
104
|
+
*/
|
|
105
|
+
affix: AffixSchema.optional(),
|
|
67
106
|
// orpc validation sharing
|
|
68
107
|
validation: import_zod.z.object({
|
|
69
108
|
useShared: import_zod.z.boolean().default(false).optional(),
|
|
70
109
|
library: import_zod.z.enum(["zod", "valibot", "arktype"]).default("zod").optional(),
|
|
71
110
|
importPath: import_zod.z.string().optional(),
|
|
72
|
-
schemaSuffix: import_zod.z.string().optional()
|
|
111
|
+
schemaSuffix: import_zod.z.string().optional(),
|
|
112
|
+
/**
|
|
113
|
+
* How the validation generator named its exports. Usually left unset: the CLI copies
|
|
114
|
+
* it from the sibling generator whose `kind` matches `library`.
|
|
115
|
+
*/
|
|
116
|
+
affix: AffixSchema.optional()
|
|
73
117
|
}).optional(),
|
|
74
118
|
// template options
|
|
75
119
|
templateOptions: import_zod.z.record(import_zod.z.string(), import_zod.z.any()).optional()
|
|
@@ -82,13 +126,95 @@ var AnalyzerSchema = import_zod.z.object({
|
|
|
82
126
|
var ConfigSchema = import_zod.z.object({
|
|
83
127
|
schema: import_zod.z.string(),
|
|
84
128
|
outDir: import_zod.z.string().default("src/api"),
|
|
129
|
+
/**
|
|
130
|
+
* How every relative specifier drzl invents spells its extension, for every generator.
|
|
131
|
+
* A generator may override it. Defaults to `js`, which is the only form that resolves
|
|
132
|
+
* under every `moduleResolution` without a compiler flag.
|
|
133
|
+
*/
|
|
134
|
+
importExtension: ImportExtensionSchema.default(import_validation_core.DEFAULT_IMPORT_EXTENSION),
|
|
85
135
|
analyzer: AnalyzerSchema.default({
|
|
86
136
|
includeRelations: true,
|
|
87
137
|
validateConstraints: true,
|
|
88
138
|
includeHeuristicRelations: false
|
|
89
139
|
}),
|
|
90
140
|
generators: import_zod.z.array(GeneratorSchema).min(1).default([{ kind: "orpc" }])
|
|
141
|
+
}).superRefine((cfg, ctx) => {
|
|
142
|
+
cfg.generators.forEach((g, i) => {
|
|
143
|
+
const report = (base, affix, schemaSuffix) => {
|
|
144
|
+
for (const issue of (0, import_validation_core.validateAffix)(affix, schemaSuffix)) {
|
|
145
|
+
ctx.addIssue({
|
|
146
|
+
code: "custom",
|
|
147
|
+
path: ["generators", i, ...base, ...issue.path],
|
|
148
|
+
message: issue.message
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
report(["affix"], g.affix, g.schemaSuffix);
|
|
153
|
+
report(
|
|
154
|
+
["validation", "affix"],
|
|
155
|
+
g.validation?.affix,
|
|
156
|
+
g.validation?.schemaSuffix
|
|
157
|
+
);
|
|
158
|
+
});
|
|
91
159
|
});
|
|
160
|
+
function sharedSchemaNames(opts) {
|
|
161
|
+
const resolved = (0, import_validation_core.resolveAffix)(opts);
|
|
162
|
+
return import_validation_core.NAME_MODES.map((mode) => (0, import_validation_core.schemaName)(mode, import_validation_core.AFFIX_PROBE_TABLE, resolved));
|
|
163
|
+
}
|
|
164
|
+
function resolveConfig(cfg) {
|
|
165
|
+
const warnings = [];
|
|
166
|
+
const generators = cfg.generators.map((g) => ({
|
|
167
|
+
...g,
|
|
168
|
+
importExtension: g.importExtension ?? cfg.importExtension
|
|
169
|
+
}));
|
|
170
|
+
for (const g of generators) {
|
|
171
|
+
if (g.kind !== "orpc") continue;
|
|
172
|
+
const v = g.validation;
|
|
173
|
+
if (!v?.useShared) continue;
|
|
174
|
+
const library = v.library ?? "zod";
|
|
175
|
+
const siblings = generators.filter((s) => s.kind === library);
|
|
176
|
+
if (siblings.length !== 1) continue;
|
|
177
|
+
const sibling = siblings[0];
|
|
178
|
+
const theirs = sharedSchemaNames({
|
|
179
|
+
affix: sibling.affix,
|
|
180
|
+
schemaSuffix: sibling.schemaSuffix
|
|
181
|
+
});
|
|
182
|
+
if (!v.affix) {
|
|
183
|
+
if (sibling.affix) {
|
|
184
|
+
g.validation = {
|
|
185
|
+
...v,
|
|
186
|
+
affix: (0, import_validation_core.resolveAffix)({
|
|
187
|
+
affix: sibling.affix,
|
|
188
|
+
schemaSuffix: sibling.schemaSuffix
|
|
189
|
+
})
|
|
190
|
+
};
|
|
191
|
+
continue;
|
|
192
|
+
}
|
|
193
|
+
const mine2 = sharedSchemaNames({ schemaSuffix: v.schemaSuffix });
|
|
194
|
+
if (mine2.join(",") !== theirs.join(",")) {
|
|
195
|
+
warnings.push(
|
|
196
|
+
`drzl config: the "orpc" generator's validation.schemaSuffix (${JSON.stringify(v.schemaSuffix ?? "Schema")}) does not match the "${library}" generator's schemaSuffix (${JSON.stringify(sibling.schemaSuffix ?? "Schema")}). The router will import ${mine2.join(", ")} but the "${library}" generator exports ${theirs.join(", ")}, so the generated router will not compile. Set both to the same value, or move to "affix", which is inherited automatically.`
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
201
|
+
const mine = sharedSchemaNames({
|
|
202
|
+
affix: v.affix,
|
|
203
|
+
schemaSuffix: v.schemaSuffix
|
|
204
|
+
});
|
|
205
|
+
if (mine.join(",") !== theirs.join(",")) {
|
|
206
|
+
throw new Error(
|
|
207
|
+
`drzl config: the "orpc" generator imports shared ${library} schemas, but its validation.affix disagrees with the "${library}" generator's own naming. The router would import ${mine.join(", ")} while the "${library}" generator exports ${theirs.join(", ")}. Make them match, or drop validation.affix and let it be inherited from the "${library}" generator.`
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
return { config: { ...cfg, generators }, warnings };
|
|
212
|
+
}
|
|
213
|
+
function finalize(raw) {
|
|
214
|
+
const { config, warnings } = resolveConfig(ConfigSchema.parse(raw));
|
|
215
|
+
for (const w of warnings) console.warn(w);
|
|
216
|
+
return config;
|
|
217
|
+
}
|
|
92
218
|
async function loadConfig(customPath) {
|
|
93
219
|
const fsp = await import("fs/promises");
|
|
94
220
|
const candidates = customPath ? [customPath] : [
|
|
@@ -108,7 +234,7 @@ async function loadConfig(customPath) {
|
|
|
108
234
|
const ext = path.extname(p).toLowerCase();
|
|
109
235
|
if (ext === ".json") {
|
|
110
236
|
const raw2 = JSON.parse(await fsp.readFile(p, "utf8"));
|
|
111
|
-
return
|
|
237
|
+
return finalize(raw2);
|
|
112
238
|
}
|
|
113
239
|
const { createJiti } = await import("jiti");
|
|
114
240
|
const stat = await fsp.stat(p);
|
|
@@ -122,12 +248,12 @@ async function loadConfig(customPath) {
|
|
|
122
248
|
// bump on edit
|
|
123
249
|
interopDefault: true,
|
|
124
250
|
tryNative: false
|
|
125
|
-
//
|
|
251
|
+
// <-- prevent native import of .ts
|
|
126
252
|
// debug: true,
|
|
127
253
|
});
|
|
128
254
|
const mod = await jiti.import(p);
|
|
129
255
|
const raw = mod?.default ?? mod;
|
|
130
|
-
return
|
|
256
|
+
return finalize(raw);
|
|
131
257
|
}
|
|
132
258
|
return null;
|
|
133
259
|
}
|
|
@@ -331,6 +457,7 @@ program.command("generate").description("Run configured generators (drzl.config.
|
|
|
331
457
|
outputHeader: g.outputHeader,
|
|
332
458
|
format: g.format,
|
|
333
459
|
templateOptions: g.templateOptions,
|
|
460
|
+
importExtension: g.importExtension,
|
|
334
461
|
validation: g.validation,
|
|
335
462
|
onProgress: ({ index }) => progress.update(index)
|
|
336
463
|
});
|
|
@@ -348,7 +475,8 @@ program.command("generate").description("Run configured generators (drzl.config.
|
|
|
348
475
|
format: g.format,
|
|
349
476
|
dataAccess: g.dataAccess,
|
|
350
477
|
dbImportPath: g.dbImportPath,
|
|
351
|
-
schemaImportPath: g.schemaImportPath
|
|
478
|
+
schemaImportPath: g.schemaImportPath,
|
|
479
|
+
importExtension: g.importExtension
|
|
352
480
|
});
|
|
353
481
|
progress.stop();
|
|
354
482
|
(0, import_ora.default)().succeed(import_chalk2.default.green(`Generated (service): ${files.length} files`));
|
|
@@ -372,7 +500,9 @@ program.command("generate").description("Run configured generators (drzl.config.
|
|
|
372
500
|
outputHeader: g.outputHeader,
|
|
373
501
|
format: g.format,
|
|
374
502
|
schemaSuffix: g.schemaSuffix,
|
|
375
|
-
fileSuffix: g.fileSuffix
|
|
503
|
+
fileSuffix: g.fileSuffix,
|
|
504
|
+
importExtension: g.importExtension,
|
|
505
|
+
affix: g.affix
|
|
376
506
|
});
|
|
377
507
|
progress.stop();
|
|
378
508
|
(0, import_ora.default)().succeed(import_chalk2.default.green(`Generated (zod): ${files.length} files`));
|
|
@@ -396,7 +526,9 @@ program.command("generate").description("Run configured generators (drzl.config.
|
|
|
396
526
|
outputHeader: g.outputHeader,
|
|
397
527
|
format: g.format,
|
|
398
528
|
schemaSuffix: g.schemaSuffix,
|
|
399
|
-
fileSuffix: g.fileSuffix
|
|
529
|
+
fileSuffix: g.fileSuffix,
|
|
530
|
+
importExtension: g.importExtension,
|
|
531
|
+
affix: g.affix
|
|
400
532
|
});
|
|
401
533
|
progress.stop();
|
|
402
534
|
(0, import_ora.default)().succeed(import_chalk2.default.green(`Generated (valibot): ${files.length} files`));
|
|
@@ -420,7 +552,9 @@ program.command("generate").description("Run configured generators (drzl.config.
|
|
|
420
552
|
outputHeader: g.outputHeader,
|
|
421
553
|
format: g.format,
|
|
422
554
|
schemaSuffix: g.schemaSuffix,
|
|
423
|
-
fileSuffix: g.fileSuffix
|
|
555
|
+
fileSuffix: g.fileSuffix,
|
|
556
|
+
importExtension: g.importExtension,
|
|
557
|
+
affix: g.affix
|
|
424
558
|
});
|
|
425
559
|
progress.stop();
|
|
426
560
|
(0, import_ora.default)().succeed(import_chalk2.default.green(`Generated (arktype): ${files.length} files`));
|
|
@@ -576,6 +710,7 @@ program.command("watch").description("Watch schema and regenerate on changes").o
|
|
|
576
710
|
outputHeader: g.outputHeader,
|
|
577
711
|
format: g.format,
|
|
578
712
|
templateOptions: g.templateOptions,
|
|
713
|
+
importExtension: g.importExtension,
|
|
579
714
|
validation: g.validation
|
|
580
715
|
});
|
|
581
716
|
opts.json ? console.log(JSON.stringify({ event: "generate_complete", kind: g.kind, files })) : console.log(
|
|
@@ -594,7 +729,8 @@ program.command("watch").description("Watch schema and regenerate on changes").o
|
|
|
594
729
|
format: g.format,
|
|
595
730
|
dataAccess: g.dataAccess,
|
|
596
731
|
dbImportPath: g.dbImportPath,
|
|
597
|
-
schemaImportPath: g.schemaImportPath
|
|
732
|
+
schemaImportPath: g.schemaImportPath,
|
|
733
|
+
importExtension: g.importExtension
|
|
598
734
|
});
|
|
599
735
|
opts.json ? console.log(JSON.stringify({ event: "generate_complete", kind: g.kind, files })) : console.log(
|
|
600
736
|
import_chalk2.default.green(`Generated (service): ${files.length} files`),
|
|
@@ -619,7 +755,9 @@ program.command("watch").description("Watch schema and regenerate on changes").o
|
|
|
619
755
|
outputHeader: g.outputHeader,
|
|
620
756
|
format: g.format,
|
|
621
757
|
schemaSuffix: g.schemaSuffix,
|
|
622
|
-
fileSuffix: g.fileSuffix
|
|
758
|
+
fileSuffix: g.fileSuffix,
|
|
759
|
+
importExtension: g.importExtension,
|
|
760
|
+
affix: g.affix
|
|
623
761
|
});
|
|
624
762
|
opts.json ? console.log(JSON.stringify({ event: "generate_complete", kind: g.kind, files })) : console.log(
|
|
625
763
|
import_chalk2.default.green(`Generated (zod): ${files.length} files`),
|
|
@@ -644,7 +782,9 @@ program.command("watch").description("Watch schema and regenerate on changes").o
|
|
|
644
782
|
outputHeader: g.outputHeader,
|
|
645
783
|
format: g.format,
|
|
646
784
|
schemaSuffix: g.schemaSuffix,
|
|
647
|
-
fileSuffix: g.fileSuffix
|
|
785
|
+
fileSuffix: g.fileSuffix,
|
|
786
|
+
importExtension: g.importExtension,
|
|
787
|
+
affix: g.affix
|
|
648
788
|
});
|
|
649
789
|
opts.json ? console.log(JSON.stringify({ event: "generate_complete", kind: g.kind, files })) : console.log(
|
|
650
790
|
import_chalk2.default.green(`Generated (valibot): ${files.length} files`),
|
|
@@ -669,7 +809,9 @@ program.command("watch").description("Watch schema and regenerate on changes").o
|
|
|
669
809
|
outputHeader: g.outputHeader,
|
|
670
810
|
format: g.format,
|
|
671
811
|
schemaSuffix: g.schemaSuffix,
|
|
672
|
-
fileSuffix: g.fileSuffix
|
|
812
|
+
fileSuffix: g.fileSuffix,
|
|
813
|
+
importExtension: g.importExtension,
|
|
814
|
+
affix: g.affix
|
|
673
815
|
});
|
|
674
816
|
opts.json ? console.log(JSON.stringify({ event: "generate_complete", kind: g.kind, files })) : console.log(
|
|
675
817
|
import_chalk2.default.green(`Generated (arktype): ${files.length} files`),
|