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 +4 -0
- package/package.json +1 -4
- package/src/build/image-optimizer.js +29 -30
- package/src/build.js +65 -38
package/README.md
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
# BertUI ⚡
|
|
2
2
|
|
|
3
|
+
[](https://github.com/your-repo)
|
|
4
|
+
[](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.
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
19
|
+
// Always return empty array to disable optimization
|
|
17
20
|
return [];
|
|
18
21
|
}
|
|
19
22
|
|
|
20
23
|
export function copyImages(srcDir, outDir) {
|
|
21
|
-
|
|
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
|
|
34
|
+
logger.warn(`⚠️ Source not found: ${srcDir}`);
|
|
27
35
|
return 0;
|
|
28
36
|
}
|
|
29
37
|
|
|
30
|
-
//
|
|
31
|
-
|
|
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
|
-
//
|
|
50
|
+
// Recursively process subdirectories
|
|
51
51
|
const subDestPath = join(targetDir, entry.name);
|
|
52
|
-
|
|
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(`
|
|
62
|
+
logger.warn(` Failed to copy ${entry.name}: ${error.message}`);
|
|
63
|
+
skipped++;
|
|
66
64
|
}
|
|
67
65
|
} else {
|
|
68
|
-
|
|
66
|
+
skipped++;
|
|
69
67
|
}
|
|
70
68
|
}
|
|
71
69
|
}
|
|
72
70
|
} catch (error) {
|
|
73
|
-
logger.error(`Error processing
|
|
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}
|
|
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 -
|
|
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
|
-
|
|
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
|
-
|
|
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('
|
|
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('
|
|
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
|
-
|
|
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
|
|
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:
|
|
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
|
|
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
|
-
//
|
|
599
|
-
const routeDir = join(outDir, route.route.
|
|
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(
|
|
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
|
|
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
|
-
${
|
|
658
|
+
${cssLinks}
|
|
632
659
|
|
|
633
660
|
<script type="importmap">
|
|
634
661
|
{
|