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 CHANGED
@@ -13,7 +13,7 @@ Zero configuration. 494ms dev server. 265ms builds. Perfect SEO with Server Isla
13
13
  [![Rust](https://img.shields.io/badge/optimizer-Rust-WASM-orange)](https://www.rust-lang.org)
14
14
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
15
15
 
16
- ```bash
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
- ✅ Generates static HTML for instant loading
109
+ ✅ Generates static HTML for instant loading
110
110
 
111
- ✅ Auto-adds to sitemap.xml
111
+ ✅ Auto-adds to sitemap.xml
112
112
 
113
- ✅ Perfect SEO without SSR complexity
113
+ ✅ Perfect SEO without SSR complexity
114
114
 
115
- ✅ Still builds in 265ms
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
- 🔄 bertui-elysia - Full-stack addon (API routes, auth, database)
244
+ 🔄 bertui-elysia - Full-stack addon (API routes, auth, database)
245
245
 
246
- 🎨 bertui-animation - GPU-accelerated animations (Zig)
246
+ 🎨 bertui-animation - GPU-accelerated animations (Zig)
247
247
 
248
- 📊 bertui-charts - High-performance charts (Rust)
248
+ 📊 bertui-charts - High-performance charts (Rust)
249
249
 
250
250
  🙏 Credits
251
251
 
252
- Runtime: Bun - The fastest JavaScript runtime
252
+ Runtime: Bun - The fastest JavaScript runtime
253
253
 
254
- Server: Elysia - Fast and elegant web framework
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
- // bertui/src/index.js - ONE EXPORT FILE TO RULE THEM ALL
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 - RUST WASM (no Rust for users)
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.jsx';
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 (backward compatibility)
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.7",
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": "./src/index.js",
15
- "default": "./src/index.js"
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 'Skipping wasm build'",
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.2.3"
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 - SAFE VERSION
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
- if (existsSync(srcStylesDir)) {
13
- const cssFiles = readdirSync(srcStylesDir).filter(f => f.endsWith('.css'));
14
-
15
- if (cssFiles.length === 0) {
16
- await Bun.write(join(stylesOutDir, 'bertui.min.css'), '/* No CSS */');
17
- return;
18
- }
19
-
20
- logger.info(`Processing ${cssFiles.length} CSS file(s)...`);
21
-
22
- let combinedCSS = '';
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
- * Safe CSS minification with fallback
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
- const { transform } = await import('lightningcss');
97
+ // Check if sass is installed
98
+ const sass = await import('sass').catch(() => null);
99
+ if (!sass) return;
63
100
 
64
- const { code } = transform({
65
- filename: 'styles.css',
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
- return code.toString();
104
+ if (scssFiles.length === 0) return;
78
105
 
79
- } catch (lightningError) {
80
- logger.warn('Lightning CSS failed, using simple minification');
106
+ logger.info(`📝 Compiling ${scssFiles.length} SCSS files...`);
81
107
 
82
- // Fallback: Simple manual minification
83
- return simpleMinifyCSS(css);
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 - CLEANED (No PageBuilder)
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 = Date.now();
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: Combining CSS...');
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 3: Copying static assets...');
58
+ logger.info('Step 4: Copying static assets...');
55
59
  await copyAllStaticAssets(root, outDir);
56
60
 
57
- logger.info('Step 4: Bundling JavaScript...');
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 5: Generating HTML with Server Islands...');
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 6: Generating sitemap.xml...');
75
+ logger.info('Step 7: Generating sitemap.xml...');
71
76
  await generateSitemap(routes, config, outDir);
72
77
 
73
- logger.info('Step 7: Generating robots.txt...');
78
+ logger.info('Step 8: Generating robots.txt...');
74
79
  await generateRobots(config, outDir, routes);
75
80
 
76
- if (existsSync(buildDir)) rmSync(buildDir, { recursive: true });
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
- const duration = Date.now() - startTime;
79
- showBuildSummary(routes, serverIslands, clientRoutes, duration);
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
- process.exit(1);
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: [buildEntry],
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: ${result.outputs.filter(o => o.kind === 'entry-point').length}`);
141
- logger.info(` Chunks: ${result.outputs.filter(o => o.kind === 'chunk').length}`);
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 showBuildSummary(routes, serverIslands, clientRoutes, duration) {
155
- logger.success(`✨ Build complete in ${duration}ms`);
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
  }