bertui 0.4.2 → 0.4.4

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.4.2",
3
+ "version": "0.4.4",
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
@@ -1,3 +1,4 @@
1
+ // src/build.js - FIXED VERSION
1
2
  import { join, relative, basename, extname, dirname } from 'path';
2
3
  import { existsSync, mkdirSync, rmSync, cpSync, readdirSync, statSync } from 'fs';
3
4
  import logger from './logger/logger.js';
@@ -39,18 +40,12 @@ export async function buildProduction(options = {}) {
39
40
  logger.info('Step 2: Building CSS with Lightning CSS...');
40
41
  await buildAllCSS(root, outDir);
41
42
 
42
- // ✅ NEW: Check if image optimization is available
43
43
  logger.info('Step 3: Checking image optimization tools...');
44
44
  const optimizationTools = await checkOptimizationTools();
45
45
 
46
46
  logger.info('Step 4: Copying and optimizing static assets...');
47
- if (optimizationTools.length > 0) {
48
- // Use WASM-powered optimization
49
- await copyAllStaticAssets(root, outDir, true);
50
- } else {
51
- // Fallback: just copy images
52
- await copyAllStaticAssets(root, outDir, false);
53
- }
47
+ // FIX 1: Copy images from BOTH src/images/ and public/
48
+ await copyAllStaticAssets(root, outDir, optimizationTools.length > 0);
54
49
 
55
50
  logger.info('Step 5: Bundling JavaScript with Bun...');
56
51
  const buildEntry = join(buildDir, 'main.js');
@@ -75,9 +70,6 @@ export async function buildProduction(options = {}) {
75
70
  external: ['react', 'react-dom', 'react-dom/client', 'react/jsx-runtime'],
76
71
  define: {
77
72
  'process.env.NODE_ENV': '"production"',
78
- 'process.env.PUBLIC_APP_NAME': JSON.stringify(envVars.PUBLIC_APP_NAME || 'BertUI App'),
79
- 'process.env.PUBLIC_API_URL': JSON.stringify(envVars.PUBLIC_API_URL || ''),
80
- 'process.env.PUBLIC_USERNAME': JSON.stringify(envVars.PUBLIC_USERNAME || ''),
81
73
  ...Object.fromEntries(
82
74
  Object.entries(envVars).map(([key, value]) => [
83
75
  `process.env.${key}`,
@@ -96,6 +88,7 @@ export async function buildProduction(options = {}) {
96
88
  logger.success('JavaScript bundled with tree-shaking');
97
89
 
98
90
  logger.info('Step 6: Generating SEO-optimized HTML files...');
91
+ // ✅ FIX 2: Generate HTML for ALL routes including index.html
99
92
  await generateProductionHTML(root, outDir, result, routes);
100
93
 
101
94
  rmSync(buildDir, { recursive: true });
@@ -131,16 +124,21 @@ export async function buildProduction(options = {}) {
131
124
  }
132
125
  }
133
126
 
134
- // ✅ UPDATED: Copy and optionally optimize static assets
127
+ // ✅ FIX 3: Enhanced asset copying with proper directory structure
135
128
  async function copyAllStaticAssets(root, outDir, optimize = true) {
136
129
  const publicDir = join(root, 'public');
137
- const srcDir = join(root, 'src');
130
+ const srcImagesDir = join(root, 'src', 'images');
138
131
 
139
132
  let assetsCopied = 0;
140
133
  let assetsOptimized = 0;
141
134
 
142
- // Copy from public/
135
+ // Create images directory in dist/
136
+ const distImagesDir = join(outDir, 'images');
137
+ mkdirSync(distImagesDir, { recursive: true });
138
+
139
+ // Copy from public/ to root of dist/
143
140
  if (existsSync(publicDir)) {
141
+ logger.info('Copying public/ assets...');
144
142
  if (optimize) {
145
143
  const result = await optimizeImages(publicDir, outDir);
146
144
  assetsOptimized += result.optimized;
@@ -149,16 +147,14 @@ async function copyAllStaticAssets(root, outDir, optimize = true) {
149
147
  }
150
148
  }
151
149
 
152
- // Copy static assets from src/ (images, fonts, etc.)
153
- if (existsSync(srcDir)) {
154
- const assetsOutDir = join(outDir, 'assets');
155
- mkdirSync(assetsOutDir, { recursive: true });
156
-
150
+ // FIX: Copy from src/images/ to dist/images/
151
+ if (existsSync(srcImagesDir)) {
152
+ logger.info('Copying src/images/ to dist/images/...');
157
153
  if (optimize) {
158
- const result = await optimizeImages(srcDir, assetsOutDir);
154
+ const result = await optimizeImages(srcImagesDir, distImagesDir);
159
155
  assetsOptimized += result.optimized;
160
156
  } else {
161
- assetsCopied += copyImages(srcDir, assetsOutDir);
157
+ assetsCopied += copyImages(srcImagesDir, distImagesDir);
162
158
  }
163
159
  }
164
160
 
@@ -556,6 +552,7 @@ function extractMetaFromSource(code) {
556
552
  }
557
553
  }
558
554
 
555
+ // ✅ FIX 4: Generate proper HTML files with correct meta tags
559
556
  async function generateProductionHTML(root, outDir, buildResult, routes) {
560
557
  const mainBundle = buildResult.outputs.find(o =>
561
558
  o.path.includes('main') && o.kind === 'entry-point'
@@ -578,18 +575,15 @@ async function generateProductionHTML(root, outDir, buildResult, routes) {
578
575
  ).join('\n');
579
576
  }
580
577
 
578
+ // ✅ Load config for default meta
581
579
  const { loadConfig } = await import('./config/loadConfig.js');
582
580
  const config = await loadConfig(root);
583
581
  const defaultMeta = config.meta || {};
584
582
 
585
583
  logger.info('Generating SEO-optimized HTML files...');
586
584
 
585
+ // ✅ FIX: Generate HTML for ALL routes (including dynamic as fallback)
587
586
  for (const route of routes) {
588
- if (route.type === 'dynamic') {
589
- logger.info(`Skipping dynamic route: ${route.route}`);
590
- continue;
591
- }
592
-
593
587
  const sourceCode = await Bun.file(route.path).text();
594
588
  const pageMeta = extractMetaFromSource(sourceCode);
595
589
  const meta = { ...defaultMeta, ...pageMeta };
@@ -598,7 +592,24 @@ async function generateProductionHTML(root, outDir, buildResult, routes) {
598
592
  logger.info(`Extracted meta for ${route.route}: ${JSON.stringify(pageMeta)}`);
599
593
  }
600
594
 
601
- const html = `<!DOCTYPE html>
595
+ const html = generateHTML(meta, route, bundlePath, userStylesheets);
596
+
597
+ let htmlPath;
598
+ if (route.route === '/') {
599
+ htmlPath = join(outDir, 'index.html');
600
+ } else {
601
+ const routeDir = join(outDir, route.route);
602
+ mkdirSync(routeDir, { recursive: true });
603
+ htmlPath = join(routeDir, 'index.html');
604
+ }
605
+
606
+ await Bun.write(htmlPath, html);
607
+ logger.success(`Generated ${route.route === '/' ? 'index.html' : route.route + '/index.html'}`);
608
+ }
609
+ }
610
+
611
+ function generateHTML(meta, route, bundlePath, userStylesheets) {
612
+ return `<!DOCTYPE html>
602
613
  <html lang="${meta.lang || 'en'}">
603
614
  <head>
604
615
  <meta charset="UTF-8">
@@ -642,17 +653,4 @@ ${userStylesheets}
642
653
  <script type="module" src="/${bundlePath}"></script>
643
654
  </body>
644
655
  </html>`;
645
-
646
- let htmlPath;
647
- if (route.route === '/') {
648
- htmlPath = join(outDir, 'index.html');
649
- } else {
650
- const routeDir = join(outDir, route.route);
651
- mkdirSync(routeDir, { recursive: true });
652
- htmlPath = join(routeDir, 'index.html');
653
- }
654
-
655
- await Bun.write(htmlPath, html);
656
- logger.success(`Generated ${route.route === '/' ? 'index.html' : route.route + '/index.html'} with meta: ${meta.title || 'default'}`);
657
- }
658
656
  }
@@ -1,4 +1,4 @@
1
- // src/server/dev-server.js - FIXED VERSION
1
+ // src/server/dev-server.js - FIXED IMAGE SERVING
2
2
  import { Elysia } from 'elysia';
3
3
  import { watch } from 'fs';
4
4
  import { join, extname } from 'path';
@@ -13,6 +13,7 @@ export async function startDevServer(options = {}) {
13
13
  const compiledDir = join(root, '.bertui', 'compiled');
14
14
  const stylesDir = join(root, '.bertui', 'styles');
15
15
  const srcDir = join(root, 'src');
16
+ const publicDir = join(root, 'public');
16
17
 
17
18
  const config = await loadConfig(root);
18
19
 
@@ -30,10 +31,10 @@ export async function startDevServer(options = {}) {
30
31
  return serveHTML(root, hasRouter, config);
31
32
  })
32
33
 
33
- // ✅ NEW: Serve images from src/images/
34
+ // ✅ FIX: Serve images from src/images/ (CRITICAL)
34
35
  .get('/images/*', async ({ params, set }) => {
35
- const imagesDir = join(srcDir, 'images');
36
- const filepath = join(imagesDir, params['*']);
36
+ const srcImagesDir = join(srcDir, 'images');
37
+ const filepath = join(srcImagesDir, params['*']);
37
38
  const file = Bun.file(filepath);
38
39
 
39
40
  if (!await file.exists()) {
@@ -47,12 +48,27 @@ export async function startDevServer(options = {}) {
47
48
  return new Response(file, {
48
49
  headers: {
49
50
  'Content-Type': contentType,
50
- 'Cache-Control': 'public, max-age=31536000'
51
+ 'Cache-Control': 'no-cache' // Dev server = no cache
51
52
  }
52
53
  });
53
54
  })
54
55
 
55
- // ✅ NEW: Serve any static asset from src/ (fonts, videos, etc.)
56
+ // ✅ Serve from public/ directory
57
+ .get('/public/*', async ({ params, set }) => {
58
+ const filepath = join(publicDir, params['*']);
59
+ const file = Bun.file(filepath);
60
+
61
+ if (!await file.exists()) {
62
+ set.status = 404;
63
+ return 'File not found';
64
+ }
65
+
66
+ return new Response(file, {
67
+ headers: { 'Cache-Control': 'no-cache' }
68
+ });
69
+ })
70
+
71
+ // ✅ Generic asset serving
56
72
  .get('/assets/*', async ({ params, set }) => {
57
73
  const filepath = join(srcDir, params['*']);
58
74
  const file = Bun.file(filepath);
@@ -68,7 +84,7 @@ export async function startDevServer(options = {}) {
68
84
  return new Response(file, {
69
85
  headers: {
70
86
  'Content-Type': contentType,
71
- 'Cache-Control': 'public, max-age=31536000'
87
+ 'Cache-Control': 'no-cache'
72
88
  }
73
89
  });
74
90
  })
@@ -108,16 +124,6 @@ export async function startDevServer(options = {}) {
108
124
  }
109
125
  }
110
126
 
111
- if (path.startsWith('public/')) {
112
- const publicDir = join(root, 'public');
113
- const filepath = join(publicDir, path.replace('public/', ''));
114
- const file = Bun.file(filepath);
115
-
116
- if (await file.exists()) {
117
- return new Response(file);
118
- }
119
- }
120
-
121
127
  set.status = 404;
122
128
  return 'File not found';
123
129
  }
@@ -436,19 +442,6 @@ ws.onclose = () => {
436
442
  });
437
443
  })
438
444
 
439
- .get('/public/*', async ({ params, set }) => {
440
- const publicDir = join(root, 'public');
441
- const filepath = join(publicDir, params['*']);
442
- const file = Bun.file(filepath);
443
-
444
- if (!await file.exists()) {
445
- set.status = 404;
446
- return 'File not found';
447
- }
448
-
449
- return new Response(file);
450
- })
451
-
452
445
  .listen(port);
453
446
 
454
447
  if (!app.server) {
@@ -458,7 +451,8 @@ ws.onclose = () => {
458
451
 
459
452
  logger.success(`🚀 Server running at http://localhost:${port}`);
460
453
  logger.info(`📁 Serving: ${root}`);
461
- logger.info(`🖼️ Images available at: /images/*`);
454
+ logger.info(`🖼️ Images: /images/* src/images/`);
455
+ logger.info(`📦 Public: /public/* → public/`);
462
456
 
463
457
  setupWatcher(root, compiledDir, clients, async () => {
464
458
  hasRouter = existsSync(join(compiledDir, 'router.js'));
@@ -536,7 +530,6 @@ ${userStylesheets}
536
530
  });
537
531
  }
538
532
 
539
- // ✅ NEW: Helper for image content types
540
533
  function getImageContentType(ext) {
541
534
  const types = {
542
535
  '.jpg': 'image/jpeg',
@@ -594,14 +587,12 @@ function setupWatcher(root, compiledDir, clients, onRecompile) {
594
587
  if (!filename) return;
595
588
 
596
589
  const ext = extname(filename);
597
-
598
- // ✅ Watch image changes too
599
590
  const watchedExtensions = ['.js', '.jsx', '.ts', '.tsx', '.css', '.png', '.jpg', '.jpeg', '.gif', '.svg', '.webp', '.avif'];
600
591
 
601
592
  if (watchedExtensions.includes(ext)) {
602
593
  logger.info(`📝 File changed: ${filename}`);
603
594
 
604
- // For images, just reload without recompiling
595
+ // For images, just reload
605
596
  if (['.png', '.jpg', '.jpeg', '.gif', '.svg', '.webp', '.avif'].includes(ext)) {
606
597
  for (const client of clients) {
607
598
  try {