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 +1 -1
- package/src/build.js +38 -40
- package/src/server/dev-server.js +26 -35
package/package.json
CHANGED
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
|
-
|
|
48
|
-
|
|
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
|
-
// ✅
|
|
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
|
|
130
|
+
const srcImagesDir = join(root, 'src', 'images');
|
|
138
131
|
|
|
139
132
|
let assetsCopied = 0;
|
|
140
133
|
let assetsOptimized = 0;
|
|
141
134
|
|
|
142
|
-
//
|
|
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
|
-
//
|
|
153
|
-
if (existsSync(
|
|
154
|
-
|
|
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(
|
|
154
|
+
const result = await optimizeImages(srcImagesDir, distImagesDir);
|
|
159
155
|
assetsOptimized += result.optimized;
|
|
160
156
|
} else {
|
|
161
|
-
assetsCopied += copyImages(
|
|
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 =
|
|
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
|
}
|
package/src/server/dev-server.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// src/server/dev-server.js - FIXED
|
|
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
|
-
// ✅
|
|
34
|
+
// ✅ FIX: Serve images from src/images/ (CRITICAL)
|
|
34
35
|
.get('/images/*', async ({ params, set }) => {
|
|
35
|
-
const
|
|
36
|
-
const filepath = join(
|
|
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': '
|
|
51
|
+
'Cache-Control': 'no-cache' // Dev server = no cache
|
|
51
52
|
}
|
|
52
53
|
});
|
|
53
54
|
})
|
|
54
55
|
|
|
55
|
-
// ✅
|
|
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': '
|
|
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
|
|
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
|
|
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 {
|