bertui 1.1.7 → 1.1.9
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 +10 -10
- package/index.js +32 -15
- package/package.json +10 -7
- package/src/build/processors/css-builder.js +116 -80
- package/src/build.js +127 -22
- package/src/cli.js +19 -4
- package/src/client/compiler.js +46 -3
- package/src/css/processor.js +46 -1
- package/src/image-optimizer/index.js +27 -0
- package/src/router/SSRRouter.js +1 -1
- package/src/router/index.js +2 -2
- package/src/serve.js +195 -0
- package/src/server/dev-server-utils.js +16 -5
- package/src/utils/cache.js +297 -0
package/README.md
CHANGED
|
@@ -13,7 +13,7 @@ Zero configuration. 494ms dev server. 265ms builds. Perfect SEO with Server Isla
|
|
|
13
13
|
[](https://www.rust-lang.org)
|
|
14
14
|
[](LICENSE)
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
|
|
17
17
|
# One command. Zero config. Instant speed. 78% smaller images.
|
|
18
18
|
bunx create-bertui my-app && cd my-app && bun run dev
|
|
19
19
|
|
|
@@ -106,13 +106,13 @@ export default function About() {
|
|
|
106
106
|
|
|
107
107
|
At build time:
|
|
108
108
|
|
|
109
|
-
|
|
109
|
+
✅ Generates static HTML for instant loading
|
|
110
110
|
|
|
111
|
-
|
|
111
|
+
✅ Auto-adds to sitemap.xml
|
|
112
112
|
|
|
113
|
-
|
|
113
|
+
✅ Perfect SEO without SSR complexity
|
|
114
114
|
|
|
115
|
-
|
|
115
|
+
✅ Still builds in 265ms
|
|
116
116
|
|
|
117
117
|
🦀 Image Optimization (Just Works)
|
|
118
118
|
|
|
@@ -241,17 +241,17 @@ Rust Required ❌ NO N/A N/A N/A
|
|
|
241
241
|
|
|
242
242
|
Future packages (in development):
|
|
243
243
|
|
|
244
|
-
|
|
244
|
+
🔄 bertui-elysia - Full-stack addon (API routes, auth, database)
|
|
245
245
|
|
|
246
|
-
|
|
246
|
+
🎨 bertui-animation - GPU-accelerated animations (Zig)
|
|
247
247
|
|
|
248
|
-
|
|
248
|
+
📊 bertui-charts - High-performance charts (Rust)
|
|
249
249
|
|
|
250
250
|
🙏 Credits
|
|
251
251
|
|
|
252
|
-
|
|
252
|
+
Runtime: Bun - The fastest JavaScript runtime
|
|
253
253
|
|
|
254
|
-
|
|
254
|
+
Server: Elysia - Fast and elegant web framework
|
|
255
255
|
|
|
256
256
|
CSS: LightningCSS - Lightning-fast CSS processing
|
|
257
257
|
|
package/index.js
CHANGED
|
@@ -1,44 +1,61 @@
|
|
|
1
|
-
//
|
|
1
|
+
// ============================================
|
|
2
|
+
// FILE: bertui/index.js (Located in root)
|
|
3
|
+
// ============================================
|
|
2
4
|
|
|
3
5
|
// Compiler
|
|
4
|
-
export { compileProject } from './client/compiler.js';
|
|
5
|
-
export { compileForBuild } from './build/compiler/index.js';
|
|
6
|
-
export { discoverRoutes } from './build/compiler/route-discoverer.js';
|
|
6
|
+
export { compileProject, compileFile } from './src/client/compiler.js';
|
|
7
|
+
export { compileForBuild } from './src/build/compiler/index.js';
|
|
8
|
+
export { discoverRoutes } from './src/build/compiler/route-discoverer.js';
|
|
7
9
|
|
|
8
10
|
// HMR
|
|
9
|
-
export { hmr } from './client/hmr-runtime.js';
|
|
11
|
+
export { hmr } from './src/client/hmr-runtime.js';
|
|
10
12
|
|
|
11
|
-
// Image Optimizer
|
|
13
|
+
// Image Optimizer
|
|
12
14
|
export {
|
|
13
15
|
optimizeImage,
|
|
14
16
|
optimizeImagesBatch,
|
|
15
17
|
hasWasm,
|
|
16
18
|
version as optimizerVersion
|
|
17
|
-
} from './image-optimizer/index.js';
|
|
19
|
+
} from './src/image-optimizer/index.js';
|
|
18
20
|
|
|
19
21
|
// Build
|
|
20
|
-
export { buildProduction } from './build.js';
|
|
21
|
-
export { optimizeImages } from './build/image-optimizer.js';
|
|
22
|
+
export { buildProduction } from './src/build.js';
|
|
23
|
+
export { optimizeImages } from './src/build/image-optimizer.js';
|
|
22
24
|
|
|
23
25
|
// Router
|
|
24
|
-
export { Router, Link, useRouter } from './router/index.js';
|
|
25
|
-
export { SSRRouter } from './router/SSRRouter.
|
|
26
|
+
export { Router, Link, useRouter } from './src/router/index.js';
|
|
27
|
+
export { SSRRouter } from './src/router/SSRRouter.js';
|
|
26
28
|
|
|
27
29
|
// Config
|
|
28
|
-
export { loadConfig, defaultConfig } from './config/index.js';
|
|
30
|
+
export { loadConfig, defaultConfig } from './src/config/index.js';
|
|
29
31
|
|
|
30
32
|
// Logger
|
|
31
|
-
export { default as logger } from './logger/logger.js';
|
|
33
|
+
export { default as logger } from './src/logger/logger.js';
|
|
32
34
|
|
|
33
|
-
// CLI
|
|
34
|
-
export { program } from './cli.js';
|
|
35
|
+
// CLI
|
|
36
|
+
export { program } from './src/cli.js';
|
|
35
37
|
|
|
36
38
|
// Version
|
|
37
39
|
export const version = '1.2.0';
|
|
38
40
|
|
|
41
|
+
// Import for default export
|
|
42
|
+
import { compileProject, compileFile } from './src/client/compiler.js';
|
|
43
|
+
import { compileForBuild } from './src/build/compiler/index.js';
|
|
44
|
+
import { discoverRoutes } from './src/build/compiler/route-discoverer.js';
|
|
45
|
+
import { hmr } from './src/client/hmr-runtime.js';
|
|
46
|
+
import { optimizeImage, optimizeImagesBatch } from './src/image-optimizer/index.js';
|
|
47
|
+
import { optimizeImages } from './src/build/image-optimizer.js';
|
|
48
|
+
import { buildProduction } from './src/build.js';
|
|
49
|
+
import { Router, Link, useRouter } from './src/router/index.js';
|
|
50
|
+
import { SSRRouter } from './src/router/SSRRouter.js';
|
|
51
|
+
import { loadConfig, defaultConfig } from './src/config/index.js';
|
|
52
|
+
import logger from './src/logger/logger.js';
|
|
53
|
+
import { program } from './src/cli.js';
|
|
54
|
+
|
|
39
55
|
// Default export
|
|
40
56
|
export default {
|
|
41
57
|
compileProject,
|
|
58
|
+
compileFile,
|
|
42
59
|
compileForBuild,
|
|
43
60
|
discoverRoutes,
|
|
44
61
|
hmr,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bertui",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.9",
|
|
4
4
|
"description": "Lightning-fast React dev server powered by Bun - Now with Rust image optimization (WASM, no Rust required for users)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./index.js",
|
|
@@ -11,8 +11,8 @@
|
|
|
11
11
|
"exports": {
|
|
12
12
|
".": {
|
|
13
13
|
"types": "./types/index.d.ts",
|
|
14
|
-
"import": "./
|
|
15
|
-
"default": "./
|
|
14
|
+
"import": "./index.js",
|
|
15
|
+
"default": "./index.js"
|
|
16
16
|
},
|
|
17
17
|
"./hmr": {
|
|
18
18
|
"import": "./src/client/hmr-runtime.js"
|
|
@@ -45,9 +45,11 @@
|
|
|
45
45
|
"scripts": {
|
|
46
46
|
"dev": "bun bin/bertui.js dev",
|
|
47
47
|
"build": "bun bin/bertui.js build",
|
|
48
|
+
"serve": "bun bin/bertui.js serve",
|
|
49
|
+
"preview": "bun bin/bertui.js serve --port 5000",
|
|
48
50
|
"build:wasm": "cd src/image-optimizer-rust && wasm-pack build --target web --out-dir ../../dist/image-optimizer/wasm && bun run fix:wasm",
|
|
49
51
|
"fix:wasm": "node scripts/fix-wasm-exports.js",
|
|
50
|
-
"prepublishOnly": "echo '
|
|
52
|
+
"prepublishOnly": "echo 'Note: Ensure WASM is built via build:wasm before publishing'",
|
|
51
53
|
"test": "cd test-app && bun run dev"
|
|
52
54
|
},
|
|
53
55
|
"dependencies": {
|
|
@@ -59,7 +61,7 @@
|
|
|
59
61
|
"peerDependencies": {
|
|
60
62
|
"bun": ">=1.0.0",
|
|
61
63
|
"react": "^18.0.0 || ^19.0.0",
|
|
62
|
-
"react-dom": "^19.
|
|
64
|
+
"react-dom": "^18.0.0 || ^19.0.0"
|
|
63
65
|
},
|
|
64
66
|
"devDependencies": {
|
|
65
67
|
"@types/react": "^18.2.0",
|
|
@@ -69,7 +71,8 @@
|
|
|
69
71
|
},
|
|
70
72
|
"optionalDependencies": {
|
|
71
73
|
"@bertui/image-optimizer-wasm": "0.1.0",
|
|
72
|
-
"oxipng": "^8.0.0"
|
|
74
|
+
"oxipng": "^8.0.0",
|
|
75
|
+
"sass": "^1.69.5"
|
|
73
76
|
},
|
|
74
77
|
"keywords": [
|
|
75
78
|
"react",
|
|
@@ -94,4 +97,4 @@
|
|
|
94
97
|
"engines": {
|
|
95
98
|
"bun": ">=1.0.0"
|
|
96
99
|
}
|
|
97
|
-
}
|
|
100
|
+
}
|
|
@@ -1,102 +1,138 @@
|
|
|
1
|
-
// bertui/src/build/processors/css-builder.js -
|
|
1
|
+
// bertui/src/build/processors/css-builder.js - WITH SCSS + CACHING
|
|
2
2
|
import { join } from 'path';
|
|
3
3
|
import { existsSync, readdirSync, mkdirSync } from 'fs';
|
|
4
4
|
import logger from '../../logger/logger.js';
|
|
5
|
+
import { globalCache } from '../../utils/cache.js';
|
|
6
|
+
import { minifyCSS, processSCSS } from '../../css/processor.js';
|
|
5
7
|
|
|
6
8
|
export async function buildAllCSS(root, outDir) {
|
|
9
|
+
const startTime = process.hrtime.bigint();
|
|
10
|
+
|
|
7
11
|
const srcStylesDir = join(root, 'src', 'styles');
|
|
8
12
|
const stylesOutDir = join(outDir, 'styles');
|
|
9
13
|
|
|
10
14
|
mkdirSync(stylesOutDir, { recursive: true });
|
|
11
15
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
for (const cssFile of cssFiles) {
|
|
24
|
-
const srcPath = join(srcStylesDir, cssFile);
|
|
25
|
-
const file = Bun.file(srcPath);
|
|
26
|
-
const cssContent = await file.text();
|
|
27
|
-
combinedCSS += `/* ${cssFile} */\n${cssContent}\n\n`;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const combinedPath = join(stylesOutDir, 'bertui.min.css');
|
|
31
|
-
|
|
32
|
-
// ✅ SAFE: Try Lightning CSS, fallback to simple minification
|
|
33
|
-
try {
|
|
34
|
-
const minified = await minifyCSSSafe(combinedCSS);
|
|
35
|
-
await Bun.write(combinedPath, minified);
|
|
36
|
-
|
|
37
|
-
const originalSize = Buffer.byteLength(combinedCSS);
|
|
38
|
-
const minifiedSize = Buffer.byteLength(minified);
|
|
39
|
-
const reduction = ((1 - minifiedSize / originalSize) * 100).toFixed(1);
|
|
40
|
-
|
|
41
|
-
logger.success(`CSS minified: ${(originalSize/1024).toFixed(2)}KB → ${(minifiedSize/1024).toFixed(2)}KB (-${reduction}%)`);
|
|
42
|
-
} catch (error) {
|
|
43
|
-
logger.warn(`CSS minification failed: ${error.message}`);
|
|
44
|
-
logger.info('Falling back to unminified CSS...');
|
|
45
|
-
await Bun.write(combinedPath, combinedCSS);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
logger.success(`✅ Combined ${cssFiles.length} CSS files`);
|
|
49
|
-
} else {
|
|
50
|
-
// No styles directory, create empty CSS
|
|
16
|
+
// Check cache for entire CSS build
|
|
17
|
+
const cacheKey = `css-build:${root}:${Date.now()}`;
|
|
18
|
+
const cached = globalCache.get(cacheKey, { ttl: 1000 }); // 1 second cache
|
|
19
|
+
|
|
20
|
+
if (cached) {
|
|
21
|
+
logger.info(`⚡ Using cached CSS (${cached.files} files)`);
|
|
22
|
+
await Bun.write(join(stylesOutDir, 'bertui.min.css'), cached.content);
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (!existsSync(srcStylesDir)) {
|
|
51
27
|
await Bun.write(join(stylesOutDir, 'bertui.min.css'), '/* No custom styles */');
|
|
52
28
|
logger.info('No styles directory found, created empty CSS');
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Process SCSS files first
|
|
33
|
+
await processSCSSDirectory(srcStylesDir, root);
|
|
34
|
+
|
|
35
|
+
// Read all CSS files (including compiled SCSS)
|
|
36
|
+
const cssFiles = readdirSync(srcStylesDir).filter(f => f.endsWith('.css'));
|
|
37
|
+
|
|
38
|
+
if (cssFiles.length === 0) {
|
|
39
|
+
await Bun.write(join(stylesOutDir, 'bertui.min.css'), '/* No CSS */');
|
|
40
|
+
return;
|
|
53
41
|
}
|
|
42
|
+
|
|
43
|
+
logger.info(`Processing ${cssFiles.length} CSS file(s)...`);
|
|
44
|
+
|
|
45
|
+
let combinedCSS = '';
|
|
46
|
+
const fileContents = [];
|
|
47
|
+
|
|
48
|
+
for (const cssFile of cssFiles) {
|
|
49
|
+
const srcPath = join(srcStylesDir, cssFile);
|
|
50
|
+
|
|
51
|
+
// Use file cache
|
|
52
|
+
const fileBuffer = await globalCache.getFile(srcPath, { logSpeed: true });
|
|
53
|
+
if (fileBuffer) {
|
|
54
|
+
const content = fileBuffer.toString('utf-8');
|
|
55
|
+
fileContents.push({ filename: cssFile, content });
|
|
56
|
+
combinedCSS += `/* ${cssFile} */\n${content}\n\n`;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const combinedPath = join(stylesOutDir, 'bertui.min.css');
|
|
61
|
+
|
|
62
|
+
// Minify with caching
|
|
63
|
+
const minifyCacheKey = `minify:${Buffer.from(combinedCSS).length}:${combinedCSS.substring(0, 100)}`;
|
|
64
|
+
let minified = globalCache.get(minifyCacheKey);
|
|
65
|
+
|
|
66
|
+
if (!minified) {
|
|
67
|
+
minified = await minifyCSS(combinedCSS, {
|
|
68
|
+
filename: 'bertui.min.css',
|
|
69
|
+
sourceMap: false
|
|
70
|
+
});
|
|
71
|
+
globalCache.set(minifyCacheKey, minified, { ttl: 60000 }); // Cache for 60 seconds
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
await Bun.write(combinedPath, minified);
|
|
75
|
+
|
|
76
|
+
const originalSize = Buffer.byteLength(combinedCSS);
|
|
77
|
+
const minifiedSize = Buffer.byteLength(minified);
|
|
78
|
+
const reduction = ((1 - minifiedSize / originalSize) * 100).toFixed(1);
|
|
79
|
+
|
|
80
|
+
const endTime = process.hrtime.bigint();
|
|
81
|
+
const duration = Number(endTime - startTime) / 1000; // Microseconds
|
|
82
|
+
|
|
83
|
+
logger.success(`CSS optimized: ${(originalSize/1024).toFixed(2)}KB → ${(minifiedSize/1024).toFixed(2)}KB (-${reduction}%)`);
|
|
84
|
+
logger.info(`⚡ Processing time: ${duration.toFixed(3)}µs`);
|
|
85
|
+
|
|
86
|
+
// Cache the final result
|
|
87
|
+
globalCache.set(cacheKey, {
|
|
88
|
+
files: cssFiles.length,
|
|
89
|
+
content: minified,
|
|
90
|
+
size: minifiedSize
|
|
91
|
+
}, { ttl: 5000 });
|
|
54
92
|
}
|
|
55
93
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
*/
|
|
59
|
-
async function minifyCSSSafe(css) {
|
|
60
|
-
// Try Lightning CSS first
|
|
94
|
+
// NEW: Process SCSS directory
|
|
95
|
+
async function processSCSSDirectory(stylesDir, root) {
|
|
61
96
|
try {
|
|
62
|
-
|
|
97
|
+
// Check if sass is installed
|
|
98
|
+
const sass = await import('sass').catch(() => null);
|
|
99
|
+
if (!sass) return;
|
|
63
100
|
|
|
64
|
-
const
|
|
65
|
-
|
|
66
|
-
code: Buffer.from(css),
|
|
67
|
-
minify: true,
|
|
68
|
-
sourceMap: false,
|
|
69
|
-
targets: {
|
|
70
|
-
chrome: 90 << 16,
|
|
71
|
-
firefox: 88 << 16,
|
|
72
|
-
safari: 14 << 16,
|
|
73
|
-
edge: 90 << 16
|
|
74
|
-
}
|
|
75
|
-
});
|
|
101
|
+
const files = readdirSync(stylesDir);
|
|
102
|
+
const scssFiles = files.filter(f => f.endsWith('.scss') || f.endsWith('.sass'));
|
|
76
103
|
|
|
77
|
-
|
|
104
|
+
if (scssFiles.length === 0) return;
|
|
78
105
|
|
|
79
|
-
|
|
80
|
-
logger.warn('Lightning CSS failed, using simple minification');
|
|
106
|
+
logger.info(`📝 Compiling ${scssFiles.length} SCSS files...`);
|
|
81
107
|
|
|
82
|
-
|
|
83
|
-
|
|
108
|
+
for (const file of scssFiles) {
|
|
109
|
+
const srcPath = join(stylesDir, file);
|
|
110
|
+
const cssPath = join(stylesDir, file.replace(/\.(scss|sass)$/, '.css'));
|
|
111
|
+
|
|
112
|
+
// Check cache
|
|
113
|
+
const fileBuffer = await globalCache.getFile(srcPath);
|
|
114
|
+
const cacheKey = `scss:${file}:${Buffer.from(fileBuffer).length}`;
|
|
115
|
+
const cached = globalCache.get(cacheKey);
|
|
116
|
+
|
|
117
|
+
if (cached && existsSync(cssPath)) {
|
|
118
|
+
logger.debug(`⚡ Cached SCSS: ${file} → ${file.replace(/\.(scss|sass)$/, '.css')}`);
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const result = sass.compile(srcPath, {
|
|
123
|
+
style: 'expanded',
|
|
124
|
+
sourceMap: false,
|
|
125
|
+
loadPaths: [stylesDir, join(root, 'node_modules')]
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
await Bun.write(cssPath, result.css);
|
|
129
|
+
globalCache.set(cacheKey, true, { ttl: 30000 });
|
|
130
|
+
|
|
131
|
+
logger.debug(` ${file} → ${file.replace(/\.(scss|sass)$/, '.css')}`);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
logger.success(`✅ SCSS compilation complete`);
|
|
135
|
+
} catch (error) {
|
|
136
|
+
logger.warn(`SCSS processing skipped: ${error.message}`);
|
|
84
137
|
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* Simple CSS minification without dependencies
|
|
89
|
-
*/
|
|
90
|
-
function simpleMinifyCSS(css) {
|
|
91
|
-
return css
|
|
92
|
-
// Remove comments
|
|
93
|
-
.replace(/\/\*[\s\S]*?\*\//g, '')
|
|
94
|
-
// Remove extra whitespace
|
|
95
|
-
.replace(/\s+/g, ' ')
|
|
96
|
-
// Remove space around { } : ; ,
|
|
97
|
-
.replace(/\s*([{}:;,])\s*/g, '$1')
|
|
98
|
-
// Remove trailing semicolons before }
|
|
99
|
-
.replace(/;}/g, '}')
|
|
100
|
-
// Remove leading/trailing whitespace
|
|
101
|
-
.trim();
|
|
102
138
|
}
|
package/src/build.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
// bertui/src/build.js -
|
|
1
|
+
// bertui/src/build.js - COMPLETE FIXED VERSION WITH PROPER EXIT
|
|
2
2
|
import { join } from 'path';
|
|
3
3
|
import { existsSync, mkdirSync, rmSync } from 'fs';
|
|
4
4
|
import logger from './logger/logger.js';
|
|
5
5
|
import { loadEnvVariables } from './utils/env.js';
|
|
6
|
+
import { globalCache } from './utils/cache.js';
|
|
6
7
|
|
|
7
8
|
import { compileForBuild } from './build/compiler/index.js';
|
|
8
9
|
import { buildAllCSS } from './build/processors/css-builder.js';
|
|
@@ -22,12 +23,12 @@ export async function buildProduction(options = {}) {
|
|
|
22
23
|
logger.bigLog('BUILDING WITH SERVER ISLANDS 🏝️', { color: 'green' });
|
|
23
24
|
logger.info('🔥 OPTIONAL SERVER CONTENT - THE GAME CHANGER');
|
|
24
25
|
|
|
25
|
-
if (existsSync(buildDir)) rmSync(buildDir, { recursive: true });
|
|
26
|
-
if (existsSync(outDir)) rmSync(outDir, { recursive: true });
|
|
26
|
+
if (existsSync(buildDir)) rmSync(buildDir, { recursive: true, force: true });
|
|
27
|
+
if (existsSync(outDir)) rmSync(outDir, { recursive: true, force: true });
|
|
27
28
|
mkdirSync(buildDir, { recursive: true });
|
|
28
29
|
mkdirSync(outDir, { recursive: true });
|
|
29
30
|
|
|
30
|
-
const startTime =
|
|
31
|
+
const startTime = process.hrtime.bigint(); // Microsecond precision
|
|
31
32
|
|
|
32
33
|
try {
|
|
33
34
|
logger.info('Step 0: Loading environment variables...');
|
|
@@ -48,53 +49,82 @@ export async function buildProduction(options = {}) {
|
|
|
48
49
|
})));
|
|
49
50
|
}
|
|
50
51
|
|
|
51
|
-
logger.info('Step 2:
|
|
52
|
+
logger.info('Step 2: Processing SCSS/SASS...');
|
|
53
|
+
await processSCSS(root, buildDir);
|
|
54
|
+
|
|
55
|
+
logger.info('Step 3: Combining CSS...');
|
|
52
56
|
await buildAllCSS(root, outDir);
|
|
53
57
|
|
|
54
|
-
logger.info('Step
|
|
58
|
+
logger.info('Step 4: Copying static assets...');
|
|
55
59
|
await copyAllStaticAssets(root, outDir);
|
|
56
60
|
|
|
57
|
-
logger.info('Step
|
|
61
|
+
logger.info('Step 5: Bundling JavaScript with Router...');
|
|
58
62
|
const buildEntry = join(buildDir, 'main.js');
|
|
63
|
+
const routerPath = join(buildDir, 'router.js');
|
|
59
64
|
|
|
60
65
|
if (!existsSync(buildEntry)) {
|
|
61
66
|
logger.error('❌ main.js not found in build directory!');
|
|
62
67
|
throw new Error('Build entry point missing');
|
|
63
68
|
}
|
|
64
69
|
|
|
65
|
-
const result = await bundleJavaScript(buildEntry, outDir, envVars, buildDir);
|
|
70
|
+
const result = await bundleJavaScript(buildEntry, routerPath, outDir, envVars, buildDir);
|
|
66
71
|
|
|
67
|
-
logger.info('Step
|
|
72
|
+
logger.info('Step 6: Generating HTML with Server Islands...');
|
|
68
73
|
await generateProductionHTML(root, outDir, result, routes, serverIslands, config);
|
|
69
74
|
|
|
70
|
-
logger.info('Step
|
|
75
|
+
logger.info('Step 7: Generating sitemap.xml...');
|
|
71
76
|
await generateSitemap(routes, config, outDir);
|
|
72
77
|
|
|
73
|
-
logger.info('Step
|
|
78
|
+
logger.info('Step 8: Generating robots.txt...');
|
|
74
79
|
await generateRobots(config, outDir, routes);
|
|
75
80
|
|
|
76
|
-
|
|
81
|
+
// Clean up build directory
|
|
82
|
+
if (existsSync(buildDir)) rmSync(buildDir, { recursive: true, force: true });
|
|
83
|
+
|
|
84
|
+
const endTime = process.hrtime.bigint();
|
|
85
|
+
const durationMicro = Number(endTime - startTime) / 1000;
|
|
86
|
+
const durationMs = durationMicro / 1000;
|
|
77
87
|
|
|
78
|
-
|
|
79
|
-
|
|
88
|
+
showBuildSummary(routes, serverIslands, clientRoutes, durationMs, durationMicro);
|
|
89
|
+
|
|
90
|
+
// ✅ FIX: Force exit after successful build
|
|
91
|
+
// Small delay to ensure all logs are flushed
|
|
92
|
+
setTimeout(() => {
|
|
93
|
+
logger.info('✅ Build process complete, exiting...');
|
|
94
|
+
process.exit(0);
|
|
95
|
+
}, 100);
|
|
80
96
|
|
|
81
97
|
} catch (error) {
|
|
82
98
|
logger.error(`Build failed: ${error.message}`);
|
|
83
99
|
if (error.stack) logger.error(error.stack);
|
|
84
|
-
if (existsSync(buildDir)) rmSync(buildDir, { recursive: true });
|
|
85
|
-
|
|
100
|
+
if (existsSync(buildDir)) rmSync(buildDir, { recursive: true, force: true });
|
|
101
|
+
|
|
102
|
+
// ✅ FIX: Force exit with error code
|
|
103
|
+
setTimeout(() => {
|
|
104
|
+
process.exit(1);
|
|
105
|
+
}, 100);
|
|
86
106
|
}
|
|
87
107
|
}
|
|
88
108
|
|
|
89
|
-
async function bundleJavaScript(buildEntry, outDir, envVars, buildDir) {
|
|
109
|
+
async function bundleJavaScript(buildEntry, routerPath, outDir, envVars, buildDir) {
|
|
90
110
|
try {
|
|
111
|
+
const hasRouter = existsSync(routerPath);
|
|
112
|
+
|
|
91
113
|
const originalCwd = process.cwd();
|
|
92
114
|
process.chdir(buildDir);
|
|
93
115
|
|
|
94
116
|
logger.info('🔧 Bundling with production JSX...');
|
|
95
117
|
|
|
118
|
+
const entrypoints = [buildEntry];
|
|
119
|
+
if (hasRouter) {
|
|
120
|
+
entrypoints.push(routerPath);
|
|
121
|
+
logger.success('✅ Router included in bundle');
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
logger.info(`📦 Entry points: ${entrypoints.map(e => e.split('/').pop()).join(', ')}`);
|
|
125
|
+
|
|
96
126
|
const result = await Bun.build({
|
|
97
|
-
entrypoints
|
|
127
|
+
entrypoints,
|
|
98
128
|
outdir: join(outDir, 'assets'),
|
|
99
129
|
target: 'browser',
|
|
100
130
|
minify: true,
|
|
@@ -129,16 +159,38 @@ async function bundleJavaScript(buildEntry, outDir, envVars, buildDir) {
|
|
|
129
159
|
if (log.position) {
|
|
130
160
|
logger.error(` File: ${log.position.file || 'unknown'}`);
|
|
131
161
|
logger.error(` Line: ${log.position.line || 'unknown'}`);
|
|
162
|
+
logger.error(` Column: ${log.position.column || 'unknown'}`);
|
|
163
|
+
}
|
|
164
|
+
if (log.position && log.position.file && existsSync(log.position.file)) {
|
|
165
|
+
try {
|
|
166
|
+
const fileContent = Bun.file(log.position.file).text();
|
|
167
|
+
const lines = fileContent.split('\n');
|
|
168
|
+
const line = lines[log.position.line - 1];
|
|
169
|
+
if (line) {
|
|
170
|
+
logger.error(` Code: ${line.trim()}`);
|
|
171
|
+
logger.error(` ${' '.repeat(log.position.column - 1)}^`);
|
|
172
|
+
}
|
|
173
|
+
} catch (e) {}
|
|
132
174
|
}
|
|
133
175
|
});
|
|
176
|
+
} else {
|
|
177
|
+
logger.error('No detailed logs available');
|
|
134
178
|
}
|
|
135
179
|
|
|
136
180
|
throw new Error('JavaScript bundling failed');
|
|
137
181
|
}
|
|
138
182
|
|
|
183
|
+
const entryPoints = result.outputs.filter(o => o.kind === 'entry-point');
|
|
184
|
+
const chunks = result.outputs.filter(o => o.kind === 'chunk');
|
|
185
|
+
|
|
139
186
|
logger.success('✅ JavaScript bundled successfully');
|
|
140
|
-
logger.info(` Entry points: ${
|
|
141
|
-
logger.info(` Chunks: ${
|
|
187
|
+
logger.info(` Entry points: ${entryPoints.length}`);
|
|
188
|
+
logger.info(` Chunks: ${chunks.length}`);
|
|
189
|
+
|
|
190
|
+
result.outputs.forEach(output => {
|
|
191
|
+
const size = (output.size / 1024).toFixed(2);
|
|
192
|
+
logger.debug(` 📄 ${output.path.split('/').pop()} (${size} KB)`);
|
|
193
|
+
});
|
|
142
194
|
|
|
143
195
|
const totalSize = result.outputs.reduce((sum, o) => sum + (o.size || 0), 0);
|
|
144
196
|
logger.info(` Total size: ${(totalSize / 1024).toFixed(2)} KB`);
|
|
@@ -147,12 +199,59 @@ async function bundleJavaScript(buildEntry, outDir, envVars, buildDir) {
|
|
|
147
199
|
|
|
148
200
|
} catch (error) {
|
|
149
201
|
logger.error('❌ Bundling error: ' + error.message);
|
|
202
|
+
if (error.stack) {
|
|
203
|
+
logger.error('Stack trace:');
|
|
204
|
+
logger.error(error.stack);
|
|
205
|
+
}
|
|
150
206
|
throw error;
|
|
151
207
|
}
|
|
152
208
|
}
|
|
153
209
|
|
|
154
|
-
function
|
|
155
|
-
|
|
210
|
+
async function processSCSS(root, buildDir) {
|
|
211
|
+
const srcStylesDir = join(root, 'src', 'styles');
|
|
212
|
+
if (!existsSync(srcStylesDir)) return;
|
|
213
|
+
|
|
214
|
+
try {
|
|
215
|
+
const sass = await import('sass').catch(() => {
|
|
216
|
+
logger.warn('⚠️ sass package not installed. Install with: bun add sass');
|
|
217
|
+
return null;
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
if (!sass) return;
|
|
221
|
+
|
|
222
|
+
const { readdirSync } = await import('fs');
|
|
223
|
+
const scssFiles = readdirSync(srcStylesDir).filter(f =>
|
|
224
|
+
f.endsWith('.scss') || f.endsWith('.sass')
|
|
225
|
+
);
|
|
226
|
+
|
|
227
|
+
if (scssFiles.length === 0) return;
|
|
228
|
+
|
|
229
|
+
logger.info(`📝 Processing ${scssFiles.length} SCSS/SASS files...`);
|
|
230
|
+
|
|
231
|
+
for (const file of scssFiles) {
|
|
232
|
+
const srcPath = join(srcStylesDir, file);
|
|
233
|
+
const cssPath = join(buildDir, 'styles', file.replace(/\.(scss|sass)$/, '.css'));
|
|
234
|
+
|
|
235
|
+
mkdirSync(join(buildDir, 'styles'), { recursive: true });
|
|
236
|
+
|
|
237
|
+
const result = sass.compile(srcPath, {
|
|
238
|
+
style: 'compressed',
|
|
239
|
+
sourceMap: false,
|
|
240
|
+
loadPaths: [srcStylesDir, join(root, 'node_modules')]
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
await Bun.write(cssPath, result.css);
|
|
244
|
+
logger.debug(` ${file} → ${file.replace(/\.(scss|sass)$/, '.css')}`);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
logger.success(`✅ Processed ${scssFiles.length} SCSS files`);
|
|
248
|
+
} catch (error) {
|
|
249
|
+
logger.error(`SCSS processing failed: ${error.message}`);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
function showBuildSummary(routes, serverIslands, clientRoutes, durationMs, durationMicro) {
|
|
254
|
+
logger.success(`✨ Build complete in ${durationMs.toFixed(3)}ms (${durationMicro.toFixed(0)}µs)`);
|
|
156
255
|
logger.bigLog('BUILD SUMMARY', { color: 'green' });
|
|
157
256
|
logger.info(`📄 Total routes: ${routes.length}`);
|
|
158
257
|
logger.info(`🏝️ Server Islands (SSG): ${serverIslands.length}`);
|
|
@@ -160,9 +259,15 @@ function showBuildSummary(routes, serverIslands, clientRoutes, duration) {
|
|
|
160
259
|
logger.info(`🗺️ Sitemap: dist/sitemap.xml`);
|
|
161
260
|
logger.info(`🤖 robots.txt: dist/robots.txt`);
|
|
162
261
|
|
|
262
|
+
const cacheStats = globalCache.getStats();
|
|
263
|
+
logger.info(`📊 Cache: ${cacheStats.hitRate} hit rate (${cacheStats.hits}/${cacheStats.hits + cacheStats.misses})`);
|
|
264
|
+
|
|
163
265
|
if (serverIslands.length > 0) {
|
|
164
266
|
logger.success('✅ Server Islands enabled - INSTANT content delivery!');
|
|
165
267
|
}
|
|
166
268
|
|
|
167
269
|
logger.bigLog('READY TO DEPLOY 🚀', { color: 'green' });
|
|
270
|
+
|
|
271
|
+
// ✅ Force log flush
|
|
272
|
+
logger.debug('Build complete, exiting...');
|
|
168
273
|
}
|