bertui 1.1.6 → 1.1.8

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,7 +1,7 @@
1
1
  {
2
2
  "name": "bertui",
3
- "version": "1.1.6",
4
- "description": "Lightning-fast React dev server powered by Bun and Elysia - Now with TypeScript support!",
3
+ "version": "1.1.8",
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",
7
7
  "types": "./types/index.d.ts",
@@ -11,22 +11,32 @@
11
11
  "exports": {
12
12
  ".": {
13
13
  "types": "./types/index.d.ts",
14
+ "import": "./index.js",
14
15
  "default": "./index.js"
15
16
  },
16
- "./styles": "./src/styles/bertui.css",
17
- "./logger": "./src/logger/logger.js",
17
+ "./hmr": {
18
+ "import": "./src/client/hmr-runtime.js"
19
+ },
20
+ "./image-optimizer": {
21
+ "import": "./src/image-optimizer/index.js"
22
+ },
18
23
  "./router": {
19
24
  "types": "./types/router.d.ts",
20
- "default": "./src/router/Router.jsx"
25
+ "import": "./src/router/index.js"
21
26
  },
22
27
  "./config": {
23
28
  "types": "./types/config.d.ts",
24
- "default": "./src/config/loadConfig.js"
25
- }
29
+ "import": "./src/config/index.js"
30
+ },
31
+ "./logger": {
32
+ "import": "./src/logger/logger.js"
33
+ },
34
+ "./styles": "./src/styles/bertui.css"
26
35
  },
27
36
  "files": [
28
37
  "bin",
29
38
  "src",
39
+ "dist",
30
40
  "types",
31
41
  "index.js",
32
42
  "README.md",
@@ -35,23 +45,45 @@
35
45
  "scripts": {
36
46
  "dev": "bun bin/bertui.js dev",
37
47
  "build": "bun bin/bertui.js build",
48
+ "build:wasm": "cd src/image-optimizer-rust && wasm-pack build --target web --out-dir ../../dist/image-optimizer/wasm && bun run fix:wasm",
49
+ "fix:wasm": "node scripts/fix-wasm-exports.js",
50
+ "prepublishOnly": "echo 'Note: Ensure WASM is built via build:wasm before publishing'",
38
51
  "test": "cd test-app && bun run dev"
39
52
  },
53
+ "dependencies": {
54
+ "elysia": "^1.0.0",
55
+ "ernest-logger": "^2.0.0",
56
+ "lightningcss": "^1.30.2",
57
+ "react-refresh": "^0.14.0"
58
+ },
59
+ "peerDependencies": {
60
+ "bun": ">=1.0.0",
61
+ "react": "^18.0.0 || ^19.0.0",
62
+ "react-dom": "^18.0.0 || ^19.0.0"
63
+ },
64
+ "devDependencies": {
65
+ "@types/react": "^18.2.0",
66
+ "@types/react-dom": "^18.2.0",
67
+ "oxipng": "^1.0.1",
68
+ "wasm-pack": "^0.12.1"
69
+ },
70
+ "optionalDependencies": {
71
+ "@bertui/image-optimizer-wasm": "0.1.0",
72
+ "oxipng": "^8.0.0"
73
+ },
40
74
  "keywords": [
41
75
  "react",
42
76
  "bun",
43
- "dev-server",
44
- "vite-alternative",
45
77
  "elysia",
46
- "build-tool",
47
- "bundler",
48
- "fast",
49
78
  "hmr",
50
- "file-based-routing",
51
- "typescript",
79
+ "fast-refresh",
80
+ "image-optimization",
81
+ "rust",
82
+ "wasm",
83
+ "server-islands",
52
84
  "seo",
53
85
  "sitemap",
54
- "server-islands"
86
+ "typescript"
55
87
  ],
56
88
  "author": "Pease Ernest",
57
89
  "license": "MIT",
@@ -59,20 +91,6 @@
59
91
  "type": "git",
60
92
  "url": "https://github.com/BunElysiaReact/BERTUI.git"
61
93
  },
62
- "dependencies": {
63
- "elysia": "^1.0.0",
64
- "ernest-logger": "^2.0.0",
65
- "lightningcss": "^1.30.2"
66
- },
67
- "peerDependencies": {
68
- "react": "^18.0.0 || ^19.0.0",
69
- "react-dom": "^19.2.3",
70
- "bun": ">=1.0.0"
71
- },
72
- "devDependencies": {
73
- "@types/react": "^18.2.0",
74
- "@types/react-dom": "^18.2.0"
75
- },
76
94
  "engines": {
77
95
  "bun": ">=1.0.0"
78
96
  }
@@ -1,86 +1,137 @@
1
- // bertui/src/build/image-optimizer.js - SIMPLE & STABLE
1
+ // bertui/src/build/image-optimizer.js - UPDATED WITH WASM
2
2
  import { join, extname } from 'path';
3
- import { existsSync, mkdirSync, readdirSync, cpSync } from 'fs';
3
+ import { existsSync, mkdirSync, readdirSync } from 'fs';
4
4
  import logger from '../logger/logger.js';
5
+ import { optimizeImage, hasWasm } from '../image-optimizer/index.js';
6
+ import { copyImagesSync } from '../images/index.js';
5
7
 
6
- /**
7
- * Simple, reliable image copying
8
- * No WASM, no optimization, just copy files
9
- */
8
+ export async function optimizeImages(srcDir, outDir, options = {}) {
9
+ const {
10
+ quality = 80,
11
+ webpQuality = 75,
12
+ verbose = false
13
+ } = options;
10
14
 
11
- export async function optimizeImages(srcDir, outDir) {
12
- // Alias for copyImages to maintain API
13
- logger.info(`📁 Copying from ${srcDir} to ${outDir}...`);
14
- const copied = copyImages(srcDir, outDir);
15
- return { optimized: 0, saved: 0, copied };
16
- }
17
-
18
- export async function checkOptimizationTools() {
19
- // Always return empty array to disable optimization
20
- return [];
21
- }
15
+ // Check if WASM is available
16
+ const wasmAvailable = await hasWasm();
17
+
18
+ if (wasmAvailable) {
19
+ logger.info(`🦀 Optimizing images with Rust WASM (quality: ${quality})...`);
20
+ } else {
21
+ logger.info('📋 Copying images (WASM optimizer not available)...');
22
+ }
22
23
 
23
- export function copyImages(srcDir, outDir) {
24
- // All common image formats
25
24
  const imageExtensions = [
26
25
  '.png', '.jpg', '.jpeg', '.webp', '.gif', '.svg',
27
26
  '.avif', '.ico', '.bmp', '.tiff', '.tif'
28
27
  ];
29
28
 
29
+ let optimized = 0;
30
30
  let copied = 0;
31
31
  let skipped = 0;
32
+ let totalSaved = 0;
33
+ const results = [];
32
34
 
33
35
  if (!existsSync(srcDir)) {
34
36
  logger.warn(`⚠️ Source not found: ${srcDir}`);
35
- return 0;
37
+ return { optimized: 0, copied: 0, saved: 0, results: [] };
36
38
  }
37
39
 
38
- // Ensure output directory exists
39
40
  mkdirSync(outDir, { recursive: true });
40
41
 
41
- function processDirectory(dir, targetDir) {
42
- try {
43
- const entries = readdirSync(dir, { withFileTypes: true });
42
+ async function processDirectory(dir, targetDir) {
43
+ const entries = readdirSync(dir, { withFileTypes: true });
44
44
 
45
- for (const entry of entries) {
46
- const srcPath = join(dir, entry.name);
47
- const destPath = join(targetDir, entry.name);
45
+ for (const entry of entries) {
46
+ const srcPath = join(dir, entry.name);
47
+ const destPath = join(targetDir, entry.name);
48
48
 
49
- if (entry.isDirectory()) {
50
- // Recursively process subdirectories
51
- const subDestPath = join(targetDir, entry.name);
52
- mkdirSync(subDestPath, { recursive: true });
53
- processDirectory(srcPath, subDestPath);
54
- } else if (entry.isFile()) {
55
- const ext = extname(entry.name).toLowerCase();
56
-
57
- if (imageExtensions.includes(ext)) {
58
- try {
59
- cpSync(srcPath, destPath);
49
+ if (entry.isDirectory()) {
50
+ const subDestPath = join(targetDir, entry.name);
51
+ mkdirSync(subDestPath, { recursive: true });
52
+ await processDirectory(srcPath, subDestPath);
53
+ } else if (entry.isFile()) {
54
+ const ext = extname(entry.name).toLowerCase();
55
+
56
+ if (imageExtensions.includes(ext)) {
57
+ try {
58
+ const file = Bun.file(srcPath);
59
+ const buffer = await file.arrayBuffer();
60
+ const originalSize = buffer.byteLength;
61
+
62
+ // Try WASM optimization first, fallback to copy
63
+ if (wasmAvailable && ['.png', '.jpg', '.jpeg', '.webp'].includes(ext)) {
64
+ const format = ext.slice(1);
65
+ const result = await optimizeImage(buffer, {
66
+ format,
67
+ quality,
68
+ webpQuality
69
+ });
70
+
71
+ await Bun.write(destPath, new Uint8Array(result.data));
72
+
73
+ const saved = originalSize - result.optimized_size;
74
+ totalSaved += saved;
75
+ optimized++;
76
+
77
+ results.push({
78
+ file: entry.name,
79
+ original: formatBytes(originalSize),
80
+ optimized: formatBytes(result.optimized_size),
81
+ saved: formatBytes(saved),
82
+ percent: result.savings_percent.toFixed(1) + '%'
83
+ });
84
+ } else {
85
+ // Just copy unsupported formats
86
+ await Bun.write(destPath, file);
60
87
  copied++;
61
- } catch (error) {
62
- logger.warn(` Failed to copy ${entry.name}: ${error.message}`);
63
- skipped++;
88
+ results.push({
89
+ file: entry.name,
90
+ status: 'copied'
91
+ });
64
92
  }
65
- } else {
66
- skipped++;
93
+ } catch (error) {
94
+ logger.warn(` Failed to process ${entry.name}: ${error.message}`);
95
+ // Fallback: copy original
96
+ await Bun.write(destPath, Bun.file(srcPath));
97
+ copied++;
67
98
  }
99
+ } else {
100
+ // Copy non-image files
101
+ await Bun.write(destPath, Bun.file(srcPath));
102
+ skipped++;
68
103
  }
69
104
  }
70
- } catch (error) {
71
- logger.error(`Error processing ${dir}: ${error.message}`);
72
105
  }
73
106
  }
74
107
 
75
- processDirectory(srcDir, outDir);
108
+ await processDirectory(srcDir, outDir);
76
109
 
77
- if (copied > 0) {
78
- logger.success(`✅ Copied ${copied} image(s) to ${outDir}`);
110
+ // Show summary
111
+ if (optimized > 0) {
112
+ logger.success(`✅ Optimized ${optimized} images with Rust WASM`);
113
+ logger.table(results.slice(0, 10));
114
+ if (results.length > 10) {
115
+ logger.info(` ... and ${results.length - 10} more images`);
116
+ }
117
+ logger.info(`📊 Total saved: ${formatBytes(totalSaved)}`);
79
118
  }
80
119
 
81
- if (skipped > 0) {
82
- logger.info(`📝 Skipped ${skipped} non-image file(s)`);
120
+ if (copied > 0) {
121
+ logger.info(`📋 Copied ${copied} images (fallback)`);
83
122
  }
84
123
 
85
- return copied;
124
+ return { optimized, copied, saved: totalSaved, results };
125
+ }
126
+
127
+ export function copyImages(srcDir, outDir) {
128
+ return copyImagesSync(srcDir, outDir);
129
+ }
130
+
131
+ function formatBytes(bytes) {
132
+ if (bytes === 0) return '0 B';
133
+ const k = 1024;
134
+ const sizes = ['B', 'KB', 'MB', 'GB'];
135
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
136
+ return `${parseFloat((bytes / Math.pow(k, i)).toFixed(2))} ${sizes[i]}`;
86
137
  }
@@ -63,6 +63,49 @@ export async function compileProject(root) {
63
63
  return { outDir, stats, routes };
64
64
  }
65
65
 
66
+ // NEW EXPORT - Single file compilation for HMR
67
+ export async function compileFile(srcPath, root) {
68
+ const srcDir = join(root, 'src');
69
+ const outDir = join(root, '.bertui', 'compiled');
70
+ const envVars = loadEnvVariables(root);
71
+
72
+ const relativePath = relative(srcDir, srcPath);
73
+ const ext = extname(srcPath);
74
+
75
+ if (!existsSync(outDir)) {
76
+ mkdirSync(outDir, { recursive: true });
77
+ }
78
+
79
+ if (['.jsx', '.tsx', '.ts'].includes(ext)) {
80
+ const fileName = srcPath.split('/').pop();
81
+ await compileFileInternal(srcPath, outDir, fileName, relativePath, root, envVars);
82
+ return {
83
+ outputPath: relativePath.replace(/\.(jsx|tsx|ts)$/, '.js'),
84
+ success: true
85
+ };
86
+ } else if (ext === '.js') {
87
+ const fileName = srcPath.split('/').pop();
88
+ const outPath = join(outDir, fileName);
89
+ let code = await Bun.file(srcPath).text();
90
+
91
+ code = removeCSSImports(code);
92
+ code = replaceEnvInCode(code, envVars);
93
+ code = fixRouterImports(code, outPath, root);
94
+
95
+ if (usesJSX(code) && !code.includes('import React')) {
96
+ code = `import React from 'react';\n${code}`;
97
+ }
98
+
99
+ await Bun.write(outPath, code);
100
+ return {
101
+ outputPath: relativePath,
102
+ success: true
103
+ };
104
+ }
105
+
106
+ return { success: false };
107
+ }
108
+
66
109
  async function discoverRoutes(pagesDir) {
67
110
  const routes = [];
68
111
 
@@ -248,7 +291,7 @@ async function compileDirectory(srcDir, outDir, root, envVars) {
248
291
  logger.debug(`Copied CSS: ${relativePath}`);
249
292
  stats.files++;
250
293
  } else if (['.jsx', '.tsx', '.ts'].includes(ext)) {
251
- await compileFile(srcPath, outDir, file, relativePath, root, envVars);
294
+ await compileFileInternal(srcPath, outDir, file, relativePath, root, envVars);
252
295
  stats.files++;
253
296
  } else if (ext === '.js') {
254
297
  const outPath = join(outDir, file);
@@ -275,7 +318,7 @@ async function compileDirectory(srcDir, outDir, root, envVars) {
275
318
  return stats;
276
319
  }
277
320
 
278
- async function compileFile(srcPath, outDir, filename, relativePath, root, envVars) {
321
+ async function compileFileInternal(srcPath, outDir, filename, relativePath, root, envVars) {
279
322
  const ext = extname(filename);
280
323
  const loader = ext === '.tsx' ? 'tsx' : ext === '.ts' ? 'ts' : 'jsx';
281
324
 
@@ -362,4 +405,4 @@ function fixRelativeImports(code) {
362
405
  });
363
406
 
364
407
  return code;
365
- }
408
+ }
@@ -0,0 +1,72 @@
1
+ // bertui/src/client/fast-refresh.js
2
+ // React Fast Refresh integration
3
+
4
+ import RefreshRuntime from 'react-refresh';
5
+
6
+ // Inject into global scope
7
+ if (typeof window !== 'undefined') {
8
+ // Setup Fast Refresh globals
9
+ window.$RefreshReg$ = (type, id) => {
10
+ RefreshRuntime.register(type, id);
11
+ };
12
+
13
+ window.$RefreshSig$ = RefreshRuntime.createSignatureFunctionForTransform;
14
+
15
+ // Store runtime in global
16
+ window.$RefreshRuntime$ = RefreshRuntime;
17
+
18
+ // Inject into global hook
19
+ if (!window.__REACT_DEVTOOLS_GLOBAL_HOOK__) {
20
+ window.__REACT_DEVTOOLS_GLOBAL_HOOK__ = {
21
+ supportsFiber: true,
22
+ inject: (fiber) => {},
23
+ onCommitFiberRoot: (rendererId, root) => {},
24
+ onCommitFiberUnmount: () => {}
25
+ };
26
+ }
27
+
28
+ // Setup refresh handler
29
+ RefreshRuntime.injectIntoGlobalHook(window);
30
+
31
+ // Create queue for batched updates
32
+ let updateQueue = [];
33
+ let scheduled = false;
34
+
35
+ const scheduleUpdate = () => {
36
+ if (scheduled) return;
37
+ scheduled = true;
38
+
39
+ queueMicrotask(() => {
40
+ scheduled = false;
41
+ const queue = updateQueue;
42
+ updateQueue = [];
43
+
44
+ if (queue.length > 0 && window.$RefreshRuntime$) {
45
+ window.$RefreshRuntime$.performReactRefresh();
46
+ }
47
+ });
48
+ };
49
+
50
+ // Listen for HMR updates
51
+ window.addEventListener('hmr-module-updated', (event) => {
52
+ const { moduleId } = event.detail;
53
+ updateQueue.push(moduleId);
54
+ scheduleUpdate();
55
+ });
56
+
57
+ console.log('%c⚡ React Fast Refresh enabled', 'color: #3b82f6; font-weight: bold');
58
+ }
59
+
60
+ // Export for module usage
61
+ export const setupFastRefresh = () => {
62
+ if (typeof window !== 'undefined' && window.$RefreshRuntime$) {
63
+ return window.$RefreshRuntime$;
64
+ }
65
+ return null;
66
+ };
67
+
68
+ export const performReactRefresh = () => {
69
+ if (typeof window !== 'undefined' && window.$RefreshRuntime$) {
70
+ window.$RefreshRuntime$.performReactRefresh();
71
+ }
72
+ };
@@ -0,0 +1,59 @@
1
+ // bertui/src/client/hmr-runtime.js
2
+ // ONE SOLUTION - Fast Refresh HMR
3
+
4
+ (function(global) {
5
+ let socket = null;
6
+ let reconnectTimer = null;
7
+
8
+ function connect() {
9
+ const protocol = global.location.protocol === 'https:' ? 'wss:' : 'ws:';
10
+ socket = new WebSocket(`${protocol}//${global.location.host}/__hmr`);
11
+
12
+ socket.onopen = () => {
13
+ console.log('%c🔥 HMR connected', 'color: #10b981; font-weight: bold');
14
+ };
15
+
16
+ socket.onmessage = async (event) => {
17
+ const data = JSON.parse(event.data);
18
+
19
+ if (data.type === 'hmr-update') {
20
+ console.log(`%c🔥 HMR: ${data.module} (${data.time}ms)`, 'color: #10b981');
21
+
22
+ try {
23
+ const url = new URL(data.module, global.location.origin);
24
+ url.searchParams.set('t', Date.now());
25
+ await import(url.toString());
26
+
27
+ if (global.$RefreshRuntime$) {
28
+ global.$RefreshRuntime$.performReactRefresh();
29
+ }
30
+ } catch (err) {
31
+ console.error('HMR update failed:', err);
32
+ global.location.reload();
33
+ }
34
+ }
35
+
36
+ if (data.type === 'full-reload') {
37
+ global.location.reload();
38
+ }
39
+ };
40
+
41
+ socket.onclose = () => {
42
+ console.log('%c⚠️ HMR disconnected, reconnecting...', 'color: #f59e0b');
43
+ reconnectTimer = setTimeout(connect, 2000);
44
+ };
45
+ }
46
+
47
+ if (global.document) {
48
+ global.addEventListener('load', connect);
49
+ }
50
+
51
+ // Fast Refresh setup
52
+ if (typeof window !== 'undefined') {
53
+ window.$RefreshReg$ = (type, id) => {};
54
+ window.$RefreshSig$ = () => (type) => type;
55
+ }
56
+
57
+ })(typeof window !== 'undefined' ? window : global);
58
+
59
+ export const hmr = { connect: () => {} };
@@ -0,0 +1,25 @@
1
+ // bertui/src/compiler/index.js - NEW FILE (PURE, NO SERVER)
2
+ export { compileProject } from '../client/compiler.js';
3
+ export { compileForBuild } from '../build/compiler/index.js';
4
+ export { discoverRoutes } from '../build/compiler/route-discoverer.js';
5
+ export { validateServerIsland } from '../build/server-island-validator.js';
6
+
7
+ // PURE JSX→JS TRANSFORMATION
8
+ export async function transformJSX(sourceCode, options = {}) {
9
+ const transpiler = new Bun.Transpiler({
10
+ loader: options.loader || 'tsx',
11
+ target: 'browser',
12
+ define: {
13
+ 'process.env.NODE_ENV': JSON.stringify(options.env || 'development')
14
+ }
15
+ });
16
+ return await transpiler.transform(sourceCode);
17
+ }
18
+
19
+ // Export everything from this directory
20
+ export { transformJSX, transformJSXSync, containsJSX, removeCSSImports, removeDotenvImports, fixRelativeImports } from './transform.js';
21
+ export { generateRouterCode } from './router-generator-pure.js';
22
+ // Re-export existing
23
+ export { compileProject } from '../client/compiler.js';
24
+ export { compileForBuild } from '../build/compiler/index.js';
25
+ export { discoverRoutes } from '../build/compiler/route-discoverer.js';
@@ -0,0 +1,104 @@
1
+ // bertui/src/compiler/router-generator-pure.js - NEW FILE
2
+ // PURE function - no file system, no server
3
+
4
+ export function generateRouterCode(routes) {
5
+ const imports = routes.map((route, i) => {
6
+ const componentName = `Page${i}`;
7
+ const importPath = route.importPath || `./pages/${route.file.replace(/\.(jsx|tsx|ts)$/, '.js')}`;
8
+ return `import ${componentName} from '${importPath}';`;
9
+ }).join('\n');
10
+
11
+ const routeConfigs = routes.map((route, i) => {
12
+ const componentName = `Page${i}`;
13
+ return ` { path: '${route.route}', component: ${componentName}, type: '${route.type}' }`;
14
+ }).join(',\n');
15
+
16
+ return `import React, { useState, useEffect, createContext, useContext } from 'react';
17
+
18
+ const RouterContext = createContext(null);
19
+
20
+ export function useRouter() {
21
+ const context = useContext(RouterContext);
22
+ if (!context) throw new Error('useRouter must be used within a Router');
23
+ return context;
24
+ }
25
+
26
+ export function Router({ routes }) {
27
+ const [currentRoute, setCurrentRoute] = useState(null);
28
+ const [params, setParams] = useState({});
29
+
30
+ useEffect(() => {
31
+ matchAndSetRoute(window.location.pathname);
32
+ const handlePopState = () => matchAndSetRoute(window.location.pathname);
33
+ window.addEventListener('popstate', handlePopState);
34
+ return () => window.removeEventListener('popstate', handlePopState);
35
+ }, [routes]);
36
+
37
+ function matchAndSetRoute(pathname) {
38
+ // Static routes
39
+ for (const route of routes) {
40
+ if (route.type === 'static' && route.path === pathname) {
41
+ setCurrentRoute(route);
42
+ setParams({});
43
+ return;
44
+ }
45
+ }
46
+ // Dynamic routes
47
+ for (const route of routes) {
48
+ if (route.type === 'dynamic') {
49
+ const pattern = route.path.replace(/\\[([^\\]]+)\\]/g, '([^/]+)');
50
+ const regex = new RegExp('^' + pattern + '$');
51
+ const match = pathname.match(regex);
52
+ if (match) {
53
+ const paramNames = [...route.path.matchAll(/\\[([^\\]]+)\\]/g)].map(m => m[1]);
54
+ const extractedParams = {};
55
+ paramNames.forEach((name, i) => { extractedParams[name] = match[i + 1]; });
56
+ setCurrentRoute(route);
57
+ setParams(extractedParams);
58
+ return;
59
+ }
60
+ }
61
+ }
62
+ setCurrentRoute(null);
63
+ setParams({});
64
+ }
65
+
66
+ function navigate(path) {
67
+ window.history.pushState({}, '', path);
68
+ matchAndSetRoute(path);
69
+ }
70
+
71
+ const Component = currentRoute?.component;
72
+ return React.createElement(
73
+ RouterContext.Provider,
74
+ { value: { currentRoute, params, navigate, pathname: window.location.pathname } },
75
+ Component ? React.createElement(Component, { params }) : React.createElement(NotFound)
76
+ );
77
+ }
78
+
79
+ export function Link({ to, children, ...props }) {
80
+ const { navigate } = useRouter();
81
+ return React.createElement('a', {
82
+ href: to,
83
+ onClick: (e) => { e.preventDefault(); navigate(to); },
84
+ ...props
85
+ }, children);
86
+ }
87
+
88
+ function NotFound() {
89
+ return React.createElement('div', {
90
+ style: { display: 'flex', flexDirection: 'column', alignItems: 'center',
91
+ justifyContent: 'center', minHeight: '100vh', fontFamily: 'system-ui' }
92
+ },
93
+ React.createElement('h1', { style: { fontSize: '6rem', margin: 0 } }, '404'),
94
+ React.createElement('p', { style: { fontSize: '1.5rem', color: '#666' } }, 'Page not found'),
95
+ React.createElement('a', { href: '/', style: { color: '#10b981', textDecoration: 'none' } }, 'Go home')
96
+ );
97
+ }
98
+
99
+ ${imports}
100
+
101
+ export const routes = [
102
+ ${routeConfigs}
103
+ ];`;
104
+ }