@vertz/ui-server 0.2.10 → 0.2.12
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 +23 -10
- package/dist/bun-dev-server.js +83 -30
- package/dist/bun-plugin/fast-refresh-runtime.d.ts +3 -1
- package/dist/bun-plugin/fast-refresh-runtime.js +38 -19
- package/dist/bun-plugin/index.d.ts +10 -0
- package/dist/bun-plugin/index.js +20 -9
- package/package.json +4 -4
package/dist/bun-dev-server.d.ts
CHANGED
|
@@ -40,6 +40,28 @@ interface ErrorDetail {
|
|
|
40
40
|
stack?: string;
|
|
41
41
|
}
|
|
42
42
|
type ErrorCategory = "build" | "resolve" | "runtime" | "ssr";
|
|
43
|
+
/** A resolved stack frame for terminal logging. */
|
|
44
|
+
interface TerminalStackFrame {
|
|
45
|
+
functionName: string | null;
|
|
46
|
+
file: string;
|
|
47
|
+
line: number;
|
|
48
|
+
column: number;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Format a runtime error for terminal output.
|
|
52
|
+
* Produces a [Browser]-prefixed message with optional file location,
|
|
53
|
+
* line text snippet, and resolved stack frames.
|
|
54
|
+
*/
|
|
55
|
+
declare function formatTerminalRuntimeError(errors: ErrorDetail[], parsedStack?: TerminalStackFrame[]): string;
|
|
56
|
+
/**
|
|
57
|
+
* Create a deduplicator for terminal runtime error logs.
|
|
58
|
+
* Returns `shouldLog` (true if this error hasn't been logged recently)
|
|
59
|
+
* and `reset` (to clear on file change).
|
|
60
|
+
*/
|
|
61
|
+
declare function createRuntimeErrorDeduplicator(): {
|
|
62
|
+
shouldLog: (message: string, file?: string, line?: number) => boolean;
|
|
63
|
+
reset: () => void;
|
|
64
|
+
};
|
|
43
65
|
interface BunDevServer {
|
|
44
66
|
start(): Promise<void>;
|
|
45
67
|
stop(): Promise<void>;
|
|
@@ -53,15 +75,6 @@ interface BunDevServer {
|
|
|
53
75
|
/** Set the last changed file path (for testing). */
|
|
54
76
|
setLastChangedFile(file: string): void;
|
|
55
77
|
}
|
|
56
|
-
interface IndexHtmlStasher {
|
|
57
|
-
stash(): void;
|
|
58
|
-
restore(): void;
|
|
59
|
-
}
|
|
60
|
-
/**
|
|
61
|
-
* Create a stasher that renames index.html during dev so Bun's built-in
|
|
62
|
-
* HMR server doesn't auto-serve it, bypassing our SSR fetch handler.
|
|
63
|
-
*/
|
|
64
|
-
declare function createIndexHtmlStasher(projectRoot: string): IndexHtmlStasher;
|
|
65
78
|
interface HMRAssets {
|
|
66
79
|
/** Discovered `/_bun/client/<hash>.js` URL, or null if not found */
|
|
67
80
|
scriptUrl: string | null;
|
|
@@ -119,4 +132,4 @@ declare function buildScriptTag(bundledScriptUrl: string | null, hmrBootstrapScr
|
|
|
119
132
|
* SSR is always on. HMR always works. No mode toggle needed.
|
|
120
133
|
*/
|
|
121
134
|
declare function createBunDevServer(options: BunDevServerOptions): BunDevServer;
|
|
122
|
-
export { parseHMRAssets, generateSSRPageHtml,
|
|
135
|
+
export { parseHMRAssets, generateSSRPageHtml, formatTerminalRuntimeError, createRuntimeErrorDeduplicator, createFetchInterceptor, createBunDevServer, buildScriptTag, SSRPageHtmlOptions, HMRAssets, FetchInterceptorOptions, ErrorDetail, ErrorCategory, BunDevServerOptions, BunDevServer };
|
package/dist/bun-dev-server.js
CHANGED
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
|
|
6
6
|
// src/bun-dev-server.ts
|
|
7
7
|
import { execSync } from "child_process";
|
|
8
|
-
import { existsSync, mkdirSync, readFileSync as readFileSync2,
|
|
8
|
+
import { existsSync, mkdirSync, readFileSync as readFileSync2, watch, writeFileSync as writeFileSync2 } from "fs";
|
|
9
9
|
import { dirname, normalize, resolve } from "path";
|
|
10
10
|
|
|
11
11
|
// src/debug-logger.ts
|
|
@@ -62,6 +62,8 @@ class DiagnosticsCollector {
|
|
|
62
62
|
wsConnectedClients = 0;
|
|
63
63
|
watcherLastChangedFile = null;
|
|
64
64
|
watcherLastChangeTime = null;
|
|
65
|
+
static MAX_RUNTIME_ERRORS = 10;
|
|
66
|
+
runtimeErrorsBuffer = [];
|
|
65
67
|
recordPluginConfig(filter, hmr, fastRefresh) {
|
|
66
68
|
this.pluginFilter = filter;
|
|
67
69
|
this.pluginHmr = hmr;
|
|
@@ -103,6 +105,19 @@ class DiagnosticsCollector {
|
|
|
103
105
|
this.watcherLastChangedFile = file;
|
|
104
106
|
this.watcherLastChangeTime = new Date().toISOString();
|
|
105
107
|
}
|
|
108
|
+
recordRuntimeError(message, source) {
|
|
109
|
+
this.runtimeErrorsBuffer.push({
|
|
110
|
+
message,
|
|
111
|
+
source,
|
|
112
|
+
timestamp: new Date().toISOString()
|
|
113
|
+
});
|
|
114
|
+
if (this.runtimeErrorsBuffer.length > DiagnosticsCollector.MAX_RUNTIME_ERRORS) {
|
|
115
|
+
this.runtimeErrorsBuffer = this.runtimeErrorsBuffer.slice(this.runtimeErrorsBuffer.length - DiagnosticsCollector.MAX_RUNTIME_ERRORS);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
clearRuntimeErrors() {
|
|
119
|
+
this.runtimeErrorsBuffer = [];
|
|
120
|
+
}
|
|
106
121
|
getSnapshot() {
|
|
107
122
|
return {
|
|
108
123
|
status: "ok",
|
|
@@ -137,7 +152,8 @@ class DiagnosticsCollector {
|
|
|
137
152
|
watcher: {
|
|
138
153
|
lastChangedFile: this.watcherLastChangedFile,
|
|
139
154
|
lastChangeTime: this.watcherLastChangeTime
|
|
140
|
-
}
|
|
155
|
+
},
|
|
156
|
+
runtimeErrors: [...this.runtimeErrorsBuffer]
|
|
141
157
|
};
|
|
142
158
|
}
|
|
143
159
|
}
|
|
@@ -1199,6 +1215,45 @@ data: ${safeSerialize(entry)}
|
|
|
1199
1215
|
}
|
|
1200
1216
|
|
|
1201
1217
|
// src/bun-dev-server.ts
|
|
1218
|
+
var MAX_TERMINAL_STACK_FRAMES = 5;
|
|
1219
|
+
function formatTerminalRuntimeError(errors, parsedStack) {
|
|
1220
|
+
const primary = errors[0];
|
|
1221
|
+
if (!primary)
|
|
1222
|
+
return "";
|
|
1223
|
+
const lines = [];
|
|
1224
|
+
lines.push(`[Browser] ${primary.message}`);
|
|
1225
|
+
if (primary.file) {
|
|
1226
|
+
const loc = primary.line ? `${primary.file}:${primary.line}${primary.column != null ? `:${primary.column}` : ""}` : primary.file;
|
|
1227
|
+
lines.push(` at ${loc}`);
|
|
1228
|
+
}
|
|
1229
|
+
if (primary.lineText) {
|
|
1230
|
+
lines.push(` \u2502 ${primary.lineText}`);
|
|
1231
|
+
}
|
|
1232
|
+
if (parsedStack?.length) {
|
|
1233
|
+
const frames = parsedStack.slice(0, MAX_TERMINAL_STACK_FRAMES);
|
|
1234
|
+
for (const frame of frames) {
|
|
1235
|
+
const fn = frame.functionName ? `${frame.functionName} ` : "";
|
|
1236
|
+
lines.push(` at ${fn}${frame.file}:${frame.line}:${frame.column}`);
|
|
1237
|
+
}
|
|
1238
|
+
}
|
|
1239
|
+
return lines.join(`
|
|
1240
|
+
`);
|
|
1241
|
+
}
|
|
1242
|
+
function createRuntimeErrorDeduplicator() {
|
|
1243
|
+
let lastKey = "";
|
|
1244
|
+
return {
|
|
1245
|
+
shouldLog(message, file, line) {
|
|
1246
|
+
const key = `${message}::${file ?? ""}::${line ?? ""}`;
|
|
1247
|
+
if (key === lastKey)
|
|
1248
|
+
return false;
|
|
1249
|
+
lastKey = key;
|
|
1250
|
+
return true;
|
|
1251
|
+
},
|
|
1252
|
+
reset() {
|
|
1253
|
+
lastKey = "";
|
|
1254
|
+
}
|
|
1255
|
+
};
|
|
1256
|
+
}
|
|
1202
1257
|
function killStaleProcess(targetPort) {
|
|
1203
1258
|
try {
|
|
1204
1259
|
const output = execSync(`lsof -ti :${targetPort}`, { encoding: "utf8" }).trim();
|
|
@@ -1217,29 +1272,6 @@ function killStaleProcess(targetPort) {
|
|
|
1217
1272
|
}
|
|
1218
1273
|
} catch {}
|
|
1219
1274
|
}
|
|
1220
|
-
function createIndexHtmlStasher(projectRoot) {
|
|
1221
|
-
const indexHtmlPath = resolve(projectRoot, "index.html");
|
|
1222
|
-
const indexHtmlBackupPath = resolve(projectRoot, ".vertz", "dev", "index.html.bak");
|
|
1223
|
-
let stashed = false;
|
|
1224
|
-
return {
|
|
1225
|
-
stash() {
|
|
1226
|
-
if (!existsSync(indexHtmlPath) && existsSync(indexHtmlBackupPath)) {
|
|
1227
|
-
renameSync(indexHtmlBackupPath, indexHtmlPath);
|
|
1228
|
-
}
|
|
1229
|
-
if (existsSync(indexHtmlPath)) {
|
|
1230
|
-
mkdirSync(resolve(projectRoot, ".vertz", "dev"), { recursive: true });
|
|
1231
|
-
renameSync(indexHtmlPath, indexHtmlBackupPath);
|
|
1232
|
-
stashed = true;
|
|
1233
|
-
}
|
|
1234
|
-
},
|
|
1235
|
-
restore() {
|
|
1236
|
-
if (stashed && existsSync(indexHtmlBackupPath)) {
|
|
1237
|
-
renameSync(indexHtmlBackupPath, indexHtmlPath);
|
|
1238
|
-
stashed = false;
|
|
1239
|
-
}
|
|
1240
|
-
}
|
|
1241
|
-
};
|
|
1242
|
-
}
|
|
1243
1275
|
function parseHMRAssets(html) {
|
|
1244
1276
|
const srcMatch = html.match(/src="(\/_bun\/client\/[^"]+\.js)"/);
|
|
1245
1277
|
const bootstrapMatch = html.match(/<script>(\(\(a\)=>\{document\.addEventListener.*?)<\/script>/);
|
|
@@ -1585,6 +1617,7 @@ function createBunDevServer(options) {
|
|
|
1585
1617
|
let lastBuildError = "";
|
|
1586
1618
|
let lastBroadcastedError = "";
|
|
1587
1619
|
let lastChangedFile = "";
|
|
1620
|
+
const terminalDedup = createRuntimeErrorDeduplicator();
|
|
1588
1621
|
const resolvePatterns = ["Could not resolve", "Module not found", "Cannot find module"];
|
|
1589
1622
|
const hmrErrorPattern = /\[vertz-hmr\] Error re-mounting (\w+): ([\s\S]*?)(?:\n\s+at |$)/;
|
|
1590
1623
|
const frontendErrorPattern = /\x1b\[31mfrontend\x1b\[0m ([\s\S]*?)(?:\n\s+from browser|$)/;
|
|
@@ -1611,6 +1644,19 @@ function createBunDevServer(options) {
|
|
|
1611
1644
|
return { message: "" };
|
|
1612
1645
|
}
|
|
1613
1646
|
const origConsoleError = console.error;
|
|
1647
|
+
function logRuntimeErrorToTerminal(errors, parsedStack) {
|
|
1648
|
+
const primary = errors[0];
|
|
1649
|
+
if (!primary)
|
|
1650
|
+
return;
|
|
1651
|
+
if (!terminalDedup.shouldLog(primary.message, primary.file, primary.line))
|
|
1652
|
+
return;
|
|
1653
|
+
const formatted = formatTerminalRuntimeError(errors, parsedStack);
|
|
1654
|
+
if (formatted) {
|
|
1655
|
+
origConsoleError(formatted);
|
|
1656
|
+
}
|
|
1657
|
+
diagnostics.recordRuntimeError(primary.message, primary.file ?? null);
|
|
1658
|
+
diagnostics.recordError("runtime", primary.message);
|
|
1659
|
+
}
|
|
1614
1660
|
console.error = (...args) => {
|
|
1615
1661
|
const text = args.map((a) => typeof a === "string" ? a : String(a)).join(" ");
|
|
1616
1662
|
if (!text.startsWith("[Server]")) {
|
|
@@ -1666,7 +1712,6 @@ function createBunDevServer(options) {
|
|
|
1666
1712
|
}
|
|
1667
1713
|
origConsoleError.apply(console, args);
|
|
1668
1714
|
};
|
|
1669
|
-
const indexHtmlStasher = createIndexHtmlStasher(projectRoot);
|
|
1670
1715
|
let cachedSpec = null;
|
|
1671
1716
|
let specWatcher = null;
|
|
1672
1717
|
const loadOpenAPISpec = () => {
|
|
@@ -1718,7 +1763,6 @@ function createBunDevServer(options) {
|
|
|
1718
1763
|
return new Response("OpenAPI spec not found", { status: 404 });
|
|
1719
1764
|
};
|
|
1720
1765
|
async function start() {
|
|
1721
|
-
indexHtmlStasher.stash();
|
|
1722
1766
|
const { plugin } = await Promise.resolve(globalThis.Bun);
|
|
1723
1767
|
const { createVertzBunPlugin } = await import("./bun-plugin/index.js");
|
|
1724
1768
|
const entryPath = resolve(projectRoot, entry);
|
|
@@ -2001,6 +2045,7 @@ data: {}
|
|
|
2001
2045
|
parsedStack: result.parsedStack
|
|
2002
2046
|
};
|
|
2003
2047
|
currentError = { category: "runtime", errors: enrichedErrors };
|
|
2048
|
+
logRuntimeErrorToTerminal(enrichedErrors, result.parsedStack);
|
|
2004
2049
|
const text2 = JSON.stringify(payload2);
|
|
2005
2050
|
for (const client of wsClients) {
|
|
2006
2051
|
client.sendText(text2);
|
|
@@ -2015,6 +2060,7 @@ data: {}
|
|
|
2015
2060
|
parsedStack: result.parsedStack
|
|
2016
2061
|
};
|
|
2017
2062
|
currentError = { category: "runtime", errors: result.errors };
|
|
2063
|
+
logRuntimeErrorToTerminal(result.errors, result.parsedStack);
|
|
2018
2064
|
const text = JSON.stringify(payload);
|
|
2019
2065
|
for (const client of wsClients) {
|
|
2020
2066
|
client.sendText(text);
|
|
@@ -2036,12 +2082,17 @@ data: {}
|
|
|
2036
2082
|
errors
|
|
2037
2083
|
};
|
|
2038
2084
|
currentError = { category: "runtime", errors };
|
|
2085
|
+
logRuntimeErrorToTerminal(errors);
|
|
2039
2086
|
const text = JSON.stringify(payload);
|
|
2040
2087
|
for (const client of wsClients) {
|
|
2041
2088
|
client.sendText(text);
|
|
2042
2089
|
}
|
|
2043
2090
|
} else {
|
|
2044
|
-
|
|
2091
|
+
const fallbackErrors = [
|
|
2092
|
+
{ message: data.message ?? "Unknown error" }
|
|
2093
|
+
];
|
|
2094
|
+
logRuntimeErrorToTerminal(fallbackErrors);
|
|
2095
|
+
broadcastError("runtime", fallbackErrors);
|
|
2045
2096
|
}
|
|
2046
2097
|
});
|
|
2047
2098
|
}
|
|
@@ -2096,6 +2147,8 @@ data: {}
|
|
|
2096
2147
|
diagnostics.recordFileChange(lastChangedFile);
|
|
2097
2148
|
logger.log("watcher", "file-changed", { file: lastChangedFile });
|
|
2098
2149
|
lastBroadcastedError = "";
|
|
2150
|
+
terminalDedup.reset();
|
|
2151
|
+
diagnostics.clearRuntimeErrors();
|
|
2099
2152
|
sourceMapResolver.invalidate();
|
|
2100
2153
|
if (logRequests) {
|
|
2101
2154
|
console.log(`[Server] File changed: ${filename}`);
|
|
@@ -2233,14 +2286,14 @@ data: {}
|
|
|
2233
2286
|
server.stop(true);
|
|
2234
2287
|
server = null;
|
|
2235
2288
|
}
|
|
2236
|
-
indexHtmlStasher.restore();
|
|
2237
2289
|
}
|
|
2238
2290
|
};
|
|
2239
2291
|
}
|
|
2240
2292
|
export {
|
|
2241
2293
|
parseHMRAssets,
|
|
2242
2294
|
generateSSRPageHtml,
|
|
2243
|
-
|
|
2295
|
+
formatTerminalRuntimeError,
|
|
2296
|
+
createRuntimeErrorDeduplicator,
|
|
2244
2297
|
createFetchInterceptor,
|
|
2245
2298
|
createBunDevServer,
|
|
2246
2299
|
buildScriptTag
|
|
@@ -7,6 +7,8 @@ type ContextScope = NonNullable<ReturnType<typeof getContextScope>>;
|
|
|
7
7
|
interface SignalRef {
|
|
8
8
|
peek(): unknown;
|
|
9
9
|
value: unknown;
|
|
10
|
+
/** Optional HMR key for name-based signal matching (set by compiler). */
|
|
11
|
+
_hmrKey?: string;
|
|
10
12
|
}
|
|
11
13
|
/**
|
|
12
14
|
* Register or update a component factory in the registry.
|
|
@@ -15,7 +17,7 @@ interface SignalRef {
|
|
|
15
17
|
* On first load: creates a new record. On HMR re-evaluation: updates
|
|
16
18
|
* the factory reference (instances are preserved for __$refreshPerform).
|
|
17
19
|
*/
|
|
18
|
-
declare function __$refreshReg(moduleId: string, name: string, factory: (...args: unknown[]) => HTMLElement): void;
|
|
20
|
+
declare function __$refreshReg(moduleId: string, name: string, factory: (...args: unknown[]) => HTMLElement, hash?: string): void;
|
|
19
21
|
/**
|
|
20
22
|
* Track a live component instance for HMR replacement.
|
|
21
23
|
*
|
|
@@ -23,6 +23,7 @@ var DIRTY_KEY = Symbol.for("vertz:fast-refresh:dirty");
|
|
|
23
23
|
var registry = globalThis[REGISTRY_KEY] ??= new Map;
|
|
24
24
|
var dirtyModules = globalThis[DIRTY_KEY] ??= new Set;
|
|
25
25
|
var performingRefresh = false;
|
|
26
|
+
var refreshSignals = null;
|
|
26
27
|
function getModule(moduleId) {
|
|
27
28
|
let mod = registry.get(moduleId);
|
|
28
29
|
if (!mod) {
|
|
@@ -31,19 +32,24 @@ function getModule(moduleId) {
|
|
|
31
32
|
}
|
|
32
33
|
return mod;
|
|
33
34
|
}
|
|
34
|
-
function __$refreshReg(moduleId, name, factory) {
|
|
35
|
+
function __$refreshReg(moduleId, name, factory, hash) {
|
|
35
36
|
const mod = getModule(moduleId);
|
|
36
37
|
const existing = mod.get(name);
|
|
37
38
|
if (existing) {
|
|
39
|
+
if (hash && existing.hash === hash)
|
|
40
|
+
return;
|
|
38
41
|
existing.factory = factory;
|
|
42
|
+
existing.hash = hash;
|
|
39
43
|
dirtyModules.add(moduleId);
|
|
40
44
|
} else {
|
|
41
|
-
mod.set(name, { factory, instances: [] });
|
|
45
|
+
mod.set(name, { factory, instances: [], hash });
|
|
42
46
|
}
|
|
43
47
|
}
|
|
44
48
|
function __$refreshTrack(moduleId, name, element, args, cleanups, contextScope, signals = []) {
|
|
45
|
-
if (performingRefresh)
|
|
49
|
+
if (performingRefresh) {
|
|
50
|
+
refreshSignals = signals;
|
|
46
51
|
return element;
|
|
52
|
+
}
|
|
47
53
|
const mod = registry.get(moduleId);
|
|
48
54
|
if (!mod)
|
|
49
55
|
return element;
|
|
@@ -76,12 +82,13 @@ function __$refreshPerform(moduleId) {
|
|
|
76
82
|
let newSignals;
|
|
77
83
|
let newContextScope;
|
|
78
84
|
try {
|
|
79
|
-
|
|
85
|
+
refreshSignals = null;
|
|
80
86
|
newElement = factory(...args);
|
|
81
|
-
newSignals =
|
|
87
|
+
newSignals = refreshSignals ?? [];
|
|
88
|
+
refreshSignals = null;
|
|
82
89
|
newContextScope = getContextScope();
|
|
83
90
|
} catch (err) {
|
|
84
|
-
|
|
91
|
+
refreshSignals = null;
|
|
85
92
|
runCleanups(newCleanups);
|
|
86
93
|
popScope();
|
|
87
94
|
setContextScope(prevScope);
|
|
@@ -89,21 +96,33 @@ function __$refreshPerform(moduleId) {
|
|
|
89
96
|
updatedInstances.push(instance);
|
|
90
97
|
continue;
|
|
91
98
|
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
const
|
|
95
|
-
|
|
96
|
-
|
|
99
|
+
try {
|
|
100
|
+
if (savedValues.length > 0) {
|
|
101
|
+
const namedSaved = new Map;
|
|
102
|
+
const unnamedSaved = [];
|
|
103
|
+
for (const s of oldSignals) {
|
|
104
|
+
if (s._hmrKey)
|
|
105
|
+
namedSaved.set(s._hmrKey, s.peek());
|
|
106
|
+
else
|
|
107
|
+
unnamedSaved.push(s.peek());
|
|
108
|
+
}
|
|
109
|
+
let unnamedIdx = 0;
|
|
110
|
+
for (const sig of newSignals) {
|
|
111
|
+
if (sig._hmrKey && namedSaved.has(sig._hmrKey)) {
|
|
112
|
+
sig.value = namedSaved.get(sig._hmrKey);
|
|
113
|
+
} else if (!sig._hmrKey && unnamedIdx < unnamedSaved.length) {
|
|
114
|
+
sig.value = unnamedSaved[unnamedIdx++];
|
|
115
|
+
}
|
|
116
|
+
}
|
|
97
117
|
}
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
118
|
+
popScope();
|
|
119
|
+
runCleanups(cleanups);
|
|
120
|
+
if (newCleanups.length > 0) {
|
|
121
|
+
_tryOnCleanup(() => runCleanups(newCleanups));
|
|
122
|
+
}
|
|
123
|
+
} finally {
|
|
124
|
+
setContextScope(prevScope);
|
|
105
125
|
}
|
|
106
|
-
setContextScope(prevScope);
|
|
107
126
|
let domSnapshot = null;
|
|
108
127
|
try {
|
|
109
128
|
domSnapshot = captureDOMState(element);
|
|
@@ -5,6 +5,11 @@ interface DebugLogger {
|
|
|
5
5
|
log(category: DebugCategory, message: string, data?: Record<string, unknown>): void;
|
|
6
6
|
isEnabled(category: DebugCategory): boolean;
|
|
7
7
|
}
|
|
8
|
+
interface RuntimeErrorEntry {
|
|
9
|
+
message: string;
|
|
10
|
+
source: string | null;
|
|
11
|
+
timestamp: string;
|
|
12
|
+
}
|
|
8
13
|
interface DiagnosticsSnapshot {
|
|
9
14
|
status: "ok";
|
|
10
15
|
uptime: number;
|
|
@@ -39,6 +44,7 @@ interface DiagnosticsSnapshot {
|
|
|
39
44
|
lastChangedFile: string | null;
|
|
40
45
|
lastChangeTime: string | null;
|
|
41
46
|
};
|
|
47
|
+
runtimeErrors: RuntimeErrorEntry[];
|
|
42
48
|
}
|
|
43
49
|
declare class DiagnosticsCollector {
|
|
44
50
|
private startTime;
|
|
@@ -61,6 +67,8 @@ declare class DiagnosticsCollector {
|
|
|
61
67
|
private wsConnectedClients;
|
|
62
68
|
private watcherLastChangedFile;
|
|
63
69
|
private watcherLastChangeTime;
|
|
70
|
+
private static readonly MAX_RUNTIME_ERRORS;
|
|
71
|
+
private runtimeErrorsBuffer;
|
|
64
72
|
recordPluginConfig(filter: string, hmr: boolean, fastRefresh: boolean): void;
|
|
65
73
|
recordPluginProcess(file: string): void;
|
|
66
74
|
recordSSRReload(success: boolean, durationMs: number, error?: string): void;
|
|
@@ -69,6 +77,8 @@ declare class DiagnosticsCollector {
|
|
|
69
77
|
recordErrorClear(): void;
|
|
70
78
|
recordWebSocketChange(count: number): void;
|
|
71
79
|
recordFileChange(file: string): void;
|
|
80
|
+
recordRuntimeError(message: string, source: string | null): void;
|
|
81
|
+
clearRuntimeErrors(): void;
|
|
72
82
|
getSnapshot(): DiagnosticsSnapshot;
|
|
73
83
|
}
|
|
74
84
|
import { BunPlugin as BunPlugin_seob6 } from "bun";
|
package/dist/bun-plugin/index.js
CHANGED
|
@@ -41,12 +41,17 @@ function injectContextStableIds(source, sourceFile, relFilePath) {
|
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
// src/bun-plugin/fast-refresh-codegen.ts
|
|
44
|
-
function generateRefreshPreamble(moduleId) {
|
|
44
|
+
function generateRefreshPreamble(moduleId, contentHash) {
|
|
45
45
|
const escapedId = moduleId.replace(/['\\]/g, "\\$&");
|
|
46
|
-
|
|
46
|
+
let code = `const __$fr = globalThis[Symbol.for('vertz:fast-refresh')];
|
|
47
47
|
` + `const { __$refreshReg, __$refreshTrack, __$refreshPerform, ` + `pushScope: __$pushScope, popScope: __$popScope, ` + `_tryOnCleanup: __$tryCleanup, runCleanups: __$runCleanups, ` + `getContextScope: __$getCtx, setContextScope: __$setCtx, ` + `startSignalCollection: __$startSigCol, stopSignalCollection: __$stopSigCol } = __$fr;
|
|
48
48
|
` + `const __$moduleId = '${escapedId}';
|
|
49
49
|
`;
|
|
50
|
+
if (contentHash) {
|
|
51
|
+
code += `const __$moduleHash = '${contentHash}';
|
|
52
|
+
`;
|
|
53
|
+
}
|
|
54
|
+
return code;
|
|
50
55
|
}
|
|
51
56
|
function generateRefreshWrapper(componentName) {
|
|
52
57
|
return `
|
|
@@ -63,17 +68,17 @@ const __$orig_${componentName} = ${componentName};
|
|
|
63
68
|
` + ` }
|
|
64
69
|
` + ` return __$refreshTrack(__$moduleId, '${componentName}', __$ret, __$args, __$scope, __$ctx, __$sigs);
|
|
65
70
|
` + `};
|
|
66
|
-
` + `__$refreshReg(__$moduleId, '${componentName}', ${componentName});
|
|
71
|
+
` + `__$refreshReg(__$moduleId, '${componentName}', ${componentName}, __$moduleHash);
|
|
67
72
|
`;
|
|
68
73
|
}
|
|
69
74
|
function generateRefreshPerform() {
|
|
70
75
|
return `__$refreshPerform(__$moduleId);
|
|
71
76
|
`;
|
|
72
77
|
}
|
|
73
|
-
function generateRefreshCode(moduleId, components) {
|
|
78
|
+
function generateRefreshCode(moduleId, components, contentHash) {
|
|
74
79
|
if (components.length === 0)
|
|
75
80
|
return null;
|
|
76
|
-
const preamble = generateRefreshPreamble(moduleId);
|
|
81
|
+
const preamble = generateRefreshPreamble(moduleId, contentHash);
|
|
77
82
|
let epilogue = "";
|
|
78
83
|
for (const comp of components) {
|
|
79
84
|
epilogue += generateRefreshWrapper(comp.name);
|
|
@@ -159,15 +164,13 @@ function createVertzBunPlugin(options) {
|
|
|
159
164
|
let refreshEpilogue = "";
|
|
160
165
|
if (fastRefresh) {
|
|
161
166
|
const components = componentAnalyzer.analyze(hydrationSourceFile);
|
|
162
|
-
const
|
|
167
|
+
const contentHash = Bun.hash(source).toString(36);
|
|
168
|
+
const refreshCode = generateRefreshCode(args.path, components, contentHash);
|
|
163
169
|
if (refreshCode) {
|
|
164
170
|
refreshPreamble = refreshCode.preamble;
|
|
165
171
|
refreshEpilogue = refreshCode.epilogue;
|
|
166
172
|
}
|
|
167
173
|
}
|
|
168
|
-
const mapBase64 = Buffer.from(remapped.toString()).toString("base64");
|
|
169
|
-
const sourceMapComment = `
|
|
170
|
-
//# sourceMappingURL=data:application/json;base64,${mapBase64}`;
|
|
171
174
|
let contents = "";
|
|
172
175
|
if (cssImportLine) {
|
|
173
176
|
contents += cssImportLine;
|
|
@@ -175,6 +178,14 @@ function createVertzBunPlugin(options) {
|
|
|
175
178
|
if (refreshPreamble) {
|
|
176
179
|
contents += refreshPreamble;
|
|
177
180
|
}
|
|
181
|
+
const prependedLines = contents.split(`
|
|
182
|
+
`).length - 1;
|
|
183
|
+
if (prependedLines > 0) {
|
|
184
|
+
remapped.mappings = ";".repeat(prependedLines) + remapped.mappings;
|
|
185
|
+
}
|
|
186
|
+
const mapBase64 = Buffer.from(remapped.toString()).toString("base64");
|
|
187
|
+
const sourceMapComment = `
|
|
188
|
+
//# sourceMappingURL=data:application/json;base64,${mapBase64}`;
|
|
178
189
|
contents += compileResult.code;
|
|
179
190
|
if (refreshEpilogue) {
|
|
180
191
|
contents += refreshEpilogue;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vertz/ui-server",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.12",
|
|
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.
|
|
58
|
-
"@vertz/ui-compiler": "^0.2.
|
|
56
|
+
"@vertz/core": "^0.2.11",
|
|
57
|
+
"@vertz/ui": "^0.2.11",
|
|
58
|
+
"@vertz/ui-compiler": "^0.2.11",
|
|
59
59
|
"magic-string": "^0.30.0",
|
|
60
60
|
"ts-morph": "^27.0.2"
|
|
61
61
|
},
|