swift-rust 0.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/README.md +78 -0
- package/bin/dev-server.mjs +1360 -0
- package/bin/error-overlay.mjs +529 -0
- package/bin/runtime/hmr-client.js +77 -0
- package/bin/swift-rust.js +96 -0
- package/dist/env.d.ts +4 -0
- package/dist/env.d.ts.map +1 -0
- package/dist/env.js +3 -0
- package/dist/env.js.map +1 -0
- package/dist/font-google.d.ts +2 -0
- package/dist/font-google.d.ts.map +1 -0
- package/dist/font-google.js +2 -0
- package/dist/font-google.js.map +1 -0
- package/dist/font-local.d.ts +3 -0
- package/dist/font-local.d.ts.map +1 -0
- package/dist/font-local.js +2 -0
- package/dist/font-local.js.map +1 -0
- package/dist/font.d.ts +3 -0
- package/dist/font.d.ts.map +1 -0
- package/dist/font.js +2 -0
- package/dist/font.js.map +1 -0
- package/dist/head.d.ts +21 -0
- package/dist/head.d.ts.map +1 -0
- package/dist/head.jsx +15 -0
- package/dist/head.jsx.map +1 -0
- package/dist/image.d.ts +3 -0
- package/dist/image.d.ts.map +1 -0
- package/dist/image.js +2 -0
- package/dist/image.js.map +1 -0
- package/dist/index.d.ts +79 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -0
- package/dist/link.d.ts +10 -0
- package/dist/link.d.ts.map +1 -0
- package/dist/link.jsx +6 -0
- package/dist/link.jsx.map +1 -0
- package/dist/pdf.d.ts +3 -0
- package/dist/pdf.d.ts.map +1 -0
- package/dist/pdf.js +2 -0
- package/dist/pdf.js.map +1 -0
- package/dist/router.d.ts +30 -0
- package/dist/router.d.ts.map +1 -0
- package/dist/router.js +29 -0
- package/dist/router.js.map +1 -0
- package/dist/smoke.test.d.ts +2 -0
- package/dist/smoke.test.d.ts.map +1 -0
- package/dist/smoke.test.js +6 -0
- package/dist/smoke.test.js.map +1 -0
- package/dist/video.d.ts +4 -0
- package/dist/video.d.ts.map +1 -0
- package/dist/video.js +3 -0
- package/dist/video.js.map +1 -0
- package/package.json +127 -0
- package/scripts/package-native.ts +113 -0
- package/scripts/postinstall.js +151 -0
|
@@ -0,0 +1,529 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
|
|
3
|
+
function escapeHtml(s) {
|
|
4
|
+
if (s == null) return "";
|
|
5
|
+
return String(s)
|
|
6
|
+
.replace(/&/g, "&")
|
|
7
|
+
.replace(/</g, "<")
|
|
8
|
+
.replace(/>/g, ">")
|
|
9
|
+
.replace(/"/g, """)
|
|
10
|
+
.replace(/'/g, "'");
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function classifyError(message) {
|
|
14
|
+
if (!message) return { kind: "error", code: "E0001", title: "Unknown error" };
|
|
15
|
+
const m = String(message);
|
|
16
|
+
|
|
17
|
+
if (/Cannot find module|SyntaxError|Unexpected token|is not defined|has no exported member|export.*not found/i.test(m)) {
|
|
18
|
+
return {
|
|
19
|
+
kind: "compile",
|
|
20
|
+
code: "E0401",
|
|
21
|
+
title: "Module or symbol not found",
|
|
22
|
+
explain:
|
|
23
|
+
"The compiler could not resolve a module, identifier, or export. " +
|
|
24
|
+
"This usually means a typo, a missing dependency, or a case-sensitivity issue.",
|
|
25
|
+
suggestions: [
|
|
26
|
+
"Check the spelling and case of the import path",
|
|
27
|
+
"Verify the file exists at the given path",
|
|
28
|
+
"Run `bun install` if the module is from a package",
|
|
29
|
+
"Check that the symbol is actually exported from the module",
|
|
30
|
+
],
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
if (/does not provide an export named|TypeError.*is not a function/i.test(m)) {
|
|
34
|
+
return {
|
|
35
|
+
kind: "compile",
|
|
36
|
+
code: "E0402",
|
|
37
|
+
title: "Invalid import or usage",
|
|
38
|
+
explain:
|
|
39
|
+
"You are trying to use something that the module does not export, " +
|
|
40
|
+
"or you are calling something that is not a function.",
|
|
41
|
+
suggestions: [
|
|
42
|
+
"Run `bun pm ls` to verify the package is installed",
|
|
43
|
+
"Check the package's public API in its README",
|
|
44
|
+
"If you added a default export, make sure to use `import X from ...`",
|
|
45
|
+
],
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
if (/Failed to fetch|FetchError|NetworkError|ENOTFOUND|ECONNREFUSED/i.test(m)) {
|
|
49
|
+
return {
|
|
50
|
+
kind: "runtime",
|
|
51
|
+
code: "E0901",
|
|
52
|
+
title: "Network request failed",
|
|
53
|
+
explain:
|
|
54
|
+
"A fetch or HTTP call to an external service failed. " +
|
|
55
|
+
"The server may be down, the URL may be wrong, or the network may be unavailable.",
|
|
56
|
+
suggestions: [
|
|
57
|
+
"Check your internet connection",
|
|
58
|
+
"Verify the URL is correct and reachable",
|
|
59
|
+
"If calling your own API, make sure the dev server is running",
|
|
60
|
+
"Check for CORS issues if calling a third-party API from the browser",
|
|
61
|
+
],
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
if (/notFound\(\)|NEXT_NOT_FOUND|NOT_FOUND/i.test(m)) {
|
|
65
|
+
return {
|
|
66
|
+
kind: "notFound",
|
|
67
|
+
code: "E1404",
|
|
68
|
+
title: "Resource not found",
|
|
69
|
+
explain:
|
|
70
|
+
"The page or resource you requested could not be found. " +
|
|
71
|
+
"This is typically thrown intentionally with notFound() to render a 404 page.",
|
|
72
|
+
suggestions: [
|
|
73
|
+
"Check the URL is correct",
|
|
74
|
+
"If this is unexpected, verify the data source returned a valid record",
|
|
75
|
+
"Add a not-found.tsx in this segment to provide a custom 404",
|
|
76
|
+
],
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
if (/redirect/i.test(m)) {
|
|
80
|
+
return {
|
|
81
|
+
kind: "redirect",
|
|
82
|
+
code: "E1301",
|
|
83
|
+
title: "Redirect",
|
|
84
|
+
explain:
|
|
85
|
+
"A redirect was thrown from this page. " +
|
|
86
|
+
"This is expected behavior when calling redirect() from a server component.",
|
|
87
|
+
suggestions: [
|
|
88
|
+
"If this is unexpected, check the calling code for an unintended redirect()",
|
|
89
|
+
],
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
if (/hydration|Text content does not match|There was an error while hydrating/i.test(m)) {
|
|
93
|
+
return {
|
|
94
|
+
kind: "runtime",
|
|
95
|
+
code: "E0501",
|
|
96
|
+
title: "Hydration mismatch",
|
|
97
|
+
explain:
|
|
98
|
+
"The server-rendered HTML does not match what the client tried to render. " +
|
|
99
|
+
"This usually means a component renders different output on the server vs. the client.",
|
|
100
|
+
suggestions: [
|
|
101
|
+
"Avoid using `new Date()`, `Math.random()`, or other non-deterministic values in render",
|
|
102
|
+
"Check that `typeof window` checks are inside `useEffect`",
|
|
103
|
+
"Verify the same data is available on both server and client",
|
|
104
|
+
],
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
if (/ENOSPC|EACCES|EADDRINUSE|EADDRNOTAVAIL|port.*already|port.*in use/i.test(m)) {
|
|
108
|
+
return {
|
|
109
|
+
kind: "system",
|
|
110
|
+
code: "E0002",
|
|
111
|
+
title: "Server failed to start",
|
|
112
|
+
explain:
|
|
113
|
+
"The dev server could not bind to the requested port, " +
|
|
114
|
+
"or it ran out of system resources.",
|
|
115
|
+
suggestions: [
|
|
116
|
+
"Close any other process using this port (usually 3000)",
|
|
117
|
+
"Run with --port 3001 to use a different port",
|
|
118
|
+
"Check disk space with `df -h`",
|
|
119
|
+
],
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
return {
|
|
123
|
+
kind: "error",
|
|
124
|
+
code: "E0001",
|
|
125
|
+
title: "An unexpected error occurred",
|
|
126
|
+
explain:
|
|
127
|
+
"The dev server caught an error it could not classify. " +
|
|
128
|
+
"Read the message and stack trace below to identify the cause.",
|
|
129
|
+
suggestions: [
|
|
130
|
+
"Read the error message carefully",
|
|
131
|
+
"Check the stack trace to find the failing function",
|
|
132
|
+
"Try reloading the page",
|
|
133
|
+
],
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function extractLocation(err) {
|
|
138
|
+
if (!err) return null;
|
|
139
|
+
const stack = err.stack || "";
|
|
140
|
+
const patterns = [
|
|
141
|
+
/at\s+.*?\s+\(([^()]+?):(\d+):(\d+)\)/,
|
|
142
|
+
/at\s+([^:]+):(\d+):(\d+)/,
|
|
143
|
+
/([^()\n]+\.(?:tsx?|jsx?|mjs|js)):(\d+):?(\d+)?/,
|
|
144
|
+
];
|
|
145
|
+
for (const p of patterns) {
|
|
146
|
+
const m = stack.match(p);
|
|
147
|
+
if (m) {
|
|
148
|
+
return {
|
|
149
|
+
file: m[1],
|
|
150
|
+
line: parseInt(m[2] || "0", 10),
|
|
151
|
+
column: parseInt(m[3] || "0", 10),
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
if (err.lineNumber) {
|
|
156
|
+
return { file: err.fileName || "<unknown>", line: err.lineNumber, column: err.columnNumber || 0 };
|
|
157
|
+
}
|
|
158
|
+
return null;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function readFileLines(filePath, targetLine, context = 3) {
|
|
162
|
+
try {
|
|
163
|
+
if (!existsSync(filePath)) return null;
|
|
164
|
+
const text = readFileSync(filePath, "utf8");
|
|
165
|
+
const lines = text.split("\n");
|
|
166
|
+
const start = Math.max(0, targetLine - 1 - context);
|
|
167
|
+
const end = Math.min(lines.length, targetLine + context);
|
|
168
|
+
const out = [];
|
|
169
|
+
for (let i = start; i < end; i++) {
|
|
170
|
+
out.push({ number: i + 1, text: lines[i] });
|
|
171
|
+
}
|
|
172
|
+
return out;
|
|
173
|
+
} catch {
|
|
174
|
+
return null;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function codeFrame(filePath, line, column, context = 3) {
|
|
179
|
+
if (!filePath || !line) return "";
|
|
180
|
+
const lines = readFileLines(filePath, line, context);
|
|
181
|
+
if (!lines || lines.length === 0) return "";
|
|
182
|
+
const maxWidth = String(lines[lines.length - 1].number).length;
|
|
183
|
+
const out = [];
|
|
184
|
+
out.push(
|
|
185
|
+
` <div class="frame-title"><span class="frame-file">${escapeHtml(filePath)}</span>:<span class="frame-line">${line}:${column || 1}</span></div>`,
|
|
186
|
+
);
|
|
187
|
+
out.push(` <pre class="frame">`);
|
|
188
|
+
for (const l of lines) {
|
|
189
|
+
const isTarget = l.number === line;
|
|
190
|
+
const gutter = String(l.number).padStart(maxWidth, " ");
|
|
191
|
+
const marker = isTarget ? "▶" : " ";
|
|
192
|
+
out.push(
|
|
193
|
+
`<span class="frame-line${isTarget ? " is-target" : ""}"><span class="frame-gutter">${gutter} ${marker}</span><span class="frame-source">${escapeHtml(l.text || " ")}</span></span>`,
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
if (column && column > 0) {
|
|
197
|
+
out.push(
|
|
198
|
+
`<span class="frame-caret">${" ".repeat(maxWidth + 2)}<span class="caret">${" ".repeat(Math.max(0, column - 1))}^</span></span>`,
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
out.push(` </pre>`);
|
|
202
|
+
return out.join("\n");
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function getExplainable({ code, title, kind }) {
|
|
206
|
+
return {
|
|
207
|
+
code,
|
|
208
|
+
title,
|
|
209
|
+
intro: kind === "notFound" ? "Not Found" : kind === "redirect" ? "Redirect" : "Something went wrong",
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
export function errorOverlayHTML({ message, stack, hint, file, line, column, kind }) {
|
|
214
|
+
const errMsg = message || "An unexpected error occurred";
|
|
215
|
+
const cls = classifyError(errMsg);
|
|
216
|
+
const finalKind = kind || cls.kind;
|
|
217
|
+
const finalCode = cls.code;
|
|
218
|
+
const finalTitle = cls.title;
|
|
219
|
+
const explain = cls.explain;
|
|
220
|
+
const suggestions = cls.suggestions || [];
|
|
221
|
+
|
|
222
|
+
const location = extractLocation({ message: errMsg, stack });
|
|
223
|
+
const finalFile = file || location?.file;
|
|
224
|
+
const finalLine = line || location?.line || 0;
|
|
225
|
+
const finalColumn = column || location?.column || 0;
|
|
226
|
+
const frame = finalFile ? codeFrame(finalFile, finalLine, finalColumn) : "";
|
|
227
|
+
|
|
228
|
+
const headerIcon = finalKind === "notFound" ? "🔍" : finalKind === "redirect" ? "↪️" : finalKind === "compile" ? "⚙️" : "✕";
|
|
229
|
+
|
|
230
|
+
const headerLabel =
|
|
231
|
+
finalKind === "notFound"
|
|
232
|
+
? "NOT FOUND"
|
|
233
|
+
: finalKind === "redirect"
|
|
234
|
+
? "REDIRECT"
|
|
235
|
+
: finalKind === "compile"
|
|
236
|
+
? "COMPILER"
|
|
237
|
+
: finalKind === "system"
|
|
238
|
+
? "SYSTEM"
|
|
239
|
+
: "ERROR";
|
|
240
|
+
|
|
241
|
+
const headerColor =
|
|
242
|
+
finalKind === "notFound"
|
|
243
|
+
? "#a3a3a3"
|
|
244
|
+
: finalKind === "redirect"
|
|
245
|
+
? "#60a5fa"
|
|
246
|
+
: finalKind === "compile"
|
|
247
|
+
? "#f59e0b"
|
|
248
|
+
: finalKind === "system"
|
|
249
|
+
? "#f97316"
|
|
250
|
+
: "#ef4444";
|
|
251
|
+
|
|
252
|
+
return `<!DOCTYPE html>
|
|
253
|
+
<html lang="en">
|
|
254
|
+
<head>
|
|
255
|
+
<meta charset="utf-8" />
|
|
256
|
+
<title>Swift Rust · ${escapeHtml(finalCode)} ${escapeHtml(finalTitle)}</title>
|
|
257
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
258
|
+
<style>
|
|
259
|
+
*, *::before, *::after { box-sizing: border-box; }
|
|
260
|
+
:root {
|
|
261
|
+
color-scheme: dark;
|
|
262
|
+
}
|
|
263
|
+
html, body { margin: 0; padding: 0; }
|
|
264
|
+
body {
|
|
265
|
+
font-family: ui-sans-serif, -apple-system, BlinkMacSystemFont, "Segoe UI", "Inter", "Helvetica Neue", sans-serif;
|
|
266
|
+
background: #09090b;
|
|
267
|
+
color: #e4e4e7;
|
|
268
|
+
min-height: 100vh;
|
|
269
|
+
font-size: 14px;
|
|
270
|
+
line-height: 1.5;
|
|
271
|
+
-webkit-font-smoothing: antialiased;
|
|
272
|
+
-moz-osx-font-smoothing: grayscale;
|
|
273
|
+
}
|
|
274
|
+
.mono { font-family: ui-monospace, "SF Mono", "JetBrains Mono", "Fira Code", Menlo, Monaco, Consolas, monospace; }
|
|
275
|
+
|
|
276
|
+
.page { max-width: 920px; margin: 0 auto; padding: 48px 24px 96px; }
|
|
277
|
+
.topbar {
|
|
278
|
+
display: flex; align-items: center; justify-content: space-between;
|
|
279
|
+
padding-bottom: 20px; margin-bottom: 32px;
|
|
280
|
+
border-bottom: 1px solid #27272a;
|
|
281
|
+
}
|
|
282
|
+
.brand { display: flex; align-items: center; gap: 10px; font-weight: 600; }
|
|
283
|
+
.brand-mark {
|
|
284
|
+
width: 22px; height: 22px; border-radius: 5px;
|
|
285
|
+
background: linear-gradient(135deg, #0070f3 0%, #7c3aed 50%, #ec4899 100%);
|
|
286
|
+
}
|
|
287
|
+
.brand-name { font-size: 14px; }
|
|
288
|
+
.topbar-right { font-size: 12px; color: #71717a; }
|
|
289
|
+
|
|
290
|
+
.card {
|
|
291
|
+
background: #0c0c0d;
|
|
292
|
+
border: 1px solid #27272a;
|
|
293
|
+
border-radius: 12px;
|
|
294
|
+
overflow: hidden;
|
|
295
|
+
margin-bottom: 16px;
|
|
296
|
+
}
|
|
297
|
+
.card-head {
|
|
298
|
+
padding: 14px 20px;
|
|
299
|
+
display: flex; align-items: center; gap: 12px;
|
|
300
|
+
background: #111113;
|
|
301
|
+
border-bottom: 1px solid #27272a;
|
|
302
|
+
}
|
|
303
|
+
.card-head .code-badge {
|
|
304
|
+
font-family: ui-monospace, "JetBrains Mono", monospace;
|
|
305
|
+
font-size: 11px; font-weight: 700; letter-spacing: 0.5px;
|
|
306
|
+
background: #1f1f23; color: #e4e4e7;
|
|
307
|
+
padding: 3px 8px; border-radius: 4px;
|
|
308
|
+
}
|
|
309
|
+
.card-head .icon { font-size: 16px; line-height: 1; }
|
|
310
|
+
.card-head .title { font-size: 14px; font-weight: 600; color: #e4e4e7; }
|
|
311
|
+
.card-head .kind {
|
|
312
|
+
margin-left: auto;
|
|
313
|
+
font-size: 10px; font-weight: 700; letter-spacing: 0.5px;
|
|
314
|
+
color: ${headerColor};
|
|
315
|
+
background: ${headerColor}1A;
|
|
316
|
+
border: 1px solid ${headerColor}33;
|
|
317
|
+
padding: 3px 8px; border-radius: 4px;
|
|
318
|
+
}
|
|
319
|
+
.card-body { padding: 20px; }
|
|
320
|
+
|
|
321
|
+
.lede {
|
|
322
|
+
font-size: 16px; font-weight: 500;
|
|
323
|
+
color: #fafafa;
|
|
324
|
+
margin-bottom: 16px; line-height: 1.5;
|
|
325
|
+
}
|
|
326
|
+
.lede .err-code {
|
|
327
|
+
font-family: ui-monospace, "JetBrains Mono", monospace;
|
|
328
|
+
font-size: 12px;
|
|
329
|
+
background: ${headerColor}1A;
|
|
330
|
+
color: ${headerColor};
|
|
331
|
+
padding: 2px 6px;
|
|
332
|
+
border-radius: 3px;
|
|
333
|
+
margin-right: 8px;
|
|
334
|
+
font-weight: 600;
|
|
335
|
+
}
|
|
336
|
+
.explain {
|
|
337
|
+
color: #a1a1aa;
|
|
338
|
+
margin-bottom: 16px;
|
|
339
|
+
font-size: 14px;
|
|
340
|
+
}
|
|
341
|
+
.section-label {
|
|
342
|
+
font-size: 11px; font-weight: 600; letter-spacing: 0.5px;
|
|
343
|
+
color: #71717a; text-transform: uppercase;
|
|
344
|
+
margin: 20px 0 10px;
|
|
345
|
+
}
|
|
346
|
+
.suggestions {
|
|
347
|
+
list-style: none; padding: 0; margin: 0;
|
|
348
|
+
border: 1px solid #27272a; border-radius: 8px;
|
|
349
|
+
overflow: hidden;
|
|
350
|
+
}
|
|
351
|
+
.suggestions li {
|
|
352
|
+
padding: 10px 14px;
|
|
353
|
+
font-size: 13px;
|
|
354
|
+
color: #d4d4d8;
|
|
355
|
+
background: #111113;
|
|
356
|
+
border-top: 1px solid #27272a;
|
|
357
|
+
display: flex; align-items: flex-start; gap: 10px;
|
|
358
|
+
}
|
|
359
|
+
.suggestions li:first-child { border-top: 0; }
|
|
360
|
+
.suggestions li::before {
|
|
361
|
+
content: "→";
|
|
362
|
+
color: #60a5fa;
|
|
363
|
+
font-weight: 600;
|
|
364
|
+
flex-shrink: 0;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
.frame {
|
|
368
|
+
background: #000;
|
|
369
|
+
border: 1px solid #27272a;
|
|
370
|
+
border-radius: 8px;
|
|
371
|
+
padding: 14px 16px;
|
|
372
|
+
overflow-x: auto;
|
|
373
|
+
font-size: 12.5px; line-height: 1.6;
|
|
374
|
+
margin: 0;
|
|
375
|
+
}
|
|
376
|
+
.frame-title {
|
|
377
|
+
font-family: ui-monospace, "JetBrains Mono", monospace;
|
|
378
|
+
font-size: 12px;
|
|
379
|
+
color: #71717a;
|
|
380
|
+
margin-bottom: 8px;
|
|
381
|
+
display: flex; align-items: center; gap: 4px;
|
|
382
|
+
}
|
|
383
|
+
.frame-file { color: #a1a1aa; }
|
|
384
|
+
.frame-line { color: #71717a; }
|
|
385
|
+
.frame-line { display: block; white-space: pre; }
|
|
386
|
+
.frame-gutter { color: #3f3f46; user-select: none; margin-right: 12px; }
|
|
387
|
+
.frame-line.is-target .frame-source { color: #fafafa; }
|
|
388
|
+
.frame-line.is-target .frame-gutter { color: #ef4444; }
|
|
389
|
+
.frame-caret { display: block; white-space: pre; color: #ef4444; }
|
|
390
|
+
.caret { background: #ef444433; color: #ef4444; }
|
|
391
|
+
|
|
392
|
+
.stack-wrap { margin-top: 12px; }
|
|
393
|
+
details summary {
|
|
394
|
+
cursor: pointer;
|
|
395
|
+
color: #a1a1aa;
|
|
396
|
+
font-size: 12px;
|
|
397
|
+
padding: 8px 0;
|
|
398
|
+
user-select: none;
|
|
399
|
+
font-weight: 500;
|
|
400
|
+
}
|
|
401
|
+
details summary:hover { color: #e4e4e7; }
|
|
402
|
+
details[open] summary { color: #fafafa; }
|
|
403
|
+
pre.stack {
|
|
404
|
+
background: #000;
|
|
405
|
+
border: 1px solid #27272a;
|
|
406
|
+
border-radius: 8px;
|
|
407
|
+
padding: 14px 16px;
|
|
408
|
+
overflow-x: auto;
|
|
409
|
+
font-size: 12px; line-height: 1.6;
|
|
410
|
+
color: #a1a1aa;
|
|
411
|
+
margin: 0;
|
|
412
|
+
max-height: 320px;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
.hint {
|
|
416
|
+
background: #1e1b4b;
|
|
417
|
+
border: 1px solid #4338ca;
|
|
418
|
+
color: #c7d2fe;
|
|
419
|
+
padding: 10px 14px;
|
|
420
|
+
border-radius: 8px;
|
|
421
|
+
font-size: 13px;
|
|
422
|
+
margin-bottom: 16px;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
.actions { display: flex; gap: 8px; margin-top: 24px; flex-wrap: wrap; }
|
|
426
|
+
.btn {
|
|
427
|
+
display: inline-flex; align-items: center; gap: 6px;
|
|
428
|
+
padding: 8px 14px; border-radius: 6px;
|
|
429
|
+
font-size: 13px; font-weight: 500;
|
|
430
|
+
border: 1px solid #27272a;
|
|
431
|
+
background: #18181b;
|
|
432
|
+
color: #e4e4e7;
|
|
433
|
+
cursor: pointer;
|
|
434
|
+
text-decoration: none;
|
|
435
|
+
font-family: inherit;
|
|
436
|
+
transition: all 0.15s ease;
|
|
437
|
+
}
|
|
438
|
+
.btn:hover { background: #27272a; border-color: #3f3f46; }
|
|
439
|
+
.btn-primary {
|
|
440
|
+
background: #fafafa;
|
|
441
|
+
color: #09090b;
|
|
442
|
+
border-color: #fafafa;
|
|
443
|
+
}
|
|
444
|
+
.btn-primary:hover { background: #e4e4e7; border-color: #e4e4e7; }
|
|
445
|
+
|
|
446
|
+
.footer {
|
|
447
|
+
margin-top: 40px;
|
|
448
|
+
padding-top: 20px;
|
|
449
|
+
border-top: 1px solid #27272a;
|
|
450
|
+
display: flex; justify-content: space-between;
|
|
451
|
+
font-size: 11px; color: #52525b;
|
|
452
|
+
}
|
|
453
|
+
.footer a { color: #71717a; text-decoration: none; }
|
|
454
|
+
.footer a:hover { color: #a1a1aa; }
|
|
455
|
+
|
|
456
|
+
@media (max-width: 640px) {
|
|
457
|
+
.page { padding: 24px 16px 64px; }
|
|
458
|
+
.topbar { flex-direction: column; align-items: flex-start; gap: 8px; }
|
|
459
|
+
}
|
|
460
|
+
</style>
|
|
461
|
+
</head>
|
|
462
|
+
<body>
|
|
463
|
+
<div class="page">
|
|
464
|
+
<div class="topbar">
|
|
465
|
+
<div class="brand">
|
|
466
|
+
<div class="brand-mark"></div>
|
|
467
|
+
<div class="brand-name">Swift Rust <span style="color:#71717a;font-weight:400">dev</span></div>
|
|
468
|
+
</div>
|
|
469
|
+
<div class="topbar-right">Press <kbd style="background:#1f1f23;border:1px solid #27272a;border-radius:3px;padding:1px 5px;font-family:ui-monospace;font-size:11px">Esc</kbd> to dismiss</div>
|
|
470
|
+
</div>
|
|
471
|
+
|
|
472
|
+
<div class="card">
|
|
473
|
+
<div class="card-head">
|
|
474
|
+
<span class="icon">${headerIcon}</span>
|
|
475
|
+
<span class="code-badge">${escapeHtml(finalCode)}</span>
|
|
476
|
+
<span class="title">${escapeHtml(finalTitle)}</span>
|
|
477
|
+
<span class="kind">${escapeHtml(headerLabel)}</span>
|
|
478
|
+
</div>
|
|
479
|
+
<div class="card-body">
|
|
480
|
+
<div class="lede">
|
|
481
|
+
<span class="err-code">${escapeHtml(finalCode)}</span>
|
|
482
|
+
${escapeHtml(errMsg.split("\n")[0])}
|
|
483
|
+
</div>
|
|
484
|
+
<div class="explain">${escapeHtml(explain)}</div>
|
|
485
|
+
|
|
486
|
+
${hint ? `<div class="hint">${escapeHtml(hint)}</div>` : ""}
|
|
487
|
+
|
|
488
|
+
${suggestions.length > 0 ? `
|
|
489
|
+
<div class="section-label">Suggested fixes</div>
|
|
490
|
+
<ul class="suggestions">
|
|
491
|
+
${suggestions.map((s) => `<li>${escapeHtml(s)}</li>`).join("")}
|
|
492
|
+
</ul>
|
|
493
|
+
` : ""}
|
|
494
|
+
|
|
495
|
+
${frame ? `
|
|
496
|
+
<div class="section-label">Source</div>
|
|
497
|
+
${frame}
|
|
498
|
+
` : ""}
|
|
499
|
+
|
|
500
|
+
${stack ? `
|
|
501
|
+
<div class="stack-wrap">
|
|
502
|
+
<details>
|
|
503
|
+
<summary>Stack trace</summary>
|
|
504
|
+
<pre class="stack">${escapeHtml(stack)}</pre>
|
|
505
|
+
</details>
|
|
506
|
+
</div>
|
|
507
|
+
` : ""}
|
|
508
|
+
|
|
509
|
+
<div class="actions">
|
|
510
|
+
<button class="btn btn-primary" onclick="location.reload()">Reload page</button>
|
|
511
|
+
<button class="btn" onclick="document.querySelectorAll('details').forEach(d => d.open = true)">Show all frames</button>
|
|
512
|
+
<a class="btn" href="/">Go home</a>
|
|
513
|
+
</div>
|
|
514
|
+
</div>
|
|
515
|
+
</div>
|
|
516
|
+
|
|
517
|
+
<div class="footer">
|
|
518
|
+
<div>Swift Rust v0.1.0 · dev mode</div>
|
|
519
|
+
<div><a href="https://github.com/swift-rust/swift-rust/issues">Report this error</a></div>
|
|
520
|
+
</div>
|
|
521
|
+
</div>
|
|
522
|
+
<script>
|
|
523
|
+
document.addEventListener("keydown", (e) => {
|
|
524
|
+
if (e.key === "Escape") location.reload();
|
|
525
|
+
});
|
|
526
|
+
</script>
|
|
527
|
+
</body>
|
|
528
|
+
</html>`;
|
|
529
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
(function () {
|
|
2
|
+
if (typeof window === "undefined" || typeof EventSource === "undefined") return;
|
|
3
|
+
var lastError = null;
|
|
4
|
+
var es = new EventSource("/_swift-rust/hmr");
|
|
5
|
+
|
|
6
|
+
function showOverlay(message) {
|
|
7
|
+
hideOverlay();
|
|
8
|
+
var el = document.createElement("div");
|
|
9
|
+
el.id = "__swift_rust_overlay__";
|
|
10
|
+
el.style.cssText =
|
|
11
|
+
"position:fixed;left:0;right:0;bottom:0;background:#ef4444;color:#fff;" +
|
|
12
|
+
"padding:14px 20px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;" +
|
|
13
|
+
"font-size:13px;line-height:1.5;z-index:2147483647;border-top:2px solid #b91c1c;" +
|
|
14
|
+
"box-shadow:0 -4px 12px rgba(0,0,0,0.2);display:flex;align-items:flex-start;gap:12px;";
|
|
15
|
+
var icon = document.createElement("strong");
|
|
16
|
+
icon.textContent = "●";
|
|
17
|
+
icon.style.cssText = "color:#fff;opacity:0.9;flex-shrink:0;";
|
|
18
|
+
var text = document.createElement("div");
|
|
19
|
+
text.style.cssText = "flex:1;white-space:pre-wrap;word-break:break-word;";
|
|
20
|
+
text.textContent = message;
|
|
21
|
+
el.appendChild(icon);
|
|
22
|
+
el.appendChild(text);
|
|
23
|
+
document.body.appendChild(el);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function hideOverlay() {
|
|
27
|
+
var old = document.getElementById("__swift_rust_overlay__");
|
|
28
|
+
if (old) old.remove();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function showToast(message) {
|
|
32
|
+
var el = document.createElement("div");
|
|
33
|
+
el.style.cssText =
|
|
34
|
+
"position:fixed;bottom:16px;right:16px;background:#0a0a0a;color:#fff;" +
|
|
35
|
+
"padding:8px 14px;border-radius:6px;font-family:ui-monospace,monospace;" +
|
|
36
|
+
"font-size:12px;z-index:2147483647;opacity:0;transition:opacity 0.2s;";
|
|
37
|
+
el.textContent = message;
|
|
38
|
+
document.body.appendChild(el);
|
|
39
|
+
requestAnimationFrame(function () {
|
|
40
|
+
el.style.opacity = "1";
|
|
41
|
+
});
|
|
42
|
+
setTimeout(function () {
|
|
43
|
+
el.style.opacity = "0";
|
|
44
|
+
setTimeout(function () { el.remove(); }, 300);
|
|
45
|
+
}, 1800);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
es.addEventListener("change", function (e) {
|
|
49
|
+
try {
|
|
50
|
+
var data = JSON.parse(e.data);
|
|
51
|
+
if (data.type === "reload") {
|
|
52
|
+
showToast("↻ Reloading…");
|
|
53
|
+
setTimeout(function () { location.reload(); }, 100);
|
|
54
|
+
} else if (data.type === "error") {
|
|
55
|
+
lastError = data.message;
|
|
56
|
+
showOverlay("[swift-rust] " + data.message);
|
|
57
|
+
} else if (data.type === "ok") {
|
|
58
|
+
if (lastError) {
|
|
59
|
+
hideOverlay();
|
|
60
|
+
lastError = null;
|
|
61
|
+
showToast("✓ Recovered");
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
} catch (err) {
|
|
65
|
+
console.error("[swift-rust] bad HMR payload", err);
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
es.addEventListener("error", function () {
|
|
70
|
+
setTimeout(function () {
|
|
71
|
+
if (es.readyState === EventSource.CLOSED) {
|
|
72
|
+
console.warn("[swift-rust] HMR connection lost, retrying…");
|
|
73
|
+
location.reload();
|
|
74
|
+
}
|
|
75
|
+
}, 1000);
|
|
76
|
+
});
|
|
77
|
+
})();
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { existsSync, realpathSync } from "node:fs";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { dirname, join, resolve } from "node:path";
|
|
5
|
+
import { spawn } from "node:child_process";
|
|
6
|
+
|
|
7
|
+
const here = dirname(realpathSync(fileURLToPath(import.meta.url)));
|
|
8
|
+
const packageRoot = resolve(here, "..");
|
|
9
|
+
const workspaceRoot = resolve(packageRoot, "../..");
|
|
10
|
+
const [, , rawCmd, ...rest] = process.argv;
|
|
11
|
+
const cmd = rawCmd || "help";
|
|
12
|
+
|
|
13
|
+
function findBun() {
|
|
14
|
+
if (process.env.SWIFT_RUST_RUNTIME) return process.env.SWIFT_RUST_RUNTIME;
|
|
15
|
+
if (process.versions && process.versions.bun) {
|
|
16
|
+
return process.execPath;
|
|
17
|
+
}
|
|
18
|
+
const candidates = [
|
|
19
|
+
process.env.BUN_INSTALL ? join(process.env.BUN_INSTALL, "bin", "bun") : null,
|
|
20
|
+
join(process.env.HOME || "", ".bun", "bin", "bun"),
|
|
21
|
+
"/usr/local/bin/bun",
|
|
22
|
+
"/opt/homebrew/bin/bun",
|
|
23
|
+
].filter(Boolean);
|
|
24
|
+
for (const p of candidates) {
|
|
25
|
+
if (existsSync(p)) return p;
|
|
26
|
+
}
|
|
27
|
+
return process.execPath;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (cmd === "dev") {
|
|
31
|
+
const devScript = join(here, "dev-server.mjs");
|
|
32
|
+
const runtime = findBun();
|
|
33
|
+
const child = spawn(runtime, [devScript, ...process.argv.slice(3)], {
|
|
34
|
+
stdio: "inherit",
|
|
35
|
+
env: process.env,
|
|
36
|
+
});
|
|
37
|
+
const code = await new Promise((r) => {
|
|
38
|
+
child.on("exit", (c) => r(c ?? 1));
|
|
39
|
+
child.on("error", () => r(1));
|
|
40
|
+
});
|
|
41
|
+
process.exit(code);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function getBinaryName() {
|
|
45
|
+
if (process.platform === "win32") return "swift-rust.exe";
|
|
46
|
+
return "swift-rust";
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function findBinary() {
|
|
50
|
+
const name = getBinaryName();
|
|
51
|
+
const candidates = [
|
|
52
|
+
join(packageRoot, "native", `${process.platform}-${process.arch}`, name),
|
|
53
|
+
join(packageRoot, "native", name),
|
|
54
|
+
join(packageRoot, "bin", name),
|
|
55
|
+
join(workspaceRoot, "target", "release", name),
|
|
56
|
+
join(workspaceRoot, "target", "debug", name),
|
|
57
|
+
];
|
|
58
|
+
for (const p of candidates) {
|
|
59
|
+
if (existsSync(p)) return p;
|
|
60
|
+
}
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function isMonorepo() {
|
|
65
|
+
return existsSync(join(workspaceRoot, "Cargo.toml"));
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function cargoRun() {
|
|
69
|
+
const child = spawn(
|
|
70
|
+
"cargo",
|
|
71
|
+
["run", "--release", "-p", "swift-rust", "--bin", "swift-rust", "--", cmd, ...rest],
|
|
72
|
+
{ stdio: "inherit", cwd: workspaceRoot, env: process.env },
|
|
73
|
+
);
|
|
74
|
+
return new Promise((resolveExit) => {
|
|
75
|
+
child.on("exit", (c) => resolveExit(c ?? 1));
|
|
76
|
+
child.on("error", () => resolveExit(1));
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const bin = findBinary();
|
|
81
|
+
if (bin) {
|
|
82
|
+
const child = spawn(bin, process.argv.slice(2), { stdio: "inherit", env: process.env });
|
|
83
|
+
const code = await new Promise((r) => {
|
|
84
|
+
child.on("exit", (c) => r(c ?? 1));
|
|
85
|
+
child.on("error", () => r(1));
|
|
86
|
+
});
|
|
87
|
+
process.exit(code);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (isMonorepo()) {
|
|
91
|
+
const code = await cargoRun();
|
|
92
|
+
process.exit(code);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
process.stderr.write(`swift-rust: binary not found. In a monorepo, run \`cargo build --release -p swift-rust\`. In a published package, install should have placed the binary in native/<platform>/.\n`);
|
|
96
|
+
process.exit(1);
|
package/dist/env.d.ts
ADDED