kavoru 0.8.9 → 0.8.11
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 +1 -0
- package/package.json +1 -1
- package/src/args.ts +7 -2
- package/src/cli.ts +10 -0
- package/src/features.ts +27 -1
- package/src/index.ts +14 -1
- package/src/module-cli.ts +79 -0
- package/src/template.ts +11 -0
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, root shims) |
|
|
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
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/cli.ts
CHANGED
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
customizeProject,
|
|
22
22
|
fetchTemplate,
|
|
23
23
|
installDependencies,
|
|
24
|
+
linkProjectCli,
|
|
24
25
|
removeGitMetadata,
|
|
25
26
|
resolveTemplateSource,
|
|
26
27
|
} from "./template";
|
|
@@ -136,6 +137,9 @@ export async function runCli(options: CliOptions): Promise<void> {
|
|
|
136
137
|
|
|
137
138
|
if (options.install) {
|
|
138
139
|
await installDependencies(targetDir);
|
|
140
|
+
if (featureSelection.cli) {
|
|
141
|
+
await linkProjectCli(targetDir);
|
|
142
|
+
}
|
|
139
143
|
}
|
|
140
144
|
} finally {
|
|
141
145
|
await rm(tempDir, { recursive: true, force: true }).catch(() => undefined);
|
|
@@ -150,6 +154,12 @@ export async function runCli(options: CliOptions): Promise<void> {
|
|
|
150
154
|
if (!options.install) {
|
|
151
155
|
console.log(" bun install");
|
|
152
156
|
}
|
|
157
|
+
if (featureSelection.cli) {
|
|
158
|
+
if (!options.install) {
|
|
159
|
+
console.log(" bun run link-cli # once: put kavoru on PATH");
|
|
160
|
+
}
|
|
161
|
+
console.log(" kavoru module <name>");
|
|
162
|
+
}
|
|
153
163
|
console.log(" bun run dev");
|
|
154
164
|
console.log();
|
|
155
165
|
console.log(" API: http://localhost:3131");
|
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,17 @@ 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
|
+
"kavoru",
|
|
126
|
+
"kavoru.cmd",
|
|
127
|
+
"scripts/kavoru-cli.ts",
|
|
128
|
+
"scripts/generate-module.ts",
|
|
129
|
+
"scripts/link-cli.ts",
|
|
130
|
+
"__tests__/generate-module.test.ts",
|
|
131
|
+
"__tests__/kavoru-cli.test.ts",
|
|
132
|
+
"__tests__/link-cli.test.ts",
|
|
133
|
+
],
|
|
116
134
|
};
|
|
117
135
|
|
|
118
136
|
const FEATURE_DEPENDENCIES: Partial<
|
|
@@ -141,6 +159,7 @@ const FEATURE_SCRIPTS: Partial<Record<FeatureId, string[]>> = {
|
|
|
141
159
|
otel: ["otel:view", "otel:tui"],
|
|
142
160
|
sentry: ["sentry:spotlight"],
|
|
143
161
|
postgres: ["seed"],
|
|
162
|
+
cli: ["link-cli"],
|
|
144
163
|
};
|
|
145
164
|
|
|
146
165
|
function resolveFeatureId(raw: string): FeatureId | null {
|
|
@@ -446,6 +465,7 @@ async function patchPackageJson(
|
|
|
446
465
|
if (!(await pkgFile.exists())) return;
|
|
447
466
|
|
|
448
467
|
const pkg = (await pkgFile.json()) as {
|
|
468
|
+
bin?: Record<string, string>;
|
|
449
469
|
dependencies?: Record<string, string>;
|
|
450
470
|
devDependencies?: Record<string, string>;
|
|
451
471
|
scripts?: Record<string, string>;
|
|
@@ -476,6 +496,12 @@ async function patchPackageJson(
|
|
|
476
496
|
pkg.scripts.start = "bun run src/index.ts";
|
|
477
497
|
}
|
|
478
498
|
|
|
499
|
+
if (!selection.cli) {
|
|
500
|
+
delete pkg.bin;
|
|
501
|
+
} else {
|
|
502
|
+
pkg.bin = { kavoru: "./bin/kavoru.js" };
|
|
503
|
+
}
|
|
504
|
+
|
|
479
505
|
await Bun.write(pkgPath, `${JSON.stringify(pkg, null, 2)}\n`);
|
|
480
506
|
}
|
|
481
507
|
|
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
|
|
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
|
+
src/models/schemas/<module-name>.ts (query, body, params schemas)
|
|
70
|
+
|
|
71
|
+
Options:
|
|
72
|
+
-f, --force Overwrite an existing module folder
|
|
73
|
+
-h, --help Show help
|
|
74
|
+
|
|
75
|
+
Examples:
|
|
76
|
+
kavoru module users
|
|
77
|
+
kavoru module user-profile --force
|
|
78
|
+
`);
|
|
79
|
+
}
|
package/src/template.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
1
2
|
import { cp, mkdir, rm } from "node:fs/promises";
|
|
2
3
|
import path from "node:path";
|
|
3
4
|
import { TEMPLATE_BRANCH } from "./constants";
|
|
@@ -149,6 +150,16 @@ export async function installDependencies(projectDir: string): Promise<void> {
|
|
|
149
150
|
await runCommand(["bun", "install"], projectDir);
|
|
150
151
|
}
|
|
151
152
|
|
|
153
|
+
export async function linkProjectCli(projectDir: string): Promise<void> {
|
|
154
|
+
const script = path.join(projectDir, "scripts/link-cli.ts");
|
|
155
|
+
if (!existsSync(script)) {
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
log.step("Linking kavoru to PATH (~/.bun/bin)");
|
|
160
|
+
await runCommand(["bun", script], projectDir);
|
|
161
|
+
}
|
|
162
|
+
|
|
152
163
|
export function resolveTemplateSource(
|
|
153
164
|
repo: string,
|
|
154
165
|
branch: string,
|