create-oven 0.1.0
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/index.js +513 -0
- package/package.json +32 -0
package/index.js
ADDED
|
@@ -0,0 +1,513 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* create-oven - Create a new Oven project
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* npx create-oven my-app
|
|
8
|
+
* bunx create-oven my-app
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import fs from 'fs';
|
|
12
|
+
import path from 'path';
|
|
13
|
+
import { fileURLToPath } from 'url';
|
|
14
|
+
|
|
15
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
16
|
+
|
|
17
|
+
const COLORS = {
|
|
18
|
+
reset: '\x1b[0m',
|
|
19
|
+
bright: '\x1b[1m',
|
|
20
|
+
red: '\x1b[31m',
|
|
21
|
+
green: '\x1b[32m',
|
|
22
|
+
yellow: '\x1b[33m',
|
|
23
|
+
blue: '\x1b[34m',
|
|
24
|
+
cyan: '\x1b[36m',
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
function log(msg) {
|
|
28
|
+
console.log(msg);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function success(msg) {
|
|
32
|
+
console.log(`${COLORS.green}${msg}${COLORS.reset}`);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function error(msg) {
|
|
36
|
+
console.error(`${COLORS.red}${msg}${COLORS.reset}`);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function info(msg) {
|
|
40
|
+
console.log(`${COLORS.cyan}${msg}${COLORS.reset}`);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async function main() {
|
|
44
|
+
const args = process.argv.slice(2);
|
|
45
|
+
const projectName = args[0];
|
|
46
|
+
|
|
47
|
+
if (!projectName || projectName === '--help' || projectName === '-h') {
|
|
48
|
+
log(`
|
|
49
|
+
🔥 ${COLORS.bright}create-oven${COLORS.reset} - Create a new Oven project
|
|
50
|
+
|
|
51
|
+
${COLORS.yellow}Usage:${COLORS.reset}
|
|
52
|
+
npx create-oven <project-name>
|
|
53
|
+
bunx create-oven <project-name>
|
|
54
|
+
|
|
55
|
+
${COLORS.yellow}Examples:${COLORS.reset}
|
|
56
|
+
npx create-oven my-app
|
|
57
|
+
bunx create-oven my-awesome-app
|
|
58
|
+
|
|
59
|
+
${COLORS.yellow}Options:${COLORS.reset}
|
|
60
|
+
-h, --help Show this help message
|
|
61
|
+
-v, --version Show version
|
|
62
|
+
|
|
63
|
+
${COLORS.cyan}Learn more: https://github.com/oven-ttta/oven-framework${COLORS.reset}
|
|
64
|
+
`);
|
|
65
|
+
process.exit(projectName ? 0 : 1);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (projectName === '--version' || projectName === '-v') {
|
|
69
|
+
log('create-oven v0.1.0');
|
|
70
|
+
process.exit(0);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
log(`
|
|
74
|
+
🔥 Creating new Oven project: ${COLORS.bright}${projectName}${COLORS.reset}
|
|
75
|
+
`);
|
|
76
|
+
|
|
77
|
+
const projectDir = path.join(process.cwd(), projectName);
|
|
78
|
+
|
|
79
|
+
// Check if directory exists
|
|
80
|
+
if (fs.existsSync(projectDir)) {
|
|
81
|
+
error(` ✗ Directory "${projectName}" already exists!`);
|
|
82
|
+
process.exit(1);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Create project directory
|
|
86
|
+
fs.mkdirSync(projectDir, { recursive: true });
|
|
87
|
+
|
|
88
|
+
// Create project structure
|
|
89
|
+
const dirs = [
|
|
90
|
+
'app',
|
|
91
|
+
'app/api/hello',
|
|
92
|
+
'app/about',
|
|
93
|
+
'public',
|
|
94
|
+
'components',
|
|
95
|
+
'lib',
|
|
96
|
+
];
|
|
97
|
+
|
|
98
|
+
for (const dir of dirs) {
|
|
99
|
+
fs.mkdirSync(path.join(projectDir, dir), { recursive: true });
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
info(' Creating project files...');
|
|
103
|
+
|
|
104
|
+
// package.json
|
|
105
|
+
const pkg = {
|
|
106
|
+
name: projectName,
|
|
107
|
+
version: '0.1.0',
|
|
108
|
+
type: 'module',
|
|
109
|
+
scripts: {
|
|
110
|
+
dev: 'bun run --hot server.ts',
|
|
111
|
+
build: 'bun build ./server.ts --outdir ./dist --target bun',
|
|
112
|
+
start: 'bun run dist/server.js',
|
|
113
|
+
},
|
|
114
|
+
dependencies: {},
|
|
115
|
+
devDependencies: {
|
|
116
|
+
'@types/bun': 'latest',
|
|
117
|
+
typescript: '^5.3.0',
|
|
118
|
+
},
|
|
119
|
+
};
|
|
120
|
+
fs.writeFileSync(
|
|
121
|
+
path.join(projectDir, 'package.json'),
|
|
122
|
+
JSON.stringify(pkg, null, 2)
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
// tsconfig.json
|
|
126
|
+
const tsconfig = {
|
|
127
|
+
compilerOptions: {
|
|
128
|
+
target: 'ESNext',
|
|
129
|
+
module: 'ESNext',
|
|
130
|
+
moduleResolution: 'bundler',
|
|
131
|
+
strict: true,
|
|
132
|
+
esModuleInterop: true,
|
|
133
|
+
skipLibCheck: true,
|
|
134
|
+
forceConsistentCasingInFileNames: true,
|
|
135
|
+
jsx: 'preserve',
|
|
136
|
+
lib: ['ESNext', 'DOM'],
|
|
137
|
+
types: ['bun-types'],
|
|
138
|
+
},
|
|
139
|
+
include: ['**/*.ts', '**/*.tsx'],
|
|
140
|
+
exclude: ['node_modules'],
|
|
141
|
+
};
|
|
142
|
+
fs.writeFileSync(
|
|
143
|
+
path.join(projectDir, 'tsconfig.json'),
|
|
144
|
+
JSON.stringify(tsconfig, null, 2)
|
|
145
|
+
);
|
|
146
|
+
|
|
147
|
+
// server.ts - Main server file
|
|
148
|
+
const serverFile = `/**
|
|
149
|
+
* Oven Server - ${projectName}
|
|
150
|
+
* A Next.js-style framework powered by Bun
|
|
151
|
+
*/
|
|
152
|
+
|
|
153
|
+
const PORT = parseInt(process.env.PORT || '3000');
|
|
154
|
+
|
|
155
|
+
// Simple router
|
|
156
|
+
const routes: Map<string, (req: Request) => Promise<Response>> = new Map();
|
|
157
|
+
|
|
158
|
+
// Scan app directory for routes
|
|
159
|
+
async function scanRoutes() {
|
|
160
|
+
const glob = new Bun.Glob('**/page.tsx');
|
|
161
|
+
for await (const file of glob.scan({ cwd: './app' })) {
|
|
162
|
+
const routePath = '/' + file.replace(/\\/page\\.tsx$/, '').replace(/^page\\.tsx$/, '');
|
|
163
|
+
const normalizedPath = routePath === '/' ? '/' : routePath;
|
|
164
|
+
|
|
165
|
+
routes.set(normalizedPath, async (req: Request) => {
|
|
166
|
+
const module = await import(\`./app/\${file}\`);
|
|
167
|
+
const content = await module.default({ params: {}, searchParams: {} });
|
|
168
|
+
|
|
169
|
+
// Wrap with layout
|
|
170
|
+
let html = content;
|
|
171
|
+
try {
|
|
172
|
+
const layoutModule = await import('./app/layout.tsx');
|
|
173
|
+
html = await layoutModule.default({ children: content, params: {} });
|
|
174
|
+
} catch {}
|
|
175
|
+
|
|
176
|
+
// Generate metadata
|
|
177
|
+
const metadata = module.metadata || {};
|
|
178
|
+
const title = typeof metadata.title === 'string' ? metadata.title : metadata.title?.default || '${projectName}';
|
|
179
|
+
|
|
180
|
+
const fullHtml = \`<!DOCTYPE html>
|
|
181
|
+
<html lang="en">
|
|
182
|
+
<head>
|
|
183
|
+
<meta charset="UTF-8">
|
|
184
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
185
|
+
<title>\${title}</title>
|
|
186
|
+
\${metadata.description ? \`<meta name="description" content="\${metadata.description}">\` : ''}
|
|
187
|
+
</head>
|
|
188
|
+
<body>
|
|
189
|
+
\${html}
|
|
190
|
+
</body>
|
|
191
|
+
</html>\`;
|
|
192
|
+
|
|
193
|
+
return new Response(fullHtml, {
|
|
194
|
+
headers: { 'Content-Type': 'text/html; charset=utf-8' },
|
|
195
|
+
});
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Scan API routes
|
|
200
|
+
const apiGlob = new Bun.Glob('**/route.ts');
|
|
201
|
+
for await (const file of apiGlob.scan({ cwd: './app' })) {
|
|
202
|
+
const routePath = '/' + file.replace(/\\/route\\.ts$/, '').replace(/^route\\.ts$/, '');
|
|
203
|
+
|
|
204
|
+
routes.set(routePath, async (req: Request) => {
|
|
205
|
+
const module = await import(\`./app/\${file}\`);
|
|
206
|
+
const method = req.method.toUpperCase();
|
|
207
|
+
const handler = module[method];
|
|
208
|
+
|
|
209
|
+
if (handler) {
|
|
210
|
+
return handler(req);
|
|
211
|
+
}
|
|
212
|
+
return new Response('Method not allowed', { status: 405 });
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Main server
|
|
218
|
+
async function main() {
|
|
219
|
+
await scanRoutes();
|
|
220
|
+
|
|
221
|
+
Bun.serve({
|
|
222
|
+
port: PORT,
|
|
223
|
+
async fetch(req: Request) {
|
|
224
|
+
const url = new URL(req.url);
|
|
225
|
+
let pathname = url.pathname;
|
|
226
|
+
|
|
227
|
+
// Remove trailing slash
|
|
228
|
+
if (pathname !== '/' && pathname.endsWith('/')) {
|
|
229
|
+
pathname = pathname.slice(0, -1);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// Try to match route
|
|
233
|
+
const handler = routes.get(pathname);
|
|
234
|
+
if (handler) {
|
|
235
|
+
return handler(req);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// Static files
|
|
239
|
+
const publicPath = './public' + pathname;
|
|
240
|
+
const file = Bun.file(publicPath);
|
|
241
|
+
if (await file.exists()) {
|
|
242
|
+
return new Response(file);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// 404
|
|
246
|
+
return new Response('Not Found', { status: 404 });
|
|
247
|
+
},
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
console.log(\`
|
|
251
|
+
🔥 Oven is ready!
|
|
252
|
+
|
|
253
|
+
➜ Local: http://localhost:\${PORT}
|
|
254
|
+
➜ Network: http://0.0.0.0:\${PORT}
|
|
255
|
+
\`);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
main();
|
|
259
|
+
`;
|
|
260
|
+
fs.writeFileSync(path.join(projectDir, 'server.ts'), serverFile);
|
|
261
|
+
|
|
262
|
+
// app/layout.tsx
|
|
263
|
+
const rootLayout = `import type { LayoutProps } from './types';
|
|
264
|
+
|
|
265
|
+
export default function RootLayout({ children }: LayoutProps) {
|
|
266
|
+
return \`
|
|
267
|
+
<div id="__oven">
|
|
268
|
+
<nav style="
|
|
269
|
+
background: linear-gradient(135deg, #ff6b35 0%, #f7931e 100%);
|
|
270
|
+
padding: 1rem 2rem;
|
|
271
|
+
display: flex;
|
|
272
|
+
justify-content: space-between;
|
|
273
|
+
align-items: center;
|
|
274
|
+
">
|
|
275
|
+
<a href="/" style="color: white; text-decoration: none; font-weight: bold; font-size: 1.25rem;">
|
|
276
|
+
🔥 ${projectName}
|
|
277
|
+
</a>
|
|
278
|
+
<div style="display: flex; gap: 1.5rem;">
|
|
279
|
+
<a href="/" style="color: rgba(255,255,255,0.9); text-decoration: none;">Home</a>
|
|
280
|
+
<a href="/about" style="color: rgba(255,255,255,0.9); text-decoration: none;">About</a>
|
|
281
|
+
<a href="/api/hello" style="color: rgba(255,255,255,0.9); text-decoration: none;">API</a>
|
|
282
|
+
</div>
|
|
283
|
+
</nav>
|
|
284
|
+
<main style="min-height: calc(100vh - 60px);">
|
|
285
|
+
\${children}
|
|
286
|
+
</main>
|
|
287
|
+
<style>
|
|
288
|
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
289
|
+
body { font-family: system-ui, -apple-system, sans-serif; }
|
|
290
|
+
</style>
|
|
291
|
+
</div>
|
|
292
|
+
\`;
|
|
293
|
+
}
|
|
294
|
+
`;
|
|
295
|
+
fs.writeFileSync(path.join(projectDir, 'app', 'layout.tsx'), rootLayout);
|
|
296
|
+
|
|
297
|
+
// app/types.ts
|
|
298
|
+
const typesFile = `export interface PageProps {
|
|
299
|
+
params: Record<string, string>;
|
|
300
|
+
searchParams: Record<string, string>;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
export interface LayoutProps {
|
|
304
|
+
children: string;
|
|
305
|
+
params: Record<string, string>;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
export interface Metadata {
|
|
309
|
+
title?: string | { default: string; template?: string };
|
|
310
|
+
description?: string;
|
|
311
|
+
keywords?: string[];
|
|
312
|
+
}
|
|
313
|
+
`;
|
|
314
|
+
fs.writeFileSync(path.join(projectDir, 'app', 'types.ts'), typesFile);
|
|
315
|
+
|
|
316
|
+
// app/page.tsx
|
|
317
|
+
const homePage = `import type { PageProps, Metadata } from './types';
|
|
318
|
+
|
|
319
|
+
export const metadata: Metadata = {
|
|
320
|
+
title: '${projectName}',
|
|
321
|
+
description: 'Built with Oven - A Next.js-style framework for Bun',
|
|
322
|
+
};
|
|
323
|
+
|
|
324
|
+
export default function HomePage({ searchParams }: PageProps) {
|
|
325
|
+
return \`
|
|
326
|
+
<div style="max-width: 800px; margin: 0 auto; padding: 4rem 2rem; text-align: center;">
|
|
327
|
+
<h1 style="font-size: 3rem; margin-bottom: 1rem; background: linear-gradient(135deg, #ff6b35, #f7931e); -webkit-background-clip: text; -webkit-text-fill-color: transparent;">
|
|
328
|
+
Welcome to ${projectName} 🔥
|
|
329
|
+
</h1>
|
|
330
|
+
<p style="color: #666; font-size: 1.2rem; margin-bottom: 2rem;">
|
|
331
|
+
Get started by editing <code style="background: #f5f5f5; padding: 0.25rem 0.5rem; border-radius: 4px;">app/page.tsx</code>
|
|
332
|
+
</p>
|
|
333
|
+
|
|
334
|
+
<div style="display: flex; gap: 1rem; justify-content: center; flex-wrap: wrap;">
|
|
335
|
+
<a href="/about" style="
|
|
336
|
+
background: linear-gradient(135deg, #ff6b35, #f7931e);
|
|
337
|
+
color: white;
|
|
338
|
+
padding: 0.75rem 1.5rem;
|
|
339
|
+
border-radius: 8px;
|
|
340
|
+
text-decoration: none;
|
|
341
|
+
font-weight: 500;
|
|
342
|
+
">
|
|
343
|
+
About Page →
|
|
344
|
+
</a>
|
|
345
|
+
<a href="/api/hello" style="
|
|
346
|
+
background: #333;
|
|
347
|
+
color: white;
|
|
348
|
+
padding: 0.75rem 1.5rem;
|
|
349
|
+
border-radius: 8px;
|
|
350
|
+
text-decoration: none;
|
|
351
|
+
font-weight: 500;
|
|
352
|
+
">
|
|
353
|
+
API Example
|
|
354
|
+
</a>
|
|
355
|
+
</div>
|
|
356
|
+
|
|
357
|
+
<div style="margin-top: 4rem; padding: 2rem; background: #f9f9f9; border-radius: 12px; text-align: left;">
|
|
358
|
+
<h3 style="margin-bottom: 1rem;">📁 Project Structure</h3>
|
|
359
|
+
<pre style="font-size: 0.9rem; color: #666;">
|
|
360
|
+
${projectName}/
|
|
361
|
+
├── app/
|
|
362
|
+
│ ├── layout.tsx # Root layout
|
|
363
|
+
│ ├── page.tsx # Home page (/)
|
|
364
|
+
│ ├── about/
|
|
365
|
+
│ │ └── page.tsx # About page (/about)
|
|
366
|
+
│ └── api/
|
|
367
|
+
│ └── hello/
|
|
368
|
+
│ └── route.ts # API route (/api/hello)
|
|
369
|
+
├── public/ # Static files
|
|
370
|
+
├── server.ts # Bun server
|
|
371
|
+
└── package.json
|
|
372
|
+
</pre>
|
|
373
|
+
</div>
|
|
374
|
+
</div>
|
|
375
|
+
\`;
|
|
376
|
+
}
|
|
377
|
+
`;
|
|
378
|
+
fs.writeFileSync(path.join(projectDir, 'app', 'page.tsx'), homePage);
|
|
379
|
+
|
|
380
|
+
// app/about/page.tsx
|
|
381
|
+
const aboutPage = `import type { PageProps, Metadata } from '../types';
|
|
382
|
+
|
|
383
|
+
export const metadata: Metadata = {
|
|
384
|
+
title: 'About',
|
|
385
|
+
description: 'About ${projectName}',
|
|
386
|
+
};
|
|
387
|
+
|
|
388
|
+
export default function AboutPage({ params }: PageProps) {
|
|
389
|
+
return \`
|
|
390
|
+
<div style="max-width: 800px; margin: 0 auto; padding: 4rem 2rem;">
|
|
391
|
+
<h1 style="font-size: 2.5rem; margin-bottom: 1rem;">About ${projectName}</h1>
|
|
392
|
+
<p style="color: #666; line-height: 1.8; margin-bottom: 1.5rem;">
|
|
393
|
+
This is the about page of your Oven application.
|
|
394
|
+
Built with Bun for maximum performance.
|
|
395
|
+
</p>
|
|
396
|
+
|
|
397
|
+
<div style="background: #fff5f2; padding: 1.5rem; border-radius: 12px; border-left: 4px solid #ff6b35;">
|
|
398
|
+
<h3 style="margin-bottom: 0.5rem;">🚀 Why Oven?</h3>
|
|
399
|
+
<ul style="color: #666; line-height: 1.8; margin-left: 1.5rem;">
|
|
400
|
+
<li>Built for Bun runtime - 4x faster than Node.js</li>
|
|
401
|
+
<li>Next.js-style file-based routing</li>
|
|
402
|
+
<li>Zero configuration needed</li>
|
|
403
|
+
<li>Full TypeScript support</li>
|
|
404
|
+
</ul>
|
|
405
|
+
</div>
|
|
406
|
+
|
|
407
|
+
<a href="/" style="color: #ff6b35; margin-top: 2rem; display: inline-block; text-decoration: none;">
|
|
408
|
+
← Back to Home
|
|
409
|
+
</a>
|
|
410
|
+
</div>
|
|
411
|
+
\`;
|
|
412
|
+
}
|
|
413
|
+
`;
|
|
414
|
+
fs.writeFileSync(path.join(projectDir, 'app', 'about', 'page.tsx'), aboutPage);
|
|
415
|
+
|
|
416
|
+
// app/api/hello/route.ts
|
|
417
|
+
const apiRoute = `// API Route: /api/hello
|
|
418
|
+
|
|
419
|
+
export async function GET(request: Request) {
|
|
420
|
+
return Response.json({
|
|
421
|
+
message: 'Hello from ${projectName}!',
|
|
422
|
+
timestamp: new Date().toISOString(),
|
|
423
|
+
framework: 'Oven 🔥',
|
|
424
|
+
});
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
export async function POST(request: Request) {
|
|
428
|
+
const body = await request.json().catch(() => ({}));
|
|
429
|
+
return Response.json({
|
|
430
|
+
message: 'Data received!',
|
|
431
|
+
data: body,
|
|
432
|
+
});
|
|
433
|
+
}
|
|
434
|
+
`;
|
|
435
|
+
fs.writeFileSync(path.join(projectDir, 'app', 'api', 'hello', 'route.ts'), apiRoute);
|
|
436
|
+
|
|
437
|
+
// .gitignore
|
|
438
|
+
const gitignore = `node_modules
|
|
439
|
+
dist
|
|
440
|
+
.oven
|
|
441
|
+
*.log
|
|
442
|
+
.DS_Store
|
|
443
|
+
.env
|
|
444
|
+
.env.local
|
|
445
|
+
`;
|
|
446
|
+
fs.writeFileSync(path.join(projectDir, '.gitignore'), gitignore);
|
|
447
|
+
|
|
448
|
+
// README.md
|
|
449
|
+
const readme = `# ${projectName}
|
|
450
|
+
|
|
451
|
+
Built with [Oven](https://github.com/oven-ttta/oven-framework) - A Next.js-style framework for Bun 🔥
|
|
452
|
+
|
|
453
|
+
## Getting Started
|
|
454
|
+
|
|
455
|
+
\`\`\`bash
|
|
456
|
+
# Install dependencies
|
|
457
|
+
bun install
|
|
458
|
+
|
|
459
|
+
# Start development server
|
|
460
|
+
bun run dev
|
|
461
|
+
|
|
462
|
+
# Build for production
|
|
463
|
+
bun run build
|
|
464
|
+
|
|
465
|
+
# Start production server
|
|
466
|
+
bun run start
|
|
467
|
+
\`\`\`
|
|
468
|
+
|
|
469
|
+
Open [http://localhost:3000](http://localhost:3000) to see your app.
|
|
470
|
+
|
|
471
|
+
## Project Structure
|
|
472
|
+
|
|
473
|
+
\`\`\`
|
|
474
|
+
${projectName}/
|
|
475
|
+
├── app/
|
|
476
|
+
│ ├── layout.tsx # Root layout
|
|
477
|
+
│ ├── page.tsx # Home page (/)
|
|
478
|
+
│ ├── about/
|
|
479
|
+
│ │ └── page.tsx # About page (/about)
|
|
480
|
+
│ └── api/
|
|
481
|
+
│ └── hello/
|
|
482
|
+
│ └── route.ts # API route (/api/hello)
|
|
483
|
+
├── public/ # Static files
|
|
484
|
+
├── server.ts # Bun server
|
|
485
|
+
└── package.json
|
|
486
|
+
\`\`\`
|
|
487
|
+
|
|
488
|
+
## Learn More
|
|
489
|
+
|
|
490
|
+
- [Oven GitHub](https://github.com/oven-ttta/oven-framework)
|
|
491
|
+
- [Bun Documentation](https://bun.sh/docs)
|
|
492
|
+
`;
|
|
493
|
+
fs.writeFileSync(path.join(projectDir, 'README.md'), readme);
|
|
494
|
+
|
|
495
|
+
success(`
|
|
496
|
+
✅ Project created successfully!
|
|
497
|
+
|
|
498
|
+
${COLORS.yellow}Next steps:${COLORS.reset}
|
|
499
|
+
|
|
500
|
+
cd ${projectName}
|
|
501
|
+
bun install
|
|
502
|
+
bun run dev
|
|
503
|
+
|
|
504
|
+
Open ${COLORS.cyan}http://localhost:3000${COLORS.reset} in your browser.
|
|
505
|
+
|
|
506
|
+
${COLORS.bright}Happy cooking! 🔥${COLORS.reset}
|
|
507
|
+
`);
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
main().catch((err) => {
|
|
511
|
+
error(`Error: ${err.message}`);
|
|
512
|
+
process.exit(1);
|
|
513
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "create-oven",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Create a new Oven project - Next.js-style framework for Bun",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"create-oven": "./index.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"index.js",
|
|
11
|
+
"templates"
|
|
12
|
+
],
|
|
13
|
+
"keywords": [
|
|
14
|
+
"bun",
|
|
15
|
+
"oven",
|
|
16
|
+
"framework",
|
|
17
|
+
"create",
|
|
18
|
+
"scaffold",
|
|
19
|
+
"typescript",
|
|
20
|
+
"fullstack"
|
|
21
|
+
],
|
|
22
|
+
"author": "oven-ttta",
|
|
23
|
+
"license": "MIT",
|
|
24
|
+
"repository": {
|
|
25
|
+
"type": "git",
|
|
26
|
+
"url": "https://github.com/oven-ttta/oven-framework"
|
|
27
|
+
},
|
|
28
|
+
"homepage": "https://github.com/oven-ttta/oven-framework#readme",
|
|
29
|
+
"engines": {
|
|
30
|
+
"node": ">=18.0.0"
|
|
31
|
+
}
|
|
32
|
+
}
|