create-oven 0.3.0 → 0.4.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.
Files changed (2) hide show
  1. package/index.js +125 -130
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -88,14 +88,14 @@ async function main() {
88
88
  }
89
89
 
90
90
  if (args.includes('--version') || args.includes('-v')) {
91
- console.log('create-oven v0.3.0');
91
+ console.log('create-oven v0.4.0');
92
92
  process.exit(0);
93
93
  }
94
94
 
95
95
  console.log(`
96
96
  ${c.bold}${c.cyan} ╔═══════════════════════════════════════╗
97
97
  ║ ║
98
- ║ 🔥 Create Oven App v0.3.0 ║
98
+ ║ 🔥 Create Oven App v0.4.0 ║
99
99
  ║ ║
100
100
  ║ Next.js-style framework for Bun ║
101
101
  ║ ║
@@ -157,6 +157,16 @@ ${c.bold}${c.cyan} ╔═══════════════════
157
157
  fs.mkdirSync(path.join(projectDir, 'app'), { recursive: true });
158
158
  fs.mkdirSync(path.join(projectDir, 'public'), { recursive: true });
159
159
 
160
+ // ============ public/oven.svg ============
161
+ fs.writeFileSync(path.join(projectDir, 'public', 'oven.svg'), `<svg width="100" height="24" viewBox="0 0 100 24" fill="none" xmlns="http://www.w3.org/2000/svg">
162
+ <text x="0" y="20" font-family="system-ui, sans-serif" font-size="20" font-weight="bold" fill="currentColor">🔥 Oven</text>
163
+ </svg>`);
164
+
165
+ // ============ public/github.svg ============
166
+ fs.writeFileSync(path.join(projectDir, 'public', 'github.svg'), `<svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
167
+ <path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z"/>
168
+ </svg>`);
169
+
160
170
  // ============ package.json ============
161
171
  const spin1 = spinner('Creating package.json...');
162
172
  const pkg = {
@@ -164,12 +174,14 @@ ${c.bold}${c.cyan} ╔═══════════════════
164
174
  version: '0.1.0',
165
175
  private: true,
166
176
  scripts: {
167
- dev: 'bun run --hot server.tsx',
168
- build: 'bun build ./server.tsx --outdir ./dist --target bun',
169
- start: 'bun run dist/server.js',
177
+ dev: 'oven dev',
178
+ build: 'oven build',
179
+ start: 'oven start',
170
180
  ...(useEslint && { lint: 'eslint .' }),
171
181
  },
172
- dependencies: {},
182
+ dependencies: {
183
+ 'oven-bun': 'latest',
184
+ },
173
185
  devDependencies: {
174
186
  ...(useTypescript && { '@types/bun': 'latest', 'typescript': '^5' }),
175
187
  ...(useTailwind && { '@tailwindcss/postcss': '^4', 'tailwindcss': '^4' }),
@@ -283,31 +295,34 @@ body {
283
295
  const spin6 = spinner(`Creating app/layout.${ext}...`);
284
296
  const layoutContent = useTypescript ? `import "./globals.css";
285
297
 
286
- export const metadata = {
298
+ export const metadata${useTypescript ? ': { title: string; description: string }' : ''} = {
287
299
  title: "Create Oven App",
288
300
  description: "Generated by create-oven",
289
301
  };
290
302
 
291
303
  export default function RootLayout({
292
304
  children,
293
- }: {
305
+ }: Readonly<{
294
306
  children: string;
295
- }) {
296
- return \`
297
- <!DOCTYPE html>
307
+ }>) {
308
+ return (
309
+ \`<!DOCTYPE html>
298
310
  <html lang="en">
299
311
  <head>
300
- <meta charset="UTF-8">
301
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
312
+ <meta charset="UTF-8" />
313
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
302
314
  <title>\${metadata.title}</title>
303
- <meta name="description" content="\${metadata.description}">
304
- <link rel="icon" href="/favicon.ico">
315
+ <meta name="description" content="\${metadata.description}" />
316
+ <link rel="icon" href="/favicon.ico" />
317
+ <link rel="preconnect" href="https://fonts.googleapis.com" />
318
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
319
+ <link href="https://fonts.googleapis.com/css2?family=Geist:wght@400;500;600;700&family=Geist+Mono&display=swap" rel="stylesheet" />
305
320
  </head>
306
- <body>
321
+ <body class="antialiased" style="font-family: 'Geist', sans-serif;">
307
322
  \${children}
308
323
  </body>
309
- </html>
310
- \`;
324
+ </html>\`
325
+ );
311
326
  }
312
327
  ` : `import "./globals.css";
313
328
 
@@ -317,21 +332,24 @@ export const metadata = {
317
332
  };
318
333
 
319
334
  export default function RootLayout({ children }) {
320
- return \`
321
- <!DOCTYPE html>
335
+ return (
336
+ \`<!DOCTYPE html>
322
337
  <html lang="en">
323
338
  <head>
324
- <meta charset="UTF-8">
325
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
339
+ <meta charset="UTF-8" />
340
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
326
341
  <title>\${metadata.title}</title>
327
- <meta name="description" content="\${metadata.description}">
328
- <link rel="icon" href="/favicon.ico">
342
+ <meta name="description" content="\${metadata.description}" />
343
+ <link rel="icon" href="/favicon.ico" />
344
+ <link rel="preconnect" href="https://fonts.googleapis.com" />
345
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
346
+ <link href="https://fonts.googleapis.com/css2?family=Geist:wght@400;500;600;700&family=Geist+Mono&display=swap" rel="stylesheet" />
329
347
  </head>
330
- <body>
348
+ <body class="antialiased" style="font-family: 'Geist', sans-serif;">
331
349
  \${children}
332
350
  </body>
333
- </html>
334
- \`;
351
+ </html>\`
352
+ );
335
353
  }
336
354
  `;
337
355
  fs.writeFileSync(path.join(projectDir, 'app', `layout.${ext}`), layoutContent);
@@ -339,131 +357,108 @@ export default function RootLayout({ children }) {
339
357
 
340
358
  // ============ app/page.tsx ============
341
359
  const spin7 = spinner(`Creating app/page.${ext}...`);
342
- const tailwindClasses = useTailwind;
343
- const pageContent = `export default function Home() {
344
- return \`
345
- <div ${tailwindClasses ? 'class="flex min-h-screen items-center justify-center bg-zinc-50 font-sans dark:bg-black"' : 'style="display: flex; min-height: 100vh; align-items: center; justify-content: center; background: #fafafa;"'}>
346
- <main ${tailwindClasses ? 'class="flex min-h-screen w-full max-w-3xl flex-col items-center justify-between py-32 px-16 bg-white dark:bg-black"' : 'style="display: flex; flex-direction: column; align-items: center; justify-content: center; max-width: 48rem; padding: 8rem 4rem; background: white;"'}>
347
- <div ${tailwindClasses ? 'class="text-6xl mb-8"' : 'style="font-size: 4rem; margin-bottom: 2rem;"'}>🔥</div>
348
-
349
- <div ${tailwindClasses ? 'class="flex flex-col items-center gap-6 text-center"' : 'style="display: flex; flex-direction: column; align-items: center; gap: 1.5rem; text-align: center;"'}>
350
- <h1 ${tailwindClasses ? 'class="text-3xl font-semibold leading-10 tracking-tight text-black dark:text-zinc-50"' : 'style="font-size: 1.875rem; font-weight: 600; line-height: 2.5rem; color: black;"'}>
351
- Welcome to Oven
360
+ const pageContent = useTailwind ? `export default function Home() {
361
+ return (
362
+ \`<div class="flex min-h-screen items-center justify-center bg-zinc-50 font-sans dark:bg-black">
363
+ <main class="flex min-h-screen w-full max-w-3xl flex-col items-center justify-between py-32 px-16 bg-white dark:bg-black sm:items-start">
364
+ <img
365
+ class="dark:invert"
366
+ src="/oven.svg"
367
+ alt="Oven logo"
368
+ width="100"
369
+ height="24"
370
+ />
371
+ <div class="flex flex-col items-center gap-6 text-center sm:items-start sm:text-left">
372
+ <h1 class="max-w-xs text-3xl font-semibold leading-10 tracking-tight text-black dark:text-zinc-50">
373
+ Get started by editing app/page.${ext}
352
374
  </h1>
353
- <p ${tailwindClasses ? 'class="max-w-md text-lg leading-8 text-zinc-600 dark:text-zinc-400"' : 'style="max-width: 28rem; font-size: 1.125rem; line-height: 2rem; color: #666;"'}>
354
- Get started by editing <code ${tailwindClasses ? 'class="bg-zinc-100 dark:bg-zinc-800 px-2 py-1 rounded"' : 'style="background: #f5f5f5; padding: 0.25rem 0.5rem; border-radius: 4px;"'}>app/page.${ext}</code>
375
+ <p class="max-w-md text-lg leading-8 text-zinc-600 dark:text-zinc-400">
376
+ Looking for a starting point? Head over to the{" "}
377
+ <a
378
+ href="https://github.com/oven-ttta/oven-framework"
379
+ class="font-medium text-zinc-950 dark:text-zinc-50"
380
+ >
381
+ Documentation
382
+ </a>{" "}
383
+ or{" "}
384
+ <a
385
+ href="https://bun.sh/docs"
386
+ class="font-medium text-zinc-950 dark:text-zinc-50"
387
+ >
388
+ Bun Docs
389
+ </a>.
355
390
  </p>
356
391
  </div>
357
-
358
- <div ${tailwindClasses ? 'class="flex flex-col gap-4 text-base font-medium sm:flex-row mt-8"' : 'style="display: flex; gap: 1rem; margin-top: 2rem;"'}>
392
+ <div class="flex flex-col gap-4 text-base font-medium sm:flex-row">
393
+ <a
394
+ class="flex h-12 w-full items-center justify-center gap-2 rounded-full bg-foreground px-5 text-background transition-colors hover:bg-[#383838] dark:hover:bg-[#ccc] md:w-[158px]"
395
+ href="https://github.com/oven-ttta/oven-framework"
396
+ target="_blank"
397
+ rel="noopener noreferrer"
398
+ style="background: #171717; color: white;"
399
+ >
400
+ <img
401
+ class="dark:invert"
402
+ src="/github.svg"
403
+ alt="GitHub"
404
+ width="16"
405
+ height="16"
406
+ />
407
+ GitHub
408
+ </a>
409
+ <a
410
+ class="flex h-12 w-full items-center justify-center rounded-full border border-solid border-black/[.08] px-5 transition-colors hover:border-transparent hover:bg-black/[.04] dark:border-white/[.145] dark:hover:bg-[#1a1a1a] md:w-[158px]"
411
+ href="https://bun.sh/docs"
412
+ target="_blank"
413
+ rel="noopener noreferrer"
414
+ >
415
+ Bun Docs
416
+ </a>
417
+ </div>
418
+ </main>
419
+ </div>\`
420
+ );
421
+ }
422
+ ` : `export default function Home() {
423
+ return (
424
+ \`<div style="display: flex; min-height: 100vh; align-items: center; justify-content: center; background: #fafafa;">
425
+ <main style="display: flex; flex-direction: column; align-items: center; justify-content: space-between; max-width: 48rem; padding: 8rem 4rem; background: white;">
426
+ <div style="font-size: 4rem; margin-bottom: 2rem;">🔥</div>
427
+ <div style="display: flex; flex-direction: column; align-items: center; gap: 1.5rem; text-align: center;">
428
+ <h1 style="font-size: 1.875rem; font-weight: 600; line-height: 2.5rem; color: black;">
429
+ Get started by editing app/page.${ext}
430
+ </h1>
431
+ <p style="max-width: 28rem; font-size: 1.125rem; line-height: 2rem; color: #666;">
432
+ Looking for a starting point? Head over to the
433
+ <a href="https://github.com/oven-ttta/oven-framework" style="font-weight: 500; color: black;">Documentation</a>
434
+ or
435
+ <a href="https://bun.sh/docs" style="font-weight: 500; color: black;">Bun Docs</a>.
436
+ </p>
437
+ </div>
438
+ <div style="display: flex; gap: 1rem; margin-top: 2rem;">
359
439
  <a
360
- ${tailwindClasses ? 'class="flex h-12 items-center justify-center gap-2 rounded-full bg-black text-white px-6 hover:bg-zinc-800 transition-colors"' : 'style="display: flex; height: 3rem; align-items: center; justify-content: center; gap: 0.5rem; border-radius: 9999px; background: black; color: white; padding: 0 1.5rem; text-decoration: none;"'}
361
440
  href="https://github.com/oven-ttta/oven-framework"
362
441
  target="_blank"
442
+ style="display: flex; height: 3rem; align-items: center; justify-content: center; gap: 0.5rem; border-radius: 9999px; background: black; color: white; padding: 0 1.5rem; text-decoration: none;"
363
443
  >
364
444
  GitHub
365
445
  </a>
366
446
  <a
367
- ${tailwindClasses ? 'class="flex h-12 items-center justify-center rounded-full border border-zinc-200 dark:border-zinc-700 px-6 hover:bg-zinc-50 dark:hover:bg-zinc-900 transition-colors"' : 'style="display: flex; height: 3rem; align-items: center; justify-content: center; border-radius: 9999px; border: 1px solid #e5e5e5; padding: 0 1.5rem; text-decoration: none; color: inherit;"'}
368
447
  href="https://bun.sh/docs"
369
448
  target="_blank"
449
+ style="display: flex; height: 3rem; align-items: center; justify-content: center; border-radius: 9999px; border: 1px solid #e5e5e5; padding: 0 1.5rem; text-decoration: none; color: inherit;"
370
450
  >
371
451
  Bun Docs
372
452
  </a>
373
453
  </div>
374
454
  </main>
375
- </div>
376
- \`;
455
+ </div>\`
456
+ );
377
457
  }
378
458
  `;
379
459
  fs.writeFileSync(path.join(projectDir, 'app', `page.${ext}`), pageContent);
380
460
  spin7.stop(`Created app/page.${ext}`);
381
461
 
382
- // ============ server.tsx ============
383
- const spin8 = spinner(`Creating server.${ext}...`);
384
- const serverContent = `/**
385
- * Oven Server
386
- * Powered by Bun
387
- */
388
-
389
- const PORT = parseInt(process.env.PORT || "3000");
390
-
391
- // Simple router
392
- const routes = new Map${useTypescript ? '<string, (req: Request) => Promise<Response>>' : ''}();
393
-
394
- async function scanRoutes() {
395
- const appDir = "./app";
396
-
397
- // Scan for page files
398
- const glob = new Bun.Glob("**/page.{tsx,jsx,ts,js}");
399
-
400
- for await (const file of glob.scan({ cwd: appDir })) {
401
- const routePath = "/" + file
402
- .replace(/\\/page\\.(tsx|jsx|ts|js)$/, "")
403
- .replace(/^page\\.(tsx|jsx|ts|js)$/, "")
404
- .replace(/\\/$/, "") || "/";
405
-
406
- routes.set(routePath === "" ? "/" : routePath, async (req${useTypescript ? ': Request' : ''}) => {
407
- const module = await import(\`\${appDir}/\${file}\`);
408
- const content = await module.default();
409
-
410
- // Wrap with layout
411
- let html = content;
412
- try {
413
- const layout = await import(\`\${appDir}/layout.tsx\`);
414
- html = await layout.default({ children: content });
415
- } catch {}
416
-
417
- return new Response(html, {
418
- headers: { "Content-Type": "text/html; charset=utf-8" },
419
- });
420
- });
421
- }
422
- }
423
-
424
- async function main() {
425
- await scanRoutes();
426
-
427
- Bun.serve({
428
- port: PORT,
429
- async fetch(req${useTypescript ? ': Request' : ''}) {
430
- const url = new URL(req.url);
431
- let pathname = url.pathname;
432
-
433
- if (pathname !== "/" && pathname.endsWith("/")) {
434
- pathname = pathname.slice(0, -1);
435
- }
436
-
437
- // Check routes
438
- const handler = routes.get(pathname);
439
- if (handler) {
440
- return handler(req);
441
- }
442
-
443
- // Static files
444
- const publicPath = "./public" + pathname;
445
- const file = Bun.file(publicPath);
446
- if (await file.exists()) {
447
- return new Response(file);
448
- }
449
-
450
- return new Response("Not Found", { status: 404 });
451
- },
452
- });
453
-
454
- console.log(\`
455
- ${c.green}▲${c.reset} Ready in \${Date.now() - start}ms
456
-
457
- ${c.dim}➜${c.reset} Local: ${c.cyan}http://localhost:\${PORT}${c.reset}
458
- \`);
459
- }
460
-
461
- const start = Date.now();
462
- main();
463
- `;
464
- fs.writeFileSync(path.join(projectDir, `server.${ext}`), serverContent);
465
- spin8.stop(`Created server.${ext}`);
466
-
467
462
  // ============ .gitignore ============
468
463
  const spin9 = spinner('Creating .gitignore...');
469
464
  fs.writeFileSync(path.join(projectDir, '.gitignore'), `# Dependencies
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-oven",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "Create a new Oven project - Next.js-style framework for Bun",
5
5
  "type": "module",
6
6
  "bin": {