bertui 1.1.4 → 1.1.5

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bertui",
3
- "version": "1.1.4",
3
+ "version": "1.1.5",
4
4
  "description": "Lightning-fast React dev server powered by Bun and Elysia - Now with TypeScript support!",
5
5
  "type": "module",
6
6
  "main": "./index.js",
@@ -1,4 +1,4 @@
1
- // bertui/src/build/generators/html-generator.js - FIXED JSX RUNTIME
1
+ // bertui/src/build/generators/html-generator.js - FIXED CSS BUILD
2
2
  import { join, relative } from 'path';
3
3
  import { mkdirSync, existsSync, cpSync } from 'fs';
4
4
  import logger from '../../logger/logger.js';
@@ -17,53 +17,75 @@ export async function generateProductionHTML(root, outDir, buildResult, routes,
17
17
  const bundlePath = relative(outDir, mainBundle.path).replace(/\\/g, '/');
18
18
  const defaultMeta = config.meta || {};
19
19
 
20
- // ✅ FIX: Check if bertui-icons is installed and copy to dist/
21
- const bertuiIconsInstalled = await copyBertuiIconsToProduction(root, outDir);
20
+ // ✅ Copy bertui-icons AND bertui-animate to dist/
21
+ const bertuiPackages = await copyBertuiPackagesToProduction(root, outDir);
22
22
 
23
23
  logger.info(`📄 Generating HTML for ${routes.length} routes...`);
24
24
 
25
- // Process in batches to avoid Bun crashes
26
25
  const BATCH_SIZE = 5;
27
26
 
28
27
  for (let i = 0; i < routes.length; i += BATCH_SIZE) {
29
28
  const batch = routes.slice(i, i + BATCH_SIZE);
30
29
  logger.debug(`Processing batch ${Math.floor(i/BATCH_SIZE) + 1}/${Math.ceil(routes.length/BATCH_SIZE)}`);
31
30
 
32
- // Process batch sequentially
33
31
  for (const route of batch) {
34
- await processSingleRoute(route, serverIslands, config, defaultMeta, bundlePath, outDir, bertuiIconsInstalled);
32
+ await processSingleRoute(route, serverIslands, config, defaultMeta, bundlePath, outDir, bertuiPackages);
35
33
  }
36
34
  }
37
35
 
38
36
  logger.success(`✅ HTML generation complete for ${routes.length} routes`);
39
37
  }
40
38
 
41
- // ✅ NEW: Copy bertui-icons to dist/ for production
42
- async function copyBertuiIconsToProduction(root, outDir) {
39
+ // ✅ UPDATED: Copy ALL bertui-* packages to dist/
40
+ async function copyBertuiPackagesToProduction(root, outDir) {
43
41
  const nodeModulesDir = join(root, 'node_modules');
44
- const bertuiIconsSource = join(nodeModulesDir, 'bertui-icons');
42
+ const packages = {
43
+ bertuiIcons: false,
44
+ bertuiAnimate: false
45
+ };
45
46
 
46
- if (!existsSync(bertuiIconsSource)) {
47
- logger.debug('bertui-icons not installed, skipping...');
48
- return false;
47
+ if (!existsSync(nodeModulesDir)) {
48
+ logger.debug('node_modules not found, skipping package copy');
49
+ return packages;
49
50
  }
50
51
 
51
- try {
52
- const bertuiIconsDest = join(outDir, 'node_modules', 'bertui-icons');
53
- mkdirSync(join(outDir, 'node_modules'), { recursive: true });
54
-
55
- // Copy the entire bertui-icons package
56
- cpSync(bertuiIconsSource, bertuiIconsDest, { recursive: true });
57
-
58
- logger.success('✅ Copied bertui-icons to dist/node_modules/');
59
- return true;
60
- } catch (error) {
61
- logger.error(`Failed to copy bertui-icons: ${error.message}`);
62
- return false;
52
+ // Copy bertui-icons
53
+ const bertuiIconsSource = join(nodeModulesDir, 'bertui-icons');
54
+ if (existsSync(bertuiIconsSource)) {
55
+ try {
56
+ const bertuiIconsDest = join(outDir, 'node_modules', 'bertui-icons');
57
+ mkdirSync(join(outDir, 'node_modules'), { recursive: true });
58
+ cpSync(bertuiIconsSource, bertuiIconsDest, { recursive: true });
59
+ logger.success('✅ Copied bertui-icons to dist/node_modules/');
60
+ packages.bertuiIcons = true;
61
+ } catch (error) {
62
+ logger.error(`Failed to copy bertui-icons: ${error.message}`);
63
+ }
63
64
  }
65
+
66
+ // ✅ NEW: Copy ONLY bertui-animate CSS files (not the whole package)
67
+ const bertuiAnimateSource = join(nodeModulesDir, 'bertui-animate', 'dist');
68
+ if (existsSync(bertuiAnimateSource)) {
69
+ try {
70
+ const bertuiAnimateDest = join(outDir, 'css');
71
+ mkdirSync(bertuiAnimateDest, { recursive: true });
72
+
73
+ // Copy minified CSS
74
+ const minCSSPath = join(bertuiAnimateSource, 'bertui-animate.min.css');
75
+ if (existsSync(minCSSPath)) {
76
+ cpSync(minCSSPath, join(bertuiAnimateDest, 'bertui-animate.min.css'));
77
+ logger.success('✅ Copied bertui-animate.min.css to dist/css/');
78
+ packages.bertuiAnimate = true;
79
+ }
80
+ } catch (error) {
81
+ logger.error(`Failed to copy bertui-animate: ${error.message}`);
82
+ }
83
+ }
84
+
85
+ return packages;
64
86
  }
65
87
 
66
- async function processSingleRoute(route, serverIslands, config, defaultMeta, bundlePath, outDir, bertuiIconsInstalled) {
88
+ async function processSingleRoute(route, serverIslands, config, defaultMeta, bundlePath, outDir, bertuiPackages) {
67
89
  try {
68
90
  const sourceCode = await Bun.file(route.path).text();
69
91
  const pageMeta = extractMetaFromSource(sourceCode);
@@ -84,7 +106,7 @@ async function processSingleRoute(route, serverIslands, config, defaultMeta, bun
84
106
  }
85
107
  }
86
108
 
87
- const html = generateHTML(meta, route, bundlePath, staticHTML, isServerIsland, bertuiIconsInstalled);
109
+ const html = generateHTML(meta, route, bundlePath, staticHTML, isServerIsland, bertuiPackages);
88
110
 
89
111
  let htmlPath;
90
112
  if (route.route === '/') {
@@ -239,8 +261,8 @@ async function extractStaticHTMLFromComponent(sourceCode, filePath) {
239
261
  }
240
262
  }
241
263
 
242
- // ✅ FIXED: Add jsx-runtime to import map
243
- function generateHTML(meta, route, bundlePath, staticHTML = '', isServerIsland = false, bertuiIconsInstalled = false) {
264
+ // ✅ UPDATED: Add bertui-animate CSS to production HTML
265
+ function generateHTML(meta, route, bundlePath, staticHTML = '', isServerIsland = false, bertuiPackages = {}) {
244
266
  const rootContent = staticHTML
245
267
  ? `<div id="root">${staticHTML}</div>`
246
268
  : '<div id="root"></div>';
@@ -249,11 +271,16 @@ function generateHTML(meta, route, bundlePath, staticHTML = '', isServerIsland =
249
271
  ? '<!-- 🏝️ Server Island: Static content rendered at build time -->'
250
272
  : '<!-- ⚡ Client-only: Content rendered by JavaScript -->';
251
273
 
252
- // ✅ FIX: Add jsx-runtime to import map for production
253
- const bertuiIconsImport = bertuiIconsInstalled
274
+ // ✅ Add bertui-icons to import map
275
+ const bertuiIconsImport = bertuiPackages.bertuiIcons
254
276
  ? ',\n "bertui-icons": "/node_modules/bertui-icons/generated/index.js"'
255
277
  : '';
256
278
 
279
+ // ✅ Add bertui-animate CSS link
280
+ const bertuiAnimateCSS = bertuiPackages.bertuiAnimate
281
+ ? ' <link rel="stylesheet" href="/css/bertui-animate.min.css">'
282
+ : '';
283
+
257
284
  return `<!DOCTYPE html>
258
285
  <html lang="${meta.lang || 'en'}">
259
286
  <head>
@@ -271,6 +298,7 @@ function generateHTML(meta, route, bundlePath, staticHTML = '', isServerIsland =
271
298
  ${meta.ogImage ? `<meta property="og:image" content="${meta.ogImage}">` : ''}
272
299
 
273
300
  <link rel="stylesheet" href="/styles/bertui.min.css">
301
+ ${bertuiAnimateCSS}
274
302
  <link rel="icon" type="image/svg+xml" href="/favicon.svg">
275
303
 
276
304
  <script type="importmap">
@@ -1,4 +1,4 @@
1
- // bertui/src/server/dev-server.js - FIXED: Bun Native HMR
1
+ // bertui/src/server/dev-server.js - FIXED: CSS Module Loading
2
2
  import { join, extname, dirname } from 'path';
3
3
  import { existsSync, readdirSync } from 'fs';
4
4
  import logger from '../logger/logger.js';
@@ -15,7 +15,6 @@ export async function startDevServer(options = {}) {
15
15
 
16
16
  const config = await loadConfig(root);
17
17
 
18
- // ✅ Track connected WebSocket clients
19
18
  const clients = new Set();
20
19
 
21
20
  let hasRouter = false;
@@ -25,30 +24,29 @@ export async function startDevServer(options = {}) {
25
24
  logger.info('File-based routing enabled');
26
25
  }
27
26
 
28
- // ✅ Use Bun.serve() with WebSocket support
29
27
  const server = Bun.serve({
30
28
  port,
31
29
 
32
30
  async fetch(req, server) {
33
31
  const url = new URL(req.url);
34
32
 
35
- // WebSocket upgrade for HMR
33
+ // WebSocket upgrade for HMR
36
34
  if (url.pathname === '/__hmr') {
37
35
  const success = server.upgrade(req);
38
36
  if (success) {
39
- return undefined; // Don't return a Response
37
+ return undefined;
40
38
  }
41
39
  return new Response('WebSocket upgrade failed', { status: 500 });
42
40
  }
43
41
 
44
- // Serve HTML
42
+ // Serve HTML
45
43
  if (url.pathname === '/' || (!url.pathname.includes('.') && !url.pathname.startsWith('/compiled'))) {
46
44
  return new Response(await serveHTML(root, hasRouter, config, port), {
47
45
  headers: { 'Content-Type': 'text/html' }
48
46
  });
49
47
  }
50
48
 
51
- // Serve compiled JavaScript
49
+ // Serve compiled JavaScript
52
50
  if (url.pathname.startsWith('/compiled/')) {
53
51
  const filepath = join(compiledDir, url.pathname.replace('/compiled/', ''));
54
52
  const file = Bun.file(filepath);
@@ -66,7 +64,22 @@ export async function startDevServer(options = {}) {
66
64
  }
67
65
  }
68
66
 
69
- // ✅ Serve CSS
67
+ // ✅ Serve bertui-animate CSS
68
+ if (url.pathname === '/bertui-animate.css') {
69
+ const bertuiAnimatePath = join(root, 'node_modules/bertui-animate/dist/bertui-animate.min.css');
70
+ const file = Bun.file(bertuiAnimatePath);
71
+
72
+ if (await file.exists()) {
73
+ return new Response(file, {
74
+ headers: {
75
+ 'Content-Type': 'text/css',
76
+ 'Cache-Control': 'no-store'
77
+ }
78
+ });
79
+ }
80
+ }
81
+
82
+ // Serve CSS
70
83
  if (url.pathname.startsWith('/styles/')) {
71
84
  const filepath = join(stylesDir, url.pathname.replace('/styles/', ''));
72
85
  const file = Bun.file(filepath);
@@ -81,7 +94,7 @@ export async function startDevServer(options = {}) {
81
94
  }
82
95
  }
83
96
 
84
- // Serve images from src/images/
97
+ // Serve images from src/images/
85
98
  if (url.pathname.startsWith('/images/')) {
86
99
  const filepath = join(srcDir, 'images', url.pathname.replace('/images/', ''));
87
100
  const file = Bun.file(filepath);
@@ -99,7 +112,7 @@ export async function startDevServer(options = {}) {
99
112
  }
100
113
  }
101
114
 
102
- // Serve from public/
115
+ // Serve from public/
103
116
  if (url.pathname.startsWith('/public/')) {
104
117
  const filepath = join(publicDir, url.pathname.replace('/public/', ''));
105
118
  const file = Bun.file(filepath);
@@ -111,14 +124,23 @@ export async function startDevServer(options = {}) {
111
124
  }
112
125
  }
113
126
 
114
- // ✅ Serve node_modules (for bertui-* packages)
127
+ // ✅ FIX: Serve node_modules with proper MIME types
115
128
  if (url.pathname.startsWith('/node_modules/')) {
116
129
  const filepath = join(root, 'node_modules', url.pathname.replace('/node_modules/', ''));
117
130
  const file = Bun.file(filepath);
118
131
 
119
132
  if (await file.exists()) {
120
133
  const ext = extname(filepath).toLowerCase();
121
- const contentType = ext === '.js' ? 'application/javascript; charset=utf-8' : getContentType(ext);
134
+
135
+ // ✅ Proper MIME type detection
136
+ let contentType;
137
+ if (ext === '.css') {
138
+ contentType = 'text/css';
139
+ } else if (ext === '.js' || ext === '.mjs') {
140
+ contentType = 'application/javascript; charset=utf-8';
141
+ } else {
142
+ contentType = getContentType(ext);
143
+ }
122
144
 
123
145
  return new Response(file, {
124
146
  headers: {
@@ -132,7 +154,6 @@ export async function startDevServer(options = {}) {
132
154
  return new Response('Not found', { status: 404 });
133
155
  },
134
156
 
135
- // ✅ WebSocket handler for HMR
136
157
  websocket: {
137
158
  open(ws) {
138
159
  clients.add(ws);
@@ -156,7 +177,6 @@ export async function startDevServer(options = {}) {
156
177
  logger.info(`📦 Public: /public/* → public/`);
157
178
  logger.info(`⚡ BertUI Packages: /node_modules/* → node_modules/`);
158
179
 
159
- // ✅ Setup file watcher
160
180
  setupFileWatcher(root, compiledDir, clients, async () => {
161
181
  hasRouter = existsSync(join(compiledDir, 'router.js'));
162
182
  });
@@ -179,6 +199,14 @@ async function serveHTML(root, hasRouter, config, port) {
179
199
  }
180
200
  }
181
201
 
202
+ // ✅ FIX: Auto-detect bertui-animate CSS (bundled version)
203
+ let bertuiAnimateStylesheet = '';
204
+ const bertuiAnimatePath = join(root, 'node_modules/bertui-animate/dist/bertui-animate.min.css');
205
+ if (existsSync(bertuiAnimatePath)) {
206
+ bertuiAnimateStylesheet = ' <link rel="stylesheet" href="/bertui-animate.css">';
207
+ logger.info('✅ bertui-animate detected');
208
+ }
209
+
182
210
  // Build import map
183
211
  const importMap = {
184
212
  "react": "https://esm.sh/react@18.2.0",
@@ -186,7 +214,7 @@ async function serveHTML(root, hasRouter, config, port) {
186
214
  "react-dom/client": "https://esm.sh/react-dom@18.2.0/client"
187
215
  };
188
216
 
189
- // Auto-detect bertui-* packages
217
+ // Auto-detect bertui-* JavaScript packages
190
218
  const nodeModulesDir = join(root, 'node_modules');
191
219
 
192
220
  if (existsSync(nodeModulesDir)) {
@@ -255,6 +283,7 @@ async function serveHTML(root, hasRouter, config, port) {
255
283
  <link rel="icon" type="image/svg+xml" href="/public/favicon.svg">
256
284
 
257
285
  ${userStylesheets}
286
+ ${bertuiAnimateStylesheet}
258
287
 
259
288
  <script type="importmap">
260
289
  ${JSON.stringify({ imports: importMap }, null, 2)}
@@ -274,9 +303,7 @@ ${userStylesheets}
274
303
  <body>
275
304
  <div id="root"></div>
276
305
 
277
- <!-- ✅ Bun Native HMR Script -->
278
306
  <script type="module">
279
- // WebSocket-based HMR (no polling!)
280
307
  const ws = new WebSocket('ws://localhost:${port}/__hmr');
281
308
 
282
309
  ws.onopen = () => {
@@ -358,7 +385,6 @@ function getContentType(ext) {
358
385
  return types[ext] || 'text/plain';
359
386
  }
360
387
 
361
- // ✅ File watcher with proper debouncing
362
388
  function setupFileWatcher(root, compiledDir, clients, onRecompile) {
363
389
  const srcDir = join(root, 'src');
364
390
  const configPath = join(root, 'bertui.config.js');
@@ -374,7 +400,6 @@ function setupFileWatcher(root, compiledDir, clients, onRecompile) {
374
400
  let recompileTimeout = null;
375
401
  const watchedExtensions = ['.js', '.jsx', '.ts', '.tsx', '.css', '.png', '.jpg', '.jpeg', '.gif', '.svg', '.webp', '.avif'];
376
402
 
377
- // Notify all clients
378
403
  function notifyClients(message) {
379
404
  for (const client of clients) {
380
405
  try {
@@ -395,7 +420,6 @@ function setupFileWatcher(root, compiledDir, clients, onRecompile) {
395
420
 
396
421
  logger.info(`📝 File changed: ${filename}`);
397
422
 
398
- // ✅ Clear previous timeout and debounce
399
423
  clearTimeout(recompileTimeout);
400
424
 
401
425
  recompileTimeout = setTimeout(async () => {
@@ -414,7 +438,6 @@ function setupFileWatcher(root, compiledDir, clients, onRecompile) {
414
438
  logger.success('✅ Recompiled successfully');
415
439
  notifyClients({ type: 'compiled' });
416
440
 
417
- // ✅ Wait 100ms before triggering reload (let compilation finish)
418
441
  setTimeout(() => {
419
442
  notifyClients({ type: 'reload' });
420
443
  }, 100);
@@ -424,10 +447,9 @@ function setupFileWatcher(root, compiledDir, clients, onRecompile) {
424
447
  } finally {
425
448
  isRecompiling = false;
426
449
  }
427
- }, 150); // ✅ Debounce: wait 150ms for multiple file changes
450
+ }, 150);
428
451
  });
429
452
 
430
- // Watch config file
431
453
  if (existsSync(configPath)) {
432
454
  watch(configPath, async (eventType) => {
433
455
  if (eventType === 'change') {