kavoru 0.8.9 → 0.8.10

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/README.md CHANGED
@@ -55,6 +55,7 @@ During setup you can pick which integrations to scaffold. Core is always include
55
55
  | `resend` | Resend email |
56
56
  | `cron` | Cron jobs |
57
57
  | `docker` | Dockerfile + Compose |
58
+ | `cli` | Project CLI (`kavoru module`, bin, scaffolds) |
58
59
 
59
60
  Interactive mode (TTY) shows a checkbox menu (↑↓ move, Space toggle, Enter confirm). Non-interactive runs use the full stack unless you pass flags.
60
61
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kavoru",
3
- "version": "0.8.9",
3
+ "version": "0.8.10",
4
4
  "description": "Scaffold a new Kavoru (Elysia + Bun) backend from the official template",
5
5
  "type": "module",
6
6
  "bin": {
package/src/args.ts CHANGED
@@ -15,9 +15,13 @@ export type CliOptions = {
15
15
 
16
16
  const HELP = `\
17
17
  Usage: kavoru [options] [directory]
18
+ kavoru module <module-name> [options]
18
19
 
19
20
  Create a new project from the Kavoru Elysia + Bun template.
20
21
 
22
+ Commands:
23
+ module <name> Generate src/modules/<name> (routes, service, types)
24
+
21
25
  Arguments:
22
26
  directory Project folder (use "." for current directory)
23
27
 
@@ -33,14 +37,15 @@ Options:
33
37
  --no-features <list> Comma-separated features to exclude
34
38
 
35
39
  Features:
36
- auth, postgres, otel, sentry, kafka, websocket, resend, cron, docker
37
- (prisma is accepted as an alias for postgres)
40
+ auth, postgres, otel, sentry, kafka, websocket, resend, cron, docker, cli
41
+ (prisma is accepted as an alias for postgres; kavoru-cli for cli)
38
42
 
39
43
  Examples:
40
44
  bunx kavoru@latest my-api
41
45
  bunx kavoru@latest my-api --minimal
42
46
  bunx kavoru@latest my-api --features auth,postgres,otel
43
47
  bunx kavoru@latest my-api --no-features kafka,docker,resend
48
+ bunx kavoru@latest module users
44
49
  `;
45
50
 
46
51
  export function parseArgs(argv: string[]): CliOptions {
package/src/features.ts CHANGED
@@ -11,10 +11,12 @@ export type FeatureId =
11
11
  | "websocket"
12
12
  | "resend"
13
13
  | "cron"
14
- | "docker";
14
+ | "docker"
15
+ | "cli";
15
16
 
16
17
  const FEATURE_ALIASES: Record<string, FeatureId> = {
17
18
  prisma: "postgres",
19
+ "kavoru-cli": "cli",
18
20
  };
19
21
 
20
22
  export type FeatureSelection = Record<FeatureId, boolean>;
@@ -71,6 +73,11 @@ export const FEATURES: FeatureDef[] = [
71
73
  label: "Docker",
72
74
  description: "Dockerfile and Docker Compose stack",
73
75
  },
76
+ {
77
+ id: "cli",
78
+ label: "Project CLI",
79
+ description: "kavoru module command, bin, and module scaffolds",
80
+ },
74
81
  ];
75
82
 
76
83
  export const FEATURE_IDS = FEATURES.map((feature) => feature.id);
@@ -113,6 +120,13 @@ const FEATURE_PATHS: Record<FeatureId, string[]> = {
113
120
  resend: ["src/infra/resend"],
114
121
  cron: ["src/schedules"],
115
122
  docker: ["docker-compose.yaml", "docker"],
123
+ cli: [
124
+ "bin/kavoru.js",
125
+ "scripts/kavoru-cli.ts",
126
+ "scripts/generate-module.ts",
127
+ "__tests__/generate-module.test.ts",
128
+ "__tests__/kavoru-cli.test.ts",
129
+ ],
116
130
  };
117
131
 
118
132
  const FEATURE_DEPENDENCIES: Partial<
@@ -141,6 +155,7 @@ const FEATURE_SCRIPTS: Partial<Record<FeatureId, string[]>> = {
141
155
  otel: ["otel:view", "otel:tui"],
142
156
  sentry: ["sentry:spotlight"],
143
157
  postgres: ["seed"],
158
+ cli: ["kavoru", "module"],
144
159
  };
145
160
 
146
161
  function resolveFeatureId(raw: string): FeatureId | null {
@@ -446,6 +461,7 @@ async function patchPackageJson(
446
461
  if (!(await pkgFile.exists())) return;
447
462
 
448
463
  const pkg = (await pkgFile.json()) as {
464
+ bin?: Record<string, string>;
449
465
  dependencies?: Record<string, string>;
450
466
  devDependencies?: Record<string, string>;
451
467
  scripts?: Record<string, string>;
@@ -476,6 +492,10 @@ async function patchPackageJson(
476
492
  pkg.scripts.start = "bun run src/index.ts";
477
493
  }
478
494
 
495
+ if (!selection.cli) {
496
+ delete pkg.bin;
497
+ }
498
+
479
499
  await Bun.write(pkgPath, `${JSON.stringify(pkg, null, 2)}\n`);
480
500
  }
481
501
 
package/src/index.ts CHANGED
@@ -3,10 +3,23 @@
3
3
  import { parseArgs, printHelp, printVersion } from "./args";
4
4
  import { runCli } from "./cli";
5
5
  import { log } from "./log";
6
+ import { printModuleHelp, runModuleCommand } from "./module-cli";
6
7
 
7
8
  async function main(): Promise<void> {
8
9
  try {
9
- const options = parseArgs(process.argv.slice(2));
10
+ const argv = process.argv.slice(2);
11
+
12
+ if (argv[0] === "module") {
13
+ if (argv.includes("-h") || argv.includes("--help")) {
14
+ printModuleHelp();
15
+ return;
16
+ }
17
+
18
+ await runModuleCommand(argv.slice(1));
19
+ return;
20
+ }
21
+
22
+ const options = parseArgs(argv);
10
23
 
11
24
  if (options.help) {
12
25
  printHelp();
@@ -0,0 +1,79 @@
1
+ import { existsSync } from "node:fs";
2
+ import path from "node:path";
3
+ import { log } from "./log";
4
+
5
+ function findProjectRoot(cwd: string): string {
6
+ let current = path.resolve(cwd);
7
+
8
+ while (true) {
9
+ const packageJson = path.join(current, "package.json");
10
+ const localCli = path.join(current, "scripts/kavoru-cli.ts");
11
+ const moduleScript = path.join(current, "scripts/generate-module.ts");
12
+ if (
13
+ existsSync(packageJson) &&
14
+ (existsSync(localCli) || existsSync(moduleScript))
15
+ ) {
16
+ return current;
17
+ }
18
+
19
+ const parent = path.dirname(current);
20
+ if (parent === current) {
21
+ break;
22
+ }
23
+ current = parent;
24
+ }
25
+
26
+ throw new Error(
27
+ "Could not find a Kavoru project with the Project CLI enabled. Scaffold with the cli feature or run from a project that includes scripts/kavoru-cli.ts.",
28
+ );
29
+ }
30
+
31
+ export async function runModuleCommand(argv: string[]): Promise<void> {
32
+ const force = argv.includes("--force") || argv.includes("-f");
33
+ const name = argv.find((arg) => !arg.startsWith("-"));
34
+
35
+ if (!name) {
36
+ throw new Error("Usage: kavoru module <module-name> [--force]");
37
+ }
38
+
39
+ const projectDir = findProjectRoot(process.cwd());
40
+ const localCli = path.join(projectDir, "scripts/kavoru-cli.ts");
41
+ const scriptPath = existsSync(localCli)
42
+ ? localCli
43
+ : path.join(projectDir, "scripts/generate-module.ts");
44
+ const cmd = existsSync(localCli)
45
+ ? ["bun", scriptPath, "module", name]
46
+ : ["bun", scriptPath, name];
47
+ if (force) cmd.push("--force");
48
+
49
+ log.info(`Generating module "${name}" in ${projectDir}`);
50
+
51
+ const proc = Bun.spawn(cmd, {
52
+ cwd: projectDir,
53
+ stdout: "inherit",
54
+ stderr: "inherit",
55
+ });
56
+
57
+ const exitCode = await proc.exited;
58
+ if (exitCode !== 0) {
59
+ process.exit(exitCode ?? 1);
60
+ }
61
+ }
62
+
63
+ export function printModuleHelp(): void {
64
+ console.log(`\
65
+ Usage: kavoru module <module-name> [options]
66
+
67
+ Generate a feature module under src/modules/<module-name>/ with:
68
+ routes.ts, service.ts, types.ts
69
+
70
+ Options:
71
+ -f, --force Overwrite an existing module folder
72
+ -h, --help Show help
73
+
74
+ Examples:
75
+ kavoru module users
76
+ kavoru module user-profile --force
77
+ bun run kavoru module billing
78
+ `);
79
+ }