aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/bun-framework-next/package.json5
-rw-r--r--packages/bun-framework-next/server-polyfills.tsx6
-rw-r--r--packages/bun-framework-next/server.development.tsx44
-rw-r--r--packages/bun-framework-next/url-polyfill.js622
4 files changed, 658 insertions, 19 deletions
diff --git a/packages/bun-framework-next/package.json b/packages/bun-framework-next/package.json
index 884d27053..db71b1190 100644
--- a/packages/bun-framework-next/package.json
+++ b/packages/bun-framework-next/package.json
@@ -1,6 +1,6 @@
{
"name": "bun-framework-next",
- "version": "12.0.5",
+ "version": "12.0.6",
"main": "empty.js",
"module": "empty.js",
"description": "Bun compatibility layer for Next.js v12.0.x",
@@ -11,8 +11,7 @@
"author": "",
"license": "MIT",
"dependencies": {
- "react-is": "^17.0.2",
- "url-polyfill": "^1.1.12"
+ "react-is": "^17.0.2"
},
"peerDependencies": {
"next": "^12.0.0"
diff --git a/packages/bun-framework-next/server-polyfills.tsx b/packages/bun-framework-next/server-polyfills.tsx
index f0a2b5c3a..8f582329a 100644
--- a/packages/bun-framework-next/server-polyfills.tsx
+++ b/packages/bun-framework-next/server-polyfills.tsx
@@ -1,7 +1,7 @@
globalThis.global = globalThis;
import { Buffer } from "buffer";
-import { URL } from "url-polyfill";
+import { URL } from "./url-polyfill";
import { TextEncoder, TextDecoder } from "./text-encoder-polyfill";
import * as React from "react";
@@ -19,8 +19,8 @@ React.Children.only = function (children) {
return onlyChildPolyfill(children);
};
-globalThis.Buffer = Buffer;
-globalThis.URL = URL;
+globalThis.Buffer ||= Buffer;
+globalThis.URL ||= URL;
// @ts-expect-error encodeInto is missing in our polyfill
globalThis.TextEncoder ||= TextEncoder;
globalThis.TextDecoder ||= TextDecoder;
diff --git a/packages/bun-framework-next/server.development.tsx b/packages/bun-framework-next/server.development.tsx
index 4ac2621e8..3689dc96e 100644
--- a/packages/bun-framework-next/server.development.tsx
+++ b/packages/bun-framework-next/server.development.tsx
@@ -17,8 +17,8 @@ if (
let buildId = 0;
-var DocumentLoaded = false;
-var DocumentNamespace;
+let DocumentLoaded = false;
+let DocumentNamespace;
import(Bun.routesDir + "_document").then(
(doc) => {
@@ -37,25 +37,39 @@ import(Bun.routesDir + "_document").then(
);
addEventListener("fetch", async (event: FetchEvent) => {
- var route = Bun.match(event);
+ const route = Bun.match(event);
// This imports the currently matched route.
- const PageNamespace = await import(route.filePath);
+ let PageNamespace: any;
+
+ try {
+ PageNamespace = await import(route.filePath);
+ } catch (exception) {
+ console.error("Error loading page:", route.filePath);
+ throw exception;
+ }
// This returns all .css files that were imported in the line above.
// It's recursive, so any file that imports a CSS file will be included.
const pageStylesheets = (Bun.getImportedStyles() as string[]).slice();
- var appRoute;
+ let appRoute: any;
try {
appRoute = await import(Bun.routesDir + "_app");
} catch (exception) {
- appRoute = null;
+ // ResolveError is defined outside of bun-framework-next in ../../src/runtime/errors
+ // @ts-expect-error
+ if (exception && !(exception instanceof ResolveError)) {
+ console.error("Error loading app:", Bun.routesDir + "_app");
+ throw exception;
+ }
}
+
const appStylesheets = (Bun.getImportedStyles() as string[]).slice();
- event.respondWith(
- render({
+ let response: Response;
+ try {
+ response = await render({
route,
PageNamespace,
appStylesheets,
@@ -66,12 +80,16 @@ addEventListener("fetch", async (event: FetchEvent) => {
routePaths: Bun.getRouteFiles(),
routeNames: Bun.getRouteNames(),
request: event.request,
- })
- );
+ });
+ } catch (exception) {
+ console.error("Error rendering route", route.filePath);
+ throw exception;
+ }
+
+ event.respondWith(response);
+
buildId++;
});
-// typescript isolated modules
+declare let Bun: any;
export {};
-
-declare var Bun: any;
diff --git a/packages/bun-framework-next/url-polyfill.js b/packages/bun-framework-next/url-polyfill.js
new file mode 100644
index 000000000..fd79686db
--- /dev/null
+++ b/packages/bun-framework-next/url-polyfill.js
@@ -0,0 +1,622 @@
+// https://github.com/NVIDIA/url-polyfill
+
+var relative = Object.create(null);
+relative["ftp"] = 21;
+relative["file"] = 0;
+relative["gopher"] = 70;
+relative["http"] = 80;
+relative["https"] = 443;
+relative["ws"] = 80;
+relative["wss"] = 443;
+
+var relativePathDotMapping = Object.create(null);
+relativePathDotMapping["%2e"] = ".";
+relativePathDotMapping[".%2e"] = "..";
+relativePathDotMapping["%2e."] = "..";
+relativePathDotMapping["%2e%2e"] = "..";
+
+function isRelativeScheme(scheme) {
+ return relative[scheme] !== undefined;
+}
+
+function invalid() {
+ clear.call(this);
+ this._isInvalid = true;
+}
+
+function IDNAToASCII(h) {
+ if ("" == h) {
+ invalid.call(this);
+ }
+ // XXX
+ return h.toLowerCase();
+}
+
+function percentEscape(c) {
+ var unicode = c.charCodeAt(0);
+ if (
+ unicode > 0x20 &&
+ unicode < 0x7f &&
+ // " # < > ? `
+ [0x22, 0x23, 0x3c, 0x3e, 0x3f, 0x60].indexOf(unicode) == -1
+ ) {
+ return c;
+ }
+ return encodeURIComponent(c);
+}
+
+function percentEscapeQuery(c) {
+ // XXX This actually needs to encode c using encoding and then
+ // convert the bytes one-by-one.
+
+ var unicode = c.charCodeAt(0);
+ if (
+ unicode > 0x20 &&
+ unicode < 0x7f &&
+ // " # < > ` (do not escape '?')
+ [0x22, 0x23, 0x3c, 0x3e, 0x60].indexOf(unicode) == -1
+ ) {
+ return c;
+ }
+ return encodeURIComponent(c);
+}
+
+var EOF = undefined,
+ ALPHA = /[a-zA-Z]/,
+ ALPHANUMERIC = /[a-zA-Z0-9\+\-\.]/;
+
+function parse(input, stateOverride, base) {
+ function err(message) {
+ errors.push(message);
+ }
+
+ var state = stateOverride || "scheme start",
+ cursor = 0,
+ buffer = "",
+ seenAt = false,
+ seenBracket = false,
+ errors = [];
+
+ loop: while ((input[cursor - 1] != EOF || cursor == 0) && !this._isInvalid) {
+ var c = input[cursor];
+ switch (state) {
+ case "scheme start":
+ if (c && ALPHA.test(c)) {
+ buffer += c.toLowerCase(); // ASCII-safe
+ state = "scheme";
+ } else if (!stateOverride) {
+ buffer = "";
+ state = "no scheme";
+ continue;
+ } else {
+ err("Invalid scheme.");
+ break loop;
+ }
+ break;
+
+ case "scheme":
+ if (c && ALPHANUMERIC.test(c)) {
+ buffer += c.toLowerCase(); // ASCII-safe
+ } else if (":" == c) {
+ this._scheme = buffer;
+ buffer = "";
+ if (stateOverride) {
+ break loop;
+ }
+ if (isRelativeScheme(this._scheme)) {
+ this._isRelative = true;
+ }
+ if ("file" == this._scheme) {
+ state = "relative";
+ } else if (this._isRelative && base && base._scheme == this._scheme) {
+ state = "relative or authority";
+ } else if (this._isRelative) {
+ state = "authority first slash";
+ } else {
+ state = "scheme data";
+ }
+ } else if (!stateOverride) {
+ buffer = "";
+ cursor = 0;
+ state = "no scheme";
+ continue;
+ } else if (EOF == c) {
+ break loop;
+ } else {
+ err("Code point not allowed in scheme: " + c);
+ break loop;
+ }
+ break;
+
+ case "scheme data":
+ if ("?" == c) {
+ query = "?";
+ state = "query";
+ } else if ("#" == c) {
+ this._fragment = "#";
+ state = "fragment";
+ } else {
+ // XXX error handling
+ if (EOF != c && "\t" != c && "\n" != c && "\r" != c) {
+ this._schemeData += percentEscape(c);
+ }
+ }
+ break;
+
+ case "no scheme":
+ if (!base || !isRelativeScheme(base._scheme)) {
+ err("Missing scheme.");
+ invalid.call(this);
+ } else {
+ state = "relative";
+ continue;
+ }
+ break;
+
+ case "relative or authority":
+ if ("/" == c && "/" == input[cursor + 1]) {
+ state = "authority ignore slashes";
+ } else {
+ err("Expected /, got: " + c);
+ state = "relative";
+ continue;
+ }
+ break;
+
+ case "relative":
+ this._isRelative = true;
+ if ("file" != this._scheme) this._scheme = base._scheme;
+ if (EOF == c) {
+ this._host = base._host;
+ this._port = base._port;
+ this._path = base._path.slice();
+ this._query = base._query;
+ this._username = base._username;
+ this._password = base._password;
+ break loop;
+ } else if ("/" == c || "\\" == c) {
+ if ("\\" == c) err("\\ is an invalid code point.");
+ state = "relative slash";
+ } else if ("?" == c) {
+ this._host = base._host;
+ this._port = base._port;
+ this._path = base._path.slice();
+ this._query = "?";
+ this._username = base._username;
+ this._password = base._password;
+ state = "query";
+ } else if ("#" == c) {
+ this._host = base._host;
+ this._port = base._port;
+ this._path = base._path.slice();
+ this._query = base._query;
+ this._fragment = "#";
+ this._username = base._username;
+ this._password = base._password;
+ state = "fragment";
+ } else {
+ var nextC = input[cursor + 1];
+ var nextNextC = input[cursor + 2];
+ if (
+ "file" != this._scheme ||
+ !ALPHA.test(c) ||
+ (nextC != ":" && nextC != "|") ||
+ (EOF != nextNextC &&
+ "/" != nextNextC &&
+ "\\" != nextNextC &&
+ "?" != nextNextC &&
+ "#" != nextNextC)
+ ) {
+ this._host = base._host;
+ this._port = base._port;
+ this._username = base._username;
+ this._password = base._password;
+ this._path = base._path.slice();
+ this._path.pop();
+ }
+ state = "relative path";
+ continue;
+ }
+ break;
+
+ case "relative slash":
+ if ("/" == c || "\\" == c) {
+ if ("\\" == c) {
+ err("\\ is an invalid code point.");
+ }
+ if ("file" == this._scheme) {
+ state = "file host";
+ } else {
+ state = "authority ignore slashes";
+ }
+ } else {
+ if ("file" != this._scheme) {
+ this._host = base._host;
+ this._port = base._port;
+ this._username = base._username;
+ this._password = base._password;
+ }
+ state = "relative path";
+ continue;
+ }
+ break;
+
+ case "authority first slash":
+ if ("/" == c) {
+ state = "authority second slash";
+ } else {
+ err("Expected '/', got: " + c);
+ state = "authority ignore slashes";
+ continue;
+ }
+ break;
+
+ case "authority second slash":
+ state = "authority ignore slashes";
+ if ("/" != c) {
+ err("Expected '/', got: " + c);
+ continue;
+ }
+ break;
+
+ case "authority ignore slashes":
+ if ("/" != c && "\\" != c) {
+ state = "authority";
+ continue;
+ } else {
+ err("Expected authority, got: " + c);
+ }
+ break;
+
+ case "authority":
+ if ("@" == c) {
+ if (seenAt) {
+ err("@ already seen.");
+ buffer += "%40";
+ }
+ seenAt = true;
+ for (var i = 0; i < buffer.length; i++) {
+ var cp = buffer[i];
+ if ("\t" == cp || "\n" == cp || "\r" == cp) {
+ err("Invalid whitespace in authority.");
+ continue;
+ }
+ // XXX check URL code points
+ if (":" == cp && null === this._password) {
+ this._password = "";
+ continue;
+ }
+ var tempC = percentEscape(cp);
+ null !== this._password
+ ? (this._password += tempC)
+ : (this._username += tempC);
+ }
+ buffer = "";
+ } else if (EOF == c || "/" == c || "\\" == c || "?" == c || "#" == c) {
+ cursor -= buffer.length;
+ buffer = "";
+ state = "host";
+ continue;
+ } else {
+ buffer += c;
+ }
+ break;
+
+ case "file host":
+ if (EOF == c || "/" == c || "\\" == c || "?" == c || "#" == c) {
+ if (
+ buffer.length == 2 &&
+ ALPHA.test(buffer[0]) &&
+ (buffer[1] == ":" || buffer[1] == "|")
+ ) {
+ state = "relative path";
+ } else if (buffer.length == 0) {
+ state = "relative path start";
+ } else {
+ this._host = IDNAToASCII.call(this, buffer);
+ buffer = "";
+ state = "relative path start";
+ }
+ continue;
+ } else if ("\t" == c || "\n" == c || "\r" == c) {
+ err("Invalid whitespace in file host.");
+ } else {
+ buffer += c;
+ }
+ break;
+
+ case "host":
+ case "hostname":
+ if (":" == c && !seenBracket) {
+ // XXX host parsing
+ this._host = IDNAToASCII.call(this, buffer);
+ buffer = "";
+ state = "port";
+ if ("hostname" == stateOverride) {
+ break loop;
+ }
+ } else if (EOF == c || "/" == c || "\\" == c || "?" == c || "#" == c) {
+ this._host = IDNAToASCII.call(this, buffer);
+ buffer = "";
+ state = "relative path start";
+ if (stateOverride) {
+ break loop;
+ }
+ continue;
+ } else if ("\t" != c && "\n" != c && "\r" != c) {
+ if ("[" == c) {
+ seenBracket = true;
+ } else if ("]" == c) {
+ seenBracket = false;
+ }
+ buffer += c;
+ } else {
+ err("Invalid code point in host/hostname: " + c);
+ }
+ break;
+
+ case "port":
+ if (/[0-9]/.test(c)) {
+ buffer += c;
+ } else if (
+ EOF == c ||
+ "/" == c ||
+ "\\" == c ||
+ "?" == c ||
+ "#" == c ||
+ stateOverride
+ ) {
+ if ("" != buffer) {
+ var temp = parseInt(buffer, 10);
+ if (temp != relative[this._scheme]) {
+ this._port = temp + "";
+ }
+ buffer = "";
+ }
+ if (stateOverride) {
+ break loop;
+ }
+ state = "relative path start";
+ continue;
+ } else if ("\t" == c || "\n" == c || "\r" == c) {
+ err("Invalid code point in port: " + c);
+ } else {
+ invalid.call(this);
+ }
+ break;
+
+ case "relative path start":
+ if ("\\" == c) err("'\\' not allowed in path.");
+ state = "relative path";
+ if ("/" != c && "\\" != c) {
+ continue;
+ }
+ break;
+
+ case "relative path":
+ if (
+ EOF == c ||
+ "/" == c ||
+ "\\" == c ||
+ (!stateOverride && ("?" == c || "#" == c))
+ ) {
+ if ("\\" == c) {
+ err("\\ not allowed in relative path.");
+ }
+ var tmp;
+ if ((tmp = relativePathDotMapping[buffer.toLowerCase()])) {
+ buffer = tmp;
+ }
+ if (".." == buffer) {
+ this._path.pop();
+ if ("/" != c && "\\" != c) {
+ this._path.push("");
+ }
+ } else if ("." == buffer && "/" != c && "\\" != c) {
+ this._path.push("");
+ } else if ("." != buffer) {
+ if (
+ "file" == this._scheme &&
+ this._path.length == 0 &&
+ buffer.length == 2 &&
+ ALPHA.test(buffer[0]) &&
+ buffer[1] == "|"
+ ) {
+ buffer = buffer[0] + ":";
+ }
+ this._path.push(buffer);
+ }
+ buffer = "";
+ if ("?" == c) {
+ this._query = "?";
+ state = "query";
+ } else if ("#" == c) {
+ this._fragment = "#";
+ state = "fragment";
+ }
+ } else if ("\t" != c && "\n" != c && "\r" != c) {
+ buffer += percentEscape(c);
+ }
+ break;
+
+ case "query":
+ if (!stateOverride && "#" == c) {
+ this._fragment = "#";
+ state = "fragment";
+ } else if (EOF != c && "\t" != c && "\n" != c && "\r" != c) {
+ this._query += percentEscapeQuery(c);
+ }
+ break;
+
+ case "fragment":
+ if (EOF != c && "\t" != c && "\n" != c && "\r" != c) {
+ this._fragment += c;
+ }
+ break;
+ }
+
+ cursor++;
+ }
+}
+
+function clear() {
+ this._scheme = "";
+ this._schemeData = "";
+ this._username = "";
+ this._password = null;
+ this._host = "";
+ this._port = "";
+ this._path = [];
+ this._query = "";
+ this._fragment = "";
+ this._isInvalid = false;
+ this._isRelative = false;
+}
+
+// Does not process domain names or IP addresses.
+// Does not handle encoding for the query parameter.
+export class URL {
+ constructor(url, base) {
+ if (base && !(base instanceof URL)) base = new URL(String(base));
+
+ if (typeof url !== "string") url = String(url);
+
+ this._url = url;
+ clear.call(this);
+
+ var input = url.replace(/^[ \t\r\n\f]+|[ \t\r\n\f]+$/g, "");
+ // encoding = encoding || 'utf-8'
+
+ parse.call(this, input, null, base);
+ }
+
+ toString() {
+ return this.href;
+ }
+ get href() {
+ if (this._isInvalid) return this._url;
+
+ var authority = "";
+ if ("" != this._username || null != this._password) {
+ authority =
+ this._username +
+ (null != this._password ? ":" + this._password : "") +
+ "@";
+ }
+
+ return (
+ this.protocol +
+ (this._isRelative ? "//" + authority + this.host : "") +
+ this.pathname +
+ this._query +
+ this._fragment
+ );
+ }
+ set href(href) {
+ clear.call(this);
+ parse.call(this, href);
+ }
+
+ get protocol() {
+ return this._scheme + ":";
+ }
+ set protocol(protocol) {
+ if (this._isInvalid) return;
+ parse.call(this, protocol + ":", "scheme start");
+ }
+
+ get host() {
+ return this._isInvalid
+ ? ""
+ : this._port
+ ? this._host + ":" + this._port
+ : this._host;
+ }
+ set host(host) {
+ if (this._isInvalid || !this._isRelative) return;
+ parse.call(this, host, "host");
+ }
+
+ get hostname() {
+ return this._host;
+ }
+ set hostname(hostname) {
+ if (this._isInvalid || !this._isRelative) return;
+ parse.call(this, hostname, "hostname");
+ }
+
+ get port() {
+ return this._port;
+ }
+ set port(port) {
+ if (this._isInvalid || !this._isRelative) return;
+ parse.call(this, port, "port");
+ }
+
+ get pathname() {
+ return this._isInvalid
+ ? ""
+ : this._isRelative
+ ? "/" + this._path.join("/")
+ : this._schemeData;
+ }
+ set pathname(pathname) {
+ if (this._isInvalid || !this._isRelative) return;
+ this._path = [];
+ parse.call(this, pathname, "relative path start");
+ }
+
+ get search() {
+ return this._isInvalid || !this._query || "?" == this._query
+ ? ""
+ : this._query;
+ }
+ set search(search) {
+ if (this._isInvalid || !this._isRelative) return;
+ this._query = "?";
+ if ("?" == search[0]) search = search.slice(1);
+ parse.call(this, search, "query");
+ }
+
+ get hash() {
+ return this._isInvalid || !this._fragment || "#" == this._fragment
+ ? ""
+ : this._fragment;
+ }
+ set hash(hash) {
+ if (this._isInvalid) return;
+ this._fragment = "#";
+ if ("#" == hash[0]) hash = hash.slice(1);
+ parse.call(this, hash, "fragment");
+ }
+
+ get origin() {
+ var host;
+ if (this._isInvalid || !this._scheme) {
+ return "";
+ }
+ // javascript: Gecko returns String(""), WebKit/Blink String("null")
+ // Gecko throws error for "data://"
+ // data: Gecko returns "", Blink returns "data://", WebKit returns "null"
+ // Gecko returns String("") for file: mailto:
+ // WebKit/Blink returns String("SCHEME://") for file: mailto:
+ switch (this._scheme) {
+ case "data":
+ case "file":
+ case "javascript":
+ case "mailto":
+ return "null";
+ }
+ host = this.host;
+ if (!host) {
+ return "";
+ }
+ return this._scheme + "://" + host;
+ }
+
+ createObjectURL(blob) {
+ return "blob:not-implemented-yet";
+ }
+
+ revokeObjectURL(blob) {}
+}