bertui 0.2.3 → 0.2.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/index.js CHANGED
@@ -32,5 +32,5 @@ export default {
32
32
  buildCSS,
33
33
  copyCSS,
34
34
  program,
35
- version: "0.2.1"
35
+ version: "0.2.5"
36
36
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bertui",
3
- "version": "0.2.3",
3
+ "version": "0.2.5",
4
4
  "description": "Lightning-fast React dev server powered by Bun and Elysia",
5
5
  "type": "module",
6
6
  "main": "./index.js",
@@ -111,7 +111,7 @@ async function generateRouter(routes, outDir, root) {
111
111
  return ` { path: '${route.route}', component: ${componentName}, type: '${route.type}' }`;
112
112
  }).join(',\n');
113
113
 
114
- const routerComponentCode = `import { useState, useEffect, createContext, useContext, createElement } from 'react';
114
+ const routerComponentCode = `import React, { useState, useEffect, createContext, useContext } from 'react';
115
115
 
116
116
  const RouterContext = createContext(null);
117
117
 
@@ -185,10 +185,10 @@ export function Router({ routes }) {
185
185
 
186
186
  const Component = currentRoute?.component;
187
187
 
188
- return createElement(
188
+ return React.createElement(
189
189
  RouterContext.Provider,
190
190
  { value: routerValue },
191
- Component ? createElement(Component, { params }) : createElement(NotFound, null)
191
+ Component ? React.createElement(Component, { params }) : React.createElement(NotFound, null)
192
192
  );
193
193
  }
194
194
 
@@ -200,11 +200,11 @@ export function Link({ to, children, ...props }) {
200
200
  navigate(to);
201
201
  }
202
202
 
203
- return createElement('a', { href: to, onClick: handleClick, ...props }, children);
203
+ return React.createElement('a', { href: to, onClick: handleClick, ...props }, children);
204
204
  }
205
205
 
206
206
  function NotFound() {
207
- return createElement(
207
+ return React.createElement(
208
208
  'div',
209
209
  {
210
210
  style: {
@@ -216,9 +216,9 @@ function NotFound() {
216
216
  fontFamily: 'system-ui'
217
217
  }
218
218
  },
219
- createElement('h1', { style: { fontSize: '6rem', margin: 0 } }, '404'),
220
- createElement('p', { style: { fontSize: '1.5rem', color: '#666' } }, 'Page not found'),
221
- createElement('a', {
219
+ React.createElement('h1', { style: { fontSize: '6rem', margin: 0 } }, '404'),
220
+ React.createElement('p', { style: { fontSize: '1.5rem', color: '#666' } }, 'Page not found'),
221
+ React.createElement('a', {
222
222
  href: '/',
223
223
  style: { color: '#10b981', textDecoration: 'none', fontSize: '1.2rem' }
224
224
  }, 'Go home')
@@ -290,13 +290,18 @@ async function compileFile(srcPath, outDir, filename, relativePath) {
290
290
  loader,
291
291
  tsconfig: {
292
292
  compilerOptions: {
293
- jsx: 'react-jsx',
294
- jsxImportSource: 'react'
293
+ jsx: 'react',
294
+ jsxFactory: 'React.createElement',
295
+ jsxFragmentFactory: 'React.Fragment'
295
296
  }
296
297
  }
297
298
  });
298
299
  let compiled = await transpiler.transform(code);
299
300
 
301
+ if (!compiled.includes('import React') && (compiled.includes('React.createElement') || compiled.includes('React.Fragment'))) {
302
+ compiled = `import React from 'react';\n${compiled}`;
303
+ }
304
+
300
305
  compiled = fixRelativeImports(compiled);
301
306
 
302
307
  const outFilename = filename.replace(/\.(jsx|tsx|ts)$/, '.js');
@@ -4,12 +4,15 @@ import { join, extname } from 'path';
4
4
  import { existsSync } from 'fs';
5
5
  import logger from '../logger/logger.js';
6
6
  import { compileProject } from '../client/compiler.js';
7
+ import { loadConfig } from '../config/loadConfig.js';
7
8
 
8
9
  export async function startDevServer(options = {}) {
9
10
  const port = parseInt(options.port) || 3000;
10
11
  const root = options.root || process.cwd();
11
12
  const compiledDir = join(root, '.bertui', 'compiled');
12
13
 
14
+ const config = await loadConfig(root);
15
+
13
16
  const clients = new Set();
14
17
  let hasRouter = false;
15
18
 
@@ -21,7 +24,7 @@ export async function startDevServer(options = {}) {
21
24
 
22
25
  const app = new Elysia()
23
26
  .get('/', async () => {
24
- return serveHTML(root, hasRouter);
27
+ return serveHTML(root, hasRouter, config);
25
28
  })
26
29
 
27
30
  .get('/*', async ({ params, set }) => {
@@ -45,11 +48,21 @@ export async function startDevServer(options = {}) {
45
48
  }
46
49
  }
47
50
 
51
+ if (path.startsWith('public/')) {
52
+ const publicDir = join(root, 'public');
53
+ const filepath = join(publicDir, path.replace('public/', ''));
54
+ const file = Bun.file(filepath);
55
+
56
+ if (await file.exists()) {
57
+ return new Response(file);
58
+ }
59
+ }
60
+
48
61
  set.status = 404;
49
62
  return 'File not found';
50
63
  }
51
64
 
52
- return serveHTML(root, hasRouter);
65
+ return serveHTML(root, hasRouter, config);
53
66
  })
54
67
 
55
68
  .get('/hmr-client.js', () => {
@@ -141,21 +154,34 @@ ws.onclose = () => {
141
154
  logger.success(`🚀 Server running at http://localhost:${port}`);
142
155
  logger.info(`📁 Serving: ${root}`);
143
156
 
144
- setupWatcher(root, compiledDir, clients, () => {
157
+ setupWatcher(root, compiledDir, clients, async () => {
145
158
  hasRouter = existsSync(join(compiledDir, 'router.js'));
146
159
  });
147
160
 
148
161
  return app;
149
162
  }
150
163
 
151
- function serveHTML(root, hasRouter) {
164
+ function serveHTML(root, hasRouter, config) {
165
+ const meta = config.meta || {};
166
+
152
167
  const html = `
153
168
  <!DOCTYPE html>
154
- <html lang="en">
169
+ <html lang="${meta.lang || 'en'}">
155
170
  <head>
156
171
  <meta charset="UTF-8">
157
172
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
158
- <title>BertUI App - Dev</title>
173
+ <title>${meta.title || 'BertUI App'}</title>
174
+
175
+ ${meta.description ? `<meta name="description" content="${meta.description}">` : ''}
176
+ ${meta.keywords ? `<meta name="keywords" content="${meta.keywords}">` : ''}
177
+ ${meta.author ? `<meta name="author" content="${meta.author}">` : ''}
178
+ ${meta.themeColor ? `<meta name="theme-color" content="${meta.themeColor}">` : ''}
179
+
180
+ ${meta.ogTitle ? `<meta property="og:title" content="${meta.ogTitle || meta.title}">` : ''}
181
+ ${meta.ogDescription ? `<meta property="og:description" content="${meta.ogDescription || meta.description}">` : ''}
182
+ ${meta.ogImage ? `<meta property="og:image" content="${meta.ogImage}">` : ''}
183
+
184
+ <link rel="icon" type="image/svg+xml" href="/public/favicon.svg">
159
185
 
160
186
  <script type="importmap">
161
187
  {
@@ -182,10 +208,7 @@ function serveHTML(root, hasRouter) {
182
208
  <body>
183
209
  <div id="root"></div>
184
210
  <script type="module" src="/hmr-client.js"></script>
185
- ${hasRouter
186
- ? '<script type="module" src="/compiled/main.js"></script>'
187
- : '<script type="module" src="/compiled/main.js"></script>'
188
- }
211
+ <script type="module" src="/compiled/main.js"></script>
189
212
  </body>
190
213
  </html>`;
191
214
 
@@ -214,6 +237,7 @@ function getContentType(ext) {
214
237
 
215
238
  function setupWatcher(root, compiledDir, clients, onRecompile) {
216
239
  const srcDir = join(root, 'src');
240
+ const configPath = join(root, 'bertui.config.js');
217
241
 
218
242
  if (!existsSync(srcDir)) {
219
243
  logger.warn('src/ directory not found');
@@ -241,7 +265,7 @@ function setupWatcher(root, compiledDir, clients, onRecompile) {
241
265
  await compileProject(root);
242
266
 
243
267
  if (onRecompile) {
244
- onRecompile();
268
+ await onRecompile();
245
269
  }
246
270
 
247
271
  for (const client of clients) {
@@ -256,4 +280,20 @@ function setupWatcher(root, compiledDir, clients, onRecompile) {
256
280
  }
257
281
  }
258
282
  });
283
+
284
+ if (existsSync(configPath)) {
285
+ watch(configPath, async (eventType) => {
286
+ if (eventType === 'change') {
287
+ logger.info('📝 Config changed, reloading...');
288
+
289
+ for (const client of clients) {
290
+ try {
291
+ client.send(JSON.stringify({ type: 'reload', file: 'bertui.config.js' }));
292
+ } catch (e) {
293
+ clients.delete(client);
294
+ }
295
+ }
296
+ }
297
+ });
298
+ }
259
299
  }