aboutsummaryrefslogtreecommitdiff
path: root/packages/bun-framework-next
diff options
context:
space:
mode:
Diffstat (limited to 'packages/bun-framework-next')
-rw-r--r--packages/bun-framework-next/bun-error.css289
-rw-r--r--packages/bun-framework-next/bun-error.tsx809
-rw-r--r--packages/bun-framework-next/bun-error/close.pngbin0 -> 757 bytes
-rw-r--r--packages/bun-framework-next/bun-error/error.pngbin0 -> 717 bytes
-rw-r--r--packages/bun-framework-next/bun-error/powered-by.pngbin0 -> 2863 bytes
-rw-r--r--packages/bun-framework-next/bun-error/powered-by.webpbin0 -> 1316 bytes
-rw-r--r--packages/bun-framework-next/bun-runtime-error.ts163
-rw-r--r--packages/bun-framework-next/client.development.tsx5
-rw-r--r--packages/bun-framework-next/fallback.development.tsx62
-rw-r--r--packages/bun-framework-next/next-image-polyfill.tsx36
-rw-r--r--packages/bun-framework-next/package.json3
-rw-r--r--packages/bun-framework-next/page-loader.ts5
-rw-r--r--packages/bun-framework-next/polyfills.tsx23
-rw-r--r--packages/bun-framework-next/renderDocument.tsx19
-rw-r--r--packages/bun-framework-next/server.development.tsx38
15 files changed, 1404 insertions, 48 deletions
diff --git a/packages/bun-framework-next/bun-error.css b/packages/bun-framework-next/bun-error.css
new file mode 100644
index 000000000..c5ed9881b
--- /dev/null
+++ b/packages/bun-framework-next/bun-error.css
@@ -0,0 +1,289 @@
+:host {
+ --bun-error-color: #e33737;
+ --bun-error-monospace: ui-monospace, Menlo, Monaco, "Cascadia Mono",
+ "Segoe UI Mono", "Roboto Mono", "Oxygen Mono", "Ubuntu Monospace",
+ "Source Code Pro", "Fira Mono", "Droid Sans Mono", "Courier New", monospace;
+}
+
+a {
+ color: inherit;
+ text-decoration: none;
+}
+
+a:hover {
+ text-decoration: underline;
+}
+#BunErrorOverlay-container {
+ box-shadow: 0px 16px 24px rgba(0, 0, 0, 0.06), 0px 2px 6px rgba(0, 0, 0, 0.1),
+ 0px 0px 1px rgba(0, 0, 0, 0.04);
+ backdrop-filter: blur(42px);
+ backface-visibility: visible;
+ border: inset 1px solid rgba(0, 0, 0, 0.2);
+ border-radius: 17px;
+ background-color: rgba(255, 255, 255, 0.92);
+ width: 480px;
+
+ position: fixed;
+ top: 120px;
+ right: 48px;
+ z-index: 999999;
+
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica,
+ Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
+}
+
+#BunErrorOverlay-container a {
+ color: inherit;
+}
+
+.BunError-Summary-ErrorIcon {
+ content: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACIAAAAgCAYAAAB3j6rJAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAJiSURBVHgBzZdLUttAEIa7RwpVeKUD4Ip8hFSB7V3kG8AJgBPgG+CcAG4QcwPfIFpiigU5gRWcqmy1AVwVaTo9Y+yyiEeakc3jX7j0GEmfe7r/6QH4IELYUJNuN/LyPMwRU2g04lYcp1BDtUEYIPSJvvNhtHI5QcTB3vX1FThKQE0xxI8XEEohEQ2n+/sROKoWyP3BwYn6qHGA553DW4CgEFUfin6328fwmiDTbldBhFXjiHNlEkUBWMoJRCUoSNm3HB6Kx0fbsW4gnpQDQLT/l4hntlGxBlHR4NJ0mndW4D08WCWuNYgv5YXhVsJmNASi0bqbnNj9P2pKtwGiyxXxcM2tJNvd/bI3Hp82b26OJOK3dc9nc+PbHMRUrhyJgqV7QowMr4iqTK4SpNK8VvRXSnNiVphcKYhO0BLzIkvAZ5WaXCmID6AeDGFLYpO7NJWzEUSbF9EAHKTagYohgcnkjCDavKoVgqNMJrcWpKZ5AQlh46LBp6enCysQ36LuFy8tnBFZ2Tkn+cnLcv6vQ1PlypViC6I+PsiEUB3ZomMLLZ+Mm+Nxzwgy7XQmsMVKKVWe95q3t7E6LEyNi3ktRZSWrTWl8rxl5JcgVeZlUJI1Gq3FWsM+cQpuCn91Ov0CiEd0Bo7RkERXq2tNPps5R4UBzlU5axAdDQDrbmr5EsSvhQs7OxG4S5ucTtb7dntYxzeeNWLzi3MhWpwnxy4d3IpSX/0yxGeor0OGmPcqWHu/FixypNY2cZvSINxZ/YT3VTIHmc0u1Qm8k9R+WYO07u7SDLHH+1bnzfNGYjPkhfJIbdr/AfAS02LsKGiuAAAAAElFTkSuQmCC");
+ width: 20px;
+ height: 19px;
+
+ margin-right: 6px;
+ display: block;
+}
+
+.BunError-Summary-CloseIcon {
+ content: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACYAAAAmCAYAAACoPemuAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAKKSURBVHgBxZjpTsMwEISnaTmLKLf4AX94/7fiFlRcJaUKl6exK8e1k7XTY6QVUbDjj/V4bdNBe2Uquio2nPdfKn6RqA7StKuir2JHxXZNO4LlKkb65zeWBLavYxdpelMxhABQCkaQI6QDuRrqCEoCdqjiFItXoeIagex1Gzqfo8zUMsSxByo+Vfz4fokaqH0sVx09xhxcCOwY5RSuQgbuHVZ58YGx0RlWK8LtoYT74wsXjEXyHM3eW4Y4JgFZ76ZV2xancAPrE+3T44MNRiCp2bnd5MK29A0rfyFsP7VRz3pxLOxoF8ctFZeYz7wRPfOgn/mHX6B5Rui1zP7gDmQaWc8TlEXytwGKKpy+dRoYMG41Um+52fHBuVBG0jH6ZpAtyMVV20MYLgRFq+xBpm1TFrjtbAo7dfUAI1SzxMr9ocF8UFIPUx2TsQxx4pTQ9G7migVAVYBiwagQnK0kKBsotdITblDz7eRDgAH7QZrop9CBj/67gbywVmTAxGdxVKEeGtoQKgkusz4QoxDUbK+zZOCibkzGW/yYtMawTNx73tPkJ/CXEj7zMEjPSY7zRWYNJtUwAGVWX2i1sgi/QabcgJn7X4p8JSEEJ139467TqS/oxAvup34+QrhOmR1ijHLV89uS2xY9+WjPN7N3hbRiu0hNj1U2BKfzGesVs/XCBzc7r0j32iLEbE1Xs2/aHtHivzQtxEzNTia+VUIoGnaA1Yl3iEptDC1fblGcb2nRbSNC3cGZpbq6MtGdCJf6f7Qm0c+38BwiJANKbzexeoJegT7FZOIA5SbdFjDXUJO6RrFTRChe82Jv7PQPbcFyMJZ0aOMd3qx47SMoyw5B6VnjF2aECyjXMFEl6B+GYop4atKAigAAAABJRU5ErkJggg==");
+ width: 22px;
+ height: 22px;
+ border-radius: 50%;
+ cursor: pointer;
+}
+
+.BunError-Summary-CloseIcon:hover {
+ transform: scale(1.2);
+
+ background-color: rgb(255, 255, 255);
+}
+
+.BunError-Summary {
+ display: grid;
+ grid-template-columns: min-content auto min-content;
+ grid-template-rows: 46px;
+ align-items: center;
+ padding: 0 18px;
+ border-bottom: 1px solid rgb(220, 220, 220);
+}
+
+.BunError-footer {
+ display: grid;
+ padding: 12px 18px;
+ justify-content: flex-end;
+ border-top: 1px solid rgb(220, 220, 220);
+ align-items: center;
+}
+
+.BunError-Summary-Title {
+ font-weight: 500;
+ letter-spacing: 0.36px;
+}
+
+.BunError-ErrorTag,
+.BunError-error-code {
+ color: rgb(165, 165, 165);
+ font-weight: 500;
+ font-size: 12pt;
+}
+
+.BunError-ErrorTag {
+ font-size: 14px;
+ text-transform: uppercase;
+ font-weight: 300;
+}
+
+.BunError-error-header {
+ display: flex;
+ align-items: center;
+ gap: 0.5ch;
+}
+
+.BunError-error-message {
+ color: var(--bun-error-color);
+ font-size: 16pt;
+ font-weight: bold;
+}
+
+.BunError-list {
+ margin-top: 14px;
+ gap: 14px;
+}
+
+.BunError-error-subtitle,
+.BunError-error-header,
+.BunError-error-message {
+ padding-left: 18px;
+ padding-right: 18px;
+}
+
+.BunError-error-subtitle {
+ font-size: 500;
+}
+
+.BunError-NativeStackTrace {
+ margin-top: 0;
+ width: 100%;
+}
+
+.BunError-NativeStackTrace-filename {
+ padding: 8px 18px;
+ white-space: nowrap;
+ text-overflow: ellipsis;
+ overflow: hidden;
+ font-weight: 500;
+ letter-spacing: 0.36px;
+ margin-bottom: 8px;
+ display: block;
+}
+
+.BunError-NativeStackTrace-filename:hover {
+ text-decoration: underline;
+}
+
+.BunError-SourceLines-lines {
+}
+
+.BunError-SourceLines {
+ display: grid;
+ grid-template-columns: min-content auto;
+ grid-template-rows: repeat(16px, 6);
+ column-gap: 13px;
+ font-size: 14px;
+ padding-left: 24px;
+ align-items: center;
+ position: relative;
+ overflow-x: auto;
+}
+
+.BunError-SourceLine-text {
+ white-space: pre;
+ display: block;
+
+ font-family: var(--bun-error-monospace);
+}
+
+.BunError-SourceLine-number {
+ font-variant: tabular-nums;
+ text-align: right;
+ user-select: none;
+ -webkit-user-select: none;
+}
+
+.BunError-SourceLine-number--empty {
+ color: rgb(165, 165, 165);
+}
+
+.BunError-SourceLine-number,
+.BunError-SourceLine-text {
+ height: 18px;
+}
+
+.BunError-SourceLines-highlighter--0,
+.BunError-SourceLines-highlighter--1,
+.BunError-SourceLines-highlighter--2,
+.BunError-SourceLines-highlighter--3,
+.BunError-SourceLines-highlighter--4,
+.BunError-SourceLines-highlighter--5 {
+ position: absolute;
+}
+
+.BunError-SourceLine-text--highlight {
+ color: #e33737;
+}
+
+#BunError-poweredBy {
+ height: 16px;
+
+ content: url("data:image/webp;base64,UklGRhwFAABXRUJQVlA4TBAFAAAv3MAHEBeioG0jx3f8MY+fGAgkbdz7Zz4Ekjbu/TMftpEkOaFfC5vgSJ4sHswjm7TZk7R34AJUQAFAQwYAQAUkQAZUAFTUz5XDIaPd77AAN2DlBrAAG/Ai8CFwcKICP6L+BzgTEBEBp6xqWyZJejDAWERMQEwHEuEip1yEmPDc/w1qRs7/OqL/EyBbtu22bQ4ViCHTKM0gUUNmIjMmZfu6s7nz3v//KwIgRfe3iP5PAP9bdVNLfR/hfdqK3bxLjDgr9Q4JGpfk7xHxPPu8KRdApEaiIpdSr8iU9id7Cn+vE6WbWqRuTORqm7pujCV+LKW8jyymEZHGALqpRer76LU4NXya9fhb5XgQZ2ks02uxtgBmtXSrBEAr1jO04mxfiazx06Lp71TgQXzbzoPYWSMoxV4pGLEWaMXz/xuHIyPevACm4r6BEXcaNBZeGPGt1NvmKij9pFL47cE48TgLaksRlF6SgnhtsKbrV/w0rVdO2yhdg+lKg+naB9OvK/+Ko+2KrNfeTfuV0/pp2q5rj3/G9OtK22C7ou3Laj6EXEvfHMYnOvM4D8R+Lf434PQ9/din0Vrt6wYsundZj+7QABzNPoeumpMa4Gj2JQ7mqlq3735vqjUCWmxzoq9D9C/217cCWbOtVPXsNvXOt7pD0NxVLV2xAklbflW9A6BF/YFD/clVawBi1XYX3+WbrHdRa4SiE8uWaDJcXbo4GhmrzhM41BWo+jtw6DtAsQJRfQegmSFqjcCpaeC9ALFaI5A0AVnvCWL1G2uEeOsJSXUnJFpjuI1xyWZsUVfok56wa6ZPesChEXZLc4VFN8ga6Zt1FAF23emL70DQGgGW7yJAbL4DQbedOdGvceRjyE2vbRD0hpfOg6h/wKw7vDyyJxwa4PZmmDV2hb74Zph0ZtHM8P6mMMw6Q5EmAA4pwzhOxrDpNw9oVijKWCtQzVCddwsUC6DWYfvi1jpsurLrMcrfnKOkOyySBkKi9UjOnkUcVd9QvzqtzFaCBprH6HPlSfVhjd3+J6VnNNoCmih8weaRVigaPhVg0XiYobgvOgPNm8dPKk93PUavb/KDDUhEExzSFiPJ/e7GcgwWLXDqOlg0A6G5FzdInocV4Pb9YYuPXhpGa4RZy6h+8x4VnYGZSIdE87GkPnJbj6WG7qU7LFoGWTeAYtYAi7WYu6THIHk/OjQNkm+g6dolv/HoVq30W9pqosleymsXay/vPVnXuL60RqDoayaeWukPtQA0detC831MxFPzo1A1RaZTbyBp3eeYbF+Z5pia7gNNlNEae/EtMHsOp+MNIFbHNQ6imrqsho7f/FjjI+bquEaA4rDmb87q8IfhZEdEP8ZyimkfHkXcq/qzMIy5qe2aGBdb7BY9GcfStTQBFM8RMXftFRmmpv7Ewxo+heYWs9oSH5dEGQa6mPXhCEHjdzkbZOGjBjAvkafzMk88XCaGcebhtCwzH2eeLss88XCeJ2CZeBhnYFrmic+a6M8AdaeaNj2OAeRenPy1HQeNi/U3jwIf/sszorAfTjqSf6u8rgBgWnrwcVBaOELuKhA0ruzVW7d/RGsMGJedKklKF5+rDuLSwad4ki4fA8G1jRdAUtkKZQn/Xb8pURYOgbhgEb5BfMYsIlwdwBmfMYtwdYBpwSLCxwCgzpiFCw0AScEsfB4BxHLw3x1vs1WIgXWaagBQOs1NAu94kadaAUCS5hsFe7wwCZx6sVF4/wI=");
+}
+
+#BunError-SourceLine-text-highlightExpression {
+ font-weight: bold;
+ text-decoration-style: wavy;
+}
+
+.BunError-JSException--TypeError #BunError-SourceLine-text-highlightExpression {
+ border: 1px solid rgba(0, 0, 0, 0.2);
+}
+.BunError-Indented {
+ display: inline-block;
+ user-select: none;
+ -webkit-user-select: none;
+}
+
+.BunError-divet {
+ vertical-align: bottom;
+ user-select: none;
+ -webkit-user-select: none;
+}
+
+.BunError-error-typename {
+ font-family: var(--bun-error-monospace);
+ color: #e39437;
+ font-weight: bold;
+}
+
+.BunError-error-muted {
+ font-weight: normal;
+ user-select: none;
+ -webkit-user-select: none;
+}
+
+.BunError-error-muted,
+.BunError-StackFrame--muted {
+ color: rgb(165, 165, 165);
+}
+
+.BunError-NativeStackTrace .BunError-error-typename {
+ user-select: none;
+ -webkit-user-select: none;
+}
+
+.BunError-StackFrame-link {
+}
+
+.BunError-StackFrame-link-content {
+ display: flex;
+ gap: 0.25ch;
+ white-space: nowrap;
+}
+
+.BunError-StackFrame {
+ display: table-row;
+}
+
+.BunError-StackFrame-identifier {
+ padding-right: 18px;
+ font-size: 0.8em;
+ font-family: var(--bun-error-monospace);
+ letter-spacing: 0.49px;
+}
+
+.BunError-error-message--mono {
+ font-family: var(--bun-error-monospace);
+}
+.BunError-StackFrame-identifier,
+.BunError-StackFrame-link {
+ display: table-cell;
+ font-weight: 500;
+}
+
+.BunError-BuildError {
+ padding-bottom: 18px;
+}
+
+.BunError-StackFrame-link-content {
+ font-size: 0.8em;
+}
+
+.BunError-StackFrames {
+ display: table;
+ table-layout: auto;
+ padding: 13px 10px;
+ margin: 8px auto;
+ border-radius: 4px;
+
+ background-color: rgb(244, 244, 244);
+}
diff --git a/packages/bun-framework-next/bun-error.tsx b/packages/bun-framework-next/bun-error.tsx
new file mode 100644
index 000000000..5d57dd0ea
--- /dev/null
+++ b/packages/bun-framework-next/bun-error.tsx
@@ -0,0 +1,809 @@
+import type {
+ FallbackMessageContainer,
+ JSException as JSExceptionType,
+ Message,
+ SourceLine,
+ StackFrame,
+ Problems,
+ FallbackStep,
+ StackTrace,
+ Location,
+ JSException,
+ WebsocketMessageBuildFailure,
+} from "../../../src/api/schema";
+
+import ReactDOM from "react-dom";
+import {
+ useCallback,
+ useState,
+ useEffect,
+ useLayoutEffect,
+ createContext,
+ useContext,
+ Children,
+} from "react";
+
+enum StackFrameScope {
+ Eval = 1,
+ Module = 2,
+ Function = 3,
+ Global = 4,
+ Wasm = 5,
+ Constructor = 6,
+}
+
+enum JSErrorCode {
+ Error = 0,
+ EvalError = 1,
+ RangeError = 2,
+ ReferenceError = 3,
+ SyntaxError = 4,
+ TypeError = 5,
+ URIError = 6,
+ AggregateError = 7,
+
+ // StackOverflow & OutOfMemoryError is not an ErrorType in <JavaScriptCore/ErrorType.h> within JSC, so the number here is just totally made up
+ OutOfMemoryError = 8,
+ BundlerError = 252,
+ StackOverflow = 253,
+ UserErrorCode = 254,
+}
+
+const JSErrorCodeLabel = {
+ 0: "Error",
+ 1: "EvalError",
+ 2: "RangeError",
+ 3: "ReferenceError",
+ 4: "SyntaxError",
+ 5: "TypeError",
+ 6: "URIError",
+ 7: "AggregateError",
+ 253: "StackOverflow",
+ 8: "OutOfMemory",
+};
+
+const BUN_ERROR_CONTAINER_ID = "__bun-error__";
+
+enum RuntimeType {
+ Nothing = 0x0,
+ Function = 0x1,
+ Undefined = 0x2,
+ Null = 0x4,
+ Boolean = 0x8,
+ AnyInt = 0x10,
+ Number = 0x20,
+ String = 0x40,
+ Object = 0x80,
+ Symbol = 0x100,
+ BigInt = 0x200,
+}
+
+enum ErrorTagType {
+ build,
+ resolve,
+ server,
+ client,
+ hmr,
+}
+
+const ErrorTag = ({ type }: { type: ErrorTagType }) => (
+ <div className={`BunError-ErrorTag BunError-ErrorTag--${ErrorTagType[type]}`}>
+ {ErrorTagType[type]}
+ </div>
+);
+
+const errorTags = [
+ <ErrorTag type={ErrorTagType.build}></ErrorTag>,
+ <ErrorTag type={ErrorTagType.resolve}></ErrorTag>,
+ <ErrorTag type={ErrorTagType.server}></ErrorTag>,
+ <ErrorTag type={ErrorTagType.client}></ErrorTag>,
+ <ErrorTag type={ErrorTagType.hmr}></ErrorTag>,
+];
+
+const normalizedFilename = (filename: string, cwd: string): string => {
+ if (filename.startsWith(cwd)) {
+ return filename.substring(cwd.length);
+ }
+
+ return filename;
+};
+
+const blobFileURL = (filename: string): string => {
+ return new URL("/blob:" + filename, location.href).href;
+};
+
+const srcFileURL = (filename: string, line: number, column: number): string => {
+ if (filename.endsWith(".bun")) {
+ return new URL("/" + filename, location.href).href;
+ }
+
+ var base = `/src:${filename}`;
+ if (line > -1) {
+ base = base + `:${line}`;
+
+ if (column > -1) {
+ base = base + `:${column}`;
+ }
+ }
+
+ return new URL(base, location.href).href;
+};
+
+class FancyTypeError {
+ constructor(exception: JSException) {
+ this.runtimeType = exception.runtime_type || 0;
+ this.runtimeTypeName = RuntimeType[this.runtimeType] || "undefined";
+ this.message = exception.message;
+ this.explain = "";
+
+ this.normalize(exception);
+ }
+
+ runtimeType: RuntimeType;
+ explain: string;
+ runtimeTypeName: string;
+ message: string;
+
+ normalize(exception: JSException) {
+ let i = exception.message.lastIndexOf(" is ");
+ if (i === -1) return;
+ const partial = exception.message.substring(i + " is ".length);
+ const nextWord = /(["a-zA-Z0-9_\.]+)\)$/.exec(partial);
+ if (nextWord && nextWord[0]) {
+ this.runtimeTypeName = nextWord[0];
+ this.runtimeTypeName = this.runtimeTypeName.substring(
+ 0,
+ this.runtimeTypeName.length - 1
+ );
+ switch (this.runtimeTypeName.toLowerCase()) {
+ case "undefined": {
+ this.runtimeType = RuntimeType.Undefined;
+ break;
+ }
+ case "null": {
+ this.runtimeType = RuntimeType.Null;
+ break;
+ }
+ case "string": {
+ this.runtimeType = RuntimeType.String;
+ break;
+ }
+ case "true":
+ case "false": {
+ this.runtimeType = RuntimeType.Boolean;
+ break;
+ }
+
+ case "number":
+ this.runtimeType = RuntimeType.Number;
+ break;
+
+ case "bigint":
+ this.runtimeType = RuntimeType.BigInt;
+ break;
+
+ case "symbol":
+ this.runtimeType = RuntimeType.Symbol;
+ break;
+ default: {
+ this.runtimeType = RuntimeType.Object;
+ break;
+ }
+ }
+ this.message = exception.message.substring(0, i);
+ this.message = this.message.substring(
+ 0,
+ this.message.lastIndexOf("(In ")
+ );
+ }
+ }
+}
+
+var onClose = dismissError;
+
+const IndentationContext = createContext(0);
+const SourceLines = ({
+ sourceLines,
+ highlight = -1,
+ highlightColumnStart = 0,
+ highlightColumnEnd = Infinity,
+ children,
+}: {
+ sourceLines: SourceLine[];
+ highlightColumnStart: number;
+ highlightColumnEnd: number;
+ highlight: number;
+}) => {
+ let start = sourceLines.length;
+ let end = 0;
+ let dedent = Infinity;
+ let originalLines = new Array(sourceLines.length);
+ let _i = 0;
+ for (let i = 0; i < sourceLines.length; i++) {
+ // bun only prints \n, no \r\n, so this should work fine
+ sourceLines[i].text = sourceLines[i].text.replaceAll("\n", "");
+
+ // This will now only trim spaces (and vertical tab character which never prints)
+ const left = sourceLines[i].text.trimLeft();
+
+ if (left.length > 0) {
+ start = Math.min(start, i);
+ end = Math.max(end, i + 1);
+
+ dedent = Math.min(sourceLines[i].text.length - left.length, dedent);
+ }
+ }
+
+ const _sourceLines = sourceLines.slice(start, end);
+ const childrenArray = children || [];
+ const numbers = new Array(_sourceLines.length + childrenArray.length);
+ const lines = new Array(_sourceLines.length + childrenArray.length);
+
+ let highlightI = 0;
+ for (let i = 0; i < _sourceLines.length; i++) {
+ const { line, text } = _sourceLines[i];
+ const classes = {
+ empty: text.trim().length === 0,
+ highlight: highlight + 1 === line || _sourceLines.length === 1,
+ };
+ if (classes.highlight) highlightI = i;
+ const _text = classes.empty ? "" : text.substring(dedent);
+ lines[i] = (
+ <div
+ key={i}
+ className={`BunError-SourceLine-text ${
+ classes.empty ? "BunError-SourceLine-text--empty" : ""
+ } ${classes.highlight ? "BunError-SourceLine-text--highlight" : ""}`}
+ >
+ {classes.highlight ? (
+ <>
+ {_text.substring(0, highlightColumnStart - dedent)}
+ <span id="BunError-SourceLine-text-highlightExpression">
+ {_text.substring(
+ highlightColumnStart - dedent,
+ highlightColumnEnd - dedent
+ )}
+ </span>
+ {_text.substring(highlightColumnEnd - dedent)}
+ </>
+ ) : (
+ _text
+ )}
+ </div>
+ );
+ numbers[i] = (
+ <div
+ key={line}
+ className={`BunError-SourceLine-number ${
+ classes.empty ? "BunError-SourceLine-number--empty" : ""
+ } ${classes.highlight ? "BunError-SourceLine-number--highlight" : ""}`}
+ >
+ {line}
+ </div>
+ );
+
+ if (classes.highlight && children) {
+ i++;
+
+ numbers.push(
+ ...childrenArray.map((child, index) => (
+ <div
+ key={"highlight-number-" + index}
+ className={`BunError-SourceLine-number ${
+ classes.empty ? "BunError-SourceLine-number--empty" : ""
+ } ${
+ classes.highlight ? "BunError-SourceLine-number--highlight" : ""
+ }`}
+ ></div>
+ ))
+ );
+ lines.push(
+ ...childrenArray.map((child, index) => (
+ <div
+ key={"highlight-line-" + index}
+ className={`BunError-SourceLine-text`}
+ >
+ {childrenArray[index]}
+ </div>
+ ))
+ );
+ }
+ }
+
+ return (
+ <IndentationContext.Provider value={dedent}>
+ <div className="BunError-SourceLines">
+ <div
+ className={`BunError-SourceLines-highlighter--${highlightI}`}
+ ></div>
+
+ <div className="BunError-SourceLines-numbers">{numbers}</div>
+ <div className="BunError-SourceLines-lines">{lines}</div>
+ </div>
+ </IndentationContext.Provider>
+ );
+};
+
+const BuildErrorSourceLines = ({ location }: { location: Location }) => {
+ const { line, line_text, column, file } = location;
+ const sourceLines: SourceLine[] = [{ line, text: line_text }];
+ return (
+ <SourceLines
+ sourceLines={sourceLines}
+ highlight={line}
+ highlightColumnStart={column}
+ highlightColumnEnd={column}
+ />
+ );
+};
+
+const BuildErrorStackTrace = ({ location }: { location: Location }) => {
+ const { cwd } = useContext(ErrorGroupContext);
+ const filename = normalizedFilename(location.file, cwd);
+ const { line, column } = location;
+ return (
+ <div className={`BunError-NativeStackTrace`}>
+ <a
+ href={srcFileURL(filename, line, column)}
+ target="_blank"
+ className="BunError-NativeStackTrace-filename"
+ >
+ {filename}:{line}:{column}
+ </a>
+ <BuildErrorSourceLines location={location} />
+ </div>
+ );
+};
+
+const StackFrameIdentifier = ({
+ functionName,
+ scope,
+}: {
+ functionName?: string;
+ scope: StackFrameScope;
+}) => {
+ switch (scope) {
+ case StackFrameScope.Constructor: {
+ return functionName ? `new ${functionName}` : "new (anonymous)";
+ break;
+ }
+
+ case StackFrameScope.Eval: {
+ return "eval";
+ break;
+ }
+
+ case StackFrameScope.Module: {
+ return "(esm)";
+ break;
+ }
+
+ case StackFrameScope.Global: {
+ return "(global)";
+ break;
+ }
+
+ case StackFrameScope.Wasm: {
+ return "(wasm)";
+ break;
+ }
+
+ default: {
+ return functionName ? functionName : "λ()";
+ break;
+ }
+ }
+};
+
+const NativeStackFrame = ({
+ frame,
+ isTop,
+}: {
+ frame: StackFrame;
+ isTop: boolean;
+}) => {
+ const { cwd } = useContext(ErrorGroupContext);
+ const {
+ file,
+ function_name: functionName,
+ position: { line, column_start: column },
+ scope,
+ } = frame;
+ const fileName = normalizedFilename(file, cwd);
+ return (
+ <div
+ className={`BunError-StackFrame ${
+ fileName.endsWith(".bun") ? "BunError-StackFrame--muted" : ""
+ }`}
+ >
+ <div
+ title={StackFrameScope[scope]}
+ className="BunError-StackFrame-identifier"
+ >
+ <StackFrameIdentifier functionName={functionName} scope={scope} />
+ </div>
+
+ <a
+ target="_blank"
+ href={blobFileURL(fileName)}
+ className="BunError-StackFrame-link"
+ >
+ <div className="BunError-StackFrame-link-content">
+ <div className={`BunError-StackFrame-file`}>{fileName}</div>
+ {line > -1 && (
+ <div className="BunError-StackFrame-line">:{line + 1}</div>
+ )}
+ {column > -1 && (
+ <div className="BunError-StackFrame-column">:{column}</div>
+ )}
+ </div>
+ </a>
+ </div>
+ );
+};
+
+const NativeStackFrames = ({ frames }) => {
+ const items = new Array(frames.length);
+ for (let i = 0; i < frames.length; i++) {
+ items[i] = <NativeStackFrame key={i} frame={frames[i]} />;
+ }
+
+ return <div className="BunError-StackFrames">{items}</div>;
+};
+
+const NativeStackTrace = ({
+ frames,
+ sourceLines,
+ children,
+}: {
+ frames: StackFrame[];
+ sourceLines: SourceLine[];
+}) => {
+ const { file = "", position } = frames[0];
+ const { cwd } = useContext(ErrorGroupContext);
+ const filename = normalizedFilename(file, cwd);
+ return (
+ <div className={`BunError-NativeStackTrace`}>
+ <a
+ href={blobFileURL(filename)}
+ target="_blank"
+ className="BunError-NativeStackTrace-filename"
+ >
+ {filename}:{position.line + 1}:{position.column_start}
+ </a>
+ {sourceLines.length > 0 && (
+ <SourceLines
+ highlight={position.line}
+ sourceLines={sourceLines}
+ highlightColumnStart={position.column_start}
+ highlightColumnEnd={position.column_stop}
+ >
+ {children}
+ </SourceLines>
+ )}
+ {frames.length > 0 && <NativeStackFrames frames={frames} />}
+ </div>
+ );
+};
+
+const divet = <span className="BunError-divet">^</span>;
+const DivetRange = ({ start, stop }) => {
+ const length = Math.max(stop - start, 0);
+ if (length === 0) return null;
+ return (
+ <span
+ className="BunError-DivetRange"
+ style={{ width: `${length - 1}ch` }}
+ ></span>
+ );
+};
+
+const Indent = ({ by, children }) => {
+ const amount = useContext(IndentationContext);
+ return (
+ <>
+ {` `.repeat(by - amount)}
+ {children}
+ </>
+ );
+};
+
+const JSException = ({ value }: { value: JSExceptionType }) => {
+ switch (value.code) {
+ case JSErrorCode.TypeError: {
+ const fancyTypeError = new FancyTypeError(value);
+
+ if (fancyTypeError.runtimeType !== RuntimeType.Nothing) {
+ return (
+ <div
+ className={`BunError-JSException BunError-JSException--TypeError`}
+ >
+ <div className="BunError-error-header">
+ <div className={`BunError-error-code`}>TypeError</div>
+ {errorTags[ErrorTagType.server]}
+ </div>
+
+ <div className={`BunError-error-message`}>
+ {fancyTypeError.message}
+ </div>
+
+ {fancyTypeError.runtimeTypeName.length && (
+ <div className={`BunError-error-subtitle`}>
+ It's{" "}
+ <span className="BunError-error-typename">
+ {fancyTypeError.runtimeTypeName}
+ </span>
+ .
+ </div>
+ )}
+
+ {value.stack && (
+ <NativeStackTrace
+ frames={value.stack.frames}
+ sourceLines={value.stack.source_lines}
+ >
+ <Indent by={value.stack.frames[0].position.column_start}>
+ <span className="BunError-error-typename">
+ {fancyTypeError.runtimeTypeName}
+ </span>
+ </Indent>
+ </NativeStackTrace>
+ )}
+ </div>
+ );
+ }
+ }
+
+ default: {
+ const newline = value.message.indexOf("\n");
+ if (newline > -1) {
+ const subtitle = value.message.substring(newline + 1).trim();
+ const message = value.message.substring(0, newline).trim();
+ if (subtitle.length) {
+ return (
+ <div className={`BunError-JSException`}>
+ <div className="BunError-error-header">
+ <div className={`BunError-error-code`}>{value.name}</div>
+ {errorTags[ErrorTagType.server]}
+ </div>
+
+ <div className={`BunError-error-message`}>{message}</div>
+ <div className={`BunError-error-subtitle`}>{subtitle}</div>
+
+ {value.stack && (
+ <NativeStackTrace
+ frames={value.stack.frames}
+ sourceLines={value.stack.source_lines}
+ />
+ )}
+ </div>
+ );
+ }
+ }
+
+ return (
+ <div className={`BunError-JSException`}>
+ <div className="BunError-error-header">
+ <div className={`BunError-error-code`}>{value.name}</div>
+ {errorTags[ErrorTagType.server]}
+ </div>
+
+ <div className={`BunError-error-message`}>{value.message}</div>
+
+ {value.stack && (
+ <NativeStackTrace
+ frames={value.stack.frames}
+ sourceLines={value.stack.source_lines}
+ />
+ )}
+ </div>
+ );
+ }
+ }
+};
+
+const Summary = ({
+ errorCount,
+ onClose,
+}: {
+ errorCount: number;
+ onClose: Function;
+}) => {
+ return (
+ <div className="BunError-Summary">
+ <div className="BunError-Summary-ErrorIcon"></div>
+ <div className="BunError-Summary-Title">
+ {errorCount}&nbsp;error{errorCount > 1 ? "s" : ""}&nbsp;on this page
+ </div>
+
+ <div onClick={onClose} className="BunError-Summary-CloseButton">
+ <div className="BunError-Summary-CloseIcon"></div>
+ </div>
+ </div>
+ );
+};
+
+const BuildError = ({ message }: { message: Message }) => {
+ let title = (message.data.text || "").trim();
+ const newline = title.indexOf("\n");
+ let subtitle = "";
+ if (newline > -1) {
+ subtitle = title.slice(newline + 1).trim();
+ title = title.slice(0, newline);
+ }
+ return (
+ <div className={`BunError-BuildError BunError-BuildError--build`}>
+ <div className="BunError-error-header">
+ <div className={`BunError-error-code`}>BuildError</div>
+ </div>
+
+ <div className={`BunError-error-message`}>{title}</div>
+
+ {subtitle.length > 0 && (
+ <div className={`BunError-error-subtitle`}>{subtitle}</div>
+ )}
+
+ {message.data.location && (
+ <BuildErrorStackTrace location={message.data.location} />
+ )}
+ </div>
+ );
+};
+
+const ResolveError = ({ message }: { message: Message }) => {
+ const { cwd } = useContext(ErrorGroupContext);
+ let title = (message.data.text || "").trim();
+ const newline = title.indexOf("\n");
+ let subtitle = null;
+ if (newline > -1) {
+ subtitle = title.slice(newline + 1).trim();
+ title = title.slice(0, newline);
+ }
+
+ return (
+ <div className={`BunError-BuildError BunError-BuildError--resolve`}>
+ <div className="BunError-error-header">
+ <div className={`BunError-error-code`}>ResolveError</div>
+ </div>
+
+ <div className={`BunError-error-message`}>
+ Can't import{" "}
+ <span className="BunError-error-message--mono">
+ {message.on.resolve}
+ </span>
+ </div>
+
+ {subtitle && <div className={`BunError-error-subtitle`}>{subtitle}</div>}
+
+ {message.data.location && (
+ <BuildErrorStackTrace location={message.data.location} />
+ )}
+ </div>
+ );
+};
+const OverlayMessageContainer = ({
+ problems,
+ reason,
+ router,
+}: FallbackMessageContainer) => {
+ return (
+ <div id="BunErrorOverlay-container">
+ <div className="BunError-content">
+ <div className="BunError-header">
+ <Summary
+ errorCount={problems.exceptions.length + problems.build.errors}
+ onClose={onClose}
+ problems={problems}
+ reason={reason}
+ />
+ </div>
+ <div className={`BunError-list`}>
+ {problems.exceptions.map((problem, index) => (
+ <JSException key={index} value={problem} />
+ ))}
+ {problems.build.msgs.map((buildMessage, index) => {
+ if (buildMessage.on.build) {
+ return <BuildError key={index} message={buildMessage} />;
+ } else if (buildMessage.on.resolve) {
+ return <ResolveError key={index} message={buildMessage} />;
+ } else {
+ throw new Error("Unknown build message type");
+ }
+ })}
+ </div>
+ <div className="BunError-footer">
+ <div id="BunError-poweredBy"></div>
+ </div>
+ </div>
+ </div>
+ );
+};
+
+const BuildFailureMessageContainer = ({
+ messages,
+}: {
+ messages: Message[];
+}) => {
+ return (
+ <div id="BunErrorOverlay-container">
+ <div className="BunError-content">
+ <div className="BunError-header">
+ <Summary onClose={onClose} errorCount={messages.length} />
+ </div>
+ <div className={`BunError-list`}>
+ {messages.map((buildMessage, index) => {
+ if (buildMessage.on.build) {
+ return <BuildError key={index} message={buildMessage} />;
+ } else if (buildMessage.on.resolve) {
+ return <ResolveError key={index} message={buildMessage} />;
+ } else {
+ throw new Error("Unknown build message type");
+ }
+ })}
+ </div>
+ <div className="BunError-footer">
+ <div id="BunError-poweredBy"></div>
+ </div>
+ </div>
+ </div>
+ );
+};
+
+const ErrorGroupContext = createContext<{ cwd: string }>(null);
+var reactRoot;
+
+function renderWithFunc(func) {
+ if (!reactRoot) {
+ const root = document.createElement("div");
+ root.id = "__bun__error-root";
+
+ reactRoot = document.createElement("div");
+ reactRoot.id = BUN_ERROR_CONTAINER_ID;
+ reactRoot.style.visibility = "hidden";
+ const link = document.createElement("link");
+ link.rel = "stylesheet";
+ link.href = new URL("/bun:erro.css", document.baseURI).href;
+ link.onload = () => {
+ reactRoot.style.visibility = "visible";
+ };
+
+ const shadowRoot = root.attachShadow({ mode: "open" });
+ shadowRoot.appendChild(link);
+ shadowRoot.appendChild(reactRoot);
+
+ document.body.appendChild(root);
+ ReactDOM.render(func(), reactRoot);
+
+ debugger;
+ } else {
+ ReactDOM.render(func(), reactRoot);
+ }
+}
+
+export function renderFallbackError(fallback: FallbackMessageContainer) {
+ return renderWithFunc(() => (
+ <ErrorGroupContext.Provider value={fallback}>
+ <OverlayMessageContainer {...fallback} />
+ </ErrorGroupContext.Provider>
+ ));
+}
+
+export function dismissError() {
+ if (reactRoot) {
+ ReactDOM.unmountComponentAtNode(reactRoot);
+ const root = document.getElementById("__bun__error-root");
+ if (root) root.remove();
+ reactRoot = null;
+ }
+}
+
+globalThis.renderBuildFailure = (
+ failure: WebsocketMessageBuildFailure,
+ cwd: string
+) => {
+ renderWithFunc(() => (
+ <ErrorGroupContext.Provider value={{ cwd }}>
+ <BuildFailureMessageContainer messages={failure.log.msgs} />
+ </ErrorGroupContext.Provider>
+ ));
+};
diff --git a/packages/bun-framework-next/bun-error/close.png b/packages/bun-framework-next/bun-error/close.png
new file mode 100644
index 000000000..11e513a1b
--- /dev/null
+++ b/packages/bun-framework-next/bun-error/close.png
Binary files differ
diff --git a/packages/bun-framework-next/bun-error/error.png b/packages/bun-framework-next/bun-error/error.png
new file mode 100644
index 000000000..c35e01a2b
--- /dev/null
+++ b/packages/bun-framework-next/bun-error/error.png
Binary files differ
diff --git a/packages/bun-framework-next/bun-error/powered-by.png b/packages/bun-framework-next/bun-error/powered-by.png
new file mode 100644
index 000000000..7e71f1357
--- /dev/null
+++ b/packages/bun-framework-next/bun-error/powered-by.png
Binary files differ
diff --git a/packages/bun-framework-next/bun-error/powered-by.webp b/packages/bun-framework-next/bun-error/powered-by.webp
new file mode 100644
index 000000000..0f48488ea
--- /dev/null
+++ b/packages/bun-framework-next/bun-error/powered-by.webp
Binary files differ
diff --git a/packages/bun-framework-next/bun-runtime-error.ts b/packages/bun-framework-next/bun-runtime-error.ts
new file mode 100644
index 000000000..331040b36
--- /dev/null
+++ b/packages/bun-framework-next/bun-runtime-error.ts
@@ -0,0 +1,163 @@
+// Based on https://github.com/stacktracejs/error-stack-parser/blob/master/error-stack-parser.js
+
+import type {
+ StackFrame as StackFrameType,
+ StackFramePosition,
+ StackFrameScope,
+} from "../../../src/api/schema";
+
+export class StackFrame implements StackFrameType {
+ function_name: string;
+ file: string;
+ position: StackFramePosition;
+ scope: StackFrameScope;
+ lineText: string = "";
+ constructor({
+ functionName: function_name = "",
+ fileName: file = "",
+ lineNumber: line = -1,
+ columnNumber: column = -1,
+ source = "",
+ }) {
+ this.function_name = function_name;
+ this.file = file;
+ if (source) this.lineText = source;
+ this.scope = 3;
+ this.position = {
+ line: line,
+ source_offset: -1,
+ line_start: -1,
+ line_stop: -1,
+ column_start: column,
+ column_stop: -1,
+ expression_start: -1,
+ expression_stop: -1,
+ };
+ }
+}
+
+const FIREFOX_SAFARI_STACK_REGEXP = /(^|@)\S+:\d+/;
+const CHROME_IE_STACK_REGEXP = /^\s*at .*(\S+:\d+|\(native\))/m;
+const SAFARI_NATIVE_CODE_REGEXP = /^(eval@)?(\[native code])?$/;
+
+export default class RuntimeError {
+ original: Error;
+ stack: StackFrame[];
+
+ static from(error: Error) {
+ const runtime = new RuntimeError();
+ runtime.original = error;
+ runtime.stack = this.parseStack(error);
+ return RuntimeError;
+ }
+
+ /**
+ * Given an Error object, extract the most information from it.
+ *
+ * @param {Error} error object
+ * @return {Array} of StackFrames
+ */
+ static parseStack(error) {
+ if (error.stack && error.stack.match(CHROME_IE_STACK_REGEXP)) {
+ return this.parseV8OrIE(error);
+ } else if (error.stack) {
+ return this.parseFFOrSafari(error);
+ } else {
+ return [];
+ }
+ }
+
+ // Separate line and column numbers from a string of the form: (URI:Line:Column)
+ static extractLocation(urlLike) {
+ // Fail-fast but return locations like "(native)"
+ if (urlLike.indexOf(":") === -1) {
+ return [urlLike];
+ }
+
+ var regExp = /(.+?)(?::(\d+))?(?::(\d+))?$/;
+ var parts = regExp.exec(urlLike.replace(/[()]/g, ""));
+ return [parts[1], parts[2] || undefined, parts[3] || undefined];
+ }
+
+ static parseV8OrIE(error) {
+ var filtered = error.stack.split("\n").filter(function (line) {
+ return !!line.match(CHROME_IE_STACK_REGEXP);
+ }, this);
+
+ return filtered.map(function (line) {
+ if (line.indexOf("(eval ") > -1) {
+ // Throw away eval information until we implement stacktrace.js/stackframe#8
+ line = line
+ .replace(/eval code/g, "eval")
+ .replace(/(\(eval at [^()]*)|(\),.*$)/g, "");
+ }
+ var sanitizedLine = line.replace(/^\s+/, "").replace(/\(eval code/g, "(");
+
+ // capture and preseve the parenthesized location "(/foo/my bar.js:12:87)" in
+ // case it has spaces in it, as the string is split on \s+ later on
+ var location = sanitizedLine.match(/ (\((.+):(\d+):(\d+)\)$)/);
+
+ // remove the parenthesized location from the line, if it was matched
+ sanitizedLine = location
+ ? sanitizedLine.replace(location[0], "")
+ : sanitizedLine;
+
+ var tokens = sanitizedLine.split(/\s+/).slice(1);
+ // if a location was matched, pass it to extractLocation() otherwise pop the last token
+ var locationParts = this.extractLocation(
+ location ? location[1] : tokens.pop()
+ );
+ var functionName = tokens.join(" ") || undefined;
+ var fileName =
+ ["eval", "<anonymous>"].indexOf(locationParts[0]) > -1
+ ? undefined
+ : locationParts[0];
+
+ return new StackFrame({
+ functionName: functionName,
+ fileName: fileName,
+ lineNumber: locationParts[1],
+ columnNumber: locationParts[2],
+ source: line,
+ });
+ }, this);
+ }
+
+ static parseFFOrSafari(error) {
+ var filtered = error.stack.split("\n").filter(function (line) {
+ return !line.match(SAFARI_NATIVE_CODE_REGEXP);
+ }, this);
+
+ return filtered.map(function (line) {
+ // Throw away eval information until we implement stacktrace.js/stackframe#8
+ if (line.indexOf(" > eval") > -1) {
+ line = line.replace(
+ / line (\d+)(?: > eval line \d+)* > eval:\d+:\d+/g,
+ ":$1"
+ );
+ }
+
+ if (line.indexOf("@") === -1 && line.indexOf(":") === -1) {
+ // Safari eval frames only have function names and nothing else
+ return new StackFrame({
+ functionName: line,
+ });
+ } else {
+ var functionNameRegex = /((.*".+"[^@]*)?[^@]*)(?:@)/;
+ var matches = line.match(functionNameRegex);
+ var functionName = matches && matches[1] ? matches[1] : undefined;
+ var locationParts = this.extractLocation(
+ line.replace(functionNameRegex, "")
+ );
+
+ return new StackFrame({
+ functionName: functionName,
+ fileName: locationParts[0],
+ lineNumber: locationParts[1],
+ columnNumber: locationParts[2],
+ source: line,
+ });
+ }
+ }, this);
+ }
+}
diff --git a/packages/bun-framework-next/client.development.tsx b/packages/bun-framework-next/client.development.tsx
index b93b1fcce..c08eb513e 100644
--- a/packages/bun-framework-next/client.development.tsx
+++ b/packages/bun-framework-next/client.development.tsx
@@ -1,5 +1,6 @@
globalThis.global = globalThis;
globalThis.Bun_disableCSSImports = true;
+import "./bun-error";
import * as React from "react";
var onlyChildPolyfill = React.Children.only;
@@ -367,7 +368,7 @@ export async function _boot(EntryPointNamespace, isError) {
<TopLevelRender
App={CachedApp}
Component={PageComponent}
- props={{ pageProps: hydrateProps }}
+ props={hydrateProps}
/>,
document.querySelector("#__next")
);
@@ -376,7 +377,7 @@ export async function _boot(EntryPointNamespace, isError) {
<TopLevelRender
App={CachedApp}
Component={PageComponent}
- props={{ pageProps: hydrateProps }}
+ props={hydrateProps}
/>,
document.querySelector("#__next")
);
diff --git a/packages/bun-framework-next/fallback.development.tsx b/packages/bun-framework-next/fallback.development.tsx
index 34e6cb349..b42835d36 100644
--- a/packages/bun-framework-next/fallback.development.tsx
+++ b/packages/bun-framework-next/fallback.development.tsx
@@ -1,43 +1,83 @@
import { insertStyleSheet } from "./page-loader";
+import type {
+ FallbackMessageContainer,
+ FallbackStep,
+} from "../../../src/api/schema";
-const globalCSSQueue = [];
-function insertGlobalStyleSheet({ detail }) {
- globalCSSQueue.push(insertStyleSheet(detail));
+var once = false;
+function insertGlobalStyleSheet(detail) {
+ if (!once) {
+ document.head.insertAdjacentHTML(
+ "beforeend",
+ `<meta name="next-head-count" content="${document.head.childElementCount}">`
+ );
+ once = true;
+ }
+ pageLoader.cssQueue.push(insertStyleSheet(detail).then(() => {}));
}
+[...globalThis["__BUN"].allImportedStyles].map((detail) =>
+ insertGlobalStyleSheet(detail)
+);
+
document.addEventListener("onimportcss", insertGlobalStyleSheet, {
passive: true,
});
import { renderError, _boot, pageLoader } from "./client.development";
+import { renderFallbackError } from "bun-error";
-export default function render({ router, reason, problems }) {
+function renderFallback({
+ router,
+ reason,
+ problems,
+}: FallbackMessageContainer) {
const route = router.routes[router.route];
+
if (!document.getElementById("__next")) {
const next = document.createElement("div");
next.id = "__next";
document.body.prepend(next);
- document.head.insertAdjacentHTML(
- "beforeend",
- `<meta name="next-head-count" content="2">`
- );
}
document.removeEventListener("onimportcss", insertGlobalStyleSheet);
document.addEventListener("onimportcss", pageLoader.onImportCSS, {
passive: true,
});
- import(route)
+
+ globalThis.__NEXT_DATA__.pages["/_app"] = [
+ ...globalThis.__NEXT_DATA__.pages["/_app"],
+ ...globalThis["__BUN"].allImportedStyles,
+ ];
+
+ return import(route)
.then((Namespace) => {
return _boot(Namespace, true);
})
.then(() => {
- const cssQueue = pageLoader.cssQueue;
+ const cssQueue = pageLoader.cssQueue.slice();
pageLoader.cssQueue = [];
- return Promise.all([...cssQueue, ...globalCSSQueue]);
+ return Promise.all([...cssQueue]);
})
.finally(() => {
document.body.style.visibility = "visible";
document.removeEventListener("onimportcss", pageLoader.onImportCSS);
});
}
+
+export default function render(props: FallbackMessageContainer) {
+ renderFallback(props).then(
+ () => {
+ Promise.all(pageLoader.cssQueue).finally(() => {
+ renderFallbackError(props);
+ document.body.style.visibility = "visible";
+ });
+ },
+ (err) => {
+ console.error(err);
+ Promise.all(pageLoader.cssQueue).finally(() => {
+ renderFallbackError(props);
+ });
+ }
+ );
+}
diff --git a/packages/bun-framework-next/next-image-polyfill.tsx b/packages/bun-framework-next/next-image-polyfill.tsx
new file mode 100644
index 000000000..edc3775d7
--- /dev/null
+++ b/packages/bun-framework-next/next-image-polyfill.tsx
@@ -0,0 +1,36 @@
+function NextImagePolyfill({
+ src,
+ width,
+ height,
+ objectFit,
+ style,
+ layout,
+ ...otherProps
+}) {
+ var _style = style;
+ if (layout === "fit") {
+ objectFit = "contain";
+ } else if (layout === "fill") {
+ objectFit = "cover";
+ }
+
+ if (objectFit) {
+ if (!_style) {
+ _style = { objectFit: objectFit };
+ } else {
+ _style.objectFit = objectFit;
+ }
+ }
+
+ return (
+ <img
+ src={src}
+ width={width}
+ height={height}
+ style={_style}
+ {...otherProps}
+ />
+ );
+}
+
+export default NextImagePolyfill;
diff --git a/packages/bun-framework-next/package.json b/packages/bun-framework-next/package.json
index af6286343..1f937589a 100644
--- a/packages/bun-framework-next/package.json
+++ b/packages/bun-framework-next/package.json
@@ -24,6 +24,9 @@
"fallback": "fallback.development.tsx",
"server": "server.development.tsx",
"css": "onimportcss",
+ "override": {
+ "next/dist/client/image.js": "next-image-polyfill.tsx"
+ },
"define": {
"client": {
".env": "NEXT_PUBLIC_",
diff --git a/packages/bun-framework-next/page-loader.ts b/packages/bun-framework-next/page-loader.ts
index 98e132a5f..fc07578db 100644
--- a/packages/bun-framework-next/page-loader.ts
+++ b/packages/bun-framework-next/page-loader.ts
@@ -15,7 +15,12 @@ export function insertStyleSheet(url: string) {
link.onerror = () => reject();
link.href = url;
+
+ // if (headCount) {
+ // document.head.insertBefore(headCount, link);
+ // } else {
document.head.appendChild(link);
+ // }
});
}
diff --git a/packages/bun-framework-next/polyfills.tsx b/packages/bun-framework-next/polyfills.tsx
new file mode 100644
index 000000000..b000c1f54
--- /dev/null
+++ b/packages/bun-framework-next/polyfills.tsx
@@ -0,0 +1,23 @@
+globalThis.global = globalThis;
+
+import { Buffer } from "buffer";
+
+globalThis.Buffer = Buffer;
+
+import * as React from "react";
+
+class URL {
+ constructor(base, source) {
+ this.pathname = source;
+ this.href = base + source;
+ }
+}
+var onlyChildPolyfill = React.Children.only;
+React.Children.only = function (children) {
+ if (children && typeof children === "object" && children.length == 1) {
+ return onlyChildPolyfill(children[0]);
+ }
+
+ return onlyChildPolyfill(children);
+};
+globalThis.URL = URL;
diff --git a/packages/bun-framework-next/renderDocument.tsx b/packages/bun-framework-next/renderDocument.tsx
index 97a65fff8..957615047 100644
--- a/packages/bun-framework-next/renderDocument.tsx
+++ b/packages/bun-framework-next/renderDocument.tsx
@@ -71,10 +71,6 @@ const notImplementedProxy = (base) =>
}
);
-globalThis.fetch = (url, options) => {
- return Promise.reject(new Error(`fetch is not implemented yet. sorry!!`));
-};
-
function getScripts(files: DocumentFiles) {
const { context, props } = this;
const {
@@ -574,6 +570,9 @@ export async function render({
ctx,
});
+ const pageProps = Object.assign({}, props.pageProps || {});
+ // This isn't correct.
+ // We don't call getServerSideProps on clients.
// This isn't correct.
// We don't call getServerSideProps on clients.
const getServerSideProps = PageNamespace.getServerSideProps;
@@ -594,7 +593,7 @@ export async function render({
if (result) {
if ("props" in result) {
if (typeof result.props === "object") {
- Object.assign(props, result.props);
+ Object.assign(pageProps, result.props);
}
}
}
@@ -615,7 +614,7 @@ export async function render({
if (result) {
if ("props" in result) {
if (typeof result.props === "object") {
- Object.assign(props, result.props);
+ Object.assign(pageProps, result.props);
}
}
}
@@ -623,6 +622,7 @@ export async function render({
const renderToString = ReactDOMServer.renderToString;
const ErrorDebug = null;
+ props.pageProps = pageProps;
const renderPage: RenderPage = (
options: ComponentsEnhancer = {}
@@ -648,7 +648,12 @@ export async function render({
const htmlOrPromise = renderToString(
<AppContainer>
- <EnhancedApp Component={EnhancedComponent} router={router} {...props} />
+ <EnhancedApp
+ Component={EnhancedComponent}
+ router={router}
+ {...props}
+ pageProps={pageProps}
+ />
</AppContainer>
);
return typeof htmlOrPromise === "string"
diff --git a/packages/bun-framework-next/server.development.tsx b/packages/bun-framework-next/server.development.tsx
index c6a7beebf..7391d9f32 100644
--- a/packages/bun-framework-next/server.development.tsx
+++ b/packages/bun-framework-next/server.development.tsx
@@ -1,23 +1,4 @@
-import * as React from "react";
-import { Buffer } from "buffer";
-globalThis.Buffer = Buffer;
-
-class URL {
- constructor(base, source) {
- this.pathname = source;
- this.href = base + source;
- }
-}
-var onlyChildPolyfill = React.Children.only;
-React.Children.only = function (children) {
- if (children && typeof children === "object" && children.length == 1) {
- return onlyChildPolyfill(children[0]);
- }
-
- return onlyChildPolyfill(children);
-};
-globalThis.URL = URL;
-globalThis.global = globalThis;
+import "./polyfills";
import { render } from "./renderDocument";
let buildId = 0;
@@ -40,14 +21,6 @@ import(Bun.routesDir + "_document").then(
);
addEventListener("fetch", async (event: FetchEvent) => {
- var appRoute;
-
- try {
- appRoute = await import(Bun.routesDir + "_app");
- } catch (exception) {
- appRoute = null;
- }
- const appStylesheets = (Bun.getImportedStyles() as string[]).slice();
var route = Bun.match(event);
// This imports the currently matched route.
@@ -57,6 +30,15 @@ addEventListener("fetch", async (event: FetchEvent) => {
// It's recursive, so any file that imports a CSS file will be included.
const pageStylesheets = (Bun.getImportedStyles() as string[]).slice();
+ var appRoute;
+
+ try {
+ appRoute = await import(Bun.routesDir + "_app");
+ } catch (exception) {
+ appRoute = null;
+ }
+ const appStylesheets = (Bun.getImportedStyles() as string[]).slice();
+
event.respondWith(
render({
route,