@drzl/cli 4.14.3 → 4.15.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/cli.js CHANGED
@@ -3,8 +3,9 @@ import {
3
3
  computeGeneratorOutputDirs,
4
4
  computeWatchTargets,
5
5
  filterTables,
6
- loadConfig
7
- } from "./chunk-HGD5CBM5.js";
6
+ loadConfig,
7
+ trpcOutDir
8
+ } from "./chunk-FZNDWYBB.js";
8
9
 
9
10
  // src/cli.ts
10
11
  import { SchemaAnalyzer } from "@drzl/analyzer";
@@ -16,6 +17,25 @@ import { Command } from "commander";
16
17
  import * as path4 from "path";
17
18
  import ora from "ora";
18
19
 
20
+ // src/trpc-options.ts
21
+ function trpcOptions(g, cfg, servicesDir) {
22
+ return {
23
+ outputDir: trpcOutDir(g, cfg),
24
+ template: g.template,
25
+ includeRelations: g.includeRelations,
26
+ naming: g.naming,
27
+ outputHeader: g.outputHeader,
28
+ format: g.format,
29
+ importExtension: g.importExtension,
30
+ validation: g.validation,
31
+ databaseInjection: g.databaseInjection,
32
+ // Where the service generator is actually writing, so `template: 'service'` emits an import
33
+ // of a module that exists. The generator defaults this to `src/services`, which is right only
34
+ // by coincidence for a config that puts them elsewhere.
35
+ servicesDir
36
+ };
37
+ }
38
+
19
39
  // src/validation-options.ts
20
40
  function validationOptions(g, cfg, outDir, caps = {}) {
21
41
  return {
@@ -91,6 +111,30 @@ async function restoreSnapshot(before, after) {
91
111
  }
92
112
  }
93
113
 
114
+ // src/generator-loader.ts
115
+ var GeneratorNotInstalledError = class extends Error {
116
+ constructor(specifier, reason) {
117
+ super(`${specifier} is not installed`);
118
+ this.specifier = specifier;
119
+ this.reason = reason;
120
+ this.name = "GeneratorNotInstalledError";
121
+ }
122
+ };
123
+ function isPackageMissing(err, specifier) {
124
+ const code = err?.code;
125
+ if (code !== "ERR_MODULE_NOT_FOUND") return false;
126
+ const message = err?.message;
127
+ return typeof message === "string" && message.includes(`'${specifier}'`);
128
+ }
129
+ async function loadGenerator(specifier, load) {
130
+ try {
131
+ return await load();
132
+ } catch (e) {
133
+ if (isPackageMissing(e, specifier)) throw new GeneratorNotInstalledError(specifier, e);
134
+ throw e;
135
+ }
136
+ }
137
+
94
138
  // src/sponsor.ts
95
139
  import chalk from "chalk";
96
140
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
@@ -194,6 +238,17 @@ function readCliVersion() {
194
238
  var CLI_VERSION = readCliVersion();
195
239
 
196
240
  // src/cli.ts
241
+ function reportGeneratorFailure(kind, e) {
242
+ if (e instanceof GeneratorNotInstalledError) {
243
+ console.error(
244
+ chalk2.red(`The ${kind} generator is not installed.`),
245
+ chalk2.yellow(`
246
+ Install with: npm install ${e.specifier}`)
247
+ );
248
+ return;
249
+ }
250
+ console.error(chalk2.red(`The ${kind} generator failed:`), e?.message ?? e);
251
+ }
197
252
  var program = new Command();
198
253
  program.name("drzl").description("DRZL - Drizzle Developer Toolkit").version(CLI_VERSION);
199
254
  program.addHelpText(
@@ -284,15 +339,40 @@ program.command("generate").description("Run configured generators (drzl.config.
284
339
  templateOptions: g.templateOptions,
285
340
  importExtension: g.importExtension,
286
341
  validation: g.validation,
342
+ // Documented on this generator since it was added and never reachable from a config
343
+ // file, because the config schema had no such key and zod stripped it in silence.
344
+ databaseInjection: g.databaseInjection,
287
345
  servicesDir,
288
346
  onProgress: ({ index }) => progress.update(index)
289
347
  });
290
348
  progress.stop();
291
349
  ora().succeed(chalk2.green(`Generated (${g.kind}): ${files.length} files`));
292
350
  files.forEach((f) => console.log(" -", chalk2.cyan(f)));
351
+ } else if (g.kind === "trpc") {
352
+ try {
353
+ const { TRPCGenerator } = await loadGenerator(
354
+ "@drzl/generator-trpc",
355
+ () => import("./dist-XBGVORL3.js")
356
+ );
357
+ const gen = new TRPCGenerator(analysis);
358
+ const { files } = await gen.generate({
359
+ ...trpcOptions(g, cfg, servicesDir),
360
+ onProgress: ({ index }) => progress.update(index)
361
+ });
362
+ progress.stop();
363
+ ora().succeed(chalk2.green(`Generated (trpc): ${files.length} files`));
364
+ files.forEach((f) => console.log(" -", chalk2.cyan(f)));
365
+ } catch (e) {
366
+ progress.stop();
367
+ reportGeneratorFailure(g.kind, e);
368
+ process.exit(1);
369
+ }
293
370
  } else if (g.kind === "service") {
294
371
  try {
295
- const { ServiceGenerator } = await import("@drzl/generator-service");
372
+ const { ServiceGenerator } = await loadGenerator(
373
+ "@drzl/generator-service",
374
+ () => import("@drzl/generator-service")
375
+ );
296
376
  const gen = new ServiceGenerator(analysis);
297
377
  const target = g.path ?? "src/services";
298
378
  const files = await gen.generate({
@@ -302,23 +382,27 @@ program.command("generate").description("Run configured generators (drzl.config.
302
382
  dataAccess: g.dataAccess,
303
383
  dbImportPath: g.dbImportPath,
304
384
  schemaImportPath: g.schemaImportPath,
305
- importExtension: g.importExtension
385
+ importExtension: g.importExtension,
386
+ // The other half of `databaseInjection`. A router generator in injection mode
387
+ // emits `Service.getById(ctx.db, id)`, and only a service generated in the same
388
+ // mode has a `db` parameter to receive it. This branch never passed the option, so
389
+ // the two halves of one generated project disagreed about the signature.
390
+ databaseInjection: g.databaseInjection
306
391
  });
307
392
  progress.stop();
308
393
  ora().succeed(chalk2.green(`Generated (service): ${files.length} files`));
309
394
  files.forEach((f) => console.log(" -", chalk2.cyan(f)));
310
395
  } catch (e) {
311
396
  progress.stop();
312
- console.error(
313
- chalk2.red("Service generator missing."),
314
- chalk2.yellow("\nInstall with: npm install @drzl/generator-service")
315
- );
316
- console.error(chalk2.gray("Error details:"), e?.message ?? e);
397
+ reportGeneratorFailure(g.kind, e);
317
398
  process.exit(1);
318
399
  }
319
400
  } else if (g.kind === "zod") {
320
401
  try {
321
- const { ZodGenerator } = await import("@drzl/generator-zod");
402
+ const { ZodGenerator } = await loadGenerator(
403
+ "@drzl/generator-zod",
404
+ () => import("@drzl/generator-zod")
405
+ );
322
406
  const gen = new ZodGenerator(analysis);
323
407
  const target = g.path ?? "src/validators/zod";
324
408
  const files = await gen.generate(
@@ -329,16 +413,15 @@ program.command("generate").description("Run configured generators (drzl.config.
329
413
  files.forEach((f) => console.log(" -", chalk2.cyan(f)));
330
414
  } catch (e) {
331
415
  progress.stop();
332
- console.error(
333
- chalk2.red("Zod generator missing."),
334
- chalk2.yellow("\nInstall with: npm install @drzl/generator-zod")
335
- );
336
- console.error(chalk2.gray("Error details:"), e?.message ?? e);
416
+ reportGeneratorFailure(g.kind, e);
337
417
  process.exit(1);
338
418
  }
339
419
  } else if (g.kind === "valibot") {
340
420
  try {
341
- const { ValibotGenerator } = await import("@drzl/generator-valibot");
421
+ const { ValibotGenerator } = await loadGenerator(
422
+ "@drzl/generator-valibot",
423
+ () => import("@drzl/generator-valibot")
424
+ );
342
425
  const gen = new ValibotGenerator(analysis);
343
426
  const target = g.path ?? "src/validators/valibot";
344
427
  const files = await gen.generate(
@@ -349,16 +432,15 @@ program.command("generate").description("Run configured generators (drzl.config.
349
432
  files.forEach((f) => console.log(" -", chalk2.cyan(f)));
350
433
  } catch (e) {
351
434
  progress.stop();
352
- console.error(
353
- chalk2.red("Valibot generator missing."),
354
- chalk2.yellow("\nInstall with: npm install @drzl/generator-valibot")
355
- );
356
- console.error(chalk2.gray("Error details:"), e?.message ?? e);
435
+ reportGeneratorFailure(g.kind, e);
357
436
  process.exit(1);
358
437
  }
359
438
  } else if (g.kind === "arktype") {
360
439
  try {
361
- const { ArkTypeGenerator } = await import("@drzl/generator-arktype");
440
+ const { ArkTypeGenerator } = await loadGenerator(
441
+ "@drzl/generator-arktype",
442
+ () => import("@drzl/generator-arktype")
443
+ );
362
444
  const gen = new ArkTypeGenerator(analysis);
363
445
  const target = g.path ?? "src/validators/arktype";
364
446
  const files = await gen.generate(
@@ -369,16 +451,15 @@ program.command("generate").description("Run configured generators (drzl.config.
369
451
  files.forEach((f) => console.log(" -", chalk2.cyan(f)));
370
452
  } catch (e) {
371
453
  progress.stop();
372
- console.error(
373
- chalk2.red("ArkType generator missing."),
374
- chalk2.yellow("\nInstall with: npm install @drzl/generator-arktype")
375
- );
376
- console.error(chalk2.gray("Error details:"), e?.message ?? e);
454
+ reportGeneratorFailure(g.kind, e);
377
455
  process.exit(1);
378
456
  }
379
457
  } else if (g.kind === "json-schema") {
380
458
  try {
381
- const { JsonSchemaGenerator } = await import("./dist-UE55LTXV.js");
459
+ const { JsonSchemaGenerator } = await loadGenerator(
460
+ "@drzl/generator-json-schema",
461
+ () => import("./dist-UE55LTXV.js")
462
+ );
382
463
  const gen = new JsonSchemaGenerator(analysis);
383
464
  const target = g.path ?? "src/validators/json-schema";
384
465
  const files = await gen.generate({
@@ -392,20 +473,15 @@ program.command("generate").description("Run configured generators (drzl.config.
392
473
  files.forEach((f) => console.log(" -", chalk2.cyan(f)));
393
474
  } catch (e) {
394
475
  progress.stop();
395
- console.error(
396
- chalk2.red("JSON Schema generator missing."),
397
- chalk2.yellow("\nInstall with: npm install @drzl/generator-json-schema"),
398
- // An optional dependency, unlike the other generators, until its npm trusted
399
- // publisher exists. A missing optional dependency is skipped rather than failing
400
- // the install, which is what keeps `npm i @drzl/cli` working meanwhile.
401
- ""
402
- );
403
- console.error(chalk2.gray("Error details:"), e?.message ?? e);
476
+ reportGeneratorFailure(g.kind, e);
404
477
  process.exit(1);
405
478
  }
406
479
  } else if (g.kind === "typebox") {
407
480
  try {
408
- const { TypeBoxGenerator } = await import("@drzl/generator-typebox");
481
+ const { TypeBoxGenerator } = await loadGenerator(
482
+ "@drzl/generator-typebox",
483
+ () => import("@drzl/generator-typebox")
484
+ );
409
485
  const gen = new TypeBoxGenerator(analysis);
410
486
  const target = g.path ?? "src/validators/typebox";
411
487
  const files = await gen.generate(
@@ -416,11 +492,7 @@ program.command("generate").description("Run configured generators (drzl.config.
416
492
  files.forEach((f) => console.log(" -", chalk2.cyan(f)));
417
493
  } catch (e) {
418
494
  progress.stop();
419
- console.error(
420
- chalk2.red("TypeBox generator missing."),
421
- chalk2.yellow("\nInstall with: npm install @drzl/generator-typebox")
422
- );
423
- console.error(chalk2.gray("Error details:"), e?.message ?? e);
495
+ reportGeneratorFailure(g.kind, e);
424
496
  process.exit(1);
425
497
  }
426
498
  }
@@ -480,7 +552,37 @@ program.command("generate:orpc").argument("<schema>", "path to drizzle schema (T
480
552
  process.exit(1);
481
553
  }
482
554
  });
483
- program.command("watch").description("Watch schema and regenerate on changes").option("-c, --config <path>", "path to drzl.config").option("--pipeline <name>", "all | analyze | generate-orpc", "all").option("--debounce <ms>", "debounce ms", "200").option("--json", "emit JSON logs", false).option("--poll", "force polling (helps WSL/Docker/remote FS)", false).action(async (opts) => {
555
+ program.command("generate:trpc").argument("<schema>", "path to drizzle schema (TS)").option("-o, --outDir <dir>", "output directory", "src/api").option("--template <name>", "standard | service", "standard").option("--includeRelations", "include relation endpoints").option("--servicesDir <dir>", "where the service generator writes", "src/services").action(async (schema, opts) => {
556
+ try {
557
+ const analyzer = new SchemaAnalyzer(schema);
558
+ const analysis = await analyzer.analyze({
559
+ includeRelations: !!opts.includeRelations,
560
+ validateConstraints: true
561
+ });
562
+ const { TRPCGenerator } = await loadGenerator(
563
+ "@drzl/generator-trpc",
564
+ () => import("./dist-XBGVORL3.js")
565
+ );
566
+ const gen = new TRPCGenerator(analysis);
567
+ const { files } = await gen.generate({
568
+ outputDir: opts.outDir,
569
+ template: opts.template,
570
+ includeRelations: !!opts.includeRelations,
571
+ // Only consulted by `--template service`, and passed unconditionally so this command
572
+ // cannot become the branch that forgets it.
573
+ servicesDir: opts.servicesDir
574
+ });
575
+ console.log(
576
+ chalk2.green(`Generated:`),
577
+ files.map((f) => chalk2.cyan(f)).join(", ")
578
+ );
579
+ maybeShowSponsorMessage({ reason: "generate:trpc" });
580
+ } catch (e) {
581
+ reportGeneratorFailure("trpc", e);
582
+ process.exit(1);
583
+ }
584
+ });
585
+ program.command("watch").description("Watch schema and regenerate on changes").option("-c, --config <path>", "path to drzl.config").option("--pipeline <name>", "all | analyze | generate-orpc | generate-trpc", "all").option("--debounce <ms>", "debounce ms", "200").option("--json", "emit JSON logs", false).option("--poll", "force polling (helps WSL/Docker/remote FS)", false).action(async (opts) => {
484
586
  let cfg = await loadConfig(opts.config);
485
587
  if (!cfg) {
486
588
  console.error(chalk2.red("No config found. Create drzl.config.ts or pass --config."));
@@ -580,8 +682,13 @@ program.command("watch").description("Watch schema and regenerate on changes").o
580
682
  return;
581
683
  }
582
684
  const newFiles = [];
685
+ const servicesDir = cfg.generators.find((x) => x.kind === "service")?.path ?? "src/services";
686
+ const PIPELINE_KINDS = {
687
+ "generate-orpc": "orpc",
688
+ "generate-trpc": "trpc"
689
+ };
583
690
  for (const g of cfg.generators) {
584
- if (opts.pipeline !== "all" && !(opts.pipeline === "generate-orpc" && g.kind === "orpc")) {
691
+ if (opts.pipeline !== "all" && PIPELINE_KINDS[opts.pipeline] !== g.kind) {
585
692
  continue;
586
693
  }
587
694
  if (g.kind === "orpc") {
@@ -595,16 +702,38 @@ program.command("watch").description("Watch schema and regenerate on changes").o
595
702
  format: g.format,
596
703
  templateOptions: g.templateOptions,
597
704
  importExtension: g.importExtension,
598
- validation: g.validation
705
+ validation: g.validation,
706
+ databaseInjection: g.databaseInjection,
707
+ servicesDir
599
708
  });
600
709
  opts.json ? console.log(JSON.stringify({ event: "generate_complete", kind: g.kind, files })) : console.log(
601
710
  chalk2.green(`Generated (${g.kind}):`),
602
711
  files.map((f) => chalk2.cyan(f)).join(", ")
603
712
  );
604
713
  newFiles.push(...files);
714
+ } else if (g.kind === "trpc") {
715
+ try {
716
+ const { TRPCGenerator } = await loadGenerator(
717
+ "@drzl/generator-trpc",
718
+ () => import("./dist-XBGVORL3.js")
719
+ );
720
+ const gen = new TRPCGenerator(analysis);
721
+ const { files } = await gen.generate(trpcOptions(g, cfg, servicesDir));
722
+ opts.json ? console.log(JSON.stringify({ event: "generate_complete", kind: g.kind, files })) : console.log(
723
+ chalk2.green(`Generated (trpc): ${files.length} files`),
724
+ files.map((f) => chalk2.cyan(f)).join(", ")
725
+ );
726
+ newFiles.push(...files);
727
+ } catch (e) {
728
+ reportGeneratorFailure(g.kind, e);
729
+ return;
730
+ }
605
731
  } else if (g.kind === "service") {
606
732
  try {
607
- const { ServiceGenerator } = await import("@drzl/generator-service");
733
+ const { ServiceGenerator } = await loadGenerator(
734
+ "@drzl/generator-service",
735
+ () => import("@drzl/generator-service")
736
+ );
608
737
  const gen = new ServiceGenerator(analysis);
609
738
  const target = g.path ?? "src/services";
610
739
  const files = await gen.generate({
@@ -614,7 +743,8 @@ program.command("watch").description("Watch schema and regenerate on changes").o
614
743
  dataAccess: g.dataAccess,
615
744
  dbImportPath: g.dbImportPath,
616
745
  schemaImportPath: g.schemaImportPath,
617
- importExtension: g.importExtension
746
+ importExtension: g.importExtension,
747
+ databaseInjection: g.databaseInjection
618
748
  });
619
749
  opts.json ? console.log(JSON.stringify({ event: "generate_complete", kind: g.kind, files })) : console.log(
620
750
  chalk2.green(`Generated (service): ${files.length} files`),
@@ -622,16 +752,15 @@ program.command("watch").description("Watch schema and regenerate on changes").o
622
752
  );
623
753
  newFiles.push(...files);
624
754
  } catch (e) {
625
- console.error(
626
- chalk2.red("Service generator missing."),
627
- chalk2.yellow("\nInstall with: npm install @drzl/generator-service")
628
- );
629
- console.error(chalk2.gray("Error details:"), e?.message ?? e);
755
+ reportGeneratorFailure(g.kind, e);
630
756
  return;
631
757
  }
632
758
  } else if (g.kind === "zod") {
633
759
  try {
634
- const { ZodGenerator } = await import("@drzl/generator-zod");
760
+ const { ZodGenerator } = await loadGenerator(
761
+ "@drzl/generator-zod",
762
+ () => import("@drzl/generator-zod")
763
+ );
635
764
  const gen = new ZodGenerator(analysis);
636
765
  const target = g.path ?? "src/validators/zod";
637
766
  const files = await gen.generate({
@@ -649,16 +778,15 @@ program.command("watch").description("Watch schema and regenerate on changes").o
649
778
  );
650
779
  newFiles.push(...files);
651
780
  } catch (e) {
652
- console.error(
653
- chalk2.red("Zod generator missing."),
654
- chalk2.yellow("\nInstall with: npm install @drzl/generator-zod")
655
- );
656
- console.error(chalk2.gray("Error details:"), e?.message ?? e);
781
+ reportGeneratorFailure(g.kind, e);
657
782
  return;
658
783
  }
659
784
  } else if (g.kind === "valibot") {
660
785
  try {
661
- const { ValibotGenerator } = await import("@drzl/generator-valibot");
786
+ const { ValibotGenerator } = await loadGenerator(
787
+ "@drzl/generator-valibot",
788
+ () => import("@drzl/generator-valibot")
789
+ );
662
790
  const gen = new ValibotGenerator(analysis);
663
791
  const target = g.path ?? "src/validators/valibot";
664
792
  const files = await gen.generate({
@@ -676,16 +804,15 @@ program.command("watch").description("Watch schema and regenerate on changes").o
676
804
  );
677
805
  newFiles.push(...files);
678
806
  } catch (e) {
679
- console.error(
680
- chalk2.red("Valibot generator missing."),
681
- chalk2.yellow("\nInstall with: npm install @drzl/generator-valibot")
682
- );
683
- console.error(chalk2.gray("Error details:"), e?.message ?? e);
807
+ reportGeneratorFailure(g.kind, e);
684
808
  return;
685
809
  }
686
810
  } else if (g.kind === "arktype") {
687
811
  try {
688
- const { ArkTypeGenerator } = await import("@drzl/generator-arktype");
812
+ const { ArkTypeGenerator } = await loadGenerator(
813
+ "@drzl/generator-arktype",
814
+ () => import("@drzl/generator-arktype")
815
+ );
689
816
  const gen = new ArkTypeGenerator(analysis);
690
817
  const target = g.path ?? "src/validators/arktype";
691
818
  const files = await gen.generate({
@@ -703,11 +830,7 @@ program.command("watch").description("Watch schema and regenerate on changes").o
703
830
  );
704
831
  newFiles.push(...files);
705
832
  } catch (e) {
706
- console.error(
707
- chalk2.red("ArkType generator missing."),
708
- chalk2.yellow("\nInstall with: npm install @drzl/generator-arktype")
709
- );
710
- console.error(chalk2.gray("Error details:"), e?.message ?? e);
833
+ reportGeneratorFailure(g.kind, e);
711
834
  return;
712
835
  }
713
836
  }
@@ -766,6 +889,8 @@ program.command("init").description("Scaffold a drzl.config.ts").option("-y, --y
766
889
  outDir: 'src/api',
767
890
  analyzer: { includeRelations: true, validateConstraints: true },
768
891
  generators: [
892
+ // For tRPC instead: { kind: 'trpc', template: 'standard', includeRelations: true }
893
+ // To run both, give one of them its own \`path\`; they share \`outDir\` otherwise.
769
894
  { kind: 'orpc', template: 'standard', includeRelations: true }
770
895
  ]
771
896
  } as const