@vertz/ui-server 0.2.3 → 0.2.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bun-dev-server.d.ts +8 -1
- package/dist/bun-dev-server.js +364 -149
- package/dist/bun-plugin/index.d.ts +76 -0
- package/dist/bun-plugin/index.js +21 -2
- package/package.json +4 -4
package/dist/bun-dev-server.d.ts
CHANGED
|
@@ -23,6 +23,12 @@ interface BunDevServerOptions {
|
|
|
23
23
|
projectRoot?: string;
|
|
24
24
|
/** Log requests. @default true */
|
|
25
25
|
logRequests?: boolean;
|
|
26
|
+
/**
|
|
27
|
+
* Editor for error overlay links. Auto-detected from VERTZ_EDITOR or EDITOR env vars.
|
|
28
|
+
* Supported: 'vscode' | 'cursor' | 'webstorm' | 'zed'
|
|
29
|
+
* @default 'vscode'
|
|
30
|
+
*/
|
|
31
|
+
editor?: string;
|
|
26
32
|
}
|
|
27
33
|
interface ErrorDetail {
|
|
28
34
|
message: string;
|
|
@@ -73,11 +79,12 @@ interface SSRPageHtmlOptions {
|
|
|
73
79
|
bodyHtml: string;
|
|
74
80
|
ssrData: unknown[];
|
|
75
81
|
scriptTag: string;
|
|
82
|
+
editor?: string;
|
|
76
83
|
}
|
|
77
84
|
/**
|
|
78
85
|
* Generate a full SSR HTML page with the given content, CSS, SSR data, and script tag.
|
|
79
86
|
*/
|
|
80
|
-
declare function generateSSRPageHtml({ title, css, bodyHtml, ssrData, scriptTag }: SSRPageHtmlOptions): string;
|
|
87
|
+
declare function generateSSRPageHtml({ title, css, bodyHtml, ssrData, scriptTag, editor }: SSRPageHtmlOptions): string;
|
|
81
88
|
interface FetchInterceptorOptions {
|
|
82
89
|
apiHandler: (req: Request) => Promise<Response>;
|
|
83
90
|
origin: string;
|
package/dist/bun-dev-server.js
CHANGED
|
@@ -5,9 +5,143 @@ import {
|
|
|
5
5
|
|
|
6
6
|
// src/bun-dev-server.ts
|
|
7
7
|
import { execSync } from "child_process";
|
|
8
|
-
import { existsSync, mkdirSync, readFileSync as readFileSync2, renameSync, watch, writeFileSync } from "fs";
|
|
8
|
+
import { existsSync, mkdirSync, readFileSync as readFileSync2, renameSync, watch, writeFileSync as writeFileSync2 } from "fs";
|
|
9
9
|
import { dirname, normalize, resolve } from "path";
|
|
10
10
|
|
|
11
|
+
// src/debug-logger.ts
|
|
12
|
+
import { appendFileSync, writeFileSync } from "fs";
|
|
13
|
+
import { join } from "path";
|
|
14
|
+
function createDebugLogger(logDir) {
|
|
15
|
+
const envValue = process.env.VERTZ_DEBUG;
|
|
16
|
+
if (!envValue) {
|
|
17
|
+
return {
|
|
18
|
+
log() {},
|
|
19
|
+
isEnabled() {
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
const enableAll = envValue === "1";
|
|
25
|
+
const enabledCategories = enableAll ? null : new Set(envValue.split(","));
|
|
26
|
+
const logFile = join(logDir, "debug.log");
|
|
27
|
+
writeFileSync(logFile, "");
|
|
28
|
+
function isEnabled(category) {
|
|
29
|
+
return enableAll || enabledCategories.has(category);
|
|
30
|
+
}
|
|
31
|
+
return {
|
|
32
|
+
log(category, message, data) {
|
|
33
|
+
if (!isEnabled(category))
|
|
34
|
+
return;
|
|
35
|
+
const entry = { cat: category, msg: message, ...data };
|
|
36
|
+
appendFileSync(logFile, JSON.stringify(entry) + `
|
|
37
|
+
`);
|
|
38
|
+
},
|
|
39
|
+
isEnabled
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// src/diagnostics-collector.ts
|
|
44
|
+
class DiagnosticsCollector {
|
|
45
|
+
startTime = Date.now();
|
|
46
|
+
pluginFilter = "";
|
|
47
|
+
pluginHmr = false;
|
|
48
|
+
pluginFastRefresh = false;
|
|
49
|
+
processedFilesSet = new Set;
|
|
50
|
+
processedCount = 0;
|
|
51
|
+
ssrModuleStatus = "pending";
|
|
52
|
+
ssrLastReloadTime = null;
|
|
53
|
+
ssrLastReloadDurationMs = null;
|
|
54
|
+
ssrLastReloadError = null;
|
|
55
|
+
ssrReloadCount = 0;
|
|
56
|
+
ssrFailedReloadCount = 0;
|
|
57
|
+
hmrBundledScriptUrl = null;
|
|
58
|
+
hmrBootstrapDiscovered = false;
|
|
59
|
+
errorCurrent = null;
|
|
60
|
+
errorLastCategory = null;
|
|
61
|
+
errorLastMessage = null;
|
|
62
|
+
wsConnectedClients = 0;
|
|
63
|
+
watcherLastChangedFile = null;
|
|
64
|
+
watcherLastChangeTime = null;
|
|
65
|
+
recordPluginConfig(filter, hmr, fastRefresh) {
|
|
66
|
+
this.pluginFilter = filter;
|
|
67
|
+
this.pluginHmr = hmr;
|
|
68
|
+
this.pluginFastRefresh = fastRefresh;
|
|
69
|
+
}
|
|
70
|
+
recordPluginProcess(file) {
|
|
71
|
+
this.processedFilesSet.add(file);
|
|
72
|
+
this.processedCount++;
|
|
73
|
+
}
|
|
74
|
+
recordSSRReload(success, durationMs, error) {
|
|
75
|
+
this.ssrReloadCount++;
|
|
76
|
+
this.ssrLastReloadTime = new Date().toISOString();
|
|
77
|
+
this.ssrLastReloadDurationMs = durationMs;
|
|
78
|
+
if (success) {
|
|
79
|
+
this.ssrModuleStatus = "loaded";
|
|
80
|
+
this.ssrLastReloadError = null;
|
|
81
|
+
} else {
|
|
82
|
+
this.ssrModuleStatus = "error";
|
|
83
|
+
this.ssrLastReloadError = error ?? null;
|
|
84
|
+
this.ssrFailedReloadCount++;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
recordHMRAssets(bundledScriptUrl, bootstrapDiscovered) {
|
|
88
|
+
this.hmrBundledScriptUrl = bundledScriptUrl;
|
|
89
|
+
this.hmrBootstrapDiscovered = bootstrapDiscovered;
|
|
90
|
+
}
|
|
91
|
+
recordError(category, message) {
|
|
92
|
+
this.errorCurrent = category;
|
|
93
|
+
this.errorLastCategory = category;
|
|
94
|
+
this.errorLastMessage = message;
|
|
95
|
+
}
|
|
96
|
+
recordErrorClear() {
|
|
97
|
+
this.errorCurrent = null;
|
|
98
|
+
}
|
|
99
|
+
recordWebSocketChange(count) {
|
|
100
|
+
this.wsConnectedClients = count;
|
|
101
|
+
}
|
|
102
|
+
recordFileChange(file) {
|
|
103
|
+
this.watcherLastChangedFile = file;
|
|
104
|
+
this.watcherLastChangeTime = new Date().toISOString();
|
|
105
|
+
}
|
|
106
|
+
getSnapshot() {
|
|
107
|
+
return {
|
|
108
|
+
status: "ok",
|
|
109
|
+
uptime: (Date.now() - this.startTime) / 1000,
|
|
110
|
+
plugin: {
|
|
111
|
+
filter: this.pluginFilter,
|
|
112
|
+
hmr: this.pluginHmr,
|
|
113
|
+
fastRefresh: this.pluginFastRefresh,
|
|
114
|
+
processedFiles: Array.from(this.processedFilesSet),
|
|
115
|
+
processedCount: this.processedCount
|
|
116
|
+
},
|
|
117
|
+
ssr: {
|
|
118
|
+
moduleStatus: this.ssrModuleStatus,
|
|
119
|
+
lastReloadTime: this.ssrLastReloadTime,
|
|
120
|
+
lastReloadDurationMs: this.ssrLastReloadDurationMs,
|
|
121
|
+
lastReloadError: this.ssrLastReloadError,
|
|
122
|
+
reloadCount: this.ssrReloadCount,
|
|
123
|
+
failedReloadCount: this.ssrFailedReloadCount
|
|
124
|
+
},
|
|
125
|
+
hmr: {
|
|
126
|
+
bundledScriptUrl: this.hmrBundledScriptUrl,
|
|
127
|
+
bootstrapDiscovered: this.hmrBootstrapDiscovered
|
|
128
|
+
},
|
|
129
|
+
errors: {
|
|
130
|
+
current: this.errorCurrent,
|
|
131
|
+
lastCategory: this.errorLastCategory,
|
|
132
|
+
lastMessage: this.errorLastMessage
|
|
133
|
+
},
|
|
134
|
+
websocket: {
|
|
135
|
+
connectedClients: this.wsConnectedClients
|
|
136
|
+
},
|
|
137
|
+
watcher: {
|
|
138
|
+
lastChangedFile: this.watcherLastChangedFile,
|
|
139
|
+
lastChangeTime: this.watcherLastChangeTime
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
11
145
|
// src/source-map-resolver.ts
|
|
12
146
|
import { readFileSync } from "fs";
|
|
13
147
|
import { resolve as resolvePath } from "path";
|
|
@@ -1114,144 +1248,168 @@ function parseHMRAssets(html) {
|
|
|
1114
1248
|
bootstrapScript: bootstrapMatch?.[1] ? `<script>${bootstrapMatch[1]}</script>` : null
|
|
1115
1249
|
};
|
|
1116
1250
|
}
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
"
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
"
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1251
|
+
function detectEditor(explicit) {
|
|
1252
|
+
if (explicit)
|
|
1253
|
+
return explicit;
|
|
1254
|
+
const env = process.env.VERTZ_EDITOR || process.env.EDITOR || "";
|
|
1255
|
+
const lower = env.toLowerCase();
|
|
1256
|
+
if (lower.includes("cursor"))
|
|
1257
|
+
return "cursor";
|
|
1258
|
+
if (lower.includes("zed"))
|
|
1259
|
+
return "zed";
|
|
1260
|
+
if (lower.includes("webstorm") || lower.includes("idea"))
|
|
1261
|
+
return "webstorm";
|
|
1262
|
+
return "vscode";
|
|
1263
|
+
}
|
|
1264
|
+
function editorHrefJs(editor) {
|
|
1265
|
+
if (editor === "webstorm" || editor === "idea") {
|
|
1266
|
+
return `V._editorHref=function(f,l){if(!f)return'';return'${editor}://open?file='+encodeURI(f)+(l?'&line='+l:'')};`;
|
|
1267
|
+
}
|
|
1268
|
+
return `V._editorHref=function(f,l){if(!f)return'';return'${editor}://file/'+encodeURI(f)+(l?':'+l:'')};`;
|
|
1269
|
+
}
|
|
1270
|
+
function buildErrorChannelScript(editor) {
|
|
1271
|
+
return [
|
|
1272
|
+
"<style>bun-hmr{display:none!important}</style>",
|
|
1273
|
+
"<script>(function(){",
|
|
1274
|
+
"var V=window.__vertz_overlay={};",
|
|
1275
|
+
editorHrefJs(editor),
|
|
1276
|
+
"V._ws=null;",
|
|
1277
|
+
"V._src=null;",
|
|
1278
|
+
"V._hadClientError=false;",
|
|
1279
|
+
"V._needsReload=false;",
|
|
1280
|
+
'var rts=sessionStorage.getItem("__vertz_recovering");',
|
|
1281
|
+
"V._recovering=rts&&(Date.now()-Number(rts)<10000);",
|
|
1282
|
+
'if(V._recovering)sessionStorage.removeItem("__vertz_recovering");',
|
|
1283
|
+
"var _reload=location.reload.bind(location);",
|
|
1284
|
+
"try{location.reload=function(){if(V._src){V._needsReload=true;return}_reload()}}catch(e){}",
|
|
1285
|
+
"V.esc=function(s){return s.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>')};",
|
|
1286
|
+
"V.formatErrors=function(errs){",
|
|
1287
|
+
`if(!errs||!errs.length)return'<p style="margin:0;color:var(--ve-muted);font-size:12px">Check your terminal for details.</p>';`,
|
|
1288
|
+
"var groups=[],seen={};",
|
|
1289
|
+
"errs.forEach(function(e){",
|
|
1290
|
+
"var k=(e.file||'')+'|'+(e.line||0);",
|
|
1291
|
+
"if(!seen[k]){seen[k]={file:e.file,absFile:e.absFile,line:e.line,lineText:e.lineText,msgs:[]};groups.push(seen[k])}",
|
|
1292
|
+
"seen[k].msgs.push({message:e.message,column:e.column})});",
|
|
1293
|
+
"return groups.map(function(g){var h='';",
|
|
1294
|
+
"if(g.file){",
|
|
1295
|
+
"var loc=V.esc(g.file)+(g.line?':'+g.line:'');",
|
|
1296
|
+
"var href=V._editorHref(g.absFile,g.line);",
|
|
1297
|
+
`h+=href?'<a href="'+href+'" style="color:var(--ve-link);font-size:12px;text-decoration:underline;text-underline-offset:2px">'+loc+'</a>'` + `:'<span style="color:var(--ve-link);font-size:12px">'+loc+'</span>';`,
|
|
1298
|
+
"h+='<br>'}",
|
|
1299
|
+
`g.msgs.forEach(function(m){h+='<div style="color:var(--ve-error);font-size:12px;margin:2px 0">'+V.esc(m.message)+'</div>'});`,
|
|
1300
|
+
`if(g.lineText){h+='<pre style="margin:4px 0 0;color:var(--ve-code);font-size:11px;background:var(--ve-code-bg);border-radius:4px;padding:6px 8px;overflow-x:auto;border:1px solid var(--ve-border)">'+V.esc(g.lineText)+'</pre>'}`,
|
|
1301
|
+
`return'<div style="margin-bottom:10px">'+h+'</div>'}).join('')};`,
|
|
1302
|
+
"V.formatStack=function(frames){",
|
|
1303
|
+
"if(!frames||!frames.length)return'';",
|
|
1304
|
+
`var h='<div style="margin-top:8px;border-top:1px solid var(--ve-border);padding-top:8px">';`,
|
|
1305
|
+
"var visible=frames.slice(0,3);var hidden=frames.slice(3);",
|
|
1306
|
+
"visible.forEach(function(f){h+=V._renderFrame(f)});",
|
|
1307
|
+
`if(hidden.length){h+='<details style="margin-top:2px"><summary style="color:var(--ve-muted);font-size:11px;cursor:pointer;list-style:none">'`,
|
|
1308
|
+
"+hidden.length+' more frame'+(hidden.length>1?'s':'')+'</summary>';",
|
|
1309
|
+
"hidden.forEach(function(f){h+=V._renderFrame(f)});h+='</details>'}",
|
|
1310
|
+
"return h+'</div>'};",
|
|
1311
|
+
"V._renderFrame=function(f){",
|
|
1312
|
+
"var name=f.functionName||'(anonymous)';",
|
|
1313
|
+
"var loc=V.esc(f.file)+(f.line?':'+f.line:'');",
|
|
1314
|
+
"var isSrc=f.file&&f.file.indexOf('src/')!==-1&&f.file.indexOf('node_modules')===-1;",
|
|
1315
|
+
"var color=isSrc?'var(--ve-fg)':'var(--ve-muted)';",
|
|
1316
|
+
"var href=V._editorHref(f.absFile,f.line);",
|
|
1317
|
+
`var link=href?'<a href="'+href+'" style="color:var(--ve-link);text-decoration:underline;text-underline-offset:2px">'+loc+'</a>':'<span>'+loc+'</span>';`,
|
|
1318
|
+
`return'<div style="font-size:11px;color:'+color+';margin:1px 0;font-family:ui-monospace,monospace">'+V.esc(name)+' '+link+'</div>'};`,
|
|
1319
|
+
"V.removeOverlay=function(){V._src=null;var e=document.getElementById('__vertz_error');if(e)e.remove();" + "var d=document.getElementById('__vertz_error_data');if(d)d.remove()};",
|
|
1320
|
+
"V.showOverlay=function(t,body,payload,src){",
|
|
1321
|
+
"V.removeOverlay();",
|
|
1322
|
+
"V._src=src||'ws';",
|
|
1323
|
+
"var d=document,c=d.createElement('div');",
|
|
1324
|
+
"c.id='__vertz_error';",
|
|
1325
|
+
"c.style.cssText='",
|
|
1326
|
+
"--ve-bg:hsl(0 0% 100%);--ve-fg:hsl(0 0% 9%);--ve-muted:hsl(0 0% 45%);",
|
|
1327
|
+
"--ve-error:hsl(0 72% 51%);--ve-link:hsl(221 83% 53%);--ve-border:hsl(0 0% 90%);",
|
|
1328
|
+
"--ve-code:hsl(24 70% 45%);--ve-code-bg:hsl(0 0% 97%);--ve-btn:hsl(0 0% 9%);--ve-btn-fg:hsl(0 0% 100%);",
|
|
1329
|
+
"position:fixed;bottom:16px;left:50%;transform:translateX(-50%);z-index:2147483647;",
|
|
1330
|
+
"background:var(--ve-bg);color:var(--ve-fg);border-radius:8px;padding:14px 16px;",
|
|
1331
|
+
"max-width:480px;width:calc(100% - 32px);font-family:ui-sans-serif,system-ui,sans-serif;",
|
|
1332
|
+
"box-shadow:0 4px 24px rgba(0,0,0,0.12),0 1px 3px rgba(0,0,0,0.08);border:1px solid var(--ve-border)';",
|
|
1333
|
+
"var st=d.createElement('style');",
|
|
1334
|
+
"st.textContent='@media(prefers-color-scheme:dark){#__vertz_error{",
|
|
1335
|
+
"--ve-bg:hsl(0 0% 7%);--ve-fg:hsl(0 0% 93%);--ve-muted:hsl(0 0% 55%);",
|
|
1336
|
+
"--ve-error:hsl(0 72% 65%);--ve-link:hsl(217 91% 70%);--ve-border:hsl(0 0% 18%);",
|
|
1337
|
+
"--ve-code:hsl(36 80% 65%);--ve-code-bg:hsl(0 0% 11%);--ve-btn:hsl(0 0% 93%);--ve-btn-fg:hsl(0 0% 7%)}}';",
|
|
1338
|
+
"d.head.appendChild(st);",
|
|
1339
|
+
`c.innerHTML='<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:10px">'`,
|
|
1340
|
+
`+'<span style="font-size:13px;font-weight:600;color:var(--ve-error)">'+V.esc(t)+'</span>'`,
|
|
1341
|
+
`+'<button id="__vertz_retry" style="background:var(--ve-btn);color:var(--ve-btn-fg);border:none;border-radius:6px;padding:4px 12px;font-size:12px;cursor:pointer;font-weight:500">Retry</button>'`,
|
|
1342
|
+
"+'</div>'+body;",
|
|
1343
|
+
"(d.body||d.documentElement).appendChild(c);",
|
|
1344
|
+
"d.getElementById('__vertz_retry').onclick=function(){location.reload()};",
|
|
1345
|
+
"if(payload){var s=d.createElement('script');s.type='application/json';s.id='__vertz_error_data';s.textContent=JSON.stringify(payload);(d.body||d.documentElement).appendChild(s)}};",
|
|
1346
|
+
"var delay=1000,maxDelay=30000;",
|
|
1347
|
+
"function connect(){",
|
|
1348
|
+
"var p=location.protocol==='https:'?'wss:':'ws:';",
|
|
1349
|
+
"var ws=new WebSocket(p+'//'+location.host+'/__vertz_errors');",
|
|
1350
|
+
"V._ws=ws;",
|
|
1351
|
+
"ws.onmessage=function(e){",
|
|
1352
|
+
"try{var m=JSON.parse(e.data);",
|
|
1353
|
+
"if(m.type==='error'){",
|
|
1354
|
+
"if(V._recovering)return;",
|
|
1355
|
+
"V.showOverlay(m.category==='build'?'Build failed':m.category==='ssr'?'SSR error':m.category==='resolve'?'Module not found':'Runtime error',V.formatErrors(m.errors)+V.formatStack(m.parsedStack),m,'ws')}",
|
|
1356
|
+
"else if(m.type==='clear'){",
|
|
1357
|
+
"if(V._needsReload){V._needsReload=false;V.removeOverlay();sessionStorage.setItem('__vertz_recovering',String(Date.now()));_reload();return}",
|
|
1358
|
+
"var a=document.getElementById('app');",
|
|
1359
|
+
"if(!a||a.innerHTML.length<50){V.removeOverlay();sessionStorage.setItem('__vertz_recovering',String(Date.now()));_reload();return}",
|
|
1360
|
+
"if(V._hadClientError)return;",
|
|
1361
|
+
"V.removeOverlay()}",
|
|
1362
|
+
"else if(m.type==='connected'){delay=1000}",
|
|
1363
|
+
"}catch(ex){}};",
|
|
1364
|
+
"ws.onclose=function(){V._ws=null;setTimeout(function(){delay=Math.min(delay*2,maxDelay);connect()},delay)};",
|
|
1365
|
+
"ws.onerror=function(){ws.close()}}",
|
|
1366
|
+
"connect();",
|
|
1367
|
+
"V._sendResolveStack=function(stack,msg){",
|
|
1368
|
+
'if(V._ws&&V._ws.readyState===1){try{V._ws.send(JSON.stringify({type:"resolve-stack",stack:stack,message:msg}))}catch(e){}}};',
|
|
1369
|
+
"function showRuntimeError(title,errors,payload){",
|
|
1370
|
+
"var a=document.getElementById('app');",
|
|
1371
|
+
"if(V._recovering&&a&&a.innerHTML.length>50)return;",
|
|
1372
|
+
"if(V._recovering)V._recovering=false;",
|
|
1373
|
+
"V._hadClientError=true;",
|
|
1374
|
+
"V.showOverlay(title,V.formatErrors(errors),payload,'client')}",
|
|
1375
|
+
"if(V._recovering){setTimeout(function(){V._recovering=false},5000)}",
|
|
1376
|
+
"window.addEventListener('error',function(e){",
|
|
1377
|
+
"var msg=e.message||String(e.error);",
|
|
1378
|
+
"var stk=e.error&&e.error.stack;",
|
|
1379
|
+
"if(stk){V._sendResolveStack(stk,msg)}",
|
|
1380
|
+
"var f=e.filename,isBundled=f&&(f.indexOf('/_bun/')!==-1||f.indexOf('blob:')!==-1);",
|
|
1381
|
+
"var errInfo=isBundled?{message:msg}:{message:msg,file:f,line:e.lineno,column:e.colno};",
|
|
1382
|
+
"showRuntimeError('Runtime error',[errInfo],{type:'error',category:'runtime',errors:[errInfo]})});",
|
|
1383
|
+
"window.addEventListener('unhandledrejection',function(e){",
|
|
1384
|
+
"var m=e.reason instanceof Error?e.reason.message:String(e.reason);",
|
|
1385
|
+
"var stk=e.reason&&e.reason.stack;",
|
|
1386
|
+
"if(stk){V._sendResolveStack(stk,m)}",
|
|
1387
|
+
"showRuntimeError('Runtime error',[{message:m}],{type:'error',category:'runtime',errors:[{message:m}]})});",
|
|
1388
|
+
"var hmrErr=false,origCE=console.error,origCL=console.log;",
|
|
1389
|
+
"console.error=function(){",
|
|
1390
|
+
"var t=Array.prototype.join.call(arguments,' ');",
|
|
1391
|
+
"var hmr=t.match(/\\[vertz-hmr\\] Error re-mounting (\\w+): ([\\s\\S]*?)(?:\\n\\s+at |$)/);",
|
|
1392
|
+
"if(hmr){hmrErr=true;V._hadClientError=true;",
|
|
1393
|
+
"V.showOverlay('Runtime error',V.formatErrors([{message:hmr[2].split('\\n')[0]}]),{type:'error',category:'runtime',errors:[{message:hmr[2].split('\\n')[0]}]},'client')}",
|
|
1394
|
+
"origCE.apply(console,arguments)};",
|
|
1395
|
+
"console.log=function(){",
|
|
1396
|
+
"var t=Array.prototype.join.call(arguments,' ');",
|
|
1397
|
+
"if(t.indexOf('[vertz-hmr] Hot updated:')!==-1){",
|
|
1398
|
+
"if(!hmrErr&&V._src==='client'){",
|
|
1399
|
+
"V._hadClientError=false;V.removeOverlay();setTimeout(function(){var a=document.getElementById('app');if(!a||a.innerHTML.length<50){V._needsReload=true}},500)}",
|
|
1400
|
+
"hmrErr=false}",
|
|
1401
|
+
"origCL.apply(console,arguments)};",
|
|
1402
|
+
"})()</script>"
|
|
1403
|
+
].join("");
|
|
1404
|
+
}
|
|
1248
1405
|
var RELOAD_GUARD_SCRIPT = `<script>(function(){var K="__vertz_reload_count",T="__vertz_reload_ts",s=sessionStorage,n=parseInt(s.getItem(K)||"0",10),t=parseInt(s.getItem(T)||"0",10),now=Date.now();if(now-t<100){n++}else{n=1}s.setItem(K,String(n));s.setItem(T,String(now));if(n>10){window.stop();s.removeItem(K);s.removeItem(T);var d=document,o=d.createElement("div");o.style.cssText="position:fixed;inset:0;z-index:2147483647;display:flex;align-items:center;justify-content:center;background:rgba(0,0,0,0.6)";var c=d.createElement("div");c.style.cssText="background:#fff;color:#1a1a1a;border-radius:12px;padding:32px;max-width:480px;width:90%;font-family:system-ui,sans-serif;text-align:center;box-shadow:0 20px 60px rgba(0,0,0,0.3)";c.innerHTML='<div style="font-size:40px;margin-bottom:16px">⚠️</div><h2 style="margin:0 0 8px;font-size:20px">Dev server connection lost</h2><p style="margin:0 0 20px;color:#666;font-size:14px;line-height:1.5">The page reloaded 10+ times in rapid succession. This usually means the dev server stopped or a build failed.</p><button id="__vertz_retry" style="background:#2563eb;color:#fff;border:none;border-radius:8px;padding:10px 24px;font-size:14px;cursor:pointer">Retry</button>';o.appendChild(c);(d.body||d.documentElement).appendChild(o);d.getElementById("__vertz_retry").onclick=function(){location.href=location.href}}else{setTimeout(function(){s.removeItem(K);s.removeItem(T)},5e3)}})()</script>`;
|
|
1249
1406
|
function generateSSRPageHtml({
|
|
1250
1407
|
title,
|
|
1251
1408
|
css,
|
|
1252
1409
|
bodyHtml,
|
|
1253
1410
|
ssrData,
|
|
1254
|
-
scriptTag
|
|
1411
|
+
scriptTag,
|
|
1412
|
+
editor = "vscode"
|
|
1255
1413
|
}) {
|
|
1256
1414
|
const ssrDataScript = ssrData.length > 0 ? `<script>window.__VERTZ_SSR_DATA__=${safeSerialize(ssrData)};</script>` : "";
|
|
1257
1415
|
return `<!doctype html>
|
|
@@ -1261,7 +1419,7 @@ function generateSSRPageHtml({
|
|
|
1261
1419
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
1262
1420
|
<title>${title}</title>
|
|
1263
1421
|
${css}
|
|
1264
|
-
${
|
|
1422
|
+
${buildErrorChannelScript(editor)}
|
|
1265
1423
|
${RELOAD_GUARD_SCRIPT}
|
|
1266
1424
|
</head>
|
|
1267
1425
|
<body>
|
|
@@ -1339,8 +1497,14 @@ function createBunDevServer(options) {
|
|
|
1339
1497
|
clientEntry: clientEntryOption,
|
|
1340
1498
|
title = "Vertz App",
|
|
1341
1499
|
projectRoot = process.cwd(),
|
|
1342
|
-
logRequests = true
|
|
1500
|
+
logRequests = true,
|
|
1501
|
+
editor: editorOption
|
|
1343
1502
|
} = options;
|
|
1503
|
+
const editor = detectEditor(editorOption);
|
|
1504
|
+
const devDir = resolve(projectRoot, ".vertz", "dev");
|
|
1505
|
+
mkdirSync(devDir, { recursive: true });
|
|
1506
|
+
const logger = createDebugLogger(devDir);
|
|
1507
|
+
const diagnostics = new DiagnosticsCollector;
|
|
1344
1508
|
let server = null;
|
|
1345
1509
|
let srcWatcherRef = null;
|
|
1346
1510
|
let refreshTimeout = null;
|
|
@@ -1379,6 +1543,8 @@ function createBunDevServer(options) {
|
|
|
1379
1543
|
return;
|
|
1380
1544
|
}
|
|
1381
1545
|
currentError = { category, errors };
|
|
1546
|
+
diagnostics.recordError(category, errors[0]?.message ?? "");
|
|
1547
|
+
logger.log("ws", "broadcast-error", { category, errorCount: errors.length });
|
|
1382
1548
|
const msg = JSON.stringify({ type: "error", category, errors });
|
|
1383
1549
|
for (const ws of wsClients) {
|
|
1384
1550
|
ws.sendText(msg);
|
|
@@ -1388,6 +1554,7 @@ function createBunDevServer(options) {
|
|
|
1388
1554
|
if (currentError === null && !pendingRuntimeError)
|
|
1389
1555
|
return;
|
|
1390
1556
|
currentError = null;
|
|
1557
|
+
diagnostics.recordErrorClear();
|
|
1391
1558
|
if (runtimeDebounceTimer) {
|
|
1392
1559
|
clearTimeout(runtimeDebounceTimer);
|
|
1393
1560
|
runtimeDebounceTimer = null;
|
|
@@ -1403,6 +1570,7 @@ function createBunDevServer(options) {
|
|
|
1403
1570
|
if (currentError === null && !pendingRuntimeError)
|
|
1404
1571
|
return;
|
|
1405
1572
|
currentError = null;
|
|
1573
|
+
diagnostics.recordErrorClear();
|
|
1406
1574
|
if (runtimeDebounceTimer) {
|
|
1407
1575
|
clearTimeout(runtimeDebounceTimer);
|
|
1408
1576
|
runtimeDebounceTimer = null;
|
|
@@ -1571,7 +1739,9 @@ function createBunDevServer(options) {
|
|
|
1571
1739
|
});
|
|
1572
1740
|
const { plugin: serverPlugin } = createVertzBunPlugin({
|
|
1573
1741
|
hmr: false,
|
|
1574
|
-
fastRefresh: false
|
|
1742
|
+
fastRefresh: false,
|
|
1743
|
+
logger,
|
|
1744
|
+
diagnostics
|
|
1575
1745
|
});
|
|
1576
1746
|
plugin(serverPlugin);
|
|
1577
1747
|
let ssrMod;
|
|
@@ -1584,19 +1754,21 @@ function createBunDevServer(options) {
|
|
|
1584
1754
|
console.error("[Server] Failed to load SSR module:", e);
|
|
1585
1755
|
process.exit(1);
|
|
1586
1756
|
}
|
|
1587
|
-
const devDir = resolve(projectRoot, ".vertz", "dev");
|
|
1588
1757
|
mkdirSync(devDir, { recursive: true });
|
|
1589
|
-
const
|
|
1758
|
+
const frInitPath = resolve(devDir, "fast-refresh-init.ts");
|
|
1759
|
+
writeFileSync2(frInitPath, `import '@vertz/ui-server/fast-refresh-runtime';
|
|
1760
|
+
if (import.meta.hot) import.meta.hot.accept();
|
|
1761
|
+
`);
|
|
1590
1762
|
const hmrShellHtml = `<!doctype html>
|
|
1591
1763
|
<html lang="en"><head>
|
|
1592
1764
|
<meta charset="UTF-8" />
|
|
1593
1765
|
<title>HMR Shell</title>
|
|
1594
1766
|
</head><body>
|
|
1595
|
-
<script type="module" src="
|
|
1767
|
+
<script type="module" src="./fast-refresh-init.ts"></script>
|
|
1596
1768
|
<script type="module" src="${clientSrc}"></script>
|
|
1597
1769
|
</body></html>`;
|
|
1598
1770
|
const hmrShellPath = resolve(devDir, "hmr-shell.html");
|
|
1599
|
-
|
|
1771
|
+
writeFileSync2(hmrShellPath, hmrShellHtml);
|
|
1600
1772
|
const hmrShellModule = __require(hmrShellPath);
|
|
1601
1773
|
setupOpenAPIWatcher();
|
|
1602
1774
|
let bundledScriptUrl = null;
|
|
@@ -1663,6 +1835,9 @@ function createBunDevServer(options) {
|
|
|
1663
1835
|
return Response.json({ errors: [{ message: msg }] });
|
|
1664
1836
|
}
|
|
1665
1837
|
}
|
|
1838
|
+
if (pathname === "/__vertz_diagnostics") {
|
|
1839
|
+
return Response.json(diagnostics.getSnapshot());
|
|
1840
|
+
}
|
|
1666
1841
|
if (openapi && request.method === "GET" && pathname === "/api/openapi.json") {
|
|
1667
1842
|
return serveOpenAPISpec();
|
|
1668
1843
|
}
|
|
@@ -1727,14 +1902,22 @@ data: {}
|
|
|
1727
1902
|
});
|
|
1728
1903
|
}
|
|
1729
1904
|
try {
|
|
1905
|
+
logger.log("ssr", "render-start", { url: pathname });
|
|
1906
|
+
const ssrStart = performance.now();
|
|
1730
1907
|
const result = await ssrRenderToString(ssrMod, pathname, { ssrTimeout: 300 });
|
|
1908
|
+
logger.log("ssr", "render-done", {
|
|
1909
|
+
url: pathname,
|
|
1910
|
+
durationMs: Math.round(performance.now() - ssrStart),
|
|
1911
|
+
htmlBytes: result.html.length
|
|
1912
|
+
});
|
|
1731
1913
|
const scriptTag = buildScriptTag(bundledScriptUrl, hmrBootstrapScript, clientSrc);
|
|
1732
1914
|
const html = generateSSRPageHtml({
|
|
1733
1915
|
title,
|
|
1734
1916
|
css: result.css,
|
|
1735
1917
|
bodyHtml: result.html,
|
|
1736
1918
|
ssrData: result.ssrData,
|
|
1737
|
-
scriptTag
|
|
1919
|
+
scriptTag,
|
|
1920
|
+
editor
|
|
1738
1921
|
});
|
|
1739
1922
|
return new Response(html, {
|
|
1740
1923
|
status: 200,
|
|
@@ -1752,14 +1935,16 @@ data: {}
|
|
|
1752
1935
|
console.error("[Server] SSR error:", err);
|
|
1753
1936
|
const errMsg = err instanceof Error ? err.message : String(err);
|
|
1754
1937
|
const errStack = err instanceof Error ? err.stack : undefined;
|
|
1755
|
-
|
|
1938
|
+
const { message: _, ...loc } = errStack ? parseSourceFromStack(errStack) : { message: "" };
|
|
1939
|
+
broadcastError("ssr", [{ message: errMsg, ...loc, stack: errStack }]);
|
|
1756
1940
|
const scriptTag = buildScriptTag(bundledScriptUrl, hmrBootstrapScript, clientSrc);
|
|
1757
1941
|
const fallbackHtml = generateSSRPageHtml({
|
|
1758
1942
|
title,
|
|
1759
1943
|
css: "",
|
|
1760
1944
|
bodyHtml: "",
|
|
1761
1945
|
ssrData: [],
|
|
1762
|
-
scriptTag
|
|
1946
|
+
scriptTag,
|
|
1947
|
+
editor
|
|
1763
1948
|
});
|
|
1764
1949
|
return new Response(fallbackHtml, {
|
|
1765
1950
|
status: 200,
|
|
@@ -1773,6 +1958,8 @@ data: {}
|
|
|
1773
1958
|
websocket: {
|
|
1774
1959
|
open(ws) {
|
|
1775
1960
|
wsClients.add(ws);
|
|
1961
|
+
diagnostics.recordWebSocketChange(wsClients.size);
|
|
1962
|
+
logger.log("ws", "client-connected", { total: wsClients.size });
|
|
1776
1963
|
ws.sendText(JSON.stringify({ type: "connected" }));
|
|
1777
1964
|
if (currentError) {
|
|
1778
1965
|
ws.sendText(JSON.stringify({
|
|
@@ -1862,6 +2049,7 @@ data: {}
|
|
|
1862
2049
|
},
|
|
1863
2050
|
close(ws) {
|
|
1864
2051
|
wsClients.delete(ws);
|
|
2052
|
+
diagnostics.recordWebSocketChange(wsClients.size);
|
|
1865
2053
|
}
|
|
1866
2054
|
},
|
|
1867
2055
|
development: {
|
|
@@ -1890,6 +2078,7 @@ data: {}
|
|
|
1890
2078
|
console.log("[Server] Extracted HMR bootstrap script");
|
|
1891
2079
|
}
|
|
1892
2080
|
}
|
|
2081
|
+
diagnostics.recordHMRAssets(bundledScriptUrl, hmrBootstrapScript !== null);
|
|
1893
2082
|
} catch (e) {
|
|
1894
2083
|
console.warn("[Server] Could not discover HMR bundled URL:", e);
|
|
1895
2084
|
}
|
|
@@ -1904,6 +2093,8 @@ data: {}
|
|
|
1904
2093
|
clearTimeout(refreshTimeout);
|
|
1905
2094
|
refreshTimeout = setTimeout(async () => {
|
|
1906
2095
|
lastChangedFile = `src/${filename}`;
|
|
2096
|
+
diagnostics.recordFileChange(lastChangedFile);
|
|
2097
|
+
logger.log("watcher", "file-changed", { file: lastChangedFile });
|
|
1907
2098
|
lastBroadcastedError = "";
|
|
1908
2099
|
sourceMapResolver.invalidate();
|
|
1909
2100
|
if (logRequests) {
|
|
@@ -1953,31 +2144,51 @@ data: {}
|
|
|
1953
2144
|
} catch {}
|
|
1954
2145
|
if (stopped)
|
|
1955
2146
|
return;
|
|
1956
|
-
|
|
2147
|
+
const cacheKeys = Object.keys(__require.cache);
|
|
2148
|
+
let cacheCleared = 0;
|
|
2149
|
+
for (const key of cacheKeys) {
|
|
1957
2150
|
if (key.startsWith(srcDir) || key.startsWith(entryPath)) {
|
|
1958
2151
|
delete __require.cache[key];
|
|
2152
|
+
cacheCleared++;
|
|
1959
2153
|
}
|
|
1960
2154
|
}
|
|
2155
|
+
logger.log("watcher", "cache-cleared", { entries: cacheCleared });
|
|
2156
|
+
const ssrWrapperPath = resolve(devDir, "ssr-reload-entry.ts");
|
|
2157
|
+
mkdirSync(devDir, { recursive: true });
|
|
2158
|
+
writeFileSync2(ssrWrapperPath, `export * from '${entryPath}';
|
|
2159
|
+
`);
|
|
2160
|
+
const ssrReloadStart = performance.now();
|
|
1961
2161
|
try {
|
|
1962
|
-
const freshMod = await import(`${
|
|
2162
|
+
const freshMod = await import(`${ssrWrapperPath}?t=${Date.now()}`);
|
|
1963
2163
|
ssrMod = freshMod;
|
|
2164
|
+
const durationMs = Math.round(performance.now() - ssrReloadStart);
|
|
2165
|
+
diagnostics.recordSSRReload(true, durationMs);
|
|
2166
|
+
logger.log("watcher", "ssr-reload", { status: "ok", durationMs });
|
|
1964
2167
|
if (logRequests) {
|
|
1965
2168
|
console.log("[Server] SSR module refreshed");
|
|
1966
2169
|
}
|
|
1967
|
-
} catch
|
|
2170
|
+
} catch {
|
|
2171
|
+
logger.log("watcher", "ssr-reload", { status: "retry" });
|
|
1968
2172
|
if (stopped)
|
|
1969
2173
|
return;
|
|
1970
2174
|
await new Promise((r) => setTimeout(r, 500));
|
|
1971
2175
|
if (stopped)
|
|
1972
2176
|
return;
|
|
1973
|
-
|
|
2177
|
+
const retryKeys = Object.keys(__require.cache);
|
|
2178
|
+
for (const key of retryKeys) {
|
|
1974
2179
|
if (key.startsWith(srcDir) || key.startsWith(entryPath)) {
|
|
1975
2180
|
delete __require.cache[key];
|
|
1976
2181
|
}
|
|
1977
2182
|
}
|
|
2183
|
+
mkdirSync(devDir, { recursive: true });
|
|
2184
|
+
writeFileSync2(ssrWrapperPath, `export * from '${entryPath}';
|
|
2185
|
+
`);
|
|
1978
2186
|
try {
|
|
1979
|
-
const freshMod = await import(`${
|
|
2187
|
+
const freshMod = await import(`${ssrWrapperPath}?t=${Date.now()}`);
|
|
1980
2188
|
ssrMod = freshMod;
|
|
2189
|
+
const durationMs = Math.round(performance.now() - ssrReloadStart);
|
|
2190
|
+
diagnostics.recordSSRReload(true, durationMs);
|
|
2191
|
+
logger.log("watcher", "ssr-reload", { status: "ok", durationMs, retry: true });
|
|
1981
2192
|
if (logRequests) {
|
|
1982
2193
|
console.log("[Server] SSR module refreshed (retry)");
|
|
1983
2194
|
}
|
|
@@ -1985,7 +2196,11 @@ data: {}
|
|
|
1985
2196
|
console.error("[Server] Failed to refresh SSR module:", e2);
|
|
1986
2197
|
const errMsg = e2 instanceof Error ? e2.message : String(e2);
|
|
1987
2198
|
const errStack = e2 instanceof Error ? e2.stack : undefined;
|
|
1988
|
-
|
|
2199
|
+
const durationMs = Math.round(performance.now() - ssrReloadStart);
|
|
2200
|
+
diagnostics.recordSSRReload(false, durationMs, errMsg);
|
|
2201
|
+
logger.log("watcher", "ssr-reload", { status: "failed", error: errMsg });
|
|
2202
|
+
const { message: _m, ...loc2 } = errStack ? parseSourceFromStack(errStack) : { message: "" };
|
|
2203
|
+
broadcastError("ssr", [{ message: errMsg, ...loc2, stack: errStack }]);
|
|
1989
2204
|
}
|
|
1990
2205
|
}
|
|
1991
2206
|
}, 100);
|
|
@@ -1,4 +1,76 @@
|
|
|
1
|
+
type ErrorCategory = "build" | "resolve" | "runtime" | "ssr";
|
|
1
2
|
import { CSSExtractionResult } from "@vertz/ui-compiler";
|
|
3
|
+
type DebugCategory = "plugin" | "ssr" | "watcher" | "ws";
|
|
4
|
+
interface DebugLogger {
|
|
5
|
+
log(category: DebugCategory, message: string, data?: Record<string, unknown>): void;
|
|
6
|
+
isEnabled(category: DebugCategory): boolean;
|
|
7
|
+
}
|
|
8
|
+
interface DiagnosticsSnapshot {
|
|
9
|
+
status: "ok";
|
|
10
|
+
uptime: number;
|
|
11
|
+
plugin: {
|
|
12
|
+
filter: string;
|
|
13
|
+
hmr: boolean;
|
|
14
|
+
fastRefresh: boolean;
|
|
15
|
+
processedFiles: string[];
|
|
16
|
+
processedCount: number;
|
|
17
|
+
};
|
|
18
|
+
ssr: {
|
|
19
|
+
moduleStatus: "pending" | "loaded" | "error";
|
|
20
|
+
lastReloadTime: string | null;
|
|
21
|
+
lastReloadDurationMs: number | null;
|
|
22
|
+
lastReloadError: string | null;
|
|
23
|
+
reloadCount: number;
|
|
24
|
+
failedReloadCount: number;
|
|
25
|
+
};
|
|
26
|
+
hmr: {
|
|
27
|
+
bundledScriptUrl: string | null;
|
|
28
|
+
bootstrapDiscovered: boolean;
|
|
29
|
+
};
|
|
30
|
+
errors: {
|
|
31
|
+
current: ErrorCategory | null;
|
|
32
|
+
lastCategory: ErrorCategory | null;
|
|
33
|
+
lastMessage: string | null;
|
|
34
|
+
};
|
|
35
|
+
websocket: {
|
|
36
|
+
connectedClients: number;
|
|
37
|
+
};
|
|
38
|
+
watcher: {
|
|
39
|
+
lastChangedFile: string | null;
|
|
40
|
+
lastChangeTime: string | null;
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
declare class DiagnosticsCollector {
|
|
44
|
+
private startTime;
|
|
45
|
+
private pluginFilter;
|
|
46
|
+
private pluginHmr;
|
|
47
|
+
private pluginFastRefresh;
|
|
48
|
+
private processedFilesSet;
|
|
49
|
+
private processedCount;
|
|
50
|
+
private ssrModuleStatus;
|
|
51
|
+
private ssrLastReloadTime;
|
|
52
|
+
private ssrLastReloadDurationMs;
|
|
53
|
+
private ssrLastReloadError;
|
|
54
|
+
private ssrReloadCount;
|
|
55
|
+
private ssrFailedReloadCount;
|
|
56
|
+
private hmrBundledScriptUrl;
|
|
57
|
+
private hmrBootstrapDiscovered;
|
|
58
|
+
private errorCurrent;
|
|
59
|
+
private errorLastCategory;
|
|
60
|
+
private errorLastMessage;
|
|
61
|
+
private wsConnectedClients;
|
|
62
|
+
private watcherLastChangedFile;
|
|
63
|
+
private watcherLastChangeTime;
|
|
64
|
+
recordPluginConfig(filter: string, hmr: boolean, fastRefresh: boolean): void;
|
|
65
|
+
recordPluginProcess(file: string): void;
|
|
66
|
+
recordSSRReload(success: boolean, durationMs: number, error?: string): void;
|
|
67
|
+
recordHMRAssets(bundledScriptUrl: string | null, bootstrapDiscovered: boolean): void;
|
|
68
|
+
recordError(category: ErrorCategory, message: string): void;
|
|
69
|
+
recordErrorClear(): void;
|
|
70
|
+
recordWebSocketChange(count: number): void;
|
|
71
|
+
recordFileChange(file: string): void;
|
|
72
|
+
getSnapshot(): DiagnosticsSnapshot;
|
|
73
|
+
}
|
|
2
74
|
import { BunPlugin as BunPlugin_seob6 } from "bun";
|
|
3
75
|
interface VertzBunPluginOptions {
|
|
4
76
|
/** Regex filter for files to transform. Defaults to .tsx files. */
|
|
@@ -21,6 +93,10 @@ interface VertzBunPluginOptions {
|
|
|
21
93
|
fastRefresh?: boolean;
|
|
22
94
|
/** Project root for computing relative paths. */
|
|
23
95
|
projectRoot?: string;
|
|
96
|
+
/** Debug logger for opt-in diagnostic logging. */
|
|
97
|
+
logger?: DebugLogger;
|
|
98
|
+
/** Diagnostics collector for the health check endpoint. */
|
|
99
|
+
diagnostics?: DiagnosticsCollector;
|
|
24
100
|
}
|
|
25
101
|
/** CSS extractions tracked across all transformed files (for dead CSS elimination). */
|
|
26
102
|
type FileExtractionsMap = Map<string, CSSExtractionResult>;
|
package/dist/bun-plugin/index.js
CHANGED
|
@@ -100,6 +100,8 @@ function createVertzBunPlugin(options) {
|
|
|
100
100
|
const cssOutDir = options?.cssOutDir ?? resolve(projectRoot, ".vertz", "css");
|
|
101
101
|
const cssExtractor = new CSSExtractor;
|
|
102
102
|
const componentAnalyzer = new ComponentAnalyzer;
|
|
103
|
+
const logger = options?.logger;
|
|
104
|
+
const diagnostics = options?.diagnostics;
|
|
103
105
|
const fileExtractions = new Map;
|
|
104
106
|
const cssSidecarMap = new Map;
|
|
105
107
|
mkdirSync(cssOutDir, { recursive: true });
|
|
@@ -108,7 +110,10 @@ function createVertzBunPlugin(options) {
|
|
|
108
110
|
setup(build) {
|
|
109
111
|
build.onLoad({ filter }, async (args) => {
|
|
110
112
|
try {
|
|
113
|
+
const startMs = logger?.isEnabled("plugin") ? performance.now() : 0;
|
|
111
114
|
const source = await Bun.file(args.path).text();
|
|
115
|
+
const relPath = relative(projectRoot, args.path);
|
|
116
|
+
logger?.log("plugin", "onLoad", { file: relPath, bytes: source.length });
|
|
112
117
|
const hydrationS = new MagicString(source);
|
|
113
118
|
const hydrationProject = new Project({
|
|
114
119
|
useInMemoryFileSystem: true,
|
|
@@ -144,8 +149,8 @@ function createVertzBunPlugin(options) {
|
|
|
144
149
|
const cssFilePath = resolve(cssOutDir, cssFileName);
|
|
145
150
|
writeFileSync(cssFilePath, extraction.css);
|
|
146
151
|
cssSidecarMap.set(args.path, cssFilePath);
|
|
147
|
-
const
|
|
148
|
-
const importPath =
|
|
152
|
+
const relPath2 = relative(dirname(args.path), cssFilePath);
|
|
153
|
+
const importPath = relPath2.startsWith(".") ? relPath2 : `./${relPath2}`;
|
|
149
154
|
cssImportLine = `import '${importPath}';
|
|
150
155
|
`;
|
|
151
156
|
}
|
|
@@ -180,6 +185,20 @@ import.meta.hot.accept();
|
|
|
180
185
|
`;
|
|
181
186
|
}
|
|
182
187
|
contents += sourceMapComment;
|
|
188
|
+
if (logger?.isEnabled("plugin")) {
|
|
189
|
+
const durationMs = Math.round(performance.now() - startMs);
|
|
190
|
+
const stages = [
|
|
191
|
+
"hydration",
|
|
192
|
+
fastRefresh ? "stableIds" : null,
|
|
193
|
+
"compile",
|
|
194
|
+
"sourceMap",
|
|
195
|
+
extraction.css.length > 0 ? "css" : null,
|
|
196
|
+
fastRefresh && refreshPreamble ? "fastRefresh" : null,
|
|
197
|
+
hmr ? "hmr" : null
|
|
198
|
+
].filter(Boolean).join(",");
|
|
199
|
+
logger.log("plugin", "done", { file: relPath, durationMs, stages });
|
|
200
|
+
}
|
|
201
|
+
diagnostics?.recordPluginProcess(relPath);
|
|
183
202
|
return { contents, loader: "tsx" };
|
|
184
203
|
} catch (err) {
|
|
185
204
|
const message = err instanceof Error ? err.message : String(err);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vertz/ui-server",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.5",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"description": "Vertz UI server-side rendering runtime",
|
|
@@ -53,9 +53,9 @@
|
|
|
53
53
|
"dependencies": {
|
|
54
54
|
"@ampproject/remapping": "^2.3.0",
|
|
55
55
|
"@jridgewell/trace-mapping": "^0.3.31",
|
|
56
|
-
"@vertz/core": "0.2.
|
|
57
|
-
"@vertz/ui": "0.2.2",
|
|
58
|
-
"@vertz/ui-compiler": "0.2.
|
|
56
|
+
"@vertz/core": "^0.2.4",
|
|
57
|
+
"@vertz/ui": "^0.2.2",
|
|
58
|
+
"@vertz/ui-compiler": "^0.2.4",
|
|
59
59
|
"magic-string": "^0.30.0",
|
|
60
60
|
"ts-morph": "^27.0.2"
|
|
61
61
|
},
|