bertui 1.2.9 → 2.0.0
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/README.md +44 -242
- package/TYPES_PATCH.md +17 -0
- package/bin/bertui.js +2 -7
- package/package.json +32 -98
- package/src/config.ts +4 -0
- package/src/index.ts +32 -0
- package/src/optional.ts +49 -0
- package/src/router.ts +3 -0
- package/tsconfig.json +29 -0
- package/LICENSE +0 -21
- package/index.js +0 -103
- package/src/analyzer/index.js +0 -370
- package/src/build/compiler/file-transpiler.js +0 -216
- package/src/build/compiler/index.js +0 -31
- package/src/build/compiler/route-discoverer.js +0 -49
- package/src/build/compiler/router-generator.js +0 -105
- package/src/build/css-builder.js +0 -81
- package/src/build/generators/html-generator.js +0 -263
- package/src/build/generators/robots-generator.js +0 -58
- package/src/build/generators/sitemap-generator.js +0 -63
- package/src/build/image-optimizer.js +0 -137
- package/src/build/processors/asset-processor.js +0 -19
- package/src/build/processors/css-builder.js +0 -142
- package/src/build/server-island-validator.js +0 -67
- package/src/build/ssr-renderer.js +0 -64
- package/src/build.js +0 -273
- package/src/cli.js +0 -131
- package/src/client/compiler.js +0 -522
- package/src/client/fast-refresh.js +0 -72
- package/src/client/hmr-runtime.js +0 -59
- package/src/compiler/index.js +0 -25
- package/src/compiler/router-generator-pure.js +0 -104
- package/src/compiler/transform.js +0 -149
- package/src/config/defaultConfig.js +0 -37
- package/src/config/index.js +0 -2
- package/src/config/loadConfig.js +0 -64
- package/src/config/og-image.png +0 -0
- package/src/css/index.js +0 -46
- package/src/css/processor.js +0 -172
- package/src/dev.js +0 -68
- package/src/hydration/index.js +0 -151
- package/src/image-optimizer/index.js +0 -103
- package/src/images/index.js +0 -102
- package/src/images/processor.js +0 -169
- package/src/layouts/index.js +0 -165
- package/src/loading/index.js +0 -210
- package/src/logger/logger.js +0 -320
- package/src/logger/notes.md +0 -20
- package/src/middleware/index.js +0 -182
- package/src/router/Router.js +0 -150
- package/src/router/SSRRouter.js +0 -156
- package/src/router/index.js +0 -3
- package/src/scaffolder/index.js +0 -310
- package/src/serve.js +0 -193
- package/src/server/dev-handler.js +0 -195
- package/src/server/dev-server-utils.js +0 -406
- package/src/server/dev-server.js +0 -15
- package/src/server/hmr-handler.js +0 -148
- package/src/server/index.js +0 -3
- package/src/server/notes.md +0 -1
- package/src/server/request-handler.js +0 -36
- package/src/server-islands/extractor.js +0 -198
- package/src/server-islands/index.js +0 -59
- package/src/styles/bertui.css +0 -210
- package/src/utils/cache.js +0 -297
- package/src/utils/env.js +0 -87
- package/src/utils/importhow.js +0 -52
- package/src/utils/index.js +0 -11
- package/src/utils/meta-extractor.js +0 -127
- package/types/bin/bertui.d.ts +0 -3
- package/types/bin/bertui.d.ts.map +0 -1
- package/types/error-overlay.d.ts +0 -2
- package/types/error-overlay.d.ts.map +0 -1
- package/types/index.d.ts +0 -26
- package/types/index.d.ts.map +0 -1
- package/types/scripts/fix-wasm-exports.d.ts +0 -2
- package/types/scripts/fix-wasm-exports.d.ts.map +0 -1
- package/types/src/analyzer/index.d.ts +0 -8
- package/types/src/analyzer/index.d.ts.map +0 -1
- package/types/src/build/compiler/file-transpiler.d.ts +0 -5
- package/types/src/build/compiler/file-transpiler.d.ts.map +0 -1
- package/types/src/build/compiler/index.d.ts +0 -12
- package/types/src/build/compiler/index.d.ts.map +0 -1
- package/types/src/build/compiler/route-discoverer.d.ts +0 -2
- package/types/src/build/compiler/route-discoverer.d.ts.map +0 -1
- package/types/src/build/compiler/router-generator.d.ts +0 -2
- package/types/src/build/compiler/router-generator.d.ts.map +0 -1
- package/types/src/build/css-builder.d.ts +0 -18
- package/types/src/build/css-builder.d.ts.map +0 -1
- package/types/src/build/generators/html-generator.d.ts +0 -2
- package/types/src/build/generators/html-generator.d.ts.map +0 -1
- package/types/src/build/generators/robots-generator.d.ts +0 -11
- package/types/src/build/generators/robots-generator.d.ts.map +0 -1
- package/types/src/build/generators/sitemap-generator.d.ts +0 -5
- package/types/src/build/generators/sitemap-generator.d.ts.map +0 -1
- package/types/src/build/image-optimizer.d.ts +0 -11
- package/types/src/build/image-optimizer.d.ts.map +0 -1
- package/types/src/build/processors/asset-processor.d.ts +0 -2
- package/types/src/build/processors/asset-processor.d.ts.map +0 -1
- package/types/src/build/processors/css-builder.d.ts +0 -2
- package/types/src/build/processors/css-builder.d.ts.map +0 -1
- package/types/src/build/server-island-validator.d.ts +0 -27
- package/types/src/build/server-island-validator.d.ts.map +0 -1
- package/types/src/build.d.ts +0 -5
- package/types/src/build.d.ts.map +0 -1
- package/types/src/cli.d.ts +0 -2
- package/types/src/cli.d.ts.map +0 -1
- package/types/src/client/compiler.d.ts +0 -16
- package/types/src/client/compiler.d.ts.map +0 -1
- package/types/src/client/fast-refresh.d.ts +0 -3
- package/types/src/client/fast-refresh.d.ts.map +0 -1
- package/types/src/client/hmr-runtime.d.ts +0 -4
- package/types/src/client/hmr-runtime.d.ts.map +0 -1
- package/types/src/compiler/index.d.ts +0 -8
- package/types/src/compiler/index.d.ts.map +0 -1
- package/types/src/compiler/router-generator-pure.d.ts +0 -2
- package/types/src/compiler/router-generator-pure.d.ts.map +0 -1
- package/types/src/compiler/transform.d.ts +0 -36
- package/types/src/compiler/transform.d.ts.map +0 -1
- package/types/src/config/defaultConfig.d.ts +0 -26
- package/types/src/config/defaultConfig.d.ts.map +0 -1
- package/types/src/config/index.d.ts +0 -3
- package/types/src/config/index.d.ts.map +0 -1
- package/types/src/config/loadConfig.d.ts +0 -2
- package/types/src/config/loadConfig.d.ts.map +0 -1
- package/types/src/css/index.d.ts +0 -6
- package/types/src/css/index.d.ts.map +0 -1
- package/types/src/css/processor.d.ts +0 -23
- package/types/src/css/processor.d.ts.map +0 -1
- package/types/src/dev.d.ts +0 -2
- package/types/src/dev.d.ts.map +0 -1
- package/types/src/hydration/index.d.ts +0 -33
- package/types/src/hydration/index.d.ts.map +0 -1
- package/types/src/image-optimizer/index.d.ts +0 -24
- package/types/src/image-optimizer/index.d.ts.map +0 -1
- package/types/src/images/index.d.ts +0 -12
- package/types/src/images/index.d.ts.map +0 -1
- package/types/src/images/processor.d.ts +0 -30
- package/types/src/images/processor.d.ts.map +0 -1
- package/types/src/layouts/index.d.ts +0 -28
- package/types/src/layouts/index.d.ts.map +0 -1
- package/types/src/loading/index.d.ts +0 -28
- package/types/src/loading/index.d.ts.map +0 -1
- package/types/src/logger/logger.d.ts +0 -30
- package/types/src/logger/logger.d.ts.map +0 -1
- package/types/src/middleware/index.d.ts +0 -61
- package/types/src/middleware/index.d.ts.map +0 -1
- package/types/src/router/Router.d.ts +0 -16
- package/types/src/router/Router.d.ts.map +0 -1
- package/types/src/router/SSRRouter.d.ts +0 -20
- package/types/src/router/SSRRouter.d.ts.map +0 -1
- package/types/src/router/index.d.ts +0 -3
- package/types/src/router/index.d.ts.map +0 -1
- package/types/src/scaffolder/index.d.ts +0 -14
- package/types/src/scaffolder/index.d.ts.map +0 -1
- package/types/src/serve.d.ts +0 -3
- package/types/src/serve.d.ts.map +0 -1
- package/types/src/server/dev-handler.d.ts +0 -13
- package/types/src/server/dev-handler.d.ts.map +0 -1
- package/types/src/server/dev-server-utils.d.ts +0 -6
- package/types/src/server/dev-server-utils.d.ts.map +0 -1
- package/types/src/server/dev-server.d.ts +0 -18
- package/types/src/server/dev-server.d.ts.map +0 -1
- package/types/src/server/hmr-handler.d.ts +0 -19
- package/types/src/server/hmr-handler.d.ts.map +0 -1
- package/types/src/server/index.d.ts +0 -4
- package/types/src/server/index.d.ts.map +0 -1
- package/types/src/server/request-handler.d.ts +0 -19
- package/types/src/server/request-handler.d.ts.map +0 -1
- package/types/src/server-islands/extractor.d.ts +0 -16
- package/types/src/server-islands/extractor.d.ts.map +0 -1
- package/types/src/server-islands/index.d.ts +0 -3
- package/types/src/server-islands/index.d.ts.map +0 -1
- package/types/src/utils/cache.d.ts +0 -52
- package/types/src/utils/cache.d.ts.map +0 -1
- package/types/src/utils/env.d.ts +0 -20
- package/types/src/utils/env.d.ts.map +0 -1
- package/types/src/utils/importhow.d.ts +0 -15
- package/types/src/utils/importhow.d.ts.map +0 -1
- package/types/src/utils/index.d.ts +0 -3
- package/types/src/utils/index.d.ts.map +0 -1
- package/types/src/utils/meta-extractor.d.ts +0 -13
- package/types/src/utils/meta-extractor.d.ts.map +0 -1
|
@@ -1,406 +0,0 @@
|
|
|
1
|
-
// bertui/src/server/dev-server-utils.js
|
|
2
|
-
import { join, extname } from 'path';
|
|
3
|
-
import { existsSync, readdirSync, watch, statSync } from 'fs';
|
|
4
|
-
import logger from '../logger/logger.js';
|
|
5
|
-
import { compileProject } from '../client/compiler.js';
|
|
6
|
-
import { globalCache } from '../utils/cache.js';
|
|
7
|
-
|
|
8
|
-
// Image content type mapping
|
|
9
|
-
export function getImageContentType(ext) {
|
|
10
|
-
const types = {
|
|
11
|
-
'.jpg': 'image/jpeg',
|
|
12
|
-
'.jpeg': 'image/jpeg',
|
|
13
|
-
'.png': 'image/png',
|
|
14
|
-
'.gif': 'image/gif',
|
|
15
|
-
'.svg': 'image/svg+xml',
|
|
16
|
-
'.webp': 'image/webp',
|
|
17
|
-
'.avif': 'image/avif',
|
|
18
|
-
'.ico': 'image/x-icon',
|
|
19
|
-
};
|
|
20
|
-
return types[ext] || 'application/octet-stream';
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
// General content type mapping
|
|
24
|
-
export function getContentType(ext) {
|
|
25
|
-
const types = {
|
|
26
|
-
'.js': 'application/javascript',
|
|
27
|
-
'.jsx': 'application/javascript',
|
|
28
|
-
'.css': 'text/css',
|
|
29
|
-
'.html': 'text/html',
|
|
30
|
-
'.json': 'application/json',
|
|
31
|
-
'.png': 'image/png',
|
|
32
|
-
'.jpg': 'image/jpeg',
|
|
33
|
-
'.jpeg': 'image/jpeg',
|
|
34
|
-
'.gif': 'image/gif',
|
|
35
|
-
'.svg': 'image/svg+xml',
|
|
36
|
-
'.webp': 'image/webp',
|
|
37
|
-
'.avif': 'image/avif',
|
|
38
|
-
'.ico': 'image/x-icon',
|
|
39
|
-
'.woff': 'font/woff',
|
|
40
|
-
'.woff2':'font/woff2',
|
|
41
|
-
'.ttf': 'font/ttf',
|
|
42
|
-
'.otf': 'font/otf',
|
|
43
|
-
'.mp4': 'video/mp4',
|
|
44
|
-
'.webm': 'video/webm',
|
|
45
|
-
'.mp3': 'audio/mpeg',
|
|
46
|
-
};
|
|
47
|
-
return types[ext] || 'text/plain';
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
51
|
-
// Import map builder — scans node_modules at runtime so newly installed
|
|
52
|
-
// packages are picked up without restarting the dev server.
|
|
53
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
54
|
-
|
|
55
|
-
// Cached importmap + the package.json mtime it was built from
|
|
56
|
-
let _cachedImportMap = null;
|
|
57
|
-
let _cachedPkgMtime = null;
|
|
58
|
-
|
|
59
|
-
export async function buildDevImportMap(root) {
|
|
60
|
-
const pkgJsonPath = join(root, 'package.json');
|
|
61
|
-
const nodeModulesDir = join(root, 'node_modules');
|
|
62
|
-
|
|
63
|
-
// Invalidate cache when package.json changes (new install happened)
|
|
64
|
-
let currentMtime = null;
|
|
65
|
-
try {
|
|
66
|
-
currentMtime = statSync(pkgJsonPath).mtimeMs;
|
|
67
|
-
} catch { /* package.json missing — fine */ }
|
|
68
|
-
|
|
69
|
-
if (_cachedImportMap && currentMtime === _cachedPkgMtime) {
|
|
70
|
-
return _cachedImportMap;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
logger.info('🔄 Rebuilding dev import map (new packages detected)...');
|
|
74
|
-
|
|
75
|
-
// Initialize importMap with default mappings
|
|
76
|
-
const importMap = {
|
|
77
|
-
'react': 'https://esm.sh/react@18.2.0',
|
|
78
|
-
'react-dom': 'https://esm.sh/react-dom@18.2.0',
|
|
79
|
-
'react-dom/client': 'https://esm.sh/react-dom@18.2.0/client',
|
|
80
|
-
'react/jsx-runtime': 'https://esm.sh/react@18.2.0/jsx-runtime',
|
|
81
|
-
'react/jsx-dev-runtime': 'https://esm.sh/react@18.2.0/jsx-dev-runtime',
|
|
82
|
-
'@bunnyx/api': '/bunnyx-api/api-client.js',
|
|
83
|
-
'@elysiajs/eden': '/node_modules/@elysiajs/eden/dist/index.mjs',
|
|
84
|
-
};
|
|
85
|
-
|
|
86
|
-
const SKIP = new Set(['react', 'react-dom', '.bin', '.cache', '.package-lock.json', '.yarn']);
|
|
87
|
-
|
|
88
|
-
if (existsSync(nodeModulesDir)) {
|
|
89
|
-
try {
|
|
90
|
-
const packages = readdirSync(nodeModulesDir);
|
|
91
|
-
|
|
92
|
-
for (const pkg of packages) {
|
|
93
|
-
if (SKIP.has(pkg) || pkg.startsWith('.') || pkg.startsWith('_')) continue;
|
|
94
|
-
|
|
95
|
-
// Handle scoped packages (@org/pkg)
|
|
96
|
-
let pkgNames = [pkg];
|
|
97
|
-
if (pkg.startsWith('@')) {
|
|
98
|
-
const scopeDir = join(nodeModulesDir, pkg);
|
|
99
|
-
try {
|
|
100
|
-
if (statSync(scopeDir).isDirectory()) {
|
|
101
|
-
pkgNames = readdirSync(scopeDir).map(sub => `${pkg}/${sub}`);
|
|
102
|
-
}
|
|
103
|
-
} catch { continue; }
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
for (const pkgName of pkgNames) {
|
|
107
|
-
const pkgDir = join(nodeModulesDir, pkgName);
|
|
108
|
-
const pkgJsonFile = join(pkgDir, 'package.json');
|
|
109
|
-
|
|
110
|
-
try {
|
|
111
|
-
if (!statSync(pkgDir).isDirectory()) continue;
|
|
112
|
-
} catch { continue; }
|
|
113
|
-
|
|
114
|
-
if (!existsSync(pkgJsonFile)) continue;
|
|
115
|
-
|
|
116
|
-
try {
|
|
117
|
-
const pkgJson = JSON.parse(await Bun.file(pkgJsonFile).text());
|
|
118
|
-
|
|
119
|
-
const possibleEntries = [
|
|
120
|
-
pkgJson.module,
|
|
121
|
-
pkgJson.browser,
|
|
122
|
-
pkgJson.main,
|
|
123
|
-
'dist/index.js',
|
|
124
|
-
'lib/index.js',
|
|
125
|
-
'index.js',
|
|
126
|
-
].filter(Boolean);
|
|
127
|
-
|
|
128
|
-
for (const entry of possibleEntries) {
|
|
129
|
-
const fullPath = join(pkgDir, entry);
|
|
130
|
-
if (existsSync(fullPath)) {
|
|
131
|
-
importMap[pkgName] = `/node_modules/${pkgName}/${entry}`;
|
|
132
|
-
logger.debug(`📦 Dev map: ${pkgName} → ${importMap[pkgName]}`);
|
|
133
|
-
break;
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
} catch { continue; }
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
} catch (error) {
|
|
140
|
-
logger.warn(`Failed to scan node_modules: ${error.message}`);
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
_cachedImportMap = importMap;
|
|
145
|
-
_cachedPkgMtime = currentMtime;
|
|
146
|
-
|
|
147
|
-
logger.success(`✅ Import map ready (${Object.keys(importMap).length} packages)`);
|
|
148
|
-
return importMap;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
152
|
-
// HTML generator — uses the live importmap so newly installed packages
|
|
153
|
-
// appear in the next page load without a server restart.
|
|
154
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
155
|
-
export async function serveHTML(root, hasRouter, config, port) {
|
|
156
|
-
// Don't cache HTML anymore — importmap can change between requests
|
|
157
|
-
const meta = config.meta || {};
|
|
158
|
-
|
|
159
|
-
const srcStylesDir = join(root, 'src', 'styles');
|
|
160
|
-
let userStylesheets = '';
|
|
161
|
-
|
|
162
|
-
if (existsSync(srcStylesDir)) {
|
|
163
|
-
try {
|
|
164
|
-
const cssFiles = readdirSync(srcStylesDir).filter(f => f.endsWith('.css'));
|
|
165
|
-
userStylesheets = cssFiles
|
|
166
|
-
.map(f => ` <link rel="stylesheet" href="/styles/${f}">`)
|
|
167
|
-
.join('\n');
|
|
168
|
-
} catch (error) {
|
|
169
|
-
logger.warn(`Could not read styles directory: ${error.message}`);
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
// Auto-detect bertui-animate CSS
|
|
174
|
-
let bertuiAnimateStylesheet = '';
|
|
175
|
-
const bertuiAnimatePath = join(root, 'node_modules/bertui-animate/dist/bertui-animate.min.css');
|
|
176
|
-
if (existsSync(bertuiAnimatePath)) {
|
|
177
|
-
bertuiAnimateStylesheet = ' <link rel="stylesheet" href="/bertui-animate.css">';
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
// ✅ Always get the latest importmap (cached until package.json changes)
|
|
181
|
-
const importMap = await buildDevImportMap(root);
|
|
182
|
-
|
|
183
|
-
return `<!DOCTYPE html>
|
|
184
|
-
<html lang="${meta.lang || 'en'}">
|
|
185
|
-
<head>
|
|
186
|
-
<meta charset="UTF-8">
|
|
187
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
188
|
-
<title>${meta.title || 'BertUI App'}</title>
|
|
189
|
-
|
|
190
|
-
${meta.description ? `<meta name="description" content="${meta.description}">` : ''}
|
|
191
|
-
${meta.keywords ? `<meta name="keywords" content="${meta.keywords}">` : ''}
|
|
192
|
-
${meta.author ? `<meta name="author" content="${meta.author}">` : ''}
|
|
193
|
-
${meta.themeColor ? `<meta name="theme-color" content="${meta.themeColor}">` : ''}
|
|
194
|
-
|
|
195
|
-
${meta.ogTitle ? `<meta property="og:title" content="${meta.ogTitle || meta.title}">` : ''}
|
|
196
|
-
${meta.ogDescription ? `<meta property="og:description" content="${meta.ogDescription || meta.description}">` : ''}
|
|
197
|
-
${meta.ogImage ? `<meta property="og:image" content="${meta.ogImage}">` : ''}
|
|
198
|
-
|
|
199
|
-
<link rel="icon" type="image/svg+xml" href="/public/favicon.svg">
|
|
200
|
-
|
|
201
|
-
${userStylesheets}
|
|
202
|
-
${bertuiAnimateStylesheet}
|
|
203
|
-
|
|
204
|
-
<script type="importmap">
|
|
205
|
-
${JSON.stringify({ imports: importMap }, null, 2)}
|
|
206
|
-
</script>
|
|
207
|
-
|
|
208
|
-
<style>
|
|
209
|
-
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
210
|
-
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; }
|
|
211
|
-
</style>
|
|
212
|
-
</head>
|
|
213
|
-
<body>
|
|
214
|
-
<div id="root"></div>
|
|
215
|
-
|
|
216
|
-
<script type="module">
|
|
217
|
-
const ws = new WebSocket('ws://localhost:${port}/__hmr');
|
|
218
|
-
|
|
219
|
-
ws.onopen = () => {
|
|
220
|
-
console.log('%c🔥 BertUI HMR connected', 'color: #10b981; font-weight: bold');
|
|
221
|
-
};
|
|
222
|
-
|
|
223
|
-
ws.onmessage = (event) => {
|
|
224
|
-
const data = JSON.parse(event.data);
|
|
225
|
-
|
|
226
|
-
if (data.type === 'reload') {
|
|
227
|
-
console.log('%c🔄 Reloading...', 'color: #f59e0b; font-weight: bold');
|
|
228
|
-
if (window.__BERTUI_HIDE_ERROR__) window.__BERTUI_HIDE_ERROR__();
|
|
229
|
-
window.location.reload();
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
if (data.type === 'recompiling') {
|
|
233
|
-
console.log('%c⚙️ Recompiling...', 'color: #3b82f6');
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
if (data.type === 'compiled') {
|
|
237
|
-
console.log('%c✅ Compilation complete', 'color: #10b981');
|
|
238
|
-
if (window.__BERTUI_HIDE_ERROR__) window.__BERTUI_HIDE_ERROR__();
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
// ✅ New: server tells browser the importmap changed → full reload
|
|
242
|
-
if (data.type === 'importmap-updated') {
|
|
243
|
-
console.log('%c📦 New packages detected — reloading...', 'color: #8b5cf6; font-weight: bold');
|
|
244
|
-
window.location.reload();
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
if (data.type === 'compilation-error') {
|
|
248
|
-
if (window.__BERTUI_SHOW_ERROR__) {
|
|
249
|
-
window.__BERTUI_SHOW_ERROR__({
|
|
250
|
-
type: 'Compilation Error',
|
|
251
|
-
message: data.message,
|
|
252
|
-
stack: data.stack,
|
|
253
|
-
file: data.file,
|
|
254
|
-
line: data.line,
|
|
255
|
-
column: data.column,
|
|
256
|
-
});
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
};
|
|
260
|
-
|
|
261
|
-
ws.onerror = (error) => {
|
|
262
|
-
console.error('%c❌ HMR connection error', 'color: #ef4444', error);
|
|
263
|
-
};
|
|
264
|
-
|
|
265
|
-
ws.onclose = () => {
|
|
266
|
-
console.log('%c⚠️ HMR disconnected. Refresh to reconnect.', 'color: #f59e0b');
|
|
267
|
-
};
|
|
268
|
-
</script>
|
|
269
|
-
|
|
270
|
-
<script src="/error-overlay.js"></script>
|
|
271
|
-
<script type="module" src="/compiled/main.js"></script>
|
|
272
|
-
</body>
|
|
273
|
-
</html>`;
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
277
|
-
// File watcher — watches src/ for code changes AND package.json for
|
|
278
|
-
// new installs. When package.json changes it invalidates the importmap
|
|
279
|
-
// cache and sends an `importmap-updated` message so the browser reloads
|
|
280
|
-
// with the new packages — no server restart needed.
|
|
281
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
282
|
-
export function setupFileWatcher(root, compiledDir, clients, onRecompile) {
|
|
283
|
-
const srcDir = join(root, 'src');
|
|
284
|
-
const pkgJson = join(root, 'package.json');
|
|
285
|
-
const configPath = join(root, 'bertui.config.js');
|
|
286
|
-
|
|
287
|
-
if (!existsSync(srcDir)) {
|
|
288
|
-
logger.warn('src/ directory not found');
|
|
289
|
-
return () => {};
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
logger.debug(`👀 Watching: ${srcDir}`);
|
|
293
|
-
logger.debug(`👀 Watching: ${pkgJson} (package installs)`);
|
|
294
|
-
|
|
295
|
-
let isRecompiling = false;
|
|
296
|
-
let recompileTimeout = null;
|
|
297
|
-
const watchedExtensions = [
|
|
298
|
-
'.js', '.jsx', '.ts', '.tsx', '.css',
|
|
299
|
-
'.png', '.jpg', '.jpeg', '.gif', '.svg', '.webp', '.avif',
|
|
300
|
-
];
|
|
301
|
-
|
|
302
|
-
function notifyClients(message) {
|
|
303
|
-
for (const client of clients) {
|
|
304
|
-
try {
|
|
305
|
-
client.send(JSON.stringify(message));
|
|
306
|
-
} catch {
|
|
307
|
-
clients.delete(client);
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
// ── Source file watcher ──────────────────────────────────────────────────
|
|
313
|
-
const srcWatcher = watch(srcDir, { recursive: true }, async (eventType, filename) => {
|
|
314
|
-
if (!filename) return;
|
|
315
|
-
const ext = extname(filename);
|
|
316
|
-
if (!watchedExtensions.includes(ext)) return;
|
|
317
|
-
|
|
318
|
-
logger.debug(`📝 File changed: ${filename}`);
|
|
319
|
-
clearTimeout(recompileTimeout);
|
|
320
|
-
|
|
321
|
-
recompileTimeout = setTimeout(async () => {
|
|
322
|
-
if (isRecompiling) return;
|
|
323
|
-
isRecompiling = true;
|
|
324
|
-
notifyClients({ type: 'recompiling' });
|
|
325
|
-
|
|
326
|
-
try {
|
|
327
|
-
await compileProject(root);
|
|
328
|
-
if (onRecompile) await onRecompile();
|
|
329
|
-
logger.success('✅ Recompiled successfully');
|
|
330
|
-
notifyClients({ type: 'compiled' });
|
|
331
|
-
setTimeout(() => notifyClients({ type: 'reload' }), 100);
|
|
332
|
-
} catch (error) {
|
|
333
|
-
logger.error(`Recompilation failed: ${error.message}`);
|
|
334
|
-
notifyClients({
|
|
335
|
-
type: 'compilation-error',
|
|
336
|
-
message: error.message,
|
|
337
|
-
stack: error.stack || null,
|
|
338
|
-
file: error.file || null,
|
|
339
|
-
line: error.line || null,
|
|
340
|
-
column: error.column || null,
|
|
341
|
-
});
|
|
342
|
-
} finally {
|
|
343
|
-
isRecompiling = false;
|
|
344
|
-
}
|
|
345
|
-
}, 150);
|
|
346
|
-
});
|
|
347
|
-
|
|
348
|
-
// ── package.json watcher — detects new npm/bun installs ─────────────────
|
|
349
|
-
let pkgWatcher = null;
|
|
350
|
-
let lastPkgMtime = null;
|
|
351
|
-
|
|
352
|
-
if (existsSync(pkgJson)) {
|
|
353
|
-
try {
|
|
354
|
-
lastPkgMtime = statSync(pkgJson).mtimeMs;
|
|
355
|
-
} catch { /* ignore */ }
|
|
356
|
-
|
|
357
|
-
pkgWatcher = watch(pkgJson, async (eventType) => {
|
|
358
|
-
if (eventType !== 'change') return;
|
|
359
|
-
|
|
360
|
-
// Debounce — installs can trigger multiple change events
|
|
361
|
-
clearTimeout(pkgWatcher._debounce);
|
|
362
|
-
pkgWatcher._debounce = setTimeout(async () => {
|
|
363
|
-
try {
|
|
364
|
-
const newMtime = statSync(pkgJson).mtimeMs;
|
|
365
|
-
if (newMtime === lastPkgMtime) return; // spurious event
|
|
366
|
-
lastPkgMtime = newMtime;
|
|
367
|
-
|
|
368
|
-
logger.info('📦 package.json changed — refreshing import map...');
|
|
369
|
-
|
|
370
|
-
// Bust the importmap cache so next serveHTML call rebuilds it
|
|
371
|
-
_cachedImportMap = null;
|
|
372
|
-
_cachedPkgMtime = null;
|
|
373
|
-
|
|
374
|
-
// Wait briefly for node_modules to finish writing
|
|
375
|
-
await new Promise(r => setTimeout(r, 800));
|
|
376
|
-
|
|
377
|
-
// Rebuild and notify browser
|
|
378
|
-
await buildDevImportMap(root);
|
|
379
|
-
notifyClients({ type: 'importmap-updated' });
|
|
380
|
-
|
|
381
|
-
logger.success('✅ Import map updated — browser reloading');
|
|
382
|
-
} catch (err) {
|
|
383
|
-
logger.warn(`package.json watch error: ${err.message}`);
|
|
384
|
-
}
|
|
385
|
-
}, 500);
|
|
386
|
-
});
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
// ── bertui.config.js watcher ─────────────────────────────────────────────
|
|
390
|
-
let configWatcher = null;
|
|
391
|
-
if (existsSync(configPath)) {
|
|
392
|
-
configWatcher = watch(configPath, (eventType) => {
|
|
393
|
-
if (eventType === 'change') {
|
|
394
|
-
logger.debug('📝 Config changed, reloading...');
|
|
395
|
-
notifyClients({ type: 'reload' });
|
|
396
|
-
}
|
|
397
|
-
});
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
// Return cleanup function
|
|
401
|
-
return () => {
|
|
402
|
-
srcWatcher.close();
|
|
403
|
-
if (pkgWatcher) pkgWatcher.close();
|
|
404
|
-
if (configWatcher) configWatcher.close();
|
|
405
|
-
};
|
|
406
|
-
}
|
package/src/server/dev-server.js
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
// bertui/src/server/dev-server.js - MODIFIED
|
|
2
|
-
// Now uses the new modular dev-handler
|
|
3
|
-
|
|
4
|
-
import { createDevHandler } from './dev-handler.js';
|
|
5
|
-
import logger from '../logger/logger.js';
|
|
6
|
-
|
|
7
|
-
export async function startDevServer(options = {}) {
|
|
8
|
-
const handler = await createDevHandler(options);
|
|
9
|
-
const server = await handler.start();
|
|
10
|
-
return { handler, server };
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
// Re-export for backward compatibility
|
|
14
|
-
export { createDevHandler } from './dev-handler.js';
|
|
15
|
-
export { handleRequest } from './request-handler.js';
|
|
@@ -1,148 +0,0 @@
|
|
|
1
|
-
// bertui/src/server/hmr-handler.js
|
|
2
|
-
// HMR WebSocket handler for development server
|
|
3
|
-
|
|
4
|
-
import logger from '../logger/logger.js';
|
|
5
|
-
import { compileFile } from '../client/compiler.js';
|
|
6
|
-
|
|
7
|
-
export class HMRHandler {
|
|
8
|
-
constructor(root) {
|
|
9
|
-
this.root = root;
|
|
10
|
-
this.clients = new Set();
|
|
11
|
-
this.compilationQueue = new Map();
|
|
12
|
-
this.pendingUpdates = new Set();
|
|
13
|
-
this.isProcessing = false;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
// WebSocket connection handler
|
|
17
|
-
onOpen(ws) {
|
|
18
|
-
this.clients.add(ws);
|
|
19
|
-
logger.debug(`HMR client connected (${this.clients.size} total)`);
|
|
20
|
-
|
|
21
|
-
// Send initial state
|
|
22
|
-
ws.send(JSON.stringify({
|
|
23
|
-
type: 'hmr-connected',
|
|
24
|
-
timestamp: Date.now()
|
|
25
|
-
}));
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
onClose(ws) {
|
|
29
|
-
this.clients.delete(ws);
|
|
30
|
-
logger.debug(`HMR client disconnected (${this.clients.size} remaining)`);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
onMessage(ws, message) {
|
|
34
|
-
try {
|
|
35
|
-
const data = JSON.parse(message);
|
|
36
|
-
|
|
37
|
-
switch (data.type) {
|
|
38
|
-
case 'hmr-accept':
|
|
39
|
-
this.handleAccept(data.module);
|
|
40
|
-
break;
|
|
41
|
-
case 'hmr-decline':
|
|
42
|
-
this.handleDecline(data.module);
|
|
43
|
-
break;
|
|
44
|
-
}
|
|
45
|
-
} catch (err) {
|
|
46
|
-
logger.error(`HMR message error: ${err.message}`);
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// Notify all clients
|
|
51
|
-
notifyAll(message) {
|
|
52
|
-
const payload = JSON.stringify(message);
|
|
53
|
-
|
|
54
|
-
for (const client of this.clients) {
|
|
55
|
-
try {
|
|
56
|
-
client.send(payload);
|
|
57
|
-
} catch (err) {
|
|
58
|
-
this.clients.delete(client);
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
// Queue file for recompilation
|
|
64
|
-
queueRecompile(filename) {
|
|
65
|
-
this.pendingUpdates.add(filename);
|
|
66
|
-
|
|
67
|
-
if (!this.isProcessing) {
|
|
68
|
-
setTimeout(() => this.processQueue(), 50);
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
// Process compilation queue
|
|
73
|
-
async processQueue() {
|
|
74
|
-
if (this.isProcessing || this.pendingUpdates.size === 0) return;
|
|
75
|
-
|
|
76
|
-
this.isProcessing = true;
|
|
77
|
-
const files = Array.from(this.pendingUpdates);
|
|
78
|
-
this.pendingUpdates.clear();
|
|
79
|
-
|
|
80
|
-
try {
|
|
81
|
-
const start = Date.now();
|
|
82
|
-
const results = [];
|
|
83
|
-
|
|
84
|
-
for (const file of files) {
|
|
85
|
-
try {
|
|
86
|
-
const result = await compileFile(file, this.root);
|
|
87
|
-
results.push(result);
|
|
88
|
-
} catch (err) {
|
|
89
|
-
logger.error(`HMR compile error: ${file} - ${err.message}`);
|
|
90
|
-
|
|
91
|
-
// Send error overlay
|
|
92
|
-
this.notifyAll({
|
|
93
|
-
type: 'compilation-error',
|
|
94
|
-
file,
|
|
95
|
-
message: err.message,
|
|
96
|
-
stack: err.stack
|
|
97
|
-
});
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
const duration = Date.now() - start;
|
|
102
|
-
|
|
103
|
-
// Notify clients of updates
|
|
104
|
-
for (const result of results) {
|
|
105
|
-
if (result && result.outputPath) {
|
|
106
|
-
this.notifyAll({
|
|
107
|
-
type: 'hmr-update',
|
|
108
|
-
module: `/compiled/${result.outputPath}`,
|
|
109
|
-
time: duration / results.length,
|
|
110
|
-
dependencies: result.dependencies || []
|
|
111
|
-
});
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
logger.success(`✅ HMR updated ${results.length} files in ${duration}ms`);
|
|
116
|
-
|
|
117
|
-
} catch (err) {
|
|
118
|
-
logger.error(`HMR queue error: ${err.message}`);
|
|
119
|
-
this.notifyAll({ type: 'full-reload' });
|
|
120
|
-
} finally {
|
|
121
|
-
this.isProcessing = false;
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
// Handle HMR accept
|
|
126
|
-
handleAccept(moduleId) {
|
|
127
|
-
// Track accepted modules if needed
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
// Handle HMR decline
|
|
131
|
-
handleDecline(moduleId) {
|
|
132
|
-
// Force full reload for declined modules
|
|
133
|
-
this.notifyAll({ type: 'full-reload' });
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
// Force full reload
|
|
137
|
-
reload() {
|
|
138
|
-
this.notifyAll({ type: 'full-reload' });
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
// Dispose handler
|
|
142
|
-
dispose() {
|
|
143
|
-
this.notifyAll({ type: 'hmr-shutdown' });
|
|
144
|
-
this.clients.clear();
|
|
145
|
-
this.pendingUpdates.clear();
|
|
146
|
-
this.compilationQueue.clear();
|
|
147
|
-
}
|
|
148
|
-
}
|
package/src/server/index.js
DELETED
package/src/server/notes.md
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
here i want to try and impliment the creation of a dev server using elisia bun library
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
// bertui/src/server/request-handler.js - NEW FILE
|
|
2
|
-
// Standalone request handler for BertUI
|
|
3
|
-
|
|
4
|
-
import { createDevHandler } from './dev-handler.js';
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Handle a single HTTP request with BertUI
|
|
8
|
-
* @param {Request} request - The HTTP request
|
|
9
|
-
* @param {Object} options - Options
|
|
10
|
-
* @param {string} options.root - Project root directory
|
|
11
|
-
* @param {number} options.port - Port number
|
|
12
|
-
* @returns {Promise<Response|null>} Response or null if not handled
|
|
13
|
-
*/
|
|
14
|
-
export async function handleRequest(request, options = {}) {
|
|
15
|
-
const handler = await createDevHandler(options);
|
|
16
|
-
return handler.handleRequest(request);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Create a middleware function for Elysia
|
|
21
|
-
* @param {Object} options - Options
|
|
22
|
-
* @returns {Function} Elysia middleware
|
|
23
|
-
*/
|
|
24
|
-
export function createElysiaMiddleware(options = {}) {
|
|
25
|
-
return async (ctx) => {
|
|
26
|
-
const response = await handleRequest(ctx.request, options);
|
|
27
|
-
if (response) {
|
|
28
|
-
ctx.set.status = response.status;
|
|
29
|
-
response.headers.forEach((value, key) => {
|
|
30
|
-
ctx.set.headers[key] = value;
|
|
31
|
-
});
|
|
32
|
-
return response.text();
|
|
33
|
-
}
|
|
34
|
-
return; // Continue to next middleware
|
|
35
|
-
};
|
|
36
|
-
}
|