bertui 0.4.6 → 1.0.1

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 CHANGED
@@ -1,5 +1,9 @@
1
1
  # BertUI ⚡
2
2
 
3
+ [![Stable: v1.0.0](https://img.shields.io/badge/Stable-v1.0.0-brightgreen)](https://github.com/your-repo)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
5
+
6
+ **The fastest, zero-config React static site generator. Now stable and production-ready.**
3
7
  Lightning-fast React development powered by Bun.
4
8
 
5
9
  ## ⚠️ Important Notice - CSS Animations Temporarily Unavailable
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bertui",
3
- "version": "0.4.6",
3
+ "version": "1.0.1",
4
4
  "description": "Lightning-fast React dev server powered by Bun and Elysia",
5
5
  "type": "module",
6
6
  "main": "./index.js",
@@ -44,9 +44,6 @@
44
44
  "url": "https://github.com/BunElysiaReact/BERTUI.git"
45
45
  },
46
46
  "dependencies": {
47
- "@jsquash/jpeg": "^1.6.0",
48
- "@jsquash/png": "^3.1.1",
49
- "@jsquash/webp": "^1.5.0",
50
47
  "elysia": "^1.0.0",
51
48
  "ernest-logger": "latest",
52
49
  "lightningcss": "^1.30.2"
@@ -1,57 +1,55 @@
1
- // bertui/src/build/image-optimizer.js - SIMPLE WORKING VERSION
1
+ // bertui/src/build/image-optimizer.js - SIMPLE & STABLE
2
2
  import { join, extname } from 'path';
3
3
  import { existsSync, mkdirSync, readdirSync, cpSync } from 'fs';
4
4
  import logger from '../logger/logger.js';
5
5
 
6
6
  /**
7
- * Simple image copying - skip WASM optimization for now
7
+ * Simple, reliable image copying
8
+ * No WASM, no optimization, just copy files
8
9
  */
10
+
9
11
  export async function optimizeImages(srcDir, outDir) {
10
- logger.info(`📋 Copying images from ${srcDir} to ${outDir}...`);
12
+ // Alias for copyImages to maintain API
13
+ logger.info(`📁 Copying from ${srcDir} to ${outDir}...`);
11
14
  const copied = copyImages(srcDir, outDir);
12
15
  return { optimized: 0, saved: 0, copied };
13
16
  }
14
17
 
15
18
  export async function checkOptimizationTools() {
16
- logger.info('📋 Image optimization disabled (simple mode)');
19
+ // Always return empty array to disable optimization
17
20
  return [];
18
21
  }
19
22
 
20
23
  export function copyImages(srcDir, outDir) {
21
- const imageExtensions = ['.png', '.jpg', '.jpeg', '.webp', '.gif', '.svg', '.avif', '.ico'];
24
+ // All common image formats
25
+ const imageExtensions = [
26
+ '.png', '.jpg', '.jpeg', '.webp', '.gif', '.svg',
27
+ '.avif', '.ico', '.bmp', '.tiff', '.tif'
28
+ ];
29
+
22
30
  let copied = 0;
31
+ let skipped = 0;
23
32
 
24
- // Check if source directory exists
25
33
  if (!existsSync(srcDir)) {
26
- logger.warn(`⚠️ Source directory not found: ${srcDir}`);
34
+ logger.warn(`⚠️ Source not found: ${srcDir}`);
27
35
  return 0;
28
36
  }
29
37
 
30
- // Create output directory if it doesn't exist
31
- if (!existsSync(outDir)) {
32
- mkdirSync(outDir, { recursive: true });
33
- logger.info(`Created directory: ${outDir}`);
34
- }
38
+ // Ensure output directory exists
39
+ mkdirSync(outDir, { recursive: true });
35
40
 
36
41
  function processDirectory(dir, targetDir) {
37
42
  try {
38
43
  const entries = readdirSync(dir, { withFileTypes: true });
39
-
40
- if (entries.length === 0) {
41
- logger.warn(`Directory empty: ${dir}`);
42
- return;
43
- }
44
44
 
45
45
  for (const entry of entries) {
46
46
  const srcPath = join(dir, entry.name);
47
47
  const destPath = join(targetDir, entry.name);
48
48
 
49
49
  if (entry.isDirectory()) {
50
- // Create subdirectory in target
50
+ // Recursively process subdirectories
51
51
  const subDestPath = join(targetDir, entry.name);
52
- if (!existsSync(subDestPath)) {
53
- mkdirSync(subDestPath, { recursive: true });
54
- }
52
+ mkdirSync(subDestPath, { recursive: true });
55
53
  processDirectory(srcPath, subDestPath);
56
54
  } else if (entry.isFile()) {
57
55
  const ext = extname(entry.name).toLowerCase();
@@ -60,28 +58,29 @@ export function copyImages(srcDir, outDir) {
60
58
  try {
61
59
  cpSync(srcPath, destPath);
62
60
  copied++;
63
- logger.debug(` ✓ ${entry.name}`);
64
61
  } catch (error) {
65
- logger.warn(` ${entry.name} - ${error.message}`);
62
+ logger.warn(` Failed to copy ${entry.name}: ${error.message}`);
63
+ skipped++;
66
64
  }
67
65
  } else {
68
- logger.debug(` - ${entry.name} (skipped, not an image)`);
66
+ skipped++;
69
67
  }
70
68
  }
71
69
  }
72
70
  } catch (error) {
73
- logger.error(`Error processing directory ${dir}: ${error.message}`);
71
+ logger.error(`Error processing ${dir}: ${error.message}`);
74
72
  }
75
73
  }
76
74
 
77
- logger.info(`Processing ${srcDir}...`);
78
75
  processDirectory(srcDir, outDir);
79
-
76
+
80
77
  if (copied > 0) {
81
- logger.success(`✅ Copied ${copied} images to ${outDir}`);
82
- } else {
83
- logger.warn(`⚠️ No images found in ${srcDir}`);
78
+ logger.success(`✅ Copied ${copied} image(s) to ${outDir}`);
84
79
  }
85
80
 
81
+ if (skipped > 0) {
82
+ logger.info(`📝 Skipped ${skipped} non-image file(s)`);
83
+ }
84
+
86
85
  return copied;
87
86
  }
package/src/build.js CHANGED
@@ -1,4 +1,4 @@
1
- // src/build.js - COMPLETELY FIXED VERSION
1
+ // src/build.js - COMPLETE FIXED VERSION v1.0.1
2
2
  import { join, relative, basename, extname, dirname } from 'path';
3
3
  import { existsSync, mkdirSync, rmSync, cpSync, readdirSync, statSync } from 'fs';
4
4
  import logger from './logger/logger.js';
@@ -45,7 +45,6 @@ export async function buildProduction(options = {}) {
45
45
  const optimizationTools = await checkOptimizationTools();
46
46
 
47
47
  logger.info('Step 4: Copying and optimizing static assets...');
48
- // SKIP OPTIMIZATION FOR NOW - JUST COPY
49
48
  await copyAllStaticAssets(root, outDir, false);
50
49
 
51
50
  logger.info('Step 5: Bundling JavaScript with Bun...');
@@ -88,14 +87,15 @@ export async function buildProduction(options = {}) {
88
87
 
89
88
  logger.success('JavaScript bundled successfully');
90
89
 
91
- // DEBUG: Show what was built
90
+ // DEBUG: Show what was built
92
91
  logger.info('Built outputs:');
93
92
  result.outputs.forEach((output, i) => {
94
- logger.info(` ${i + 1}. ${relative(outDir, output.path)} (${output.kind})`);
93
+ const relativePath = relative(outDir, output.path);
94
+ logger.info(` ${i + 1}. ${relativePath} (${output.kind}, ${output.size} bytes)`);
95
95
  });
96
96
 
97
97
  logger.info('Step 6: Generating HTML files...');
98
- // ✅ CRITICAL FIX: Generate HTML files
98
+ // ✅ CRITICAL FIX: Generate HTML files WITH CSS
99
99
  await generateProductionHTML(root, outDir, result, routes);
100
100
 
101
101
  // Clean up build directory
@@ -141,26 +141,27 @@ async function copyAllStaticAssets(root, outDir, optimize = true) {
141
141
  const publicDir = join(root, 'public');
142
142
  const srcImagesDir = join(root, 'src', 'images');
143
143
 
144
- // ALWAYS use simple copy for now
145
- logger.info('Using simple asset copy (optimization disabled)...');
144
+ logger.info('📦 Copying static assets...');
146
145
 
147
146
  // Copy from public/ to root of dist/
148
147
  if (existsSync(publicDir)) {
149
- logger.info('📁 Copying public/ directory...');
148
+ logger.info(' Copying public/ directory...');
150
149
  copyImages(publicDir, outDir);
151
150
  } else {
152
- logger.info('No public/ directory found');
151
+ logger.info(' No public/ directory found');
153
152
  }
154
153
 
155
154
  // Copy from src/images/ to dist/images/
156
155
  if (existsSync(srcImagesDir)) {
157
- logger.info('🖼️ Copying src/images/ to dist/images/...');
156
+ logger.info(' Copying src/images/ to dist/images/...');
158
157
  const distImagesDir = join(outDir, 'images');
159
158
  mkdirSync(distImagesDir, { recursive: true });
160
159
  copyImages(srcImagesDir, distImagesDir);
161
160
  } else {
162
- logger.info('No src/images/ directory found');
161
+ logger.info(' No src/images/ directory found');
163
162
  }
163
+
164
+ logger.success('✅ All assets copied');
164
165
  }
165
166
 
166
167
  async function buildAllCSS(root, outDir) {
@@ -171,6 +172,7 @@ async function buildAllCSS(root, outDir) {
171
172
 
172
173
  if (existsSync(srcStylesDir)) {
173
174
  const cssFiles = readdirSync(srcStylesDir).filter(f => f.endsWith('.css'));
175
+ logger.info(`Found ${cssFiles.length} CSS files to minify`);
174
176
  for (const cssFile of cssFiles) {
175
177
  const srcPath = join(srcStylesDir, cssFile);
176
178
  const destPath = join(stylesOutDir, cssFile.replace('.css', '.min.css'));
@@ -550,14 +552,9 @@ function extractMetaFromSource(code) {
550
552
  }
551
553
  }
552
554
 
553
- // ✅ CRITICAL FIX: Generate HTML files
555
+ // ✅ CRITICAL FIX: Generate HTML files WITH CSS
554
556
  async function generateProductionHTML(root, outDir, buildResult, routes) {
555
- if (routes.length === 0) {
556
- logger.warn('No routes found, skipping HTML generation');
557
- return;
558
- }
559
-
560
- logger.info(`Generating HTML files for ${routes.length} routes...`);
557
+ logger.info('Step 6: Generating HTML files with CSS...');
561
558
 
562
559
  // Find main JS bundle
563
560
  const mainBundle = buildResult.outputs.find(o =>
@@ -565,23 +562,42 @@ async function generateProductionHTML(root, outDir, buildResult, routes) {
565
562
  );
566
563
 
567
564
  if (!mainBundle) {
568
- logger.error('Could not find main bundle in build output');
569
- // List all outputs for debugging
570
- logger.info('Available outputs:');
571
- buildResult.outputs.forEach((o, i) => {
572
- logger.info(` ${i + 1}. ${o.path} (${o.kind})`);
573
- });
565
+ logger.error('Could not find main bundle');
574
566
  return;
575
567
  }
576
568
 
577
569
  const bundlePath = relative(outDir, mainBundle.path).replace(/\\/g, '/');
578
- logger.info(`Main bundle: ${bundlePath}`);
570
+ logger.info(`Main JS bundle: /${bundlePath}`);
571
+
572
+ // ✅ CRITICAL FIX: Get CSS files from DIST directory
573
+ const stylesOutDir = join(outDir, 'styles');
574
+ let cssLinks = '';
575
+
576
+ if (existsSync(stylesOutDir)) {
577
+ const cssFiles = readdirSync(stylesOutDir).filter(f => f.endsWith('.min.css'));
578
+ logger.info(`Found ${cssFiles.length} CSS files: ${cssFiles.join(', ')}`);
579
+
580
+ if (cssFiles.length > 0) {
581
+ // Create CSS link tags
582
+ cssLinks = cssFiles.map(cssFile =>
583
+ ` <link rel="stylesheet" href="/styles/${cssFile}">`
584
+ ).join('\n');
585
+
586
+ logger.info(`Generated CSS links:\n${cssLinks}`);
587
+ } else {
588
+ logger.warn('⚠️ No .min.css files found in dist/styles/');
589
+ }
590
+ } else {
591
+ logger.error('❌ dist/styles/ directory not found!');
592
+ }
579
593
 
580
- // Load config for default meta
594
+ // Load config
581
595
  const { loadConfig } = await import('./config/loadConfig.js');
582
596
  const config = await loadConfig(root);
583
597
  const defaultMeta = config.meta || {};
584
598
 
599
+ logger.info(`Generating HTML for ${routes.length} routes...`);
600
+
585
601
  // Generate HTML for each route
586
602
  for (const route of routes) {
587
603
  try {
@@ -589,32 +605,30 @@ async function generateProductionHTML(root, outDir, buildResult, routes) {
589
605
  const pageMeta = extractMetaFromSource(sourceCode);
590
606
  const meta = { ...defaultMeta, ...pageMeta };
591
607
 
592
- const html = generateHTML(meta, route, bundlePath);
608
+ const html = generateHTML(meta, route, bundlePath, cssLinks);
593
609
 
594
610
  let htmlPath;
595
611
  if (route.route === '/') {
596
612
  htmlPath = join(outDir, 'index.html');
597
613
  } else {
598
- // Create directory for the route
599
- const routeDir = join(outDir, route.route.slice(1)); // Remove leading slash
614
+ // Remove leading slash for directory creation
615
+ const routeDir = join(outDir, route.route.replace(/^\//, ''));
600
616
  mkdirSync(routeDir, { recursive: true });
601
617
  htmlPath = join(routeDir, 'index.html');
602
618
  }
603
619
 
604
620
  await Bun.write(htmlPath, html);
605
- logger.success(`Generated: ${route.route === '/' ? 'index.html' : route.route + '/index.html'}`);
621
+ logger.success(`✅ Generated: ${route.route === '/' ? '/' : route.route}`);
622
+
606
623
  } catch (error) {
607
624
  logger.error(`Failed to generate HTML for ${route.route}: ${error.message}`);
608
625
  }
609
626
  }
627
+
628
+ logger.success('✨ All HTML files generated with CSS!');
610
629
  }
611
630
 
612
- function generateHTML(meta, route, bundlePath) {
613
- const cssFiles = ['global.min.css', 'home.min.css'];
614
- const stylesheets = cssFiles.map(css =>
615
- ` <link rel="stylesheet" href="/styles/${css}">`
616
- ).join('\n');
617
-
631
+ function generateHTML(meta, route, bundlePath, cssLinks) {
618
632
  return `<!DOCTYPE html>
619
633
  <html lang="${meta.lang || 'en'}">
620
634
  <head>
@@ -622,13 +636,26 @@ function generateHTML(meta, route, bundlePath) {
622
636
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
623
637
  <title>${meta.title || 'BertUI App'}</title>
624
638
 
625
- <meta name="description" content="${meta.description || 'Built with BertUI - Lightning fast React development'}">
639
+ <meta name="description" content="${meta.description || 'Built with BertUI'}">
626
640
  ${meta.keywords ? `<meta name="keywords" content="${meta.keywords}">` : ''}
627
641
  ${meta.author ? `<meta name="author" content="${meta.author}">` : ''}
642
+ ${meta.themeColor ? `<meta name="theme-color" content="${meta.themeColor}">` : ''}
643
+
644
+ <meta property="og:title" content="${meta.ogTitle || meta.title || 'BertUI App'}">
645
+ <meta property="og:description" content="${meta.ogDescription || meta.description || 'Built with BertUI'}">
646
+ ${meta.ogImage ? `<meta property="og:image" content="${meta.ogImage}">` : ''}
647
+ <meta property="og:type" content="website">
648
+ <meta property="og:url" content="${route.route}">
649
+
650
+ <meta name="twitter:card" content="summary_large_image">
651
+ <meta name="twitter:title" content="${meta.twitterTitle || meta.ogTitle || meta.title || 'BertUI App'}">
652
+ <meta name="twitter:description" content="${meta.twitterDescription || meta.ogDescription || meta.description || 'Built with BertUI'}">
653
+ ${meta.twitterImage || meta.ogImage ? `<meta name="twitter:image" content="${meta.twitterImage || meta.ogImage}">` : ''}
628
654
 
629
655
  <link rel="icon" type="image/svg+xml" href="/favicon.svg">
656
+ <link rel="canonical" href="${route.route}">
630
657
 
631
- ${stylesheets}
658
+ ${cssLinks}
632
659
 
633
660
  <script type="importmap">
634
661
  {