revine 1.1.3 → 1.2.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/dist/client.d.ts +6 -4
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +2 -1
- package/dist/components/Image.d.ts +42 -0
- package/dist/components/Image.d.ts.map +1 -0
- package/dist/components/Image.js +106 -0
- package/dist/runtime/bundler/errorBoundary.d.ts +2 -0
- package/dist/runtime/bundler/errorBoundary.d.ts.map +1 -0
- package/dist/runtime/bundler/errorBoundary.js +122 -0
- package/dist/runtime/bundler/revinePlugin.d.ts.map +1 -1
- package/dist/runtime/bundler/revinePlugin.js +361 -2
- package/package.json +1 -1
- package/src/client.ts +6 -4
- package/src/components/Image.tsx +269 -0
- package/src/runtime/bundler/revinePlugin.ts +373 -2
|
@@ -1,4 +1,347 @@
|
|
|
1
1
|
const VIRTUAL_ROUTING_ID = "\0revine:routing";
|
|
2
|
+
const errorBoundaryComponent = `
|
|
3
|
+
function RevineErrorDialog() {
|
|
4
|
+
const error = useRouteError();
|
|
5
|
+
const [expanded, setExpanded] = React.useState(false);
|
|
6
|
+
const [copied, setCopied] = React.useState(false);
|
|
7
|
+
|
|
8
|
+
const message = error?.message || String(error) || "An unexpected error occurred.";
|
|
9
|
+
const stack = error?.stack || "";
|
|
10
|
+
const stackLines = stack
|
|
11
|
+
.split("\\n")
|
|
12
|
+
.filter((l) => l.trim().startsWith("at "))
|
|
13
|
+
.slice(0, 8)
|
|
14
|
+
.join("\\n");
|
|
15
|
+
|
|
16
|
+
const handleCopy = () => {
|
|
17
|
+
const text = message + (stackLines ? "\\n\\n" + stackLines : "");
|
|
18
|
+
navigator.clipboard.writeText(text).then(() => {
|
|
19
|
+
setCopied(true);
|
|
20
|
+
setTimeout(() => setCopied(false), 2000);
|
|
21
|
+
});
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
return React.createElement(
|
|
25
|
+
"div",
|
|
26
|
+
{ style: overlayStyle },
|
|
27
|
+
React.createElement(
|
|
28
|
+
"div",
|
|
29
|
+
{ style: dialogStyle },
|
|
30
|
+
|
|
31
|
+
React.createElement(
|
|
32
|
+
"div",
|
|
33
|
+
{ style: topBarStyle },
|
|
34
|
+
React.createElement(
|
|
35
|
+
"div",
|
|
36
|
+
{ style: brandStyle },
|
|
37
|
+
React.createElement(
|
|
38
|
+
"svg",
|
|
39
|
+
{ width: "18", height: "18", viewBox: "0 0 24 24", fill: "none",
|
|
40
|
+
stroke: "#a78bfa", strokeWidth: "2.2", strokeLinecap: "round",
|
|
41
|
+
strokeLinejoin: "round", style: { flexShrink: 0 } },
|
|
42
|
+
React.createElement("polygon", { points: "13 2 3 14 12 14 11 22 21 10 12 10 13 2" })
|
|
43
|
+
),
|
|
44
|
+
React.createElement("span", { style: brandNameStyle }, "Revine")
|
|
45
|
+
),
|
|
46
|
+
React.createElement("span", { style: badgeStyle }, "Runtime Error")
|
|
47
|
+
),
|
|
48
|
+
|
|
49
|
+
React.createElement("div", { style: dividerStyle }),
|
|
50
|
+
|
|
51
|
+
React.createElement(
|
|
52
|
+
"div",
|
|
53
|
+
{ style: headerStyle },
|
|
54
|
+
React.createElement(
|
|
55
|
+
"div",
|
|
56
|
+
{ style: iconWrapStyle },
|
|
57
|
+
React.createElement(
|
|
58
|
+
"svg",
|
|
59
|
+
{ width: "16", height: "16", viewBox: "0 0 24 24", fill: "none",
|
|
60
|
+
stroke: "#f87171", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round" },
|
|
61
|
+
React.createElement("circle", { cx: "12", cy: "12", r: "10" }),
|
|
62
|
+
React.createElement("line", { x1: "12", y1: "8", x2: "12", y2: "12" }),
|
|
63
|
+
React.createElement("line", { x1: "12", y1: "16", x2: "12.01", y2: "16" })
|
|
64
|
+
)
|
|
65
|
+
),
|
|
66
|
+
React.createElement("span", { style: titleStyle }, "Application Error")
|
|
67
|
+
),
|
|
68
|
+
|
|
69
|
+
React.createElement(
|
|
70
|
+
"div",
|
|
71
|
+
{ style: messagePanelStyle },
|
|
72
|
+
React.createElement("p", { style: messageStyle }, message),
|
|
73
|
+
React.createElement(
|
|
74
|
+
"button",
|
|
75
|
+
{ onClick: handleCopy, style: copyBtnStyle, title: "Copy error" },
|
|
76
|
+
copied
|
|
77
|
+
? React.createElement(
|
|
78
|
+
"svg",
|
|
79
|
+
{ width: "14", height: "14", viewBox: "0 0 24 24", fill: "none",
|
|
80
|
+
stroke: "#4ade80", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round" },
|
|
81
|
+
React.createElement("polyline", { points: "20 6 9 17 4 12" })
|
|
82
|
+
)
|
|
83
|
+
: React.createElement(
|
|
84
|
+
"svg",
|
|
85
|
+
{ width: "14", height: "14", viewBox: "0 0 24 24", fill: "none",
|
|
86
|
+
stroke: "#888", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" },
|
|
87
|
+
React.createElement("rect", { x: "9", y: "9", width: "13", height: "13", rx: "2", ry: "2" }),
|
|
88
|
+
React.createElement("path", { d: "M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" })
|
|
89
|
+
)
|
|
90
|
+
)
|
|
91
|
+
),
|
|
92
|
+
|
|
93
|
+
stackLines.length > 0 &&
|
|
94
|
+
React.createElement(
|
|
95
|
+
"div",
|
|
96
|
+
{ style: stackSectionStyle },
|
|
97
|
+
React.createElement(
|
|
98
|
+
"button",
|
|
99
|
+
{ onClick: () => setExpanded((v) => !v), style: toggleBtnStyle },
|
|
100
|
+
React.createElement(
|
|
101
|
+
"svg",
|
|
102
|
+
{ width: "12", height: "12", viewBox: "0 0 24 24", fill: "none",
|
|
103
|
+
stroke: "#666", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round",
|
|
104
|
+
style: { transform: expanded ? "rotate(90deg)" : "rotate(0deg)",
|
|
105
|
+
transition: "transform 200ms ease", flexShrink: 0 } },
|
|
106
|
+
React.createElement("polyline", { points: "9 18 15 12 9 6" })
|
|
107
|
+
),
|
|
108
|
+
React.createElement("span", null, "Stack trace")
|
|
109
|
+
),
|
|
110
|
+
expanded &&
|
|
111
|
+
React.createElement("pre", { style: stackStyle }, stackLines)
|
|
112
|
+
),
|
|
113
|
+
|
|
114
|
+
React.createElement(
|
|
115
|
+
"div",
|
|
116
|
+
{ style: actionsStyle },
|
|
117
|
+
React.createElement(
|
|
118
|
+
"button",
|
|
119
|
+
{ onClick: () => window.location.reload(), style: primaryBtnStyle },
|
|
120
|
+
React.createElement(
|
|
121
|
+
"svg",
|
|
122
|
+
{ width: "14", height: "14", viewBox: "0 0 24 24", fill: "none",
|
|
123
|
+
stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round" },
|
|
124
|
+
React.createElement("polyline", { points: "23 4 23 10 17 10" }),
|
|
125
|
+
React.createElement("path", { d: "M20.49 15a9 9 0 1 1-2.12-9.36L23 10" })
|
|
126
|
+
),
|
|
127
|
+
"Reload page"
|
|
128
|
+
),
|
|
129
|
+
React.createElement(
|
|
130
|
+
"button",
|
|
131
|
+
{ onClick: () => (window.location.href = "/"), style: secondaryBtnStyle },
|
|
132
|
+
React.createElement(
|
|
133
|
+
"svg",
|
|
134
|
+
{ width: "14", height: "14", viewBox: "0 0 24 24", fill: "none",
|
|
135
|
+
stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round" },
|
|
136
|
+
React.createElement("path", { d: "M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z" }),
|
|
137
|
+
React.createElement("polyline", { points: "9 22 9 12 15 12 15 22" })
|
|
138
|
+
),
|
|
139
|
+
"Go to home"
|
|
140
|
+
)
|
|
141
|
+
)
|
|
142
|
+
)
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const overlayStyle = {
|
|
147
|
+
position: "fixed", inset: 0,
|
|
148
|
+
background: "rgba(0,0,0,0.72)",
|
|
149
|
+
backdropFilter: "blur(6px)",
|
|
150
|
+
display: "flex", alignItems: "center", justifyContent: "center",
|
|
151
|
+
zIndex: 9999,
|
|
152
|
+
};
|
|
153
|
+
const dialogStyle = {
|
|
154
|
+
background: "#141414",
|
|
155
|
+
border: "1px solid #2a2a2a",
|
|
156
|
+
borderRadius: "14px",
|
|
157
|
+
padding: "0",
|
|
158
|
+
maxWidth: "580px", width: "92%",
|
|
159
|
+
boxShadow: "0 32px 80px rgba(0,0,0,0.7), 0 0 0 1px rgba(255,255,255,0.04) inset",
|
|
160
|
+
color: "#e5e5e5",
|
|
161
|
+
overflow: "hidden",
|
|
162
|
+
fontFamily: "system-ui, -apple-system, sans-serif",
|
|
163
|
+
};
|
|
164
|
+
const topBarStyle = {
|
|
165
|
+
display: "flex", alignItems: "center", justifyContent: "space-between",
|
|
166
|
+
padding: "12px 18px", background: "#0e0e0e",
|
|
167
|
+
};
|
|
168
|
+
const brandStyle = { display: "flex", alignItems: "center", gap: "7px" };
|
|
169
|
+
const brandNameStyle = {
|
|
170
|
+
fontSize: "13px", fontWeight: 700,
|
|
171
|
+
color: "#c4b5fd", letterSpacing: "0.04em",
|
|
172
|
+
fontFamily: "system-ui, sans-serif",
|
|
173
|
+
};
|
|
174
|
+
const badgeStyle = {
|
|
175
|
+
fontSize: "11px", fontWeight: 600, color: "#f87171",
|
|
176
|
+
background: "rgba(248,113,113,0.1)",
|
|
177
|
+
border: "1px solid rgba(248,113,113,0.2)",
|
|
178
|
+
borderRadius: "999px", padding: "2px 10px", letterSpacing: "0.03em",
|
|
179
|
+
};
|
|
180
|
+
const dividerStyle = { height: "1px", background: "#1f1f1f" };
|
|
181
|
+
const headerStyle = {
|
|
182
|
+
display: "flex", alignItems: "center", gap: "10px",
|
|
183
|
+
padding: "20px 22px 0 22px",
|
|
184
|
+
};
|
|
185
|
+
const iconWrapStyle = {
|
|
186
|
+
width: "28px", height: "28px", borderRadius: "8px",
|
|
187
|
+
background: "rgba(248,113,113,0.1)",
|
|
188
|
+
border: "1px solid rgba(248,113,113,0.15)",
|
|
189
|
+
display: "flex", alignItems: "center", justifyContent: "center", flexShrink: 0,
|
|
190
|
+
};
|
|
191
|
+
const titleStyle = { fontSize: "15px", fontWeight: 650, color: "#fff", letterSpacing: "-0.01em" };
|
|
192
|
+
const messagePanelStyle = {
|
|
193
|
+
position: "relative", margin: "14px 22px 0 22px",
|
|
194
|
+
background: "rgba(248,113,113,0.05)",
|
|
195
|
+
border: "1px solid rgba(248,113,113,0.12)",
|
|
196
|
+
borderRadius: "8px", padding: "12px 40px 12px 14px",
|
|
197
|
+
};
|
|
198
|
+
const messageStyle = {
|
|
199
|
+
fontFamily: "ui-monospace, 'Cascadia Code', 'Fira Code', monospace",
|
|
200
|
+
fontSize: "12.5px", color: "#fca5a5",
|
|
201
|
+
margin: 0, lineHeight: 1.65, wordBreak: "break-word",
|
|
202
|
+
};
|
|
203
|
+
const copyBtnStyle = {
|
|
204
|
+
position: "absolute", top: "10px", right: "10px",
|
|
205
|
+
background: "rgba(255,255,255,0.05)", border: "1px solid #2e2e2e",
|
|
206
|
+
borderRadius: "6px", width: "28px", height: "28px",
|
|
207
|
+
display: "flex", alignItems: "center", justifyContent: "center",
|
|
208
|
+
cursor: "pointer", transition: "background 150ms ease", flexShrink: 0,
|
|
209
|
+
};
|
|
210
|
+
const stackSectionStyle = { margin: "14px 22px 0 22px" };
|
|
211
|
+
const toggleBtnStyle = {
|
|
212
|
+
background: "none", border: "none", cursor: "pointer",
|
|
213
|
+
color: "#666", fontSize: "12px", padding: "4px 0",
|
|
214
|
+
display: "flex", alignItems: "center", gap: "6px",
|
|
215
|
+
letterSpacing: "0.02em", transition: "color 150ms ease",
|
|
216
|
+
};
|
|
217
|
+
const stackStyle = {
|
|
218
|
+
background: "#0a0a0a", border: "1px solid #222", borderRadius: "8px",
|
|
219
|
+
padding: "14px 16px", fontSize: "11px", color: "#888",
|
|
220
|
+
overflowX: "auto", lineHeight: 1.8, marginTop: "8px", marginBottom: 0,
|
|
221
|
+
whiteSpace: "pre-wrap", wordBreak: "break-all",
|
|
222
|
+
fontFamily: "ui-monospace, 'Cascadia Code', monospace",
|
|
223
|
+
};
|
|
224
|
+
const actionsStyle = {
|
|
225
|
+
display: "flex", gap: "10px", padding: "18px 22px 22px 22px", marginTop: "16px",
|
|
226
|
+
};
|
|
227
|
+
const primaryBtnStyle = {
|
|
228
|
+
flex: 1, padding: "10px 0", borderRadius: "8px", border: "none",
|
|
229
|
+
background: "linear-gradient(135deg, #7c3aed, #6d28d9)",
|
|
230
|
+
color: "#fff", fontWeight: 600, fontSize: "13px", cursor: "pointer",
|
|
231
|
+
display: "flex", alignItems: "center", justifyContent: "center", gap: "7px",
|
|
232
|
+
letterSpacing: "0.01em", boxShadow: "0 2px 12px rgba(124,58,237,0.35)",
|
|
233
|
+
fontFamily: "system-ui, sans-serif",
|
|
234
|
+
};
|
|
235
|
+
const secondaryBtnStyle = {
|
|
236
|
+
flex: 1, padding: "10px 0", borderRadius: "8px",
|
|
237
|
+
border: "1px solid #2e2e2e", background: "rgba(255,255,255,0.03)",
|
|
238
|
+
color: "#999", fontSize: "13px", cursor: "pointer",
|
|
239
|
+
display: "flex", alignItems: "center", justifyContent: "center", gap: "7px",
|
|
240
|
+
letterSpacing: "0.01em", fontFamily: "system-ui, sans-serif",
|
|
241
|
+
};
|
|
242
|
+
`;
|
|
243
|
+
// ── Shared overlay HTML builder (used in both the inline script and module error handler)
|
|
244
|
+
// Written as a plain JS string so it can be embedded inside the injected <script> tag.
|
|
245
|
+
const overlayScriptContent = `
|
|
246
|
+
(function () {
|
|
247
|
+
function showOverlay(title, message, detail) {
|
|
248
|
+
if (document.getElementById('__revine_error_overlay__')) return;
|
|
249
|
+
|
|
250
|
+
var overlay = document.createElement('div');
|
|
251
|
+
overlay.id = '__revine_error_overlay__';
|
|
252
|
+
overlay.style.cssText = 'position:fixed;inset:0;z-index:99999;background:rgba(0,0,0,0.72);backdrop-filter:blur(6px);display:flex;align-items:center;justify-content:center;font-family:system-ui,sans-serif';
|
|
253
|
+
|
|
254
|
+
var inner = document.createElement('div');
|
|
255
|
+
inner.style.cssText = 'background:#141414;border:1px solid #2a2a2a;border-radius:14px;max-width:580px;width:92%;overflow:hidden;box-shadow:0 32px 80px rgba(0,0,0,0.7)';
|
|
256
|
+
|
|
257
|
+
// Top bar
|
|
258
|
+
var topBar = document.createElement('div');
|
|
259
|
+
topBar.style.cssText = 'display:flex;align-items:center;justify-content:space-between;padding:12px 18px;background:#0e0e0e';
|
|
260
|
+
topBar.innerHTML = '<div style="display:flex;align-items:center;gap:7px"><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#a78bfa" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"/></svg><span style="font-size:13px;font-weight:700;color:#c4b5fd;letter-spacing:0.04em">Revine</span></div><span style="font-size:11px;font-weight:600;color:#f87171;background:rgba(248,113,113,0.1);border:1px solid rgba(248,113,113,0.2);border-radius:999px;padding:2px 10px">' + title + '</span>';
|
|
261
|
+
|
|
262
|
+
// Divider
|
|
263
|
+
var divider = document.createElement('div');
|
|
264
|
+
divider.style.cssText = 'height:1px;background:#1f1f1f';
|
|
265
|
+
|
|
266
|
+
// Body
|
|
267
|
+
var body = document.createElement('div');
|
|
268
|
+
body.style.cssText = 'padding:20px 22px 0';
|
|
269
|
+
|
|
270
|
+
var header = document.createElement('div');
|
|
271
|
+
header.style.cssText = 'display:flex;align-items:center;gap:10px;margin-bottom:14px';
|
|
272
|
+
header.innerHTML = '<div style="width:28px;height:28px;border-radius:8px;flex-shrink:0;background:rgba(248,113,113,0.1);border:1px solid rgba(248,113,113,0.15);display:flex;align-items:center;justify-content:center"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#f87171" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/></svg></div><span style="font-size:15px;font-weight:600;color:#fff">' + message + '</span>';
|
|
273
|
+
|
|
274
|
+
// Message panel with copy button
|
|
275
|
+
var msgPanel = document.createElement('div');
|
|
276
|
+
msgPanel.style.cssText = 'position:relative;background:rgba(248,113,113,0.05);border:1px solid rgba(248,113,113,0.12);border-radius:8px;padding:12px 44px 12px 14px;margin-bottom:14px';
|
|
277
|
+
|
|
278
|
+
var pre = document.createElement('pre');
|
|
279
|
+
pre.id = '__revine_err_detail__';
|
|
280
|
+
pre.style.cssText = 'font-family:ui-monospace,monospace;font-size:12px;color:#fca5a5;margin:0;line-height:1.65;white-space:pre-wrap;word-break:break-all';
|
|
281
|
+
pre.textContent = detail;
|
|
282
|
+
|
|
283
|
+
var copyBtn = document.createElement('button');
|
|
284
|
+
copyBtn.textContent = '⎘';
|
|
285
|
+
copyBtn.style.cssText = 'position:absolute;top:10px;right:10px;background:rgba(255,255,255,0.05);border:1px solid #2e2e2e;border-radius:6px;width:28px;height:28px;display:flex;align-items:center;justify-content:center;cursor:pointer;color:#888;font-size:13px';
|
|
286
|
+
copyBtn.onclick = function() {
|
|
287
|
+
navigator.clipboard.writeText(pre.textContent || '').then(function() {
|
|
288
|
+
copyBtn.textContent = '✓';
|
|
289
|
+
setTimeout(function() { copyBtn.textContent = '⎘'; }, 2000);
|
|
290
|
+
});
|
|
291
|
+
};
|
|
292
|
+
|
|
293
|
+
msgPanel.appendChild(pre);
|
|
294
|
+
msgPanel.appendChild(copyBtn);
|
|
295
|
+
body.appendChild(header);
|
|
296
|
+
body.appendChild(msgPanel);
|
|
297
|
+
|
|
298
|
+
// Actions
|
|
299
|
+
var actions = document.createElement('div');
|
|
300
|
+
actions.style.cssText = 'display:flex;gap:10px;padding:4px 22px 22px';
|
|
301
|
+
|
|
302
|
+
var reloadBtn = document.createElement('button');
|
|
303
|
+
reloadBtn.textContent = 'Reload page';
|
|
304
|
+
reloadBtn.style.cssText = 'flex:1;padding:10px 0;border-radius:8px;border:none;background:linear-gradient(135deg,#7c3aed,#6d28d9);color:#fff;font-weight:600;font-size:13px;cursor:pointer;box-shadow:0 2px 12px rgba(124,58,237,0.35)';
|
|
305
|
+
reloadBtn.onclick = function() { window.location.reload(); };
|
|
306
|
+
|
|
307
|
+
var dismissBtn = document.createElement('button');
|
|
308
|
+
dismissBtn.textContent = 'Dismiss';
|
|
309
|
+
dismissBtn.style.cssText = 'flex:1;padding:10px 0;border-radius:8px;border:1px solid #2e2e2e;background:rgba(255,255,255,0.03);color:#999;font-size:13px;cursor:pointer';
|
|
310
|
+
dismissBtn.onclick = function() { overlay.remove(); };
|
|
311
|
+
|
|
312
|
+
actions.appendChild(reloadBtn);
|
|
313
|
+
actions.appendChild(dismissBtn);
|
|
314
|
+
|
|
315
|
+
inner.appendChild(topBar);
|
|
316
|
+
inner.appendChild(divider);
|
|
317
|
+
inner.appendChild(body);
|
|
318
|
+
inner.appendChild(actions);
|
|
319
|
+
overlay.appendChild(inner);
|
|
320
|
+
document.body.appendChild(overlay);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// Expose globally so the module onerror attribute can call it
|
|
324
|
+
window.__revineShowOverlay = showOverlay;
|
|
325
|
+
|
|
326
|
+
// Catches runtime JS errors and async rejections
|
|
327
|
+
window.addEventListener('error', function(e) {
|
|
328
|
+
// Ignore errors that already have an overlay
|
|
329
|
+
if (document.getElementById('__revine_error_overlay__')) return;
|
|
330
|
+
var msg = e.message || 'Unknown error';
|
|
331
|
+
var src = e.filename ? e.filename.replace(location.origin, '') : '';
|
|
332
|
+
var detail = src ? msg + '\\n\\nSource: ' + src + (e.lineno ? ':' + e.lineno : '') : msg;
|
|
333
|
+
showOverlay('Module Error', 'Failed to load module', detail);
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
window.addEventListener('unhandledrejection', function(e) {
|
|
337
|
+
if (document.getElementById('__revine_error_overlay__')) return;
|
|
338
|
+
var reason = e.reason;
|
|
339
|
+
var msg = (reason && reason.message) ? reason.message : String(reason || 'Unhandled Promise rejection');
|
|
340
|
+
var detail = (reason && reason.stack) ? reason.stack : msg;
|
|
341
|
+
showOverlay('Unhandled Rejection', 'Promise rejected', detail);
|
|
342
|
+
});
|
|
343
|
+
})();
|
|
344
|
+
`;
|
|
2
345
|
export function revinePlugin() {
|
|
3
346
|
return {
|
|
4
347
|
name: "revine",
|
|
@@ -8,11 +351,25 @@ export function revinePlugin() {
|
|
|
8
351
|
return VIRTUAL_ROUTING_ID;
|
|
9
352
|
}
|
|
10
353
|
},
|
|
354
|
+
transformIndexHtml(html) {
|
|
355
|
+
// 1. Inject the overlay listener script into <head>
|
|
356
|
+
let result = html.replace("</head>", `<script>${overlayScriptContent}</script></head>`);
|
|
357
|
+
// 2. Find the main module entry <script> tag and attach an onerror handler.
|
|
358
|
+
// This is the ONLY way to catch ES module link-time errors like
|
|
359
|
+
// "does not provide an export named 'X'" — window.onerror does NOT fire for these.
|
|
360
|
+
result = result.replace(/(<script\s[^>]*type=["']module["'][^>]*src=["'][^"']+["'][^>]*)(\/?>)/g, (_match, opening, closing) => opening +
|
|
361
|
+
` onerror="window.__revineShowOverlay && window.__revineShowOverlay('Module Error','Failed to load application',event.type+': A module failed to load. Check all imports are valid and run \\'npm run build\\' in the Revine package.')"` +
|
|
362
|
+
closing);
|
|
363
|
+
return result;
|
|
364
|
+
},
|
|
11
365
|
load(id) {
|
|
12
366
|
if (id === VIRTUAL_ROUTING_ID) {
|
|
13
367
|
return `
|
|
14
|
-
import { createBrowserRouter } from "react-router-dom";
|
|
368
|
+
import { createBrowserRouter, useRouteError } from "react-router-dom";
|
|
15
369
|
import { lazy, Suspense, createElement } from "react";
|
|
370
|
+
import React from "react";
|
|
371
|
+
|
|
372
|
+
${errorBoundaryComponent}
|
|
16
373
|
|
|
17
374
|
const notFoundModules = import.meta.glob("/src/NotFound.tsx", { eager: true });
|
|
18
375
|
const NotFoundComponent = Object.values(notFoundModules)[0]?.default;
|
|
@@ -81,7 +438,7 @@ const routes = pageEntries.map(([filePath, component]) => {
|
|
|
81
438
|
|
|
82
439
|
const fallback = Loading
|
|
83
440
|
? createElement(Loading)
|
|
84
|
-
: createElement("div", null, "Loading
|
|
441
|
+
: createElement("div", null, "Loading\u2026");
|
|
85
442
|
|
|
86
443
|
const pageElement = createElement(
|
|
87
444
|
Suspense,
|
|
@@ -92,6 +449,7 @@ const routes = pageEntries.map(([filePath, component]) => {
|
|
|
92
449
|
return {
|
|
93
450
|
path: routePath,
|
|
94
451
|
element: layouts.length > 0 ? wrapWithLayouts(pageElement, layouts) : pageElement,
|
|
452
|
+
errorElement: createElement(RevineErrorDialog),
|
|
95
453
|
};
|
|
96
454
|
});
|
|
97
455
|
|
|
@@ -100,6 +458,7 @@ routes.push({
|
|
|
100
458
|
element: NotFoundComponent
|
|
101
459
|
? createElement(NotFoundComponent)
|
|
102
460
|
: createElement("div", null, "404 - Page Not Found"),
|
|
461
|
+
errorElement: createElement(RevineErrorDialog),
|
|
103
462
|
});
|
|
104
463
|
|
|
105
464
|
export const router = createBrowserRouter(routes);
|
package/package.json
CHANGED
package/src/client.ts
CHANGED
|
@@ -4,12 +4,14 @@ export {
|
|
|
4
4
|
useLocation,
|
|
5
5
|
useNavigate,
|
|
6
6
|
useParams,
|
|
7
|
-
useSearchParams
|
|
7
|
+
useSearchParams,
|
|
8
8
|
} from "react-router-dom";
|
|
9
|
-
export
|
|
9
|
+
export { Image } from "./components/Image.js";
|
|
10
|
+
export type { ImageProps } from "./components/Image.js";
|
|
10
11
|
export { Link } from "./components/Link.js";
|
|
12
|
+
export type { LinkProps } from "./components/Link.js";
|
|
11
13
|
export { NavLink } from "./components/NavLink.js";
|
|
14
|
+
export type { NavLinkProps } from "./components/NavLink.js";
|
|
12
15
|
export { defineConfig } from "./runtime/defineConfig.js";
|
|
13
|
-
export type { LayoutProps } from "./runtime/types.js";
|
|
14
|
-
export type { LinkProps } from "./components/Link.js";
|
|
15
16
|
export { env, envAll } from "./runtime/env.js";
|
|
17
|
+
export type { LayoutProps } from "./runtime/types.js";
|