bertui 0.3.4 → 0.3.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bertui",
3
- "version": "0.3.4",
3
+ "version": "0.3.5",
4
4
  "description": "Lightning-fast React dev server powered by Bun and Elysia",
5
5
  "type": "module",
6
6
  "main": "./index.js",
package/src/build.js CHANGED
@@ -26,16 +26,22 @@ export async function buildProduction(options = {}) {
26
26
 
27
27
  try {
28
28
  logger.info('Step 1: Compiling for production...');
29
- await compileForBuild(root, buildDir);
29
+ const { routes } = await compileForBuild(root, buildDir);
30
30
  logger.success('Production compilation complete');
31
31
 
32
32
  logger.info('Step 2: Building CSS with Lightning CSS...');
33
33
  await buildAllCSS(root, outDir);
34
34
 
35
+
35
36
  const publicDir = join(root, 'public');
36
37
  if (existsSync(publicDir)) {
37
38
  logger.info('Step 3: Copying public assets...');
38
- cpSync(publicDir, outDir, { recursive: true });
39
+ const publicFiles = readdirSync(publicDir);
40
+ for (const file of publicFiles) {
41
+ const srcFile = join(publicDir, file);
42
+ const destFile = join(outDir, file);
43
+ cpSync(srcFile, destFile);
44
+ }
39
45
  logger.success('Public assets copied');
40
46
  } else {
41
47
  logger.info('Step 3: No public directory found, skipping...');
@@ -72,8 +78,8 @@ export async function buildProduction(options = {}) {
72
78
 
73
79
  logger.success('JavaScript bundled with tree-shaking');
74
80
 
75
- logger.info('Step 5: Generating index.html...');
76
- await generateProductionHTML(root, outDir, result);
81
+ logger.info('Step 5: Generating HTML files with page-specific meta...');
82
+ await generateProductionHTML(root, outDir, result, routes);
77
83
 
78
84
  rmSync(buildDir, { recursive: true });
79
85
  logger.info('Cleaned up .bertuibuild/');
@@ -144,6 +150,8 @@ async function compileForBuild(root, buildDir) {
144
150
  await generateBuildRouter(routes, buildDir);
145
151
  logger.info('Generated router for build');
146
152
  }
153
+
154
+ return { routes };
147
155
  }
148
156
 
149
157
  async function discoverRoutes(pagesDir) {
@@ -356,7 +364,6 @@ async function compileBuildDirectory(srcDir, buildDir, root) {
356
364
  const outPath = join(buildDir, file);
357
365
  let code = await Bun.file(srcPath).text();
358
366
 
359
- // CRITICAL FIX: Remove CSS imports
360
367
  code = removeCSSImports(code);
361
368
  code = fixBuildImports(code, srcPath, outPath, root);
362
369
 
@@ -373,7 +380,6 @@ async function compileBuildFile(srcPath, buildDir, filename, root) {
373
380
  try {
374
381
  let code = await Bun.file(srcPath).text();
375
382
 
376
- // CRITICAL FIX: Remove CSS imports before transpilation
377
383
  code = removeCSSImports(code);
378
384
 
379
385
  const outFilename = filename.replace(/\.(jsx|tsx|ts)$/, '.js');
@@ -407,7 +413,6 @@ async function compileBuildFile(srcPath, buildDir, filename, root) {
407
413
  }
408
414
  }
409
415
 
410
- // NEW FUNCTION: Remove all CSS imports
411
416
  function removeCSSImports(code) {
412
417
  code = code.replace(/import\s+['"][^'"]*\.css['"];?\s*/g, '');
413
418
  code = code.replace(/import\s+['"]bertui\/styles['"]\s*;?\s*/g, '');
@@ -442,7 +447,29 @@ function fixRelativeImports(code) {
442
447
  return code;
443
448
  }
444
449
 
445
- async function generateProductionHTML(root, outDir, buildResult) {
450
+ // NEW: Extract meta from page component
451
+ async function extractMetaFromPage(pagePath) {
452
+ try {
453
+ const code = await Bun.file(pagePath).text();
454
+
455
+ // Match: export const meta = { ... }
456
+ const metaRegex = /export\s+const\s+meta\s*=\s*(\{[\s\S]*?\});/;
457
+ const match = code.match(metaRegex);
458
+
459
+ if (!match) return null;
460
+
461
+ // Parse the object (simple eval - production should use AST parser)
462
+ const metaStr = match[1];
463
+ const meta = eval(`(${metaStr})`);
464
+
465
+ return meta;
466
+ } catch (error) {
467
+ logger.warn(`Failed to extract meta from ${pagePath}: ${error.message}`);
468
+ return null;
469
+ }
470
+ }
471
+
472
+ async function generateProductionHTML(root, outDir, buildResult, routes) {
446
473
  const mainBundle = buildResult.outputs.find(o =>
447
474
  o.path.includes('main') && o.kind === 'entry-point'
448
475
  );
@@ -453,7 +480,6 @@ async function generateProductionHTML(root, outDir, buildResult) {
453
480
 
454
481
  const bundlePath = mainBundle.path.replace(outDir, '').replace(/^\//, '');
455
482
 
456
- // Find user CSS files
457
483
  const srcStylesDir = join(root, 'src', 'styles');
458
484
  let userStylesheets = '';
459
485
 
@@ -464,14 +490,40 @@ async function generateProductionHTML(root, outDir, buildResult) {
464
490
  ).join('\n');
465
491
  }
466
492
 
467
- const html = `<!DOCTYPE html>
493
+ // Generate HTML for each route
494
+ logger.info('Generating HTML files for each route...');
495
+
496
+ for (const route of routes) {
497
+ if (route.type === 'dynamic') continue; // Skip dynamic routes
498
+
499
+ // Extract meta from page component
500
+ const pageMeta = await extractMetaFromPage(route.path);
501
+
502
+ const meta = pageMeta || {
503
+ title: 'BertUI App',
504
+ description: 'Built with BertUI - Lightning fast React development'
505
+ };
506
+
507
+ const html = `<!DOCTYPE html>
468
508
  <html lang="en">
469
509
  <head>
470
510
  <meta charset="UTF-8">
471
511
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
472
- <meta name="description" content="Built with BertUI - Lightning fast React development">
473
- <title>BertUI App</title>
512
+ <title>${meta.title}</title>
513
+
514
+ <meta name="description" content="${meta.description || ''}">
515
+ ${meta.keywords ? `<meta name="keywords" content="${meta.keywords}">` : ''}
516
+ ${meta.author ? `<meta name="author" content="${meta.author}">` : ''}
517
+ ${meta.themeColor ? `<meta name="theme-color" content="${meta.themeColor}">` : ''}
518
+
519
+ ${meta.ogTitle ? `<meta property="og:title" content="${meta.ogTitle}">` : `<meta property="og:title" content="${meta.title}">`}
520
+ ${meta.ogDescription ? `<meta property="og:description" content="${meta.ogDescription}">` : meta.description ? `<meta property="og:description" content="${meta.description}">` : ''}
521
+ ${meta.ogImage ? `<meta property="og:image" content="${meta.ogImage}">` : ''}
522
+
523
+ <link rel="icon" type="image/svg+xml" href="/favicon.svg">
524
+
474
525
  ${userStylesheets}
526
+
475
527
  <script type="importmap">
476
528
  {
477
529
  "imports": {
@@ -488,7 +540,18 @@ ${userStylesheets}
488
540
  <script type="module" src="/${bundlePath}"></script>
489
541
  </body>
490
542
  </html>`;
491
-
492
- await Bun.write(join(outDir, 'index.html'), html);
493
- logger.success('Generated index.html');
543
+
544
+ // Determine output path
545
+ let htmlPath;
546
+ if (route.route === '/') {
547
+ htmlPath = join(outDir, 'index.html');
548
+ } else {
549
+ const routeDir = join(outDir, route.route);
550
+ mkdirSync(routeDir, { recursive: true });
551
+ htmlPath = join(routeDir, 'index.html');
552
+ }
553
+
554
+ await Bun.write(htmlPath, html);
555
+ logger.success(`Generated ${route.route === '/' ? 'index.html' : route.route + '/index.html'}`);
556
+ }
494
557
  }
@@ -166,6 +166,7 @@ ws.onclose = () => {
166
166
  });
167
167
  })
168
168
 
169
+ // Around line 60, update the route handler:
169
170
  .get('/public/*', async ({ params, set }) => {
170
171
  const publicDir = join(root, 'public');
171
172
  const filepath = join(publicDir, params['*']);
@@ -225,7 +226,7 @@ function serveHTML(root, hasRouter, config) {
225
226
  ${meta.ogDescription ? `<meta property="og:description" content="${meta.ogDescription || meta.description}">` : ''}
226
227
  ${meta.ogImage ? `<meta property="og:image" content="${meta.ogImage}">` : ''}
227
228
 
228
- <link rel="icon" type="image/svg+xml" href="/public/favicon.svg">
229
+ <link rel="icon" type="image/svg+xml" href="/favicon.svg">
229
230
 
230
231
  ${userStylesheets}
231
232