@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.
- package/README.md +136 -16
- package/dist/cli/{index.js → bin.js} +3036 -1421
- package/dist/container/index.js +250 -0
- package/dist/context/index.js +219 -0
- package/dist/database/index.js +493 -0
- package/dist/frontend/index.js +7697 -0
- package/dist/health/index.js +364 -0
- package/dist/i18n/index.js +345 -0
- package/dist/index.js +11043 -6482
- package/dist/jobs/index.js +819 -0
- package/dist/lock/index.js +367 -0
- package/dist/logger/index.js +281 -0
- package/dist/metrics/index.js +289 -0
- package/dist/middleware/index.js +77 -0
- package/dist/migrations/index.js +571 -0
- package/dist/modules/index.js +3346 -0
- package/dist/notification/index.js +484 -0
- package/dist/observability/index.js +331 -0
- package/dist/openapi/index.js +776 -0
- package/dist/orm/index.js +1356 -0
- package/dist/router/index.js +886 -0
- package/dist/rpc/index.js +691 -0
- package/dist/schema/index.js +400 -0
- package/dist/telemetry/index.js +595 -0
- package/dist/template/index.js +640 -0
- package/dist/templates/index.js +640 -0
- package/dist/testing/index.js +1111 -0
- package/dist/types/index.js +60 -0
- package/package.json +121 -27
- package/src/cache/index.ts +2 -1
- package/src/cli/bin.ts +2 -2
- package/src/cli/commands/build.ts +183 -165
- package/src/cli/commands/dev.ts +96 -89
- package/src/cli/commands/generate.ts +142 -111
- package/src/cli/commands/help.ts +20 -16
- package/src/cli/commands/index.ts +3 -6
- package/src/cli/commands/migration.ts +124 -105
- package/src/cli/commands/new.ts +392 -438
- package/src/cli/commands/start.ts +81 -79
- package/src/cli/core/args.ts +68 -50
- package/src/cli/core/console.ts +89 -95
- package/src/cli/core/index.ts +4 -4
- package/src/cli/core/prompt.ts +65 -62
- package/src/cli/core/spinner.ts +23 -20
- package/src/cli/index.ts +46 -38
- package/src/cli/templates/database/index.ts +61 -0
- package/src/cli/templates/database/mysql.ts +14 -0
- package/src/cli/templates/database/none.ts +16 -0
- package/src/cli/templates/database/postgresql.ts +14 -0
- package/src/cli/templates/database/sqlite.ts +14 -0
- package/src/cli/templates/deploy.ts +29 -26
- package/src/cli/templates/docker.ts +41 -30
- package/src/cli/templates/frontend/index.ts +63 -0
- package/src/cli/templates/frontend/none.ts +17 -0
- package/src/cli/templates/frontend/react.ts +140 -0
- package/src/cli/templates/frontend/solid.ts +134 -0
- package/src/cli/templates/frontend/svelte.ts +131 -0
- package/src/cli/templates/frontend/vue.ts +130 -0
- package/src/cli/templates/generators/index.ts +339 -0
- package/src/cli/templates/generators/types.ts +56 -0
- package/src/cli/templates/index.ts +35 -2
- package/src/cli/templates/project/api.ts +81 -0
- package/src/cli/templates/project/default.ts +140 -0
- package/src/cli/templates/project/fullstack.ts +111 -0
- package/src/cli/templates/project/index.ts +95 -0
- package/src/cli/templates/project/minimal.ts +45 -0
- package/src/cli/templates/project/types.ts +94 -0
- package/src/cli/templates/project/website.ts +263 -0
- package/src/cli/utils/fs.ts +55 -41
- package/src/cli/utils/index.ts +3 -2
- package/src/cli/utils/strings.ts +47 -33
- package/src/cli/utils/version.ts +47 -0
- package/src/config/env-validation.ts +100 -0
- package/src/config/env.ts +169 -41
- package/src/config/index.ts +28 -20
- package/src/config/loader.ts +25 -16
- package/src/config/merge.ts +21 -10
- package/src/config/types.ts +545 -25
- package/src/config/validation.ts +215 -7
- package/src/container/forward-ref.ts +22 -22
- package/src/container/index.ts +34 -12
- package/src/context/index.ts +11 -1
- package/src/database/index.ts +7 -190
- package/src/database/orm/builder.ts +457 -0
- package/src/database/orm/casts/index.ts +130 -0
- package/src/database/orm/casts/types.ts +25 -0
- package/src/database/orm/compiler.ts +304 -0
- package/src/database/orm/hooks/index.ts +114 -0
- package/src/database/orm/index.ts +61 -0
- package/src/database/orm/model-registry.ts +59 -0
- package/src/database/orm/model.ts +821 -0
- package/src/database/orm/relationships/base.ts +146 -0
- package/src/database/orm/relationships/belongs-to-many.ts +179 -0
- package/src/database/orm/relationships/belongs-to.ts +56 -0
- package/src/database/orm/relationships/has-many.ts +45 -0
- package/src/database/orm/relationships/has-one.ts +41 -0
- package/src/database/orm/relationships/index.ts +11 -0
- package/src/database/orm/scopes/index.ts +55 -0
- package/src/events/__tests__/event-system.test.ts +235 -0
- package/src/events/config.ts +238 -0
- package/src/events/example-usage.ts +185 -0
- package/src/events/index.ts +278 -0
- package/src/events/manager.ts +385 -0
- package/src/events/registry.ts +182 -0
- package/src/events/types.ts +124 -0
- package/src/frontend/api-routes.ts +65 -23
- package/src/frontend/bundler.ts +76 -34
- package/src/frontend/console-client.ts +2 -2
- package/src/frontend/console-stream.ts +94 -38
- package/src/frontend/dev-server.ts +94 -46
- package/src/frontend/file-router.ts +61 -19
- package/src/frontend/frameworks/index.ts +37 -10
- package/src/frontend/frameworks/react.ts +10 -8
- package/src/frontend/frameworks/solid.ts +11 -9
- package/src/frontend/frameworks/svelte.ts +15 -9
- package/src/frontend/frameworks/vue.ts +13 -11
- package/src/frontend/hmr-client.ts +12 -10
- package/src/frontend/hmr.ts +146 -103
- package/src/frontend/index.ts +14 -5
- package/src/frontend/islands.ts +41 -22
- package/src/frontend/isr.ts +59 -37
- package/src/frontend/layout.ts +36 -21
- package/src/frontend/ssr/react.ts +74 -27
- package/src/frontend/ssr/solid.ts +54 -20
- package/src/frontend/ssr/svelte.ts +48 -14
- package/src/frontend/ssr/vue.ts +50 -18
- package/src/frontend/ssr.ts +83 -39
- package/src/frontend/types.ts +91 -56
- package/src/health/index.ts +21 -9
- package/src/i18n/engine.ts +305 -0
- package/src/i18n/index.ts +38 -0
- package/src/i18n/loader.ts +218 -0
- package/src/i18n/middleware.ts +164 -0
- package/src/i18n/negotiator.ts +162 -0
- package/src/i18n/types.ts +158 -0
- package/src/index.ts +179 -27
- package/src/jobs/drivers/memory.ts +315 -0
- package/src/jobs/drivers/redis.ts +459 -0
- package/src/jobs/index.ts +30 -0
- package/src/jobs/queue.ts +281 -0
- package/src/jobs/types.ts +295 -0
- package/src/jobs/worker.ts +380 -0
- package/src/logger/index.ts +1 -3
- package/src/logger/transports/index.ts +62 -22
- package/src/metrics/index.ts +25 -16
- package/src/migrations/index.ts +9 -0
- package/src/modules/filters.ts +13 -17
- package/src/modules/guards.ts +49 -26
- package/src/modules/index.ts +409 -298
- package/src/modules/interceptors.ts +58 -20
- package/src/modules/lazy.ts +11 -19
- package/src/modules/lifecycle.ts +15 -7
- package/src/modules/metadata.ts +15 -5
- package/src/modules/pipes.ts +94 -72
- package/src/notification/channels/base.ts +68 -0
- package/src/notification/channels/email.ts +105 -0
- package/src/notification/channels/push.ts +104 -0
- package/src/notification/channels/sms.ts +105 -0
- package/src/notification/channels/whatsapp.ts +104 -0
- package/src/notification/index.ts +48 -0
- package/src/notification/service.ts +354 -0
- package/src/notification/types.ts +344 -0
- package/src/observability/__tests__/observability.test.ts +483 -0
- package/src/observability/breadcrumbs.ts +114 -0
- package/src/observability/index.ts +136 -0
- package/src/observability/interceptor.ts +85 -0
- package/src/observability/service.ts +303 -0
- package/src/observability/trace.ts +37 -0
- package/src/observability/types.ts +196 -0
- package/src/openapi/__tests__/decorators.test.ts +335 -0
- package/src/openapi/__tests__/document-builder.test.ts +285 -0
- package/src/openapi/__tests__/route-scanner.test.ts +334 -0
- package/src/openapi/__tests__/schema-generator.test.ts +275 -0
- package/src/openapi/decorators.ts +328 -0
- package/src/openapi/document-builder.ts +274 -0
- package/src/openapi/index.ts +112 -0
- package/src/openapi/metadata.ts +112 -0
- package/src/openapi/route-scanner.ts +289 -0
- package/src/openapi/schema-generator.ts +256 -0
- package/src/openapi/swagger-module.ts +166 -0
- package/src/openapi/types.ts +398 -0
- package/src/orm/index.ts +10 -0
- package/src/rpc/index.ts +3 -1
- package/src/schema/index.ts +9 -0
- package/src/security/index.ts +15 -6
- package/src/ssg/index.ts +9 -8
- package/src/telemetry/index.ts +76 -22
- package/src/template/index.ts +7 -0
- package/src/templates/engine.ts +224 -0
- package/src/templates/index.ts +9 -0
- package/src/templates/loader.ts +331 -0
- package/src/templates/renderers/markdown.ts +212 -0
- package/src/templates/renderers/simple.ts +269 -0
- package/src/templates/types.ts +154 -0
- package/src/testing/index.ts +100 -27
- package/src/types/optional-deps.d.ts +347 -187
- package/src/validation/index.ts +92 -2
- package/src/validation/schemas.ts +536 -0
- package/tests/integration/fullstack.test.ts +4 -4
- package/tests/unit/database.test.ts +2 -72
- package/tests/unit/env-validation.test.ts +166 -0
- package/tests/unit/events.test.ts +910 -0
- package/tests/unit/i18n.test.ts +455 -0
- package/tests/unit/jobs.test.ts +493 -0
- package/tests/unit/notification.test.ts +988 -0
- package/tests/unit/observability.test.ts +453 -0
- package/tests/unit/orm/builder.test.ts +323 -0
- package/tests/unit/orm/casts.test.ts +179 -0
- package/tests/unit/orm/compiler.test.ts +220 -0
- package/tests/unit/orm/eager-loading.test.ts +285 -0
- package/tests/unit/orm/hooks.test.ts +191 -0
- package/tests/unit/orm/model.test.ts +373 -0
- package/tests/unit/orm/relationships.test.ts +303 -0
- package/tests/unit/orm/scopes.test.ts +74 -0
- package/tests/unit/templates-simple.test.ts +53 -0
- package/tests/unit/templates.test.ts +454 -0
- package/tests/unit/validation.test.ts +18 -24
- package/tsconfig.json +11 -3
package/src/cli/commands/new.ts
CHANGED
|
@@ -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
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
} from
|
|
19
|
-
import {
|
|
20
|
-
import {
|
|
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
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
type
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
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
|
|
42
|
+
return "Project name is required";
|
|
70
43
|
}
|
|
71
44
|
|
|
72
45
|
if (!/^[a-zA-Z0-9_-]+$/.test(name)) {
|
|
73
|
-
return
|
|
46
|
+
return "Project name can only contain letters, numbers, hyphens, and underscores";
|
|
74
47
|
}
|
|
75
48
|
|
|
76
|
-
if (name.startsWith(
|
|
77
|
-
return
|
|
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
|
|
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(
|
|
91
|
-
|
|
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
|
-
|
|
95
|
-
|
|
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
|
-
|
|
101
|
-
typescript:
|
|
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:
|
|
110
|
-
build:
|
|
111
|
-
start:
|
|
112
|
-
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:
|
|
123
|
-
type:
|
|
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:
|
|
141
|
-
module:
|
|
142
|
-
moduleResolution:
|
|
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:
|
|
123
|
+
jsx: "react-jsx",
|
|
148
124
|
paths: {
|
|
149
|
-
|
|
150
|
-
|
|
125
|
+
"@buenojs/bueno": ["./node_modules/@buenojs/bueno/dist/index.d.ts"],
|
|
126
|
+
},
|
|
151
127
|
},
|
|
152
|
-
include: [
|
|
153
|
-
exclude: [
|
|
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 ===
|
|
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.
|
|
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:
|
|
298
|
+
text: "Creating project structure",
|
|
395
299
|
task: async () => {
|
|
396
|
-
|
|
397
|
-
await createDirectory(joinPaths(projectPath,
|
|
398
|
-
await createDirectory(
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
await createDirectory(
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
await createDirectory(
|
|
405
|
-
|
|
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:
|
|
333
|
+
text: "Creating package.json",
|
|
412
334
|
task: async () => {
|
|
413
335
|
await writeFile(
|
|
414
|
-
joinPaths(projectPath,
|
|
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:
|
|
344
|
+
text: "Creating tsconfig.json",
|
|
423
345
|
task: async () => {
|
|
424
346
|
await writeFile(
|
|
425
|
-
joinPaths(projectPath,
|
|
347
|
+
joinPaths(projectPath, "tsconfig.json"),
|
|
426
348
|
getTsConfigTemplate(),
|
|
427
349
|
);
|
|
428
350
|
},
|
|
429
351
|
});
|
|
430
352
|
|
|
431
|
-
// Create
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
joinPaths(projectPath,
|
|
437
|
-
|
|
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
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
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:
|
|
378
|
+
text: "Creating .env.example",
|
|
456
379
|
task: async () => {
|
|
457
380
|
await writeFile(
|
|
458
|
-
joinPaths(projectPath,
|
|
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:
|
|
389
|
+
text: "Creating .gitignore",
|
|
467
390
|
task: async () => {
|
|
468
391
|
await writeFile(
|
|
469
|
-
joinPaths(projectPath,
|
|
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:
|
|
400
|
+
text: "Creating README.md",
|
|
478
401
|
task: async () => {
|
|
479
402
|
await writeFile(
|
|
480
|
-
joinPaths(projectPath,
|
|
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:
|
|
412
|
+
text: "Creating Dockerfile",
|
|
490
413
|
task: async () => {
|
|
491
414
|
await writeFile(
|
|
492
|
-
joinPaths(projectPath,
|
|
493
|
-
getDockerfileTemplate(
|
|
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:
|
|
425
|
+
text: "Creating .dockerignore",
|
|
500
426
|
task: async () => {
|
|
501
427
|
await writeFile(
|
|
502
|
-
joinPaths(projectPath,
|
|
428
|
+
joinPaths(projectPath, ".dockerignore"),
|
|
503
429
|
getDockerignoreTemplate(),
|
|
504
430
|
);
|
|
505
431
|
},
|
|
506
432
|
});
|
|
507
433
|
|
|
508
434
|
tasks.push({
|
|
509
|
-
text:
|
|
435
|
+
text: "Creating docker-compose.yml",
|
|
510
436
|
task: async () => {
|
|
511
437
|
await writeFile(
|
|
512
|
-
joinPaths(projectPath,
|
|
513
|
-
getDockerComposeTemplate(
|
|
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:
|
|
448
|
+
text: "Creating .env.docker",
|
|
520
449
|
task: async () => {
|
|
521
450
|
await writeFile(
|
|
522
|
-
joinPaths(projectPath,
|
|
523
|
-
getDockerEnvTemplate(
|
|
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(
|
|
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,
|
|
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(
|
|
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
|
-
|
|
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,
|
|
575
|
-
name:
|
|
576
|
-
alias:
|
|
577
|
-
type:
|
|
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,
|
|
582
|
-
name:
|
|
583
|
-
alias:
|
|
584
|
-
type:
|
|
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,
|
|
589
|
-
name:
|
|
590
|
-
alias:
|
|
591
|
-
type:
|
|
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,
|
|
596
|
-
const skipGit = hasFlag(args,
|
|
597
|
-
const docker = hasFlag(args,
|
|
598
|
-
const link = hasFlag(args,
|
|
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,
|
|
602
|
-
const validPlatforms: DeployPlatform[] = [
|
|
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
|
-
|
|
623
|
-
|
|
624
|
-
|
|
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
|
-
|
|
572
|
+
// Only ask for framework if template supports it
|
|
573
|
+
if ((template === "fullstack" || template === "default") && !framework) {
|
|
634
574
|
framework = await select<FrontendFramework>(
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
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
|
-
|
|
581
|
+
// Website template doesn't need database or frontend selection
|
|
582
|
+
if (template !== "website" && !database) {
|
|
647
583
|
database = await select<DatabaseDriver>(
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
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 ||
|
|
661
|
-
framework = framework ||
|
|
662
|
-
database = database ||
|
|
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
|
-
[
|
|
690
|
-
[
|
|
691
|
-
[
|
|
692
|
-
[
|
|
693
|
-
[
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
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([
|
|
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(
|
|
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(
|
|
709
|
-
const installSpinner = spinner(
|
|
658
|
+
cliConsole.subheader("Installing dependencies...");
|
|
659
|
+
const installSpinner = spinner("Running bun install...");
|
|
710
660
|
|
|
711
661
|
try {
|
|
712
|
-
const proc = Bun.spawn([
|
|
662
|
+
const proc = Bun.spawn(["bun", "install"], {
|
|
713
663
|
cwd: projectPath,
|
|
714
|
-
stdout:
|
|
715
|
-
stderr:
|
|
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(
|
|
671
|
+
installSpinner.success("Dependencies installed");
|
|
722
672
|
} else {
|
|
723
|
-
installSpinner.warn(
|
|
673
|
+
installSpinner.warn(
|
|
674
|
+
"Failed to install dependencies. Run `bun install` manually.",
|
|
675
|
+
);
|
|
724
676
|
}
|
|
725
677
|
} catch {
|
|
726
|
-
installSpinner.warn(
|
|
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(
|
|
732
|
-
const linkSpinner = spinner(
|
|
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([
|
|
689
|
+
const proc = Bun.spawn(["bun", "link", "@buenojs/bueno"], {
|
|
736
690
|
cwd: projectPath,
|
|
737
|
-
stdout:
|
|
738
|
-
stderr:
|
|
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(
|
|
698
|
+
linkSpinner.success("Local @buenojs/bueno linked successfully");
|
|
745
699
|
} else {
|
|
746
|
-
linkSpinner.warn(
|
|
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(
|
|
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
|
-
//
|
|
755
|
-
if
|
|
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(
|
|
718
|
+
cliConsole.log("");
|
|
719
|
+
cliConsole.log("Next steps:");
|
|
788
720
|
cliConsole.log(` ${colors.cyan(`cd ${kebabCase(name)}`)}`);
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
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:
|
|
798
|
-
description:
|
|
746
|
+
name: "new",
|
|
747
|
+
description: "Create a new Bueno project",
|
|
799
748
|
positionals: [
|
|
800
749
|
{
|
|
801
|
-
name:
|
|
750
|
+
name: "name",
|
|
802
751
|
required: false,
|
|
803
|
-
description:
|
|
752
|
+
description: "Project name",
|
|
804
753
|
},
|
|
805
754
|
],
|
|
806
755
|
options: [
|
|
807
756
|
{
|
|
808
|
-
name:
|
|
809
|
-
alias:
|
|
810
|
-
type:
|
|
811
|
-
description:
|
|
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:
|
|
815
|
-
alias:
|
|
816
|
-
type:
|
|
817
|
-
description:
|
|
764
|
+
name: "framework",
|
|
765
|
+
alias: "f",
|
|
766
|
+
type: "string",
|
|
767
|
+
description: "Frontend framework (react, vue, svelte, solid, none)",
|
|
818
768
|
},
|
|
819
769
|
{
|
|
820
|
-
name:
|
|
821
|
-
alias:
|
|
822
|
-
type:
|
|
823
|
-
description:
|
|
770
|
+
name: "database",
|
|
771
|
+
alias: "d",
|
|
772
|
+
type: "string",
|
|
773
|
+
description: "Database driver (sqlite, postgresql, mysql, none)",
|
|
824
774
|
},
|
|
825
775
|
{
|
|
826
|
-
name:
|
|
827
|
-
type:
|
|
776
|
+
name: "skip-install",
|
|
777
|
+
type: "boolean",
|
|
828
778
|
default: false,
|
|
829
|
-
description:
|
|
779
|
+
description: "Skip dependency installation",
|
|
830
780
|
},
|
|
831
781
|
{
|
|
832
|
-
name:
|
|
833
|
-
type:
|
|
782
|
+
name: "skip-git",
|
|
783
|
+
type: "boolean",
|
|
834
784
|
default: false,
|
|
835
|
-
description:
|
|
785
|
+
description:
|
|
786
|
+
"Skip git initialization (deprecated - git init is no longer automatic)",
|
|
836
787
|
},
|
|
837
788
|
{
|
|
838
|
-
name:
|
|
839
|
-
type:
|
|
789
|
+
name: "docker",
|
|
790
|
+
type: "boolean",
|
|
840
791
|
default: false,
|
|
841
|
-
description:
|
|
792
|
+
description:
|
|
793
|
+
"Include Docker configuration (Dockerfile, docker-compose.yml)",
|
|
842
794
|
},
|
|
843
795
|
{
|
|
844
|
-
name:
|
|
845
|
-
type:
|
|
796
|
+
name: "link",
|
|
797
|
+
type: "boolean",
|
|
846
798
|
default: false,
|
|
847
|
-
description:
|
|
799
|
+
description: "Use local @buenojs/bueno via bun link (for development)",
|
|
848
800
|
},
|
|
849
801
|
{
|
|
850
|
-
name:
|
|
851
|
-
type:
|
|
852
|
-
description:
|
|
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:
|
|
856
|
-
alias:
|
|
857
|
-
type:
|
|
808
|
+
name: "yes",
|
|
809
|
+
alias: "y",
|
|
810
|
+
type: "boolean",
|
|
858
811
|
default: false,
|
|
859
|
-
description:
|
|
812
|
+
description: "Use default options without prompts",
|
|
860
813
|
},
|
|
861
814
|
],
|
|
862
815
|
examples: [
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
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
|
+
);
|