@buenojs/bueno 0.8.3 → 0.8.5

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.
Files changed (218) hide show
  1. package/README.md +136 -16
  2. package/dist/cli/{index.js → bin.js} +3036 -1421
  3. package/dist/container/index.js +250 -0
  4. package/dist/context/index.js +219 -0
  5. package/dist/database/index.js +493 -0
  6. package/dist/frontend/index.js +7697 -0
  7. package/dist/health/index.js +364 -0
  8. package/dist/i18n/index.js +345 -0
  9. package/dist/index.js +11043 -6482
  10. package/dist/jobs/index.js +819 -0
  11. package/dist/lock/index.js +367 -0
  12. package/dist/logger/index.js +281 -0
  13. package/dist/metrics/index.js +289 -0
  14. package/dist/middleware/index.js +77 -0
  15. package/dist/migrations/index.js +571 -0
  16. package/dist/modules/index.js +3346 -0
  17. package/dist/notification/index.js +484 -0
  18. package/dist/observability/index.js +331 -0
  19. package/dist/openapi/index.js +776 -0
  20. package/dist/orm/index.js +1356 -0
  21. package/dist/router/index.js +886 -0
  22. package/dist/rpc/index.js +691 -0
  23. package/dist/schema/index.js +400 -0
  24. package/dist/telemetry/index.js +595 -0
  25. package/dist/template/index.js +640 -0
  26. package/dist/templates/index.js +640 -0
  27. package/dist/testing/index.js +1111 -0
  28. package/dist/types/index.js +60 -0
  29. package/package.json +121 -27
  30. package/src/cache/index.ts +2 -1
  31. package/src/cli/bin.ts +2 -2
  32. package/src/cli/commands/build.ts +183 -165
  33. package/src/cli/commands/dev.ts +96 -89
  34. package/src/cli/commands/generate.ts +142 -111
  35. package/src/cli/commands/help.ts +20 -16
  36. package/src/cli/commands/index.ts +3 -6
  37. package/src/cli/commands/migration.ts +124 -105
  38. package/src/cli/commands/new.ts +392 -438
  39. package/src/cli/commands/start.ts +81 -79
  40. package/src/cli/core/args.ts +68 -50
  41. package/src/cli/core/console.ts +89 -95
  42. package/src/cli/core/index.ts +4 -4
  43. package/src/cli/core/prompt.ts +65 -62
  44. package/src/cli/core/spinner.ts +23 -20
  45. package/src/cli/index.ts +46 -38
  46. package/src/cli/templates/database/index.ts +61 -0
  47. package/src/cli/templates/database/mysql.ts +14 -0
  48. package/src/cli/templates/database/none.ts +16 -0
  49. package/src/cli/templates/database/postgresql.ts +14 -0
  50. package/src/cli/templates/database/sqlite.ts +14 -0
  51. package/src/cli/templates/deploy.ts +29 -26
  52. package/src/cli/templates/docker.ts +41 -30
  53. package/src/cli/templates/frontend/index.ts +63 -0
  54. package/src/cli/templates/frontend/none.ts +17 -0
  55. package/src/cli/templates/frontend/react.ts +140 -0
  56. package/src/cli/templates/frontend/solid.ts +134 -0
  57. package/src/cli/templates/frontend/svelte.ts +131 -0
  58. package/src/cli/templates/frontend/vue.ts +130 -0
  59. package/src/cli/templates/generators/index.ts +339 -0
  60. package/src/cli/templates/generators/types.ts +56 -0
  61. package/src/cli/templates/index.ts +35 -2
  62. package/src/cli/templates/project/api.ts +81 -0
  63. package/src/cli/templates/project/default.ts +140 -0
  64. package/src/cli/templates/project/fullstack.ts +111 -0
  65. package/src/cli/templates/project/index.ts +95 -0
  66. package/src/cli/templates/project/minimal.ts +45 -0
  67. package/src/cli/templates/project/types.ts +94 -0
  68. package/src/cli/templates/project/website.ts +263 -0
  69. package/src/cli/utils/fs.ts +55 -41
  70. package/src/cli/utils/index.ts +3 -2
  71. package/src/cli/utils/strings.ts +47 -33
  72. package/src/cli/utils/version.ts +47 -0
  73. package/src/config/env-validation.ts +100 -0
  74. package/src/config/env.ts +169 -41
  75. package/src/config/index.ts +28 -20
  76. package/src/config/loader.ts +25 -16
  77. package/src/config/merge.ts +21 -10
  78. package/src/config/types.ts +545 -25
  79. package/src/config/validation.ts +215 -7
  80. package/src/container/forward-ref.ts +22 -22
  81. package/src/container/index.ts +34 -12
  82. package/src/context/index.ts +11 -1
  83. package/src/database/index.ts +7 -190
  84. package/src/database/orm/builder.ts +457 -0
  85. package/src/database/orm/casts/index.ts +130 -0
  86. package/src/database/orm/casts/types.ts +25 -0
  87. package/src/database/orm/compiler.ts +304 -0
  88. package/src/database/orm/hooks/index.ts +114 -0
  89. package/src/database/orm/index.ts +61 -0
  90. package/src/database/orm/model-registry.ts +59 -0
  91. package/src/database/orm/model.ts +821 -0
  92. package/src/database/orm/relationships/base.ts +146 -0
  93. package/src/database/orm/relationships/belongs-to-many.ts +179 -0
  94. package/src/database/orm/relationships/belongs-to.ts +56 -0
  95. package/src/database/orm/relationships/has-many.ts +45 -0
  96. package/src/database/orm/relationships/has-one.ts +41 -0
  97. package/src/database/orm/relationships/index.ts +11 -0
  98. package/src/database/orm/scopes/index.ts +55 -0
  99. package/src/events/__tests__/event-system.test.ts +235 -0
  100. package/src/events/config.ts +238 -0
  101. package/src/events/example-usage.ts +185 -0
  102. package/src/events/index.ts +278 -0
  103. package/src/events/manager.ts +385 -0
  104. package/src/events/registry.ts +182 -0
  105. package/src/events/types.ts +124 -0
  106. package/src/frontend/api-routes.ts +65 -23
  107. package/src/frontend/bundler.ts +76 -34
  108. package/src/frontend/console-client.ts +2 -2
  109. package/src/frontend/console-stream.ts +94 -38
  110. package/src/frontend/dev-server.ts +94 -46
  111. package/src/frontend/file-router.ts +61 -19
  112. package/src/frontend/frameworks/index.ts +37 -10
  113. package/src/frontend/frameworks/react.ts +10 -8
  114. package/src/frontend/frameworks/solid.ts +11 -9
  115. package/src/frontend/frameworks/svelte.ts +15 -9
  116. package/src/frontend/frameworks/vue.ts +13 -11
  117. package/src/frontend/hmr-client.ts +12 -10
  118. package/src/frontend/hmr.ts +146 -103
  119. package/src/frontend/index.ts +14 -5
  120. package/src/frontend/islands.ts +41 -22
  121. package/src/frontend/isr.ts +59 -37
  122. package/src/frontend/layout.ts +36 -21
  123. package/src/frontend/ssr/react.ts +74 -27
  124. package/src/frontend/ssr/solid.ts +54 -20
  125. package/src/frontend/ssr/svelte.ts +48 -14
  126. package/src/frontend/ssr/vue.ts +50 -18
  127. package/src/frontend/ssr.ts +83 -39
  128. package/src/frontend/types.ts +91 -56
  129. package/src/health/index.ts +21 -9
  130. package/src/i18n/engine.ts +305 -0
  131. package/src/i18n/index.ts +38 -0
  132. package/src/i18n/loader.ts +218 -0
  133. package/src/i18n/middleware.ts +164 -0
  134. package/src/i18n/negotiator.ts +162 -0
  135. package/src/i18n/types.ts +158 -0
  136. package/src/index.ts +179 -27
  137. package/src/jobs/drivers/memory.ts +315 -0
  138. package/src/jobs/drivers/redis.ts +459 -0
  139. package/src/jobs/index.ts +30 -0
  140. package/src/jobs/queue.ts +281 -0
  141. package/src/jobs/types.ts +295 -0
  142. package/src/jobs/worker.ts +380 -0
  143. package/src/logger/index.ts +1 -3
  144. package/src/logger/transports/index.ts +62 -22
  145. package/src/metrics/index.ts +25 -16
  146. package/src/migrations/index.ts +9 -0
  147. package/src/modules/filters.ts +13 -17
  148. package/src/modules/guards.ts +49 -26
  149. package/src/modules/index.ts +409 -298
  150. package/src/modules/interceptors.ts +58 -20
  151. package/src/modules/lazy.ts +11 -19
  152. package/src/modules/lifecycle.ts +15 -7
  153. package/src/modules/metadata.ts +15 -5
  154. package/src/modules/pipes.ts +94 -72
  155. package/src/notification/channels/base.ts +68 -0
  156. package/src/notification/channels/email.ts +105 -0
  157. package/src/notification/channels/push.ts +104 -0
  158. package/src/notification/channels/sms.ts +105 -0
  159. package/src/notification/channels/whatsapp.ts +104 -0
  160. package/src/notification/index.ts +48 -0
  161. package/src/notification/service.ts +354 -0
  162. package/src/notification/types.ts +344 -0
  163. package/src/observability/__tests__/observability.test.ts +483 -0
  164. package/src/observability/breadcrumbs.ts +114 -0
  165. package/src/observability/index.ts +136 -0
  166. package/src/observability/interceptor.ts +85 -0
  167. package/src/observability/service.ts +303 -0
  168. package/src/observability/trace.ts +37 -0
  169. package/src/observability/types.ts +196 -0
  170. package/src/openapi/__tests__/decorators.test.ts +335 -0
  171. package/src/openapi/__tests__/document-builder.test.ts +285 -0
  172. package/src/openapi/__tests__/route-scanner.test.ts +334 -0
  173. package/src/openapi/__tests__/schema-generator.test.ts +275 -0
  174. package/src/openapi/decorators.ts +328 -0
  175. package/src/openapi/document-builder.ts +274 -0
  176. package/src/openapi/index.ts +112 -0
  177. package/src/openapi/metadata.ts +112 -0
  178. package/src/openapi/route-scanner.ts +289 -0
  179. package/src/openapi/schema-generator.ts +256 -0
  180. package/src/openapi/swagger-module.ts +166 -0
  181. package/src/openapi/types.ts +398 -0
  182. package/src/orm/index.ts +10 -0
  183. package/src/rpc/index.ts +3 -1
  184. package/src/schema/index.ts +9 -0
  185. package/src/security/index.ts +15 -6
  186. package/src/ssg/index.ts +9 -8
  187. package/src/telemetry/index.ts +76 -22
  188. package/src/template/index.ts +7 -0
  189. package/src/templates/engine.ts +224 -0
  190. package/src/templates/index.ts +9 -0
  191. package/src/templates/loader.ts +331 -0
  192. package/src/templates/renderers/markdown.ts +212 -0
  193. package/src/templates/renderers/simple.ts +269 -0
  194. package/src/templates/types.ts +154 -0
  195. package/src/testing/index.ts +100 -27
  196. package/src/types/optional-deps.d.ts +347 -187
  197. package/src/validation/index.ts +92 -2
  198. package/src/validation/schemas.ts +536 -0
  199. package/tests/integration/fullstack.test.ts +4 -4
  200. package/tests/unit/database.test.ts +2 -72
  201. package/tests/unit/env-validation.test.ts +166 -0
  202. package/tests/unit/events.test.ts +910 -0
  203. package/tests/unit/i18n.test.ts +455 -0
  204. package/tests/unit/jobs.test.ts +493 -0
  205. package/tests/unit/notification.test.ts +988 -0
  206. package/tests/unit/observability.test.ts +453 -0
  207. package/tests/unit/orm/builder.test.ts +323 -0
  208. package/tests/unit/orm/casts.test.ts +179 -0
  209. package/tests/unit/orm/compiler.test.ts +220 -0
  210. package/tests/unit/orm/eager-loading.test.ts +285 -0
  211. package/tests/unit/orm/hooks.test.ts +191 -0
  212. package/tests/unit/orm/model.test.ts +373 -0
  213. package/tests/unit/orm/relationships.test.ts +303 -0
  214. package/tests/unit/orm/scopes.test.ts +74 -0
  215. package/tests/unit/templates-simple.test.ts +53 -0
  216. package/tests/unit/templates.test.ts +454 -0
  217. package/tests/unit/validation.test.ts +18 -24
  218. package/tsconfig.json +11 -3
@@ -4,81 +4,54 @@
4
4
  * Create a new Bueno project
5
5
  */
6
6
 
7
- import { defineCommand } from './index';
8
- import { getOption, hasFlag, getOptionValues, type ParsedArgs } from '../core/args';
9
- import { cliConsole, colors, printTable } from '../core/console';
10
- import { prompt, confirm, select, isInteractive } from '../core/prompt';
11
- import { spinner, runTasks, type TaskOptions } from '../core/spinner';
12
7
  import {
13
- fileExists,
14
- writeFile,
15
- createDirectory,
16
- copyDirectory,
17
- joinPaths,
18
- } from '../utils/fs';
19
- import { kebabCase } from '../utils/strings';
20
- import { CLIError, CLIErrorType } from '../index';
21
- import {
22
- getDockerfileTemplate,
23
- getDockerignoreTemplate,
24
- getDockerComposeTemplate,
25
- getDockerEnvTemplate,
26
- } from '../templates/docker';
8
+ type ParsedArgs,
9
+ getOption,
10
+ getOptionValues,
11
+ hasFlag,
12
+ } from "../core/args";
13
+ import { cliConsole, colors, printTable } from "../core/console";
14
+ import { isInteractive, prompt, select } from "../core/prompt";
15
+ import { type TaskOptions, runTasks, spinner } from "../core/spinner";
16
+ import { CLIError, CLIErrorType } from "../index";
27
17
  import {
28
18
  type DeployPlatform,
29
- getDeployTemplate,
30
19
  getDeployFilename,
31
20
  getDeployPlatformName,
32
- } from '../templates/deploy';
33
-
34
- /**
35
- * Project templates
36
- */
37
- type ProjectTemplate = 'default' | 'minimal' | 'fullstack' | 'api';
38
-
39
- /**
40
- * Frontend frameworks
41
- */
42
- type FrontendFramework = 'react' | 'vue' | 'svelte' | 'solid';
43
-
44
- /**
45
- * Database drivers
46
- */
47
- type DatabaseDriver = 'sqlite' | 'postgresql' | 'mysql';
48
-
49
- /**
50
- * Project configuration
51
- */
52
- interface ProjectConfig {
53
- name: string;
54
- template: ProjectTemplate;
55
- framework: FrontendFramework;
56
- database: DatabaseDriver;
57
- skipInstall: boolean;
58
- skipGit: boolean;
59
- docker: boolean;
60
- deploy: DeployPlatform[];
61
- link: boolean;
62
- }
21
+ getDeployTemplate,
22
+ } from "../templates";
23
+ import {
24
+ type DatabaseDriver,
25
+ type FrontendFramework,
26
+ type ProjectConfig,
27
+ type ProjectTemplate,
28
+ getDatabaseOptions,
29
+ getFrontendOptions,
30
+ getTemplateOptions,
31
+ } from "../templates";
32
+ import { createDirectory, fileExists, joinPaths, writeFile } from "../utils/fs";
33
+ import { kebabCase } from "../utils/strings";
34
+ import { getBuenoDependency } from "../utils/version";
35
+ import { defineCommand } from "./index";
63
36
 
64
37
  /**
65
38
  * Validate project name
66
39
  */
67
40
  function validateProjectName(name: string): boolean | string {
68
41
  if (!name || name.length === 0) {
69
- return 'Project name is required';
42
+ return "Project name is required";
70
43
  }
71
44
 
72
45
  if (!/^[a-zA-Z0-9_-]+$/.test(name)) {
73
- return 'Project name can only contain letters, numbers, hyphens, and underscores';
46
+ return "Project name can only contain letters, numbers, hyphens, and underscores";
74
47
  }
75
48
 
76
- if (name.startsWith('-') || name.startsWith('_')) {
77
- return 'Project name cannot start with a hyphen or underscore';
49
+ if (name.startsWith("-") || name.startsWith("_")) {
50
+ return "Project name cannot start with a hyphen or underscore";
78
51
  }
79
52
 
80
53
  if (name.length > 100) {
81
- return 'Project name is too long (max 100 characters)';
54
+ return "Project name is too long (max 100 characters)";
82
55
  }
83
56
 
84
57
  return true;
@@ -87,40 +60,43 @@ function validateProjectName(name: string): boolean | string {
87
60
  /**
88
61
  * Get package.json template
89
62
  */
90
- function getPackageJsonTemplate(config: ProjectConfig): string {
91
- const dependencies: Record<string, string> = {};
92
-
63
+ function getPackageJsonTemplate(
64
+ config: ProjectConfig,
65
+ template: {
66
+ dependencies?: Record<string, string>;
67
+ devDependencies?: Record<string, string>;
68
+ scripts?: Record<string, string>;
69
+ },
70
+ ): string {
71
+ const dependencies: Record<string, string> = {
72
+ ...getBuenoDependency(),
73
+ ...(template.dependencies || {}),
74
+ };
75
+
93
76
  // If using link, don't add @buenojs/bueno to dependencies
94
- // It will be linked from the local version
95
- if (!config.link) {
96
- dependencies['@buenojs/bueno'] = '^0.8.0';
77
+ if (config.link) {
78
+ delete dependencies["@buenojs/bueno"];
97
79
  }
98
80
 
99
81
  const devDependencies: Record<string, string> = {
100
- '@types/bun': 'latest',
101
- typescript: '^5.3.0',
82
+ "@types/bun": "latest",
83
+ typescript: "^5.3.0",
84
+ ...(template.devDependencies || {}),
102
85
  };
103
86
 
104
- if (config.template === 'fullstack' || config.template === 'default') {
105
- dependencies.zod = '^4.0.0';
106
- }
107
-
108
87
  const scripts: Record<string, string> = {
109
- dev: 'bun run --watch server/main.ts',
110
- build: 'bun build ./server/main.ts --outdir ./dist --target bun',
111
- start: 'bun run dist/main.js',
112
- test: 'bun test',
88
+ dev: "bun run --watch server/main.ts",
89
+ build: "bun build ./server/main.ts --outdir ./dist --target bun",
90
+ start: "bun run dist/main.js",
91
+ test: "bun test",
92
+ ...(template.scripts || {}),
113
93
  };
114
94
 
115
- if (config.template === 'fullstack') {
116
- scripts['dev:frontend'] = 'bun run --watch client/index.html';
117
- }
118
-
119
95
  return JSON.stringify(
120
96
  {
121
97
  name: kebabCase(config.name),
122
- version: '0.1.0',
123
- type: 'module',
98
+ version: "0.1.0",
99
+ type: "module",
124
100
  scripts,
125
101
  dependencies,
126
102
  devDependencies,
@@ -137,156 +113,31 @@ function getTsConfigTemplate(): string {
137
113
  return JSON.stringify(
138
114
  {
139
115
  compilerOptions: {
140
- target: 'ESNext',
141
- module: 'ESNext',
142
- moduleResolution: 'bundler',
116
+ target: "ESNext",
117
+ module: "ESNext",
118
+ moduleResolution: "bundler",
143
119
  strict: true,
144
120
  skipLibCheck: true,
145
121
  esModuleInterop: true,
146
122
  allowSyntheticDefaultImports: true,
147
- jsx: 'react-jsx',
123
+ jsx: "react-jsx",
148
124
  paths: {
149
- '@buenojs/bueno': ['./node_modules/@buenojs/bueno/dist/index.d.ts'],
150
- },
125
+ "@buenojs/bueno": ["./node_modules/@buenojs/bueno/dist/index.d.ts"],
126
+ },
151
127
  },
152
- include: ['server/**/*', 'client/**/*'],
153
- exclude: ['node_modules', 'dist'],
128
+ include: ["server/**/*", "client/**/*"],
129
+ exclude: ["node_modules", "dist"],
154
130
  },
155
131
  null,
156
132
  2,
157
133
  );
158
134
  }
159
135
 
160
- /**
161
- * Get main.ts template
162
- */
163
- function getMainTemplate(config: ProjectConfig): string {
164
- if (config.template === 'minimal') {
165
- return `import { createServer } from '@buenojs/bueno';
166
-
167
- const app = createServer();
168
-
169
- app.router.get('/', () => {
170
- return { message: 'Hello, Bueno!' };
171
- });
172
-
173
- await app.listen(3000);
174
- `;
175
- }
176
-
177
- return `import { createApp, Module, Controller, Get, Injectable } from '@buenojs/bueno';
178
- import type { Context } from '@buenojs/bueno';
179
-
180
- // Services
181
- @Injectable()
182
- export class AppService {
183
- findAll() {
184
- return { message: 'Welcome to Bueno!', items: [] };
185
- }
186
- }
187
-
188
- // Controllers
189
- @Controller()
190
- export class AppController {
191
- constructor(private readonly appService: AppService) {}
192
-
193
- @Get()
194
- hello() {
195
- return new Response(\`<html>
196
- <head>
197
- <title>Welcome to Bueno</title>
198
- <style>
199
- body { font-family: system-ui, sans-serif; max-width: 800px; margin: 50px auto; padding: 20px; }
200
- h1 { color: #2563eb; }
201
- code { background: #f3f4f6; padding: 2px 6px; border-radius: 4px; }
202
- pre { background: #f3f4f6; padding: 16px; border-radius: 8px; overflow-x: auto; }
203
- a { color: #2563eb; }
204
- </style>
205
- </head>
206
- <body>
207
- <h1>🎉 Welcome to Bueno Framework!</h1>
208
- <p>Your Bun-native full-stack framework is running successfully.</p>
209
-
210
- <h2>Getting Started</h2>
211
- <ul>
212
- <li>Edit <code>server/main.ts</code> to modify this app</li>
213
- <li>Add routes using the <code>@Get()</code>, <code>@Post()</code> decorators</li>
214
- <li>Create services with <code>@Injectable()</code> and inject them in controllers</li>
215
- </ul>
216
-
217
- <h2>Documentation</h2>
218
- <p>Visit <a href="https://buenojs.dev">https://buenojs.dev</a> for full documentation.</p>
219
-
220
- <h2>Quick Example</h2>
221
- <pre><code>@Controller('/api')
222
- class MyController {
223
- @Get('/users')
224
- getUsers() {
225
- return { users: [] };
226
- }
227
- }</code></pre>
228
- </body>
229
- </html>\`, {
230
- headers: { 'Content-Type': 'text/html; charset=utf-8' }
231
- });
232
- }
233
-
234
- @Get('health')
235
- health(ctx: Context) {
236
- return { status: 'ok', timestamp: new Date().toISOString() };
237
- }
238
- }
239
-
240
- // Module
241
- @Module({
242
- controllers: [AppController],
243
- providers: [AppService],
244
- })
245
- export class AppModule {}
246
-
247
- // Bootstrap
248
- const app = createApp(AppModule);
249
- await app.listen(3000);
250
- `;
251
- }
252
-
253
- /**
254
- * Get bueno.config.ts template
255
- */
256
- function getConfigTemplate(config: ProjectConfig): string {
257
- const dbConfig = config.database === 'sqlite'
258
- ? `{ url: 'sqlite:./data.db' }`
259
- : `{ url: process.env.DATABASE_URL ?? '${config.database}://localhost/${kebabCase(config.name)}' }`;
260
-
261
- return `import { defineConfig } from '@buenojs/bueno';
262
-
263
- export default defineConfig({
264
- server: {
265
- port: 3000,
266
- host: 'localhost',
267
- },
268
-
269
- database: ${dbConfig},
270
-
271
- logger: {
272
- level: 'info',
273
- pretty: true,
274
- },
275
-
276
- health: {
277
- enabled: true,
278
- healthPath: '/health',
279
- readyPath: '/ready',
280
- },
281
- });
282
- `;
283
- }
284
-
285
136
  /**
286
137
  * Get .env.example template
287
138
  */
288
139
  function getEnvExampleTemplate(config: ProjectConfig): string {
289
- if (config.database === 'sqlite') {
140
+ if (config.database === "none" || config.database === "sqlite") {
290
141
  return `# Bueno Environment Variables
291
142
  NODE_ENV=development
292
143
  `;
@@ -341,9 +192,17 @@ coverage/
341
192
  * Get README.md template
342
193
  */
343
194
  function getReadmeTemplate(config: ProjectConfig): string {
195
+ const templateDescriptions: Record<ProjectTemplate, string> = {
196
+ default: "Standard project with modules and database",
197
+ minimal: "Bare minimum project structure",
198
+ fullstack: "Full-stack project with SSR and frontend",
199
+ api: "API-only project without frontend",
200
+ website: "Static website with SSG",
201
+ };
202
+
344
203
  return `# ${config.name}
345
204
 
346
- A Bueno application.
205
+ A Bueno application - ${templateDescriptions[config.template]}.
347
206
 
348
207
  ## Getting Started
349
208
 
@@ -375,87 +234,151 @@ bun run start
375
234
 
376
235
  ## Learn More
377
236
 
378
- - [Bueno Documentation](https://github.com/sivaraj/bueno#readme)
237
+ - [Bueno Documentation](https://bueno.github.io)
379
238
  - [Bun Documentation](https://bun.sh/docs)
380
239
  `;
381
240
  }
382
241
 
242
+ /**
243
+ * Get bueno.config.ts template
244
+ */
245
+ function getConfigTemplate(config: ProjectConfig): string {
246
+ let dbConfig = "undefined";
247
+
248
+ if (config.database === "sqlite") {
249
+ dbConfig = `{ url: 'sqlite:./data.db' }`;
250
+ } else if (config.database === "postgresql") {
251
+ dbConfig = `{ url: process.env.DATABASE_URL ?? 'postgresql://localhost/${kebabCase(config.name)}' }`;
252
+ } else if (config.database === "mysql") {
253
+ dbConfig = `{ url: process.env.DATABASE_URL ?? 'mysql://localhost/${kebabCase(config.name)}' }`;
254
+ }
255
+
256
+ return `import { defineConfig } from '@buenojs/bueno';
257
+
258
+ export default defineConfig({
259
+ server: {
260
+ port: 3000,
261
+ host: 'localhost',
262
+ },
263
+
264
+ ${config.database !== "none" ? `database: ${dbConfig},` : ""}
265
+
266
+ logger: {
267
+ level: 'info',
268
+ pretty: true,
269
+ },
270
+
271
+ health: {
272
+ enabled: true,
273
+ healthPath: '/health',
274
+ readyPath: '/ready',
275
+ },
276
+ });
277
+ `;
278
+ }
279
+
383
280
  /**
384
281
  * Create project files
385
282
  */
386
283
  async function createProjectFiles(
387
284
  projectPath: string,
388
285
  config: ProjectConfig,
286
+ templateResult: {
287
+ files: { path: string; content: string }[];
288
+ directories: string[];
289
+ dependencies?: Record<string, string>;
290
+ devDependencies?: Record<string, string>;
291
+ scripts?: Record<string, string>;
292
+ },
389
293
  ): Promise<void> {
390
294
  const tasks: TaskOptions[] = [];
391
295
 
392
296
  // Create directories
393
297
  tasks.push({
394
- text: 'Creating project structure',
298
+ text: "Creating project structure",
395
299
  task: async () => {
396
- await createDirectory(joinPaths(projectPath, 'server', 'modules', 'app'));
397
- await createDirectory(joinPaths(projectPath, 'server', 'common', 'middleware'));
398
- await createDirectory(joinPaths(projectPath, 'server', 'common', 'guards'));
399
- await createDirectory(joinPaths(projectPath, 'server', 'common', 'interceptors'));
400
- await createDirectory(joinPaths(projectPath, 'server', 'common', 'pipes'));
401
- await createDirectory(joinPaths(projectPath, 'server', 'common', 'filters'));
402
- await createDirectory(joinPaths(projectPath, 'server', 'database', 'migrations'));
403
- await createDirectory(joinPaths(projectPath, 'server', 'config'));
404
- await createDirectory(joinPaths(projectPath, 'tests', 'unit'));
405
- await createDirectory(joinPaths(projectPath, 'tests', 'integration'));
300
+ // Base directories
301
+ await createDirectory(joinPaths(projectPath, "server", "modules", "app"));
302
+ await createDirectory(
303
+ joinPaths(projectPath, "server", "common", "middleware"),
304
+ );
305
+ await createDirectory(
306
+ joinPaths(projectPath, "server", "common", "guards"),
307
+ );
308
+ await createDirectory(
309
+ joinPaths(projectPath, "server", "common", "interceptors"),
310
+ );
311
+ await createDirectory(
312
+ joinPaths(projectPath, "server", "common", "pipes"),
313
+ );
314
+ await createDirectory(
315
+ joinPaths(projectPath, "server", "common", "filters"),
316
+ );
317
+ await createDirectory(
318
+ joinPaths(projectPath, "server", "database", "migrations"),
319
+ );
320
+ await createDirectory(joinPaths(projectPath, "server", "config"));
321
+ await createDirectory(joinPaths(projectPath, "tests", "unit"));
322
+ await createDirectory(joinPaths(projectPath, "tests", "integration"));
323
+
324
+ // Template-specific directories
325
+ for (const dir of templateResult.directories) {
326
+ await createDirectory(joinPaths(projectPath, dir));
327
+ }
406
328
  },
407
329
  });
408
330
 
409
331
  // Create package.json
410
332
  tasks.push({
411
- text: 'Creating package.json',
333
+ text: "Creating package.json",
412
334
  task: async () => {
413
335
  await writeFile(
414
- joinPaths(projectPath, 'package.json'),
415
- getPackageJsonTemplate(config),
336
+ joinPaths(projectPath, "package.json"),
337
+ getPackageJsonTemplate(config, templateResult),
416
338
  );
417
339
  },
418
340
  });
419
341
 
420
342
  // Create tsconfig.json
421
343
  tasks.push({
422
- text: 'Creating tsconfig.json',
344
+ text: "Creating tsconfig.json",
423
345
  task: async () => {
424
346
  await writeFile(
425
- joinPaths(projectPath, 'tsconfig.json'),
347
+ joinPaths(projectPath, "tsconfig.json"),
426
348
  getTsConfigTemplate(),
427
349
  );
428
350
  },
429
351
  });
430
352
 
431
- // Create main.ts
432
- tasks.push({
433
- text: 'Creating server/main.ts',
434
- task: async () => {
435
- await writeFile(
436
- joinPaths(projectPath, 'server', 'main.ts'),
437
- getMainTemplate(config),
438
- );
439
- },
440
- });
353
+ // Create template-specific files
354
+ for (const file of templateResult.files) {
355
+ tasks.push({
356
+ text: `Creating ${file.path}`,
357
+ task: async () => {
358
+ await writeFile(joinPaths(projectPath, file.path), file.content);
359
+ },
360
+ });
361
+ }
441
362
 
442
- // Create bueno.config.ts
443
- tasks.push({
444
- text: 'Creating bueno.config.ts',
445
- task: async () => {
446
- await writeFile(
447
- joinPaths(projectPath, 'bueno.config.ts'),
448
- getConfigTemplate(config),
449
- );
450
- },
451
- });
363
+ // Create bueno.config.ts (not for website template)
364
+ if (config.template !== "website") {
365
+ tasks.push({
366
+ text: "Creating bueno.config.ts",
367
+ task: async () => {
368
+ await writeFile(
369
+ joinPaths(projectPath, "bueno.config.ts"),
370
+ getConfigTemplate(config),
371
+ );
372
+ },
373
+ });
374
+ }
452
375
 
453
376
  // Create .env.example
454
377
  tasks.push({
455
- text: 'Creating .env.example',
378
+ text: "Creating .env.example",
456
379
  task: async () => {
457
380
  await writeFile(
458
- joinPaths(projectPath, '.env.example'),
381
+ joinPaths(projectPath, ".env.example"),
459
382
  getEnvExampleTemplate(config),
460
383
  );
461
384
  },
@@ -463,10 +386,10 @@ async function createProjectFiles(
463
386
 
464
387
  // Create .gitignore
465
388
  tasks.push({
466
- text: 'Creating .gitignore',
389
+ text: "Creating .gitignore",
467
390
  task: async () => {
468
391
  await writeFile(
469
- joinPaths(projectPath, '.gitignore'),
392
+ joinPaths(projectPath, ".gitignore"),
470
393
  getGitignoreTemplate(),
471
394
  );
472
395
  },
@@ -474,53 +397,62 @@ async function createProjectFiles(
474
397
 
475
398
  // Create README.md
476
399
  tasks.push({
477
- text: 'Creating README.md',
400
+ text: "Creating README.md",
478
401
  task: async () => {
479
402
  await writeFile(
480
- joinPaths(projectPath, 'README.md'),
403
+ joinPaths(projectPath, "README.md"),
481
404
  getReadmeTemplate(config),
482
405
  );
483
406
  },
484
407
  });
485
408
 
486
409
  // Create Docker files if enabled
487
- if (config.docker) {
410
+ if (config.docker && config.template !== "website") {
488
411
  tasks.push({
489
- text: 'Creating Dockerfile',
412
+ text: "Creating Dockerfile",
490
413
  task: async () => {
491
414
  await writeFile(
492
- joinPaths(projectPath, 'Dockerfile'),
493
- getDockerfileTemplate(config.name, config.database),
415
+ joinPaths(projectPath, "Dockerfile"),
416
+ getDockerfileTemplate(
417
+ config.name,
418
+ config.database === "none" ? undefined : config.database,
419
+ ),
494
420
  );
495
421
  },
496
422
  });
497
423
 
498
424
  tasks.push({
499
- text: 'Creating .dockerignore',
425
+ text: "Creating .dockerignore",
500
426
  task: async () => {
501
427
  await writeFile(
502
- joinPaths(projectPath, '.dockerignore'),
428
+ joinPaths(projectPath, ".dockerignore"),
503
429
  getDockerignoreTemplate(),
504
430
  );
505
431
  },
506
432
  });
507
433
 
508
434
  tasks.push({
509
- text: 'Creating docker-compose.yml',
435
+ text: "Creating docker-compose.yml",
510
436
  task: async () => {
511
437
  await writeFile(
512
- joinPaths(projectPath, 'docker-compose.yml'),
513
- getDockerComposeTemplate(config.name, config.database),
438
+ joinPaths(projectPath, "docker-compose.yml"),
439
+ getDockerComposeTemplate(
440
+ config.name,
441
+ config.database === "none" ? undefined : config.database,
442
+ ),
514
443
  );
515
444
  },
516
445
  });
517
446
 
518
447
  tasks.push({
519
- text: 'Creating .env.docker',
448
+ text: "Creating .env.docker",
520
449
  task: async () => {
521
450
  await writeFile(
522
- joinPaths(projectPath, '.env.docker'),
523
- getDockerEnvTemplate(config.name, config.database),
451
+ joinPaths(projectPath, ".env.docker"),
452
+ getDockerEnvTemplate(
453
+ config.name,
454
+ config.database === "none" ? undefined : config.database,
455
+ ),
524
456
  );
525
457
  },
526
458
  });
@@ -534,7 +466,11 @@ async function createProjectFiles(
534
466
  task: async () => {
535
467
  await writeFile(
536
468
  joinPaths(projectPath, filename),
537
- getDeployTemplate(platform, config.name, config.database),
469
+ getDeployTemplate(
470
+ platform,
471
+ config.name,
472
+ config.database === "none" ? "sqlite" : config.database,
473
+ ),
538
474
  );
539
475
  },
540
476
  });
@@ -543,24 +479,32 @@ async function createProjectFiles(
543
479
  await runTasks(tasks);
544
480
  }
545
481
 
482
+ // Import docker templates
483
+ import {
484
+ getDockerComposeTemplate,
485
+ getDockerEnvTemplate,
486
+ getDockerfileTemplate,
487
+ getDockerignoreTemplate,
488
+ } from "../templates";
489
+
546
490
  /**
547
491
  * Handle new command
548
492
  */
549
493
  async function handleNew(args: ParsedArgs): Promise<void> {
550
494
  // Get project name
551
495
  let name = args.positionals[0];
552
- const useDefaults = hasFlag(args, 'yes') || hasFlag(args, 'y');
496
+ const useDefaults = hasFlag(args, "yes") || hasFlag(args, "y");
553
497
 
554
498
  // Interactive prompts if no name provided
555
499
  if (!name && isInteractive()) {
556
- name = await prompt('Project name:', {
500
+ name = await prompt("Project name:", {
557
501
  validate: validateProjectName,
558
502
  });
559
503
  }
560
504
 
561
505
  if (!name) {
562
506
  throw new CLIError(
563
- 'Project name is required. Usage: bueno new <project-name>',
507
+ "Project name is required. Usage: bueno new <project-name>",
564
508
  CLIErrorType.INVALID_ARGS,
565
509
  );
566
510
  }
@@ -571,37 +515,37 @@ async function handleNew(args: ParsedArgs): Promise<void> {
571
515
  }
572
516
 
573
517
  // Get options
574
- let template = getOption(args, 'template', {
575
- name: 'template',
576
- alias: 't',
577
- type: 'string',
578
- description: '',
518
+ let template = getOption(args, "template", {
519
+ name: "template",
520
+ alias: "t",
521
+ type: "string",
522
+ description: "",
579
523
  }) as ProjectTemplate;
580
524
 
581
- let framework = getOption(args, 'framework', {
582
- name: 'framework',
583
- alias: 'f',
584
- type: 'string',
585
- description: '',
525
+ let framework = getOption(args, "framework", {
526
+ name: "framework",
527
+ alias: "f",
528
+ type: "string",
529
+ description: "",
586
530
  }) as FrontendFramework;
587
531
 
588
- let database = getOption(args, 'database', {
589
- name: 'database',
590
- alias: 'd',
591
- type: 'string',
592
- description: '',
532
+ let database = getOption(args, "database", {
533
+ name: "database",
534
+ alias: "d",
535
+ type: "string",
536
+ description: "",
593
537
  }) as DatabaseDriver;
594
538
 
595
- const skipInstall = hasFlag(args, 'skip-install');
596
- const skipGit = hasFlag(args, 'skip-git');
597
- const docker = hasFlag(args, 'docker');
598
- const link = hasFlag(args, 'link');
599
-
539
+ const skipInstall = hasFlag(args, "skip-install");
540
+ const skipGit = hasFlag(args, "skip-git");
541
+ const docker = hasFlag(args, "docker");
542
+ const link = hasFlag(args, "link");
543
+
600
544
  // Get deployment platforms (can be specified multiple times)
601
- const deployPlatforms = getOptionValues(args, 'deploy');
602
- const validPlatforms: DeployPlatform[] = ['render', 'fly', 'railway'];
545
+ const deployPlatforms = getOptionValues(args, "deploy");
546
+ const validPlatforms: DeployPlatform[] = ["render", "fly", "railway"];
603
547
  const deploy: DeployPlatform[] = [];
604
-
548
+
605
549
  for (const platform of deployPlatforms) {
606
550
  if (validPlatforms.includes(platform as DeployPlatform)) {
607
551
  if (!deploy.includes(platform as DeployPlatform)) {
@@ -609,7 +553,7 @@ async function handleNew(args: ParsedArgs): Promise<void> {
609
553
  }
610
554
  } else {
611
555
  throw new CLIError(
612
- `Invalid deployment platform: ${platform}. Valid options are: ${validPlatforms.join(', ')}`,
556
+ `Invalid deployment platform: ${platform}. Valid options are: ${validPlatforms.join(", ")}`,
613
557
  CLIErrorType.INVALID_ARGS,
614
558
  );
615
559
  }
@@ -619,53 +563,44 @@ async function handleNew(args: ParsedArgs): Promise<void> {
619
563
  if (!useDefaults && isInteractive()) {
620
564
  if (!template) {
621
565
  template = await select<ProjectTemplate>(
622
- 'Select a template:',
623
- [
624
- { value: 'default', name: 'Default - Standard project with modules and database' },
625
- { value: 'minimal', name: 'Minimal - Bare minimum project structure' },
626
- { value: 'fullstack', name: 'Fullstack - Full-stack project with SSR and auth' },
627
- { value: 'api', name: 'API - API-only project without frontend' },
628
- ],
629
- { default: 'default' },
566
+ "Select a template:",
567
+ getTemplateOptions(),
568
+ { default: "default" },
630
569
  );
631
570
  }
632
571
 
633
- if ((template === 'fullstack' || template === 'default') && !framework) {
572
+ // Only ask for framework if template supports it
573
+ if ((template === "fullstack" || template === "default") && !framework) {
634
574
  framework = await select<FrontendFramework>(
635
- 'Select a frontend framework:',
636
- [
637
- { value: 'react', name: 'React' },
638
- { value: 'vue', name: 'Vue' },
639
- { value: 'svelte', name: 'Svelte' },
640
- { value: 'solid', name: 'Solid' },
641
- ],
642
- { default: 'react' },
575
+ "Select a frontend framework:",
576
+ getFrontendOptions(),
577
+ { default: "react" },
643
578
  );
644
579
  }
645
580
 
646
- if (!database) {
581
+ // Website template doesn't need database or frontend selection
582
+ if (template !== "website" && !database) {
647
583
  database = await select<DatabaseDriver>(
648
- 'Select a database:',
649
- [
650
- { value: 'sqlite', name: 'SQLite - Local file-based database' },
651
- { value: 'postgresql', name: 'PostgreSQL - Production-ready relational database' },
652
- { value: 'mysql', name: 'MySQL - Popular relational database' },
653
- ],
654
- { default: 'sqlite' },
584
+ "Select a database:",
585
+ getDatabaseOptions(),
586
+ { default: "sqlite" },
655
587
  );
656
588
  }
657
589
  }
658
590
 
659
591
  // Set defaults
660
- template = template || 'default';
661
- framework = framework || 'react';
662
- database = database || 'sqlite';
592
+ template = template || "default";
593
+ framework = framework || "react";
594
+ database = database || "sqlite";
595
+
596
+ // For website template, override database and framework
597
+ const isWebsite = template === "website";
663
598
 
664
599
  const config: ProjectConfig = {
665
600
  name,
666
601
  template,
667
- framework,
668
- database,
602
+ framework: isWebsite ? "none" : framework,
603
+ database: isWebsite ? "none" : database,
669
604
  skipInstall,
670
605
  skipGit,
671
606
  docker,
@@ -686,194 +621,213 @@ async function handleNew(args: ParsedArgs): Promise<void> {
686
621
  cliConsole.header(`Creating a new Bueno project: ${colors.cyan(name)}`);
687
622
 
688
623
  const rows = [
689
- ['Template', template],
690
- ['Framework', framework],
691
- ['Database', database],
692
- ['Docker', docker ? colors.green('Yes') : colors.red('No')],
693
- ['Deploy', deploy.length > 0 ? colors.green(deploy.map(getDeployPlatformName).join(', ')) : colors.red('None')],
694
- ['Install dependencies', skipInstall ? colors.red('No') : colors.green('Yes')],
695
- ['Use local package', link ? colors.green('Yes (bun link)') : colors.red('No')],
696
- ['Initialize git', skipGit ? colors.red('No') : colors.green('Yes')],
624
+ ["Template", template],
625
+ ["Framework", isWebsite ? "N/A (Static Site)" : framework],
626
+ ["Database", isWebsite ? "N/A" : database],
627
+ ["Docker", docker ? colors.green("Yes") : colors.red("No")],
628
+ [
629
+ "Deploy",
630
+ deploy.length > 0
631
+ ? colors.green(deploy.map(getDeployPlatformName).join(", "))
632
+ : colors.red("None"),
633
+ ],
634
+ [
635
+ "Install dependencies",
636
+ skipInstall ? colors.red("No") : colors.green("Yes"),
637
+ ],
638
+ [
639
+ "Use local package",
640
+ link ? colors.green("Yes (bun link)") : colors.red("No"),
641
+ ],
697
642
  ];
698
643
 
699
- printTable(['Setting', 'Value'], rows);
700
- cliConsole.log('');
644
+ printTable(["Setting", "Value"], rows);
645
+ cliConsole.log("");
646
+
647
+ // Get the appropriate template function
648
+ const { getProjectTemplate } = await import("../templates");
649
+ const templateFn = getProjectTemplate(template);
650
+ const templateResult = templateFn(config);
701
651
 
702
652
  // Create project
703
- cliConsole.subheader('Creating project files...');
704
- await createProjectFiles(projectPath, config);
653
+ cliConsole.subheader("Creating project files...");
654
+ await createProjectFiles(projectPath, config, templateResult);
705
655
 
706
656
  // Install dependencies
707
657
  if (!skipInstall) {
708
- cliConsole.subheader('Installing dependencies...');
709
- const installSpinner = spinner('Running bun install...');
658
+ cliConsole.subheader("Installing dependencies...");
659
+ const installSpinner = spinner("Running bun install...");
710
660
 
711
661
  try {
712
- const proc = Bun.spawn(['bun', 'install'], {
662
+ const proc = Bun.spawn(["bun", "install"], {
713
663
  cwd: projectPath,
714
- stdout: 'pipe',
715
- stderr: 'pipe',
664
+ stdout: "pipe",
665
+ stderr: "pipe",
716
666
  });
717
667
 
718
668
  const exitCode = await proc.exited;
719
669
 
720
670
  if (exitCode === 0) {
721
- installSpinner.success('Dependencies installed');
671
+ installSpinner.success("Dependencies installed");
722
672
  } else {
723
- installSpinner.warn('Failed to install dependencies. Run `bun install` manually.');
673
+ installSpinner.warn(
674
+ "Failed to install dependencies. Run `bun install` manually.",
675
+ );
724
676
  }
725
677
  } catch {
726
- installSpinner.warn('Failed to install dependencies. Run `bun install` manually.');
678
+ installSpinner.warn(
679
+ "Failed to install dependencies. Run `bun install` manually.",
680
+ );
727
681
  }
728
-
682
+
729
683
  // Link local @buenojs/bueno if --link flag is set
730
684
  if (link) {
731
- cliConsole.subheader('Linking local @buenojs/bueno...');
732
- const linkSpinner = spinner('Running bun link @buenojs/bueno...');
685
+ cliConsole.subheader("Linking local @buenojs/bueno...");
686
+ const linkSpinner = spinner("Running bun link @buenojs/bueno...");
733
687
 
734
688
  try {
735
- const proc = Bun.spawn(['bun', 'link', '@buenojs/bueno'], {
689
+ const proc = Bun.spawn(["bun", "link", "@buenojs/bueno"], {
736
690
  cwd: projectPath,
737
- stdout: 'pipe',
738
- stderr: 'pipe',
691
+ stdout: "pipe",
692
+ stderr: "pipe",
739
693
  });
740
694
 
741
695
  const exitCode = await proc.exited;
742
696
 
743
697
  if (exitCode === 0) {
744
- linkSpinner.success('Local @buenojs/bueno linked successfully');
698
+ linkSpinner.success("Local @buenojs/bueno linked successfully");
745
699
  } else {
746
- linkSpinner.warn('Failed to link @buenojs/bueno. Make sure you have run `bun link` in the bueno directory first.');
700
+ linkSpinner.warn(
701
+ "Failed to link @buenojs/bueno. Make sure you have run `bun link` in the bueno directory first.",
702
+ );
747
703
  }
748
704
  } catch {
749
- linkSpinner.warn('Failed to link @buenojs/bueno. Make sure you have run `bun link` in the bueno directory first.');
705
+ linkSpinner.warn(
706
+ "Failed to link @buenojs/bueno. Make sure you have run `bun link` in the bueno directory first.",
707
+ );
750
708
  }
751
709
  }
752
710
  }
753
711
 
754
- // Initialize git
755
- if (!skipGit) {
756
- cliConsole.subheader('Initializing git repository...');
757
- const gitSpinner = spinner('Running git init...');
758
-
759
- try {
760
- const proc = Bun.spawn(['git', 'init'], {
761
- cwd: projectPath,
762
- stdout: 'pipe',
763
- stderr: 'pipe',
764
- });
765
-
766
- const exitCode = await proc.exited;
767
-
768
- if (exitCode === 0) {
769
- // Add all files
770
- Bun.spawn(['git', 'add', '.'], { cwd: projectPath });
771
- Bun.spawn(['git', 'commit', '-m', 'Initial commit from Bueno CLI'], {
772
- cwd: projectPath,
773
- });
774
- gitSpinner.success('Git repository initialized');
775
- } else {
776
- gitSpinner.warn('Failed to initialize git. Run `git init` manually.');
777
- }
778
- } catch {
779
- gitSpinner.warn('Failed to initialize git. Run `git init` manually.');
780
- }
781
- }
712
+ // Git initialization - now disabled by default (removed)
713
+ // Users can run `git init` manually if needed
782
714
 
783
715
  // Show success message
784
- cliConsole.log('');
716
+ cliConsole.log("");
785
717
  cliConsole.success(`Project created successfully!`);
786
- cliConsole.log('');
787
- cliConsole.log('Next steps:');
718
+ cliConsole.log("");
719
+ cliConsole.log("Next steps:");
788
720
  cliConsole.log(` ${colors.cyan(`cd ${kebabCase(name)}`)}`);
789
- cliConsole.log(` ${colors.cyan('bun run dev')}`);
790
- cliConsole.log('');
791
- cliConsole.log(`Documentation: ${colors.dim('https://github.com/sivaraj/bueno')}`);
721
+
722
+ if (isWebsite) {
723
+ cliConsole.log(
724
+ ` ${colors.cyan("bun run dev")} - Start development server`,
725
+ );
726
+ cliConsole.log(` ${colors.cyan("bun run build")} - Build static site`);
727
+ } else {
728
+ cliConsole.log(` ${colors.cyan("bun run dev")}`);
729
+ }
730
+
731
+ cliConsole.log("");
732
+ cliConsole.log(
733
+ `Documentation: ${colors.dim("https://github.com/sivaraj/bueno")}`,
734
+ );
735
+ }
736
+
737
+ // Import the template function getter dynamically
738
+ async function importTemplateFn(template: ProjectTemplate) {
739
+ const { getProjectTemplate } = await import("../templates");
740
+ return getProjectTemplate(template);
792
741
  }
793
742
 
794
743
  // Register the command
795
744
  defineCommand(
796
745
  {
797
- name: 'new',
798
- description: 'Create a new Bueno project',
746
+ name: "new",
747
+ description: "Create a new Bueno project",
799
748
  positionals: [
800
749
  {
801
- name: 'name',
750
+ name: "name",
802
751
  required: false,
803
- description: 'Project name',
752
+ description: "Project name",
804
753
  },
805
754
  ],
806
755
  options: [
807
756
  {
808
- name: 'template',
809
- alias: 't',
810
- type: 'string',
811
- description: 'Project template (default, minimal, fullstack, api)',
757
+ name: "template",
758
+ alias: "t",
759
+ type: "string",
760
+ description:
761
+ "Project template (default, minimal, fullstack, api, website)",
812
762
  },
813
763
  {
814
- name: 'framework',
815
- alias: 'f',
816
- type: 'string',
817
- description: 'Frontend framework (react, vue, svelte, solid)',
764
+ name: "framework",
765
+ alias: "f",
766
+ type: "string",
767
+ description: "Frontend framework (react, vue, svelte, solid, none)",
818
768
  },
819
769
  {
820
- name: 'database',
821
- alias: 'd',
822
- type: 'string',
823
- description: 'Database driver (sqlite, postgresql, mysql)',
770
+ name: "database",
771
+ alias: "d",
772
+ type: "string",
773
+ description: "Database driver (sqlite, postgresql, mysql, none)",
824
774
  },
825
775
  {
826
- name: 'skip-install',
827
- type: 'boolean',
776
+ name: "skip-install",
777
+ type: "boolean",
828
778
  default: false,
829
- description: 'Skip dependency installation',
779
+ description: "Skip dependency installation",
830
780
  },
831
781
  {
832
- name: 'skip-git',
833
- type: 'boolean',
782
+ name: "skip-git",
783
+ type: "boolean",
834
784
  default: false,
835
- description: 'Skip git initialization',
785
+ description:
786
+ "Skip git initialization (deprecated - git init is no longer automatic)",
836
787
  },
837
788
  {
838
- name: 'docker',
839
- type: 'boolean',
789
+ name: "docker",
790
+ type: "boolean",
840
791
  default: false,
841
- description: 'Include Docker configuration (Dockerfile, docker-compose.yml)',
792
+ description:
793
+ "Include Docker configuration (Dockerfile, docker-compose.yml)",
842
794
  },
843
795
  {
844
- name: 'link',
845
- type: 'boolean',
796
+ name: "link",
797
+ type: "boolean",
846
798
  default: false,
847
- description: 'Use local @buenojs/bueno via bun link (for development)',
799
+ description: "Use local @buenojs/bueno via bun link (for development)",
848
800
  },
849
801
  {
850
- name: 'deploy',
851
- type: 'string',
852
- description: 'Deployment platform configuration (render, fly, railway). Can be specified multiple times.',
802
+ name: "deploy",
803
+ type: "string",
804
+ description:
805
+ "Deployment platform configuration (render, fly, railway). Can be specified multiple times.",
853
806
  },
854
807
  {
855
- name: 'yes',
856
- alias: 'y',
857
- type: 'boolean',
808
+ name: "yes",
809
+ alias: "y",
810
+ type: "boolean",
858
811
  default: false,
859
- description: 'Use default options without prompts',
812
+ description: "Use default options without prompts",
860
813
  },
861
814
  ],
862
815
  examples: [
863
- 'bueno new my-app',
864
- 'bueno new my-api --template api',
865
- 'bueno new my-fullstack --template fullstack --framework react',
866
- 'bueno new my-project --database postgresql',
867
- 'bueno new my-app --docker',
868
- 'bueno new my-app --docker --database postgresql',
869
- 'bueno new my-app --deploy render',
870
- 'bueno new my-app --deploy fly',
871
- 'bueno new my-app --deploy render --deploy fly',
872
- 'bueno new my-app --docker --deploy render',
873
- 'bueno new my-app --docker --database postgresql --deploy render',
874
- 'bueno new my-app -y',
875
- 'bueno new my-app --link',
816
+ "bueno new my-app",
817
+ "bueno new my-api --template api",
818
+ "bueno new my-fullstack --template fullstack --framework react",
819
+ "bueno new my-project --database postgresql",
820
+ "bueno new my-website --template website",
821
+ "bueno new my-app --docker",
822
+ "bueno new my-app --docker --database postgresql",
823
+ "bueno new my-app --deploy render",
824
+ "bueno new my-app --deploy fly",
825
+ "bueno new my-app --deploy render --deploy fly",
826
+ "bueno new my-app --docker --deploy render",
827
+ "bueno new my-app --docker --database postgresql --deploy render",
828
+ "bueno new my-app -y",
829
+ "bueno new my-app --link",
876
830
  ],
877
831
  },
878
832
  handleNew,
879
- );
833
+ );