1#!/usr/bin/env bun
2
3/**
4 * Webette CLI entrypoint (`build` / `serve`).
5 */
6
7import { build } from "./core/build";
8import { serve } from "./core/serve";
9import { createProjectLogger } from "./logging/logger";
10import { getVersion } from "./version";
11
12type CliOptions = {
13 verbose: boolean;
14 exportModel?: {
15 only?: boolean;
16 };
17 readContent?: boolean;
18 includeDrafts?: boolean; // serve-only flag to show/hide draft entries
19};
20
21function parseArgs(argv: string[]) {
22 // Remove the first 2 entries (bun + script)
23 const rawArgs = argv.slice(2);
24
25 let verbose = false;
26 const exportModel: CliOptions["exportModel"] = {};
27 let readContent: boolean | undefined = undefined;
28 let includeDrafts: boolean | undefined = undefined;
29
30 const filtered: string[] = [];
31
32 for (let i = 0; i < rawArgs.length; i++) {
33 const arg = rawArgs[i];
34 if (arg === undefined) continue;
35
36 if (arg === "--verbose" || arg === "-v") {
37 verbose = true;
38 continue;
39 }
40
41 if (arg === "--export-model-only") {
42 exportModel.only = true;
43 continue;
44 }
45
46 if (arg === "--skip-content") {
47 readContent = false;
48 continue;
49 }
50
51 if (arg === "--include-drafts") {
52 includeDrafts = true;
53 continue;
54 }
55
56 if (arg === "--no-include-drafts") {
57 includeDrafts = false;
58 continue;
59 }
60
61 filtered.push(arg);
62 }
63
64 // Remove flags from the "useful" args
65
66 const command = filtered[0]; // "build" or "serve"
67 const sitePath = filtered[1]; // path to the site folder
68
69 const options: CliOptions = {
70 verbose,
71 exportModel: Object.keys(exportModel).length ? exportModel : undefined,
72 readContent,
73 includeDrafts,
74 };
75
76 return { command, sitePath, options };
77}
78
79async function main() {
80 const { command, sitePath, options } = parseArgs(Bun.argv);
81
82 if (!command) {
83 console.log(`webette ${getVersion()}`);
84 console.log("Usage:");
85 console.log(" webette build [site-folder]");
86 console.log(" webette serve [site-folder]");
87 console.log("");
88 console.log("If no folder is provided, the current directory is used.");
89 console.log("");
90 console.log("Options:");
91 console.log(" -v, --verbose Show debug logs");
92 console.log(" --export-model-only Export the model and skip HTML generation");
93 console.log(" --skip-content Do not read/resolve block contents");
94 console.log(" --include-drafts Serve draft entries (default in serve)");
95 console.log(" --no-include-drafts Hide draft entries even in serve");
96 return;
97 }
98
99 const rootDir = sitePath ?? process.cwd();
100
101 // 🔹 NEW: create a logger for this project + this command
102 const logger = await createProjectLogger(
103 rootDir,
104 `webette.cli.${command}`,
105 options.verbose
106 );
107
108 try {
109 switch (command) {
110 case "build":
111 await build(rootDir, options, logger);
112 break;
113
114 case "serve":
115 await serve(rootDir, options, logger);
116 break;
117
118 default:
119 logger.error("cli.unknownCommand", { command });
120 console.log("Available commands: build, serve");
121 console.log("Options:");
122 console.log(" -v, --verbose Show debug logs");
123 console.log(" --export-model-only Export the model and skip HTML generation");
124 console.log(" --skip-content Do not read/resolve block contents");
125 console.log(" --include-drafts Serve draft entries (default in serve)");
126 console.log(" --no-include-drafts Hide draft entries even in serve");
127 break;
128 }
129 } catch (error) {
130 logger.error("error.unexpected", { error: String(error) });
131 process.exitCode = 1;
132 }
133}
134
135await main();