bertui 0.2.5 → 0.2.6

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": "0.2.5",
3
+ "version": "0.2.6",
4
4
  "description": "Lightning-fast React dev server powered by Bun and Elysia",
5
5
  "type": "module",
6
6
  "main": "./index.js",
@@ -1,3 +1,349 @@
1
+ // import { existsSync, mkdirSync, readdirSync, statSync } from 'fs';
2
+ // import { join, extname, relative } from 'path';
3
+ // import logger from '../logger/logger.js';
4
+
5
+ // export async function compileProject(root) {
6
+ // logger.bigLog('COMPILING PROJECT', { color: 'blue' });
7
+
8
+ // const srcDir = join(root, 'src');
9
+ // const pagesDir = join(srcDir, 'pages');
10
+ // const outDir = join(root, '.bertui', 'compiled');
11
+
12
+ // if (!existsSync(srcDir)) {
13
+ // logger.error('src/ directory not found!');
14
+ // process.exit(1);
15
+ // }
16
+
17
+ // if (!existsSync(outDir)) {
18
+ // mkdirSync(outDir, { recursive: true });
19
+ // logger.info('Created .bertui/compiled/');
20
+ // }
21
+
22
+ // let routes = [];
23
+ // if (existsSync(pagesDir)) {
24
+ // routes = await discoverRoutes(pagesDir);
25
+ // logger.info(`Discovered ${routes.length} routes`);
26
+
27
+ // if (routes.length > 0) {
28
+ // logger.bigLog('ROUTES DISCOVERED', { color: 'blue' });
29
+ // logger.table(routes.map((r, i) => ({
30
+ // '': i,
31
+ // route: r.route,
32
+ // file: r.file,
33
+ // type: r.type
34
+ // })));
35
+ // }
36
+ // }
37
+
38
+ // const startTime = Date.now();
39
+ // const stats = await compileDirectory(srcDir, outDir, root);
40
+ // const duration = Date.now() - startTime;
41
+
42
+ // if (routes.length > 0) {
43
+ // await generateRouter(routes, outDir, root);
44
+ // logger.info('Generated router.js');
45
+ // }
46
+
47
+ // logger.success(`Compiled ${stats.files} files in ${duration}ms`);
48
+ // logger.info(`Output: ${outDir}`);
49
+
50
+ // return { outDir, stats, routes };
51
+ // }
52
+
53
+ // async function discoverRoutes(pagesDir) {
54
+ // const routes = [];
55
+
56
+ // async function scanDirectory(dir, basePath = '') {
57
+ // const entries = readdirSync(dir, { withFileTypes: true });
58
+
59
+ // for (const entry of entries) {
60
+ // const fullPath = join(dir, entry.name);
61
+ // const relativePath = join(basePath, entry.name);
62
+
63
+ // if (entry.isDirectory()) {
64
+ // await scanDirectory(fullPath, relativePath);
65
+ // } else if (entry.isFile()) {
66
+ // const ext = extname(entry.name);
67
+ // if (['.jsx', '.tsx', '.js', '.ts'].includes(ext)) {
68
+ // const fileName = entry.name.replace(ext, '');
69
+
70
+ // let route = '/' + relativePath.replace(/\\/g, '/').replace(ext, '');
71
+
72
+ // if (fileName === 'index') {
73
+ // route = route.replace('/index', '') || '/';
74
+ // }
75
+
76
+ // const isDynamic = fileName.includes('[') && fileName.includes(']');
77
+ // const type = isDynamic ? 'dynamic' : 'static';
78
+
79
+ // routes.push({
80
+ // route: route === '' ? '/' : route,
81
+ // file: relativePath.replace(/\\/g, '/'),
82
+ // path: fullPath,
83
+ // type
84
+ // });
85
+ // }
86
+ // }
87
+ // }
88
+ // }
89
+
90
+ // await scanDirectory(pagesDir);
91
+
92
+ // routes.sort((a, b) => {
93
+ // if (a.type === b.type) {
94
+ // return a.route.localeCompare(b.route);
95
+ // }
96
+ // return a.type === 'static' ? -1 : 1;
97
+ // });
98
+
99
+ // return routes;
100
+ // }
101
+
102
+ // async function generateRouter(routes, outDir, root) {
103
+ // const imports = routes.map((route, i) => {
104
+ // const componentName = `Page${i}`;
105
+ // const importPath = `./pages/${route.file.replace(/\.(jsx|tsx|ts)$/, '.js')}`;
106
+ // return `import ${componentName} from '${importPath}';`;
107
+ // }).join('\n');
108
+
109
+ // const routeConfigs = routes.map((route, i) => {
110
+ // const componentName = `Page${i}`;
111
+ // return ` { path: '${route.route}', component: ${componentName}, type: '${route.type}' }`;
112
+ // }).join(',\n');
113
+
114
+ // const routerComponentCode = `import React, { useState, useEffect, createContext, useContext } from 'react';
115
+
116
+ // const RouterContext = createContext(null);
117
+
118
+ // export function useRouter() {
119
+ // const context = useContext(RouterContext);
120
+ // if (!context) {
121
+ // throw new Error('useRouter must be used within a Router component');
122
+ // }
123
+ // return context;
124
+ // }
125
+
126
+ // export function Router({ routes }) {
127
+ // const [currentRoute, setCurrentRoute] = useState(null);
128
+ // const [params, setParams] = useState({});
129
+
130
+ // useEffect(() => {
131
+ // matchAndSetRoute(window.location.pathname);
132
+
133
+ // const handlePopState = () => {
134
+ // matchAndSetRoute(window.location.pathname);
135
+ // };
136
+
137
+ // window.addEventListener('popstate', handlePopState);
138
+ // return () => window.removeEventListener('popstate', handlePopState);
139
+ // }, [routes]);
140
+
141
+ // function matchAndSetRoute(pathname) {
142
+ // for (const route of routes) {
143
+ // if (route.type === 'static' && route.path === pathname) {
144
+ // setCurrentRoute(route);
145
+ // setParams({});
146
+ // return;
147
+ // }
148
+ // }
149
+
150
+ // for (const route of routes) {
151
+ // if (route.type === 'dynamic') {
152
+ // const pattern = route.path.replace(/\\[([^\\]]+)\\]/g, '([^/]+)');
153
+ // const regex = new RegExp('^' + pattern + '$');
154
+ // const match = pathname.match(regex);
155
+
156
+ // if (match) {
157
+ // const paramNames = [...route.path.matchAll(/\\[([^\\]]+)\\]/g)].map(m => m[1]);
158
+ // const extractedParams = {};
159
+ // paramNames.forEach((name, i) => {
160
+ // extractedParams[name] = match[i + 1];
161
+ // });
162
+
163
+ // setCurrentRoute(route);
164
+ // setParams(extractedParams);
165
+ // return;
166
+ // }
167
+ // }
168
+ // }
169
+
170
+ // setCurrentRoute(null);
171
+ // setParams({});
172
+ // }
173
+
174
+ // function navigate(path) {
175
+ // window.history.pushState({}, '', path);
176
+ // matchAndSetRoute(path);
177
+ // }
178
+
179
+ // const routerValue = {
180
+ // currentRoute,
181
+ // params,
182
+ // navigate,
183
+ // pathname: window.location.pathname
184
+ // };
185
+
186
+ // const Component = currentRoute?.component;
187
+
188
+ // return React.createElement(
189
+ // RouterContext.Provider,
190
+ // { value: routerValue },
191
+ // Component ? React.createElement(Component, { params }) : React.createElement(NotFound, null)
192
+ // );
193
+ // }
194
+
195
+ // export function Link({ to, children, ...props }) {
196
+ // const { navigate } = useRouter();
197
+
198
+ // function handleClick(e) {
199
+ // e.preventDefault();
200
+ // navigate(to);
201
+ // }
202
+
203
+ // return React.createElement('a', { href: to, onClick: handleClick, ...props }, children);
204
+ // }
205
+
206
+ // function NotFound() {
207
+ // return React.createElement(
208
+ // 'div',
209
+ // {
210
+ // style: {
211
+ // display: 'flex',
212
+ // flexDirection: 'column',
213
+ // alignItems: 'center',
214
+ // justifyContent: 'center',
215
+ // minHeight: '100vh',
216
+ // fontFamily: 'system-ui'
217
+ // }
218
+ // },
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
+ // href: '/',
223
+ // style: { color: '#10b981', textDecoration: 'none', fontSize: '1.2rem' }
224
+ // }, 'Go home')
225
+ // );
226
+ // }
227
+
228
+ // ${imports}
229
+
230
+ // export const routes = [
231
+ // ${routeConfigs}
232
+ // ];
233
+ // `;
234
+
235
+ // const routerPath = join(outDir, 'router.js');
236
+ // await Bun.write(routerPath, routerComponentCode);
237
+ // }
238
+
239
+ // async function compileDirectory(srcDir, outDir, root) {
240
+ // const stats = { files: 0, skipped: 0 };
241
+
242
+ // const files = readdirSync(srcDir);
243
+
244
+ // for (const file of files) {
245
+ // const srcPath = join(srcDir, file);
246
+ // const stat = statSync(srcPath);
247
+
248
+ // if (stat.isDirectory()) {
249
+ // const subOutDir = join(outDir, file);
250
+ // mkdirSync(subOutDir, { recursive: true });
251
+ // const subStats = await compileDirectory(srcPath, subOutDir, root);
252
+ // stats.files += subStats.files;
253
+ // stats.skipped += subStats.skipped;
254
+ // } else {
255
+ // const ext = extname(file);
256
+ // const relativePath = relative(join(root, 'src'), srcPath);
257
+
258
+ // if (['.jsx', '.tsx', '.ts'].includes(ext)) {
259
+ // await compileFile(srcPath, outDir, file, relativePath);
260
+ // stats.files++;
261
+ // } else if (ext === '.js') {
262
+ // const outPath = join(outDir, file);
263
+ // let code = await Bun.file(srcPath).text();
264
+
265
+ // code = fixImports(code);
266
+
267
+ // await Bun.write(outPath, code);
268
+ // logger.debug(`Copied: ${relativePath}`);
269
+ // stats.files++;
270
+ // } else {
271
+ // logger.debug(`Skipped: ${relativePath}`);
272
+ // stats.skipped++;
273
+ // }
274
+ // }
275
+ // }
276
+
277
+ // return stats;
278
+ // }
279
+
280
+ // async function compileFile(srcPath, outDir, filename, relativePath) {
281
+ // const ext = extname(filename);
282
+ // const loader = ext === '.tsx' ? 'tsx' : ext === '.ts' ? 'ts' : 'jsx';
283
+
284
+ // try {
285
+ // let code = await Bun.file(srcPath).text();
286
+
287
+ // code = fixImports(code);
288
+
289
+ // const transpiler = new Bun.Transpiler({
290
+ // loader,
291
+ // tsconfig: {
292
+ // compilerOptions: {
293
+ // jsx: 'react',
294
+ // jsxFactory: 'React.createElement',
295
+ // jsxFragmentFactory: 'React.Fragment'
296
+ // }
297
+ // }
298
+ // });
299
+ // let compiled = await transpiler.transform(code);
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
+
305
+ // compiled = fixRelativeImports(compiled);
306
+
307
+ // const outFilename = filename.replace(/\.(jsx|tsx|ts)$/, '.js');
308
+ // const outPath = join(outDir, outFilename);
309
+
310
+ // await Bun.write(outPath, compiled);
311
+ // logger.debug(`Compiled: ${relativePath} → ${outFilename}`);
312
+ // } catch (error) {
313
+ // logger.error(`Failed to compile ${relativePath}: ${error.message}`);
314
+ // throw error;
315
+ // }
316
+ // }
317
+
318
+ // function fixImports(code) {
319
+ // code = code.replace(/import\s+['"]bertui\/styles['"]\s*;?\s*/g, '');
320
+
321
+ // code = code.replace(
322
+ // /from\s+['"]bertui\/router['"]/g,
323
+ // "from '/compiled/router.js'"
324
+ // );
325
+
326
+ // code = code.replace(
327
+ // /from\s+['"]\.\.\/\.bertui\/compiled\/([^'"]+)['"]/g,
328
+ // "from '/compiled/$1'"
329
+ // );
330
+
331
+ // return code;
332
+ // }
333
+
334
+ // function fixRelativeImports(code) {
335
+ // const importRegex = /from\s+['"](\.\.[\/\\]|\.\/)((?:[^'"]+?)(?<!\.js|\.jsx|\.ts|\.tsx|\.json))['"];?/g;
336
+
337
+ // code = code.replace(importRegex, (match, prefix, path) => {
338
+ // if (path.endsWith('/') || /\.\w+$/.test(path)) {
339
+ // return match;
340
+ // }
341
+ // return `from '${prefix}${path}.js';`;
342
+ // });
343
+
344
+ // return code;
345
+ // }
346
+
1
347
  import { existsSync, mkdirSync, readdirSync, statSync } from 'fs';
2
348
  import { join, extname, relative } from 'path';
3
349
  import logger from '../logger/logger.js';
@@ -316,16 +662,19 @@ async function compileFile(srcPath, outDir, filename, relativePath) {
316
662
  }
317
663
 
318
664
  function fixImports(code) {
665
+ // Remove bertui/styles imports
319
666
  code = code.replace(/import\s+['"]bertui\/styles['"]\s*;?\s*/g, '');
320
667
 
668
+ // Fix bertui/router imports to use relative path to compiled router
321
669
  code = code.replace(
322
670
  /from\s+['"]bertui\/router['"]/g,
323
- "from '/compiled/router.js'"
671
+ "from '../.bertui/compiled/router.js'"
324
672
  );
325
673
 
674
+ // Also handle any absolute /compiled/ paths
326
675
  code = code.replace(
327
- /from\s+['"]\.\.\/\.bertui\/compiled\/([^'"]+)['"]/g,
328
- "from '/compiled/$1'"
676
+ /from\s+['"]\/compiled\/router\.js['"]/g,
677
+ "from '../.bertui/compiled/router.js'"
329
678
  );
330
679
 
331
680
  return code;
@@ -31,6 +31,24 @@ export async function startDevServer(options = {}) {
31
31
  const path = params['*'];
32
32
 
33
33
  if (path.includes('.')) {
34
+ // Handle .bertui/compiled/ paths
35
+ if (path.startsWith('.bertui/compiled/')) {
36
+ const filePath = join(root, path);
37
+ const file = Bun.file(filePath);
38
+
39
+ if (await file.exists()) {
40
+ const ext = extname(path);
41
+ const contentType = ext === '.js' ? 'application/javascript' : getContentType(ext);
42
+
43
+ return new Response(await file.text(), {
44
+ headers: {
45
+ 'Content-Type': contentType,
46
+ 'Cache-Control': 'no-store'
47
+ }
48
+ });
49
+ }
50
+ }
51
+
34
52
  if (path.startsWith('compiled/')) {
35
53
  const filePath = join(compiledDir, path.replace('compiled/', ''));
36
54
  const file = Bun.file(filePath);
@@ -188,8 +206,7 @@ function serveHTML(root, hasRouter, config) {
188
206
  "imports": {
189
207
  "react": "https://esm.sh/react@18.2.0",
190
208
  "react-dom": "https://esm.sh/react-dom@18.2.0",
191
- "react-dom/client": "https://esm.sh/react-dom@18.2.0/client",
192
- "bertui/router": "/compiled/router.js"
209
+ "react-dom/client": "https://esm.sh/react-dom@18.2.0/client"
193
210
  }
194
211
  }
195
212
  </script>
@@ -208,7 +225,7 @@ function serveHTML(root, hasRouter, config) {
208
225
  <body>
209
226
  <div id="root"></div>
210
227
  <script type="module" src="/hmr-client.js"></script>
211
- <script type="module" src="/compiled/main.js"></script>
228
+ <script type="module" src="/.bertui/compiled/main.js"></script>
212
229
  </body>
213
230
  </html>`;
214
231