bertui 0.2.7 → 0.2.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bertui",
3
- "version": "0.2.7",
3
+ "version": "0.2.9",
4
4
  "description": "Lightning-fast React dev server powered by Bun and Elysia",
5
5
  "type": "module",
6
6
  "main": "./index.js",
package/src/build.js CHANGED
@@ -1,52 +1,64 @@
1
1
  // src/build.js
2
2
  import { join } from 'path';
3
- import { existsSync, mkdirSync, rmSync, cpSync } from 'fs';
3
+ import { existsSync, mkdirSync, rmSync, cpSync, readdirSync, statSync } from 'fs';
4
+ import { extname, relative, dirname } from 'path';
4
5
  import logger from './logger/logger.js';
5
6
  import { buildCSS } from './build/css-builder.js';
6
7
 
7
8
  export async function buildProduction(options = {}) {
8
9
  const root = options.root || process.cwd();
10
+ const buildDir = join(root, '.bertuibuild');
9
11
  const outDir = join(root, 'dist');
10
12
 
11
13
  logger.bigLog('BUILDING FOR PRODUCTION', { color: 'green' });
12
14
 
13
- // Clean dist folder
15
+ // Clean folders
16
+ if (existsSync(buildDir)) {
17
+ rmSync(buildDir, { recursive: true });
18
+ }
14
19
  if (existsSync(outDir)) {
15
20
  rmSync(outDir, { recursive: true });
16
21
  logger.info('Cleaned dist/');
17
22
  }
23
+
24
+ mkdirSync(buildDir, { recursive: true });
18
25
  mkdirSync(outDir, { recursive: true });
19
26
 
20
27
  const startTime = Date.now();
21
28
 
22
29
  try {
23
- // Step 1: Build CSS from BertUI library
24
- logger.info('Step 1: Building CSS...');
30
+ // Step 1: Compile for production
31
+ logger.info('Step 1: Compiling for production...');
32
+ await compileForBuild(root, buildDir);
33
+ logger.success('Production compilation complete');
34
+
35
+ // Step 2: Build CSS from BertUI library
36
+ logger.info('Step 2: Building CSS...');
25
37
  const bertuiCssSource = join(import.meta.dir, 'styles/bertui.css');
26
38
  const bertuiCssDest = join(outDir, 'styles/bertui.min.css');
27
39
  await buildCSS(bertuiCssSource, bertuiCssDest);
28
40
 
29
- // Step 2: Copy public assets if they exist
41
+ // Step 3: Copy public assets if they exist
30
42
  const publicDir = join(root, 'public');
31
43
  if (existsSync(publicDir)) {
32
- logger.info('Step 2: Copying public assets...');
44
+ logger.info('Step 3: Copying public assets...');
33
45
  cpSync(publicDir, outDir, { recursive: true });
34
46
  logger.success('Public assets copied');
35
47
  } else {
36
- logger.info('Step 2: No public directory found, skipping...');
48
+ logger.info('Step 3: No public directory found, skipping...');
37
49
  }
38
50
 
39
- // Step 3: Build JavaScript with Bun's bundler
40
- logger.info('Step 3: Bundling JavaScript...');
41
- const mainEntry = join(root, 'src/main.jsx');
51
+ // Step 4: Build JavaScript with Bun's bundler
52
+ logger.info('Step 4: Bundling JavaScript...');
53
+ const buildEntry = join(buildDir, 'main.js');
42
54
 
43
- if (!existsSync(mainEntry)) {
44
- logger.error('Entry point not found: src/main.jsx');
55
+ if (!existsSync(buildEntry)) {
56
+ logger.error('Build entry point not found: .bertuibuild/main.js');
45
57
  process.exit(1);
46
58
  }
47
59
 
48
60
  const result = await Bun.build({
49
- entrypoints: [mainEntry],
61
+ entrypoints: [buildEntry],
50
62
  outdir: join(outDir, 'assets'),
51
63
  target: 'browser',
52
64
  minify: true,
@@ -57,7 +69,7 @@ export async function buildProduction(options = {}) {
57
69
  chunk: 'chunks/[name]-[hash].js',
58
70
  asset: '[name]-[hash].[ext]'
59
71
  },
60
- external: [] // Don't externalize anything for browser builds
72
+ external: ['react', 'react-dom']
61
73
  });
62
74
 
63
75
  if (!result.success) {
@@ -68,10 +80,14 @@ export async function buildProduction(options = {}) {
68
80
 
69
81
  logger.success('JavaScript bundled');
70
82
 
71
- // Step 4: Generate index.html
72
- logger.info('Step 4: Generating index.html...');
83
+ // Step 5: Generate index.html
84
+ logger.info('Step 5: Generating index.html...');
73
85
  await generateProductionHTML(root, outDir, result);
74
86
 
87
+ // Step 6: Clean up build folder
88
+ rmSync(buildDir, { recursive: true });
89
+ logger.info('Cleaned up .bertuibuild/');
90
+
75
91
  const duration = Date.now() - startTime;
76
92
  logger.success(`✨ Build complete in ${duration}ms`);
77
93
  logger.info(`šŸ“¦ Output: ${outDir}`);
@@ -82,17 +98,337 @@ export async function buildProduction(options = {}) {
82
98
  size: `${(o.size / 1024).toFixed(2)} KB`
83
99
  })));
84
100
 
101
+ // Show deployment instructions
102
+ logger.bigLog('READY TO DEPLOY', { color: 'green' });
103
+ console.log('\nšŸ“¤ Deploy your app:\n');
104
+ console.log(' Vercel: bunx vercel');
105
+ console.log(' Netlify: bunx netlify deploy');
106
+ console.log('\nšŸ” Preview locally:\n');
107
+ console.log(' bun run preview\n');
108
+
85
109
  } catch (error) {
86
110
  logger.error(`Build failed: ${error.message}`);
87
111
  if (error.stack) {
88
112
  logger.error(error.stack);
89
113
  }
114
+
115
+ // Clean up on error
116
+ if (existsSync(buildDir)) {
117
+ rmSync(buildDir, { recursive: true });
118
+ }
119
+
90
120
  process.exit(1);
91
121
  }
92
122
  }
93
123
 
124
+ async function compileForBuild(root, buildDir) {
125
+ const srcDir = join(root, 'src');
126
+ const pagesDir = join(srcDir, 'pages');
127
+
128
+ if (!existsSync(srcDir)) {
129
+ throw new Error('src/ directory not found!');
130
+ }
131
+
132
+ // Discover routes
133
+ let routes = [];
134
+ if (existsSync(pagesDir)) {
135
+ routes = await discoverRoutes(pagesDir);
136
+ logger.info(`Found ${routes.length} routes`);
137
+ }
138
+
139
+ // Compile all source files
140
+ await compileBuildDirectory(srcDir, buildDir, root);
141
+
142
+ // Generate router if we have routes
143
+ if (routes.length > 0) {
144
+ await generateBuildRouter(routes, buildDir);
145
+ logger.info('Generated router for build');
146
+ }
147
+ }
148
+
149
+ async function discoverRoutes(pagesDir) {
150
+ const routes = [];
151
+
152
+ async function scanDirectory(dir, basePath = '') {
153
+ const entries = readdirSync(dir, { withFileTypes: true });
154
+
155
+ for (const entry of entries) {
156
+ const fullPath = join(dir, entry.name);
157
+ const relativePath = join(basePath, entry.name);
158
+
159
+ if (entry.isDirectory()) {
160
+ await scanDirectory(fullPath, relativePath);
161
+ } else if (entry.isFile()) {
162
+ const ext = extname(entry.name);
163
+ if (['.jsx', '.tsx', '.js', '.ts'].includes(ext)) {
164
+ const fileName = entry.name.replace(ext, '');
165
+
166
+ let route = '/' + relativePath.replace(/\\/g, '/').replace(ext, '');
167
+
168
+ if (fileName === 'index') {
169
+ route = route.replace('/index', '') || '/';
170
+ }
171
+
172
+ const isDynamic = fileName.includes('[') && fileName.includes(']');
173
+ const type = isDynamic ? 'dynamic' : 'static';
174
+
175
+ routes.push({
176
+ route: route === '' ? '/' : route,
177
+ file: relativePath.replace(/\\/g, '/'),
178
+ path: fullPath,
179
+ type
180
+ });
181
+ }
182
+ }
183
+ }
184
+ }
185
+
186
+ await scanDirectory(pagesDir);
187
+
188
+ routes.sort((a, b) => {
189
+ if (a.type === b.type) {
190
+ return a.route.localeCompare(b.route);
191
+ }
192
+ return a.type === 'static' ? -1 : 1;
193
+ });
194
+
195
+ return routes;
196
+ }
197
+
198
+ async function generateBuildRouter(routes, buildDir) {
199
+ const imports = routes.map((route, i) => {
200
+ const componentName = `Page${i}`;
201
+ const importPath = `./pages/${route.file.replace(/\.(jsx|tsx|ts)$/, '.js')}`;
202
+ return `import ${componentName} from '${importPath}';`;
203
+ }).join('\n');
204
+
205
+ const routeConfigs = routes.map((route, i) => {
206
+ const componentName = `Page${i}`;
207
+ return ` { path: '${route.route}', component: ${componentName}, type: '${route.type}' }`;
208
+ }).join(',\n');
209
+
210
+ const routerCode = `import React, { useState, useEffect, createContext, useContext } from 'react';
211
+
212
+ const RouterContext = createContext(null);
213
+
214
+ export function useRouter() {
215
+ const context = useContext(RouterContext);
216
+ if (!context) {
217
+ throw new Error('useRouter must be used within a Router component');
218
+ }
219
+ return context;
220
+ }
221
+
222
+ export function Router({ routes }) {
223
+ const [currentRoute, setCurrentRoute] = useState(null);
224
+ const [params, setParams] = useState({});
225
+
226
+ useEffect(() => {
227
+ matchAndSetRoute(window.location.pathname);
228
+
229
+ const handlePopState = () => {
230
+ matchAndSetRoute(window.location.pathname);
231
+ };
232
+
233
+ window.addEventListener('popstate', handlePopState);
234
+ return () => window.removeEventListener('popstate', handlePopState);
235
+ }, [routes]);
236
+
237
+ function matchAndSetRoute(pathname) {
238
+ for (const route of routes) {
239
+ if (route.type === 'static' && route.path === pathname) {
240
+ setCurrentRoute(route);
241
+ setParams({});
242
+ return;
243
+ }
244
+ }
245
+
246
+ for (const route of routes) {
247
+ if (route.type === 'dynamic') {
248
+ const pattern = route.path.replace(/\\[([^\\]]+)\\]/g, '([^/]+)');
249
+ const regex = new RegExp('^' + pattern + '$');
250
+ const match = pathname.match(regex);
251
+
252
+ if (match) {
253
+ const paramNames = [...route.path.matchAll(/\\[([^\\]]+)\\]/g)].map(m => m[1]);
254
+ const extractedParams = {};
255
+ paramNames.forEach((name, i) => {
256
+ extractedParams[name] = match[i + 1];
257
+ });
258
+
259
+ setCurrentRoute(route);
260
+ setParams(extractedParams);
261
+ return;
262
+ }
263
+ }
264
+ }
265
+
266
+ setCurrentRoute(null);
267
+ setParams({});
268
+ }
269
+
270
+ function navigate(path) {
271
+ window.history.pushState({}, '', path);
272
+ matchAndSetRoute(path);
273
+ }
274
+
275
+ const routerValue = {
276
+ currentRoute,
277
+ params,
278
+ navigate,
279
+ pathname: window.location.pathname
280
+ };
281
+
282
+ const Component = currentRoute?.component;
283
+
284
+ return React.createElement(
285
+ RouterContext.Provider,
286
+ { value: routerValue },
287
+ Component ? React.createElement(Component, { params }) : React.createElement(NotFound, null)
288
+ );
289
+ }
290
+
291
+ export function Link({ to, children, ...props }) {
292
+ const { navigate } = useRouter();
293
+
294
+ function handleClick(e) {
295
+ e.preventDefault();
296
+ navigate(to);
297
+ }
298
+
299
+ return React.createElement('a', { href: to, onClick: handleClick, ...props }, children);
300
+ }
301
+
302
+ function NotFound() {
303
+ return React.createElement(
304
+ 'div',
305
+ {
306
+ style: {
307
+ display: 'flex',
308
+ flexDirection: 'column',
309
+ alignItems: 'center',
310
+ justifyContent: 'center',
311
+ minHeight: '100vh',
312
+ fontFamily: 'system-ui'
313
+ }
314
+ },
315
+ React.createElement('h1', { style: { fontSize: '6rem', margin: 0 } }, '404'),
316
+ React.createElement('p', { style: { fontSize: '1.5rem', color: '#666' } }, 'Page not found'),
317
+ React.createElement('a', {
318
+ href: '/',
319
+ style: { color: '#10b981', textDecoration: 'none', fontSize: '1.2rem' }
320
+ }, 'Go home')
321
+ );
322
+ }
323
+
324
+ ${imports}
325
+
326
+ export const routes = [
327
+ ${routeConfigs}
328
+ ];
329
+ `;
330
+
331
+ await Bun.write(join(buildDir, 'router.js'), routerCode);
332
+ }
333
+
334
+ async function compileBuildDirectory(srcDir, buildDir, root) {
335
+ const files = readdirSync(srcDir);
336
+
337
+ for (const file of files) {
338
+ const srcPath = join(srcDir, file);
339
+ const stat = statSync(srcPath);
340
+
341
+ if (stat.isDirectory()) {
342
+ const subBuildDir = join(buildDir, file);
343
+ mkdirSync(subBuildDir, { recursive: true });
344
+ await compileBuildDirectory(srcPath, subBuildDir, root);
345
+ } else {
346
+ const ext = extname(file);
347
+
348
+ if (['.jsx', '.tsx', '.ts'].includes(ext)) {
349
+ await compileBuildFile(srcPath, buildDir, file, root);
350
+ } else if (ext === '.js') {
351
+ const outPath = join(buildDir, file);
352
+ let code = await Bun.file(srcPath).text();
353
+ code = fixBuildImports(code, srcPath, outPath, root);
354
+ await Bun.write(outPath, code);
355
+ }
356
+ }
357
+ }
358
+ }
359
+
360
+ async function compileBuildFile(srcPath, buildDir, filename, root) {
361
+ const ext = extname(filename);
362
+ const loader = ext === '.tsx' ? 'tsx' : ext === '.ts' ? 'ts' : 'jsx';
363
+
364
+ try {
365
+ let code = await Bun.file(srcPath).text();
366
+
367
+ const outFilename = filename.replace(/\.(jsx|tsx|ts)$/, '.js');
368
+ const outPath = join(buildDir, outFilename);
369
+
370
+ code = fixBuildImports(code, srcPath, outPath, root);
371
+
372
+ const transpiler = new Bun.Transpiler({
373
+ loader,
374
+ tsconfig: {
375
+ compilerOptions: {
376
+ jsx: 'react',
377
+ jsxFactory: 'React.createElement',
378
+ jsxFragmentFactory: 'React.Fragment'
379
+ }
380
+ }
381
+ });
382
+
383
+ let compiled = await transpiler.transform(code);
384
+
385
+ if (!compiled.includes('import React') && (compiled.includes('React.createElement') || compiled.includes('React.Fragment'))) {
386
+ compiled = `import React from 'react';\n${compiled}`;
387
+ }
388
+
389
+ compiled = fixRelativeImports(compiled);
390
+
391
+ await Bun.write(outPath, compiled);
392
+ } catch (error) {
393
+ logger.error(`Failed to compile ${filename}: ${error.message}`);
394
+ throw error;
395
+ }
396
+ }
397
+
398
+ function fixBuildImports(code, srcPath, outPath, root) {
399
+ // Remove bertui/styles imports
400
+ code = code.replace(/import\s+['"]bertui\/styles['"]\s*;?\s*/g, '');
401
+
402
+ const buildDir = join(root, '.bertuibuild');
403
+ const routerPath = join(buildDir, 'router.js');
404
+
405
+ // Calculate relative path from output file to router.js
406
+ const relativeToRouter = relative(dirname(outPath), routerPath).replace(/\\/g, '/');
407
+ const routerImport = relativeToRouter.startsWith('.') ? relativeToRouter : './' + relativeToRouter;
408
+
409
+ // Replace bertui/router imports
410
+ code = code.replace(
411
+ /from\s+['"]bertui\/router['"]/g,
412
+ `from '${routerImport}'`
413
+ );
414
+
415
+ return code;
416
+ }
417
+
418
+ function fixRelativeImports(code) {
419
+ const importRegex = /from\s+['"](\.\.[\/\\]|\.\/)((?:[^'"]+?)(?<!\.js|\.jsx|\.ts|\.tsx|\.json))['"];?/g;
420
+
421
+ code = code.replace(importRegex, (match, prefix, path) => {
422
+ if (path.endsWith('/') || /\.\w+$/.test(path)) {
423
+ return match;
424
+ }
425
+ return `from '${prefix}${path}.js';`;
426
+ });
427
+
428
+ return code;
429
+ }
430
+
94
431
  async function generateProductionHTML(root, outDir, buildResult) {
95
- // Find the main bundle
96
432
  const mainBundle = buildResult.outputs.find(o =>
97
433
  o.path.includes('main') && o.kind === 'entry-point'
98
434
  );
@@ -111,6 +447,14 @@ async function generateProductionHTML(root, outDir, buildResult) {
111
447
  <meta name="description" content="Built with BertUI - Lightning fast React development">
112
448
  <title>BertUI App</title>
113
449
  <link rel="stylesheet" href="/styles/bertui.min.css">
450
+ <script type="importmap">
451
+ {
452
+ "imports": {
453
+ "react": "https://esm.sh/react@18.2.0",
454
+ "react-dom": "https://esm.sh/react-dom@18.2.0"
455
+ }
456
+ }
457
+ </script>
114
458
  </head>
115
459
  <body>
116
460
  <div id="root"></div>
@@ -1,349 +1,3 @@
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
-
347
1
  import { existsSync, mkdirSync, readdirSync, statSync } from 'fs';
348
2
  import { join, extname, relative } from 'path';
349
3
  import logger from '../logger/logger.js';
@@ -602,13 +256,13 @@ async function compileDirectory(srcDir, outDir, root) {
602
256
  const relativePath = relative(join(root, 'src'), srcPath);
603
257
 
604
258
  if (['.jsx', '.tsx', '.ts'].includes(ext)) {
605
- await compileFile(srcPath, outDir, file, relativePath, root);
259
+ await compileFile(srcPath, outDir, file, relativePath);
606
260
  stats.files++;
607
261
  } else if (ext === '.js') {
608
262
  const outPath = join(outDir, file);
609
263
  let code = await Bun.file(srcPath).text();
610
264
 
611
- code = fixImports(code, srcPath, root);
265
+ code = fixImports(code);
612
266
 
613
267
  await Bun.write(outPath, code);
614
268
  logger.debug(`Copied: ${relativePath}`);
@@ -623,14 +277,14 @@ async function compileDirectory(srcDir, outDir, root) {
623
277
  return stats;
624
278
  }
625
279
 
626
- async function compileFile(srcPath, outDir, filename, relativePath, root) {
280
+ async function compileFile(srcPath, outDir, filename, relativePath) {
627
281
  const ext = extname(filename);
628
282
  const loader = ext === '.tsx' ? 'tsx' : ext === '.ts' ? 'ts' : 'jsx';
629
283
 
630
284
  try {
631
285
  let code = await Bun.file(srcPath).text();
632
286
 
633
- code = fixImports(code, srcPath, root);
287
+ code = fixImports(code);
634
288
 
635
289
  const transpiler = new Bun.Transpiler({
636
290
  loader,
@@ -661,34 +315,18 @@ async function compileFile(srcPath, outDir, filename, relativePath, root) {
661
315
  }
662
316
  }
663
317
 
664
- function fixImports(code, srcPath, root) {
665
- // Remove bertui/styles imports
318
+ function fixImports(code) {
666
319
  code = code.replace(/import\s+['"]bertui\/styles['"]\s*;?\s*/g, '');
667
320
 
668
- // Check if this is main.jsx - it will be in src/main.jsx
669
- const isMainFile = srcPath.includes('main.jsx') || srcPath.includes('main.js');
321
+ code = code.replace(
322
+ /from\s+['"]bertui\/router['"]/g,
323
+ "from '/compiled/router.js'"
324
+ );
670
325
 
671
- if (isMainFile) {
672
- // For main.jsx: router.js is in the same compiled directory
673
- code = code.replace(
674
- /from\s+['"]bertui\/router['"]/g,
675
- "from './router.js'"
676
- );
677
- code = code.replace(
678
- /from\s+['"]\/compiled\/router\.js['"]/g,
679
- "from './router.js'"
680
- );
681
- code = code.replace(
682
- /from\s+['"]\.\.\/\.bertui\/compiled\/router\.js['"]/g,
683
- "from './router.js'"
684
- );
685
- } else {
686
- // For page components: router.js is up one level from pages/
687
- code = code.replace(
688
- /from\s+['"]bertui\/router['"]/g,
689
- "from '../router.js'"
690
- );
691
- }
326
+ code = code.replace(
327
+ /from\s+['"]\.\.\/\.bertui\/compiled\/([^'"]+)['"]/g,
328
+ "from '/compiled/$1'"
329
+ );
692
330
 
693
331
  return code;
694
332
  }
@@ -704,4 +342,5 @@ function fixRelativeImports(code) {
704
342
  });
705
343
 
706
344
  return code;
707
- }
345
+ }
346
+