bun-dev-server 0.9.85 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/file.svg +4 -0
- package/dist/assets/folder.svg +4 -0
- package/dist/assets/index.ejs +18 -0
- package/dist/assets/output.ejs +74 -0
- package/dist/assets/parent.svg +4 -0
- package/dist/assets/serveOutputStyles.css +166 -0
- package/dist/buildManager.d.ts +20 -0
- package/dist/bunClientHmr.d.ts +1 -0
- package/dist/bunServeConfig.d.ts +1 -1
- package/dist/bunTSWatcher.d.ts +1 -1
- package/dist/configManager.d.ts +18 -0
- package/dist/fileWatcher.d.ts +12 -0
- package/dist/httpHandler.d.ts +17 -0
- package/dist/index.js +765 -545
- package/dist/staticAssets.d.ts +18 -0
- package/dist/utils/cors.d.ts +10 -0
- package/dist/utils/filesystem.d.ts +17 -0
- package/package.json +8 -5
package/dist/index.js
CHANGED
|
@@ -17,6 +17,76 @@ var __toESM = (mod, isNodeMode, target) => {
|
|
|
17
17
|
};
|
|
18
18
|
var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
|
19
19
|
|
|
20
|
+
// node_modules/picocolors/picocolors.js
|
|
21
|
+
var require_picocolors = __commonJS((exports, module) => {
|
|
22
|
+
var p = process || {};
|
|
23
|
+
var argv = p.argv || [];
|
|
24
|
+
var env = p.env || {};
|
|
25
|
+
var isColorSupported = !(!!env.NO_COLOR || argv.includes("--no-color")) && (!!env.FORCE_COLOR || argv.includes("--color") || p.platform === "win32" || (p.stdout || {}).isTTY && env.TERM !== "dumb" || !!env.CI);
|
|
26
|
+
var formatter = (open, close, replace = open) => (input) => {
|
|
27
|
+
let string = "" + input, index = string.indexOf(close, open.length);
|
|
28
|
+
return ~index ? open + replaceClose(string, close, replace, index) + close : open + string + close;
|
|
29
|
+
};
|
|
30
|
+
var replaceClose = (string, close, replace, index) => {
|
|
31
|
+
let result = "", cursor = 0;
|
|
32
|
+
do {
|
|
33
|
+
result += string.substring(cursor, index) + replace;
|
|
34
|
+
cursor = index + close.length;
|
|
35
|
+
index = string.indexOf(close, cursor);
|
|
36
|
+
} while (~index);
|
|
37
|
+
return result + string.substring(cursor);
|
|
38
|
+
};
|
|
39
|
+
var createColors = (enabled = isColorSupported) => {
|
|
40
|
+
let f = enabled ? formatter : () => String;
|
|
41
|
+
return {
|
|
42
|
+
isColorSupported: enabled,
|
|
43
|
+
reset: f("\x1B[0m", "\x1B[0m"),
|
|
44
|
+
bold: f("\x1B[1m", "\x1B[22m", "\x1B[22m\x1B[1m"),
|
|
45
|
+
dim: f("\x1B[2m", "\x1B[22m", "\x1B[22m\x1B[2m"),
|
|
46
|
+
italic: f("\x1B[3m", "\x1B[23m"),
|
|
47
|
+
underline: f("\x1B[4m", "\x1B[24m"),
|
|
48
|
+
inverse: f("\x1B[7m", "\x1B[27m"),
|
|
49
|
+
hidden: f("\x1B[8m", "\x1B[28m"),
|
|
50
|
+
strikethrough: f("\x1B[9m", "\x1B[29m"),
|
|
51
|
+
black: f("\x1B[30m", "\x1B[39m"),
|
|
52
|
+
red: f("\x1B[31m", "\x1B[39m"),
|
|
53
|
+
green: f("\x1B[32m", "\x1B[39m"),
|
|
54
|
+
yellow: f("\x1B[33m", "\x1B[39m"),
|
|
55
|
+
blue: f("\x1B[34m", "\x1B[39m"),
|
|
56
|
+
magenta: f("\x1B[35m", "\x1B[39m"),
|
|
57
|
+
cyan: f("\x1B[36m", "\x1B[39m"),
|
|
58
|
+
white: f("\x1B[37m", "\x1B[39m"),
|
|
59
|
+
gray: f("\x1B[90m", "\x1B[39m"),
|
|
60
|
+
bgBlack: f("\x1B[40m", "\x1B[49m"),
|
|
61
|
+
bgRed: f("\x1B[41m", "\x1B[49m"),
|
|
62
|
+
bgGreen: f("\x1B[42m", "\x1B[49m"),
|
|
63
|
+
bgYellow: f("\x1B[43m", "\x1B[49m"),
|
|
64
|
+
bgBlue: f("\x1B[44m", "\x1B[49m"),
|
|
65
|
+
bgMagenta: f("\x1B[45m", "\x1B[49m"),
|
|
66
|
+
bgCyan: f("\x1B[46m", "\x1B[49m"),
|
|
67
|
+
bgWhite: f("\x1B[47m", "\x1B[49m"),
|
|
68
|
+
blackBright: f("\x1B[90m", "\x1B[39m"),
|
|
69
|
+
redBright: f("\x1B[91m", "\x1B[39m"),
|
|
70
|
+
greenBright: f("\x1B[92m", "\x1B[39m"),
|
|
71
|
+
yellowBright: f("\x1B[93m", "\x1B[39m"),
|
|
72
|
+
blueBright: f("\x1B[94m", "\x1B[39m"),
|
|
73
|
+
magentaBright: f("\x1B[95m", "\x1B[39m"),
|
|
74
|
+
cyanBright: f("\x1B[96m", "\x1B[39m"),
|
|
75
|
+
whiteBright: f("\x1B[97m", "\x1B[39m"),
|
|
76
|
+
bgBlackBright: f("\x1B[100m", "\x1B[49m"),
|
|
77
|
+
bgRedBright: f("\x1B[101m", "\x1B[49m"),
|
|
78
|
+
bgGreenBright: f("\x1B[102m", "\x1B[49m"),
|
|
79
|
+
bgYellowBright: f("\x1B[103m", "\x1B[49m"),
|
|
80
|
+
bgBlueBright: f("\x1B[104m", "\x1B[49m"),
|
|
81
|
+
bgMagentaBright: f("\x1B[105m", "\x1B[49m"),
|
|
82
|
+
bgCyanBright: f("\x1B[106m", "\x1B[49m"),
|
|
83
|
+
bgWhiteBright: f("\x1B[107m", "\x1B[49m")
|
|
84
|
+
};
|
|
85
|
+
};
|
|
86
|
+
module.exports = createColors();
|
|
87
|
+
module.exports.createColors = createColors;
|
|
88
|
+
});
|
|
89
|
+
|
|
20
90
|
// node_modules/ejs/lib/utils.js
|
|
21
91
|
var require_utils = __commonJS((exports) => {
|
|
22
92
|
var regExpChars = /[|\\{}()[\]^$+*?.]/g;
|
|
@@ -776,76 +846,6 @@ var require_ejs = __commonJS((exports) => {
|
|
|
776
846
|
}
|
|
777
847
|
});
|
|
778
848
|
|
|
779
|
-
// node_modules/picocolors/picocolors.js
|
|
780
|
-
var require_picocolors = __commonJS((exports, module) => {
|
|
781
|
-
var p = process || {};
|
|
782
|
-
var argv = p.argv || [];
|
|
783
|
-
var env = p.env || {};
|
|
784
|
-
var isColorSupported = !(!!env.NO_COLOR || argv.includes("--no-color")) && (!!env.FORCE_COLOR || argv.includes("--color") || p.platform === "win32" || (p.stdout || {}).isTTY && env.TERM !== "dumb" || !!env.CI);
|
|
785
|
-
var formatter = (open, close, replace = open) => (input) => {
|
|
786
|
-
let string = "" + input, index = string.indexOf(close, open.length);
|
|
787
|
-
return ~index ? open + replaceClose(string, close, replace, index) + close : open + string + close;
|
|
788
|
-
};
|
|
789
|
-
var replaceClose = (string, close, replace, index) => {
|
|
790
|
-
let result = "", cursor = 0;
|
|
791
|
-
do {
|
|
792
|
-
result += string.substring(cursor, index) + replace;
|
|
793
|
-
cursor = index + close.length;
|
|
794
|
-
index = string.indexOf(close, cursor);
|
|
795
|
-
} while (~index);
|
|
796
|
-
return result + string.substring(cursor);
|
|
797
|
-
};
|
|
798
|
-
var createColors = (enabled = isColorSupported) => {
|
|
799
|
-
let f = enabled ? formatter : () => String;
|
|
800
|
-
return {
|
|
801
|
-
isColorSupported: enabled,
|
|
802
|
-
reset: f("\x1B[0m", "\x1B[0m"),
|
|
803
|
-
bold: f("\x1B[1m", "\x1B[22m", "\x1B[22m\x1B[1m"),
|
|
804
|
-
dim: f("\x1B[2m", "\x1B[22m", "\x1B[22m\x1B[2m"),
|
|
805
|
-
italic: f("\x1B[3m", "\x1B[23m"),
|
|
806
|
-
underline: f("\x1B[4m", "\x1B[24m"),
|
|
807
|
-
inverse: f("\x1B[7m", "\x1B[27m"),
|
|
808
|
-
hidden: f("\x1B[8m", "\x1B[28m"),
|
|
809
|
-
strikethrough: f("\x1B[9m", "\x1B[29m"),
|
|
810
|
-
black: f("\x1B[30m", "\x1B[39m"),
|
|
811
|
-
red: f("\x1B[31m", "\x1B[39m"),
|
|
812
|
-
green: f("\x1B[32m", "\x1B[39m"),
|
|
813
|
-
yellow: f("\x1B[33m", "\x1B[39m"),
|
|
814
|
-
blue: f("\x1B[34m", "\x1B[39m"),
|
|
815
|
-
magenta: f("\x1B[35m", "\x1B[39m"),
|
|
816
|
-
cyan: f("\x1B[36m", "\x1B[39m"),
|
|
817
|
-
white: f("\x1B[37m", "\x1B[39m"),
|
|
818
|
-
gray: f("\x1B[90m", "\x1B[39m"),
|
|
819
|
-
bgBlack: f("\x1B[40m", "\x1B[49m"),
|
|
820
|
-
bgRed: f("\x1B[41m", "\x1B[49m"),
|
|
821
|
-
bgGreen: f("\x1B[42m", "\x1B[49m"),
|
|
822
|
-
bgYellow: f("\x1B[43m", "\x1B[49m"),
|
|
823
|
-
bgBlue: f("\x1B[44m", "\x1B[49m"),
|
|
824
|
-
bgMagenta: f("\x1B[45m", "\x1B[49m"),
|
|
825
|
-
bgCyan: f("\x1B[46m", "\x1B[49m"),
|
|
826
|
-
bgWhite: f("\x1B[47m", "\x1B[49m"),
|
|
827
|
-
blackBright: f("\x1B[90m", "\x1B[39m"),
|
|
828
|
-
redBright: f("\x1B[91m", "\x1B[39m"),
|
|
829
|
-
greenBright: f("\x1B[92m", "\x1B[39m"),
|
|
830
|
-
yellowBright: f("\x1B[93m", "\x1B[39m"),
|
|
831
|
-
blueBright: f("\x1B[94m", "\x1B[39m"),
|
|
832
|
-
magentaBright: f("\x1B[95m", "\x1B[39m"),
|
|
833
|
-
cyanBright: f("\x1B[96m", "\x1B[39m"),
|
|
834
|
-
whiteBright: f("\x1B[97m", "\x1B[39m"),
|
|
835
|
-
bgBlackBright: f("\x1B[100m", "\x1B[49m"),
|
|
836
|
-
bgRedBright: f("\x1B[101m", "\x1B[49m"),
|
|
837
|
-
bgGreenBright: f("\x1B[102m", "\x1B[49m"),
|
|
838
|
-
bgYellowBright: f("\x1B[103m", "\x1B[49m"),
|
|
839
|
-
bgBlueBright: f("\x1B[104m", "\x1B[49m"),
|
|
840
|
-
bgMagentaBright: f("\x1B[105m", "\x1B[49m"),
|
|
841
|
-
bgCyanBright: f("\x1B[106m", "\x1B[49m"),
|
|
842
|
-
bgWhiteBright: f("\x1B[107m", "\x1B[49m")
|
|
843
|
-
};
|
|
844
|
-
};
|
|
845
|
-
module.exports = createColors();
|
|
846
|
-
module.exports.createColors = createColors;
|
|
847
|
-
});
|
|
848
|
-
|
|
849
849
|
// node_modules/eventemitter3/index.js
|
|
850
850
|
var require_eventemitter3 = __commonJS((exports, module) => {
|
|
851
851
|
var has = Object.prototype.hasOwnProperty;
|
|
@@ -1026,278 +1026,219 @@ var require_eventemitter3 = __commonJS((exports, module) => {
|
|
|
1026
1026
|
});
|
|
1027
1027
|
|
|
1028
1028
|
// src/server.ts
|
|
1029
|
-
var
|
|
1030
|
-
var {$: $2, build } = globalThis.Bun;
|
|
1031
|
-
|
|
1032
|
-
// src/serveOutputTemplate.ejs
|
|
1033
|
-
var serveOutputTemplate_default = `<!DOCTYPE html>\r
|
|
1034
|
-
<html lang="en">\r
|
|
1035
|
-
\r
|
|
1036
|
-
<head>\r
|
|
1037
|
-
<meta charset="UTF-8" />\r
|
|
1038
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0" />\r
|
|
1039
|
-
<title>Document</title>\r
|
|
1040
|
-
</head>\r
|
|
1041
|
-
\r
|
|
1042
|
-
<body>\r
|
|
1043
|
-
<a href="../">..</a>\r
|
|
1044
|
-
<% dirs.forEach(element => { %> <br /><a href=".<%= element.requestPath %>/<%= element.name %>"><%= element.name %></a> <% }) %>\r
|
|
1045
|
-
<% files.forEach(element => { %> <br /><a href=".<%= element.requestPath %>/<%= element.name %>"><%= element.name %></a> <% }) %>\r
|
|
1046
|
-
</body>\r
|
|
1047
|
-
\r
|
|
1048
|
-
</html>`;
|
|
1029
|
+
var import_picocolors3 = __toESM(require_picocolors(), 1);
|
|
1049
1030
|
|
|
1050
|
-
// src/
|
|
1051
|
-
var
|
|
1052
|
-
|
|
1053
|
-
<head>\r
|
|
1054
|
-
<meta charset="UTF-8" />\r
|
|
1055
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0" />\r
|
|
1056
|
-
<title>Bun HTML File</title>\r
|
|
1057
|
-
<% for (const hashedJs of hashedImports) { %>\r
|
|
1058
|
-
<script type="module" src="<%= hashedJs %>"></script>\r
|
|
1059
|
-
<% } %>\r
|
|
1060
|
-
</head>\r
|
|
1061
|
-
\r
|
|
1062
|
-
<body>\r
|
|
1063
|
-
<div id="app"></div>\r
|
|
1064
|
-
</body>\r
|
|
1065
|
-
</html>\r
|
|
1066
|
-
`;
|
|
1031
|
+
// src/httpHandler.ts
|
|
1032
|
+
var import_ejs = __toESM(require_ejs(), 1);
|
|
1033
|
+
import { readFile, readdir } from "fs/promises";
|
|
1067
1034
|
|
|
1068
|
-
// src/
|
|
1069
|
-
|
|
1070
|
-
|
|
1035
|
+
// src/utils/cors.ts
|
|
1036
|
+
function withCORSHeaders(response, request) {
|
|
1037
|
+
response.headers.set("Access-Control-Allow-Origin", request?.headers.get("origin") ?? "*");
|
|
1038
|
+
response.headers.set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, PATCH, OPTIONS");
|
|
1039
|
+
response.headers.set("Access-Control-Allow-Credentials", "true");
|
|
1040
|
+
response.headers.set("Cache-Control", "no-store, no-cache, must-revalidate");
|
|
1041
|
+
return response;
|
|
1042
|
+
}
|
|
1071
1043
|
|
|
1072
|
-
// src/
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1044
|
+
// src/utils/filesystem.ts
|
|
1045
|
+
var {$ } = globalThis.Bun;
|
|
1046
|
+
import { access, constants } from "fs/promises";
|
|
1047
|
+
async function cleanDirectory(dst) {
|
|
1048
|
+
const { stderr, exitCode } = await $`rm -rf ${dst}/*`.nothrow();
|
|
1049
|
+
if (exitCode !== 0) {
|
|
1050
|
+
if (stderr.indexOf("no matches found") > -1) {
|
|
1051
|
+
console.log("Directory is empty");
|
|
1052
|
+
} else {
|
|
1053
|
+
console.warn("Unable to clean directory", stderr.toString("utf8"));
|
|
1054
|
+
}
|
|
1076
1055
|
}
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
if (
|
|
1081
|
-
|
|
1056
|
+
}
|
|
1057
|
+
function convertBytes(bytes) {
|
|
1058
|
+
const sizes = ["Bytes", "KB", "MB", "GB", "TB"];
|
|
1059
|
+
if (bytes == 0) {
|
|
1060
|
+
return "n/a";
|
|
1082
1061
|
}
|
|
1083
|
-
|
|
1084
|
-
|
|
1062
|
+
const floored = Math.floor(Math.log(bytes) / Math.log(1024));
|
|
1063
|
+
const i = floored;
|
|
1064
|
+
if (i == 0) {
|
|
1065
|
+
return bytes + " " + sizes[i];
|
|
1085
1066
|
}
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1067
|
+
return (bytes / Math.pow(1024, i)).toFixed(1) + " " + sizes[i];
|
|
1068
|
+
}
|
|
1069
|
+
async function checkObjectExists(fsPath) {
|
|
1070
|
+
try {
|
|
1071
|
+
await access(fsPath, constants.R_OK);
|
|
1072
|
+
return true;
|
|
1073
|
+
} catch (e) {
|
|
1074
|
+
if (e?.code === "ENOENT") {
|
|
1075
|
+
return false;
|
|
1076
|
+
}
|
|
1077
|
+
const msg = `Error while accessing path ${fsPath}`;
|
|
1078
|
+
console.error(msg, e);
|
|
1079
|
+
return false;
|
|
1091
1080
|
}
|
|
1092
|
-
|
|
1093
|
-
|
|
1081
|
+
}
|
|
1082
|
+
|
|
1083
|
+
// src/httpHandler.ts
|
|
1084
|
+
function handleErrorResponse(req, err) {
|
|
1085
|
+
const msg = `Error while processing request ${req.url}`;
|
|
1086
|
+
console.error(msg, err);
|
|
1087
|
+
return withCORSHeaders(new Response(msg, { status: 500 }), req);
|
|
1088
|
+
}
|
|
1089
|
+
async function handlePathRequest(requestPath, req, finalConfig, destinationPath) {
|
|
1090
|
+
let fsPath = destinationPath + requestPath;
|
|
1091
|
+
const objThere = await checkObjectExists(fsPath);
|
|
1092
|
+
let isDirectory = false;
|
|
1093
|
+
if (objThere) {
|
|
1094
1094
|
try {
|
|
1095
|
-
|
|
1095
|
+
await readFile(fsPath);
|
|
1096
1096
|
} catch (e) {
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
}
|
|
1102
|
-
if (parsed?.type === "output") {
|
|
1103
|
-
console.table(devServer, parsed.message);
|
|
1104
|
-
return;
|
|
1105
|
-
}
|
|
1106
|
-
if (parsed?.type === "reload") {
|
|
1107
|
-
window.location.reload();
|
|
1108
|
-
return;
|
|
1109
|
-
}
|
|
1110
|
-
if (parsed?.type === "tscerror") {
|
|
1111
|
-
console.error(parsed.message);
|
|
1112
|
-
return;
|
|
1113
|
-
}
|
|
1114
|
-
if (parsed?.type === "error") {
|
|
1115
|
-
console.error(parsed.message);
|
|
1116
|
-
let newDiv = window.document.getElementById("bun-hmr-error");
|
|
1117
|
-
const divExists = !!newDiv;
|
|
1118
|
-
if (!newDiv) {
|
|
1119
|
-
newDiv = window.document.createElement("div");
|
|
1120
|
-
}
|
|
1121
|
-
newDiv.id = "bun-hmr-error";
|
|
1122
|
-
newDiv.innerText += parsed.message;
|
|
1123
|
-
if (!divExists) {
|
|
1124
|
-
window.document.body.appendChild(newDiv);
|
|
1097
|
+
if (e.code === "EISDIR") {
|
|
1098
|
+
isDirectory = true;
|
|
1099
|
+
} else {
|
|
1100
|
+
throw e;
|
|
1125
1101
|
}
|
|
1126
|
-
return;
|
|
1127
1102
|
}
|
|
1103
|
+
} else {
|
|
1104
|
+
if (requestPath.toLowerCase() !== "/index.html") {
|
|
1105
|
+
finalConfig.logRequests && console.log(`${404} ${req.url}`);
|
|
1106
|
+
return withCORSHeaders(new Response("", { status: 404 }), req);
|
|
1107
|
+
}
|
|
1108
|
+
requestPath = "/";
|
|
1109
|
+
isDirectory = true;
|
|
1110
|
+
fsPath = destinationPath + requestPath;
|
|
1128
1111
|
}
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
foundServer.socket?.removeEventListener("error", errorHandler);
|
|
1132
|
-
foundServer.socket?.removeEventListener("message", messageHandler);
|
|
1133
|
-
foundServer.socket?.removeEventListener("open", messageHandler);
|
|
1134
|
-
foundServer.socket?.removeEventListener("close", closeHandler);
|
|
1135
|
-
foundServer.socket = null;
|
|
1136
|
-
setTimeout(function() {
|
|
1137
|
-
console.log(devServer, "Attempting to reconnect...");
|
|
1138
|
-
hotReload();
|
|
1139
|
-
}, 5000);
|
|
1140
|
-
}
|
|
1141
|
-
function openHandler(ev) {
|
|
1142
|
-
console.log(devServer, "Connected to Bun Dev Server");
|
|
1112
|
+
if (!isDirectory) {
|
|
1113
|
+
return handleFileRequest(fsPath, req, finalConfig);
|
|
1143
1114
|
}
|
|
1144
|
-
|
|
1145
|
-
foundServer.socket.addEventListener("message", messageHandler);
|
|
1146
|
-
foundServer.socket.addEventListener("close", closeHandler);
|
|
1147
|
-
foundServer.socket.addEventListener("open", openHandler);
|
|
1115
|
+
return handleDirectoryRequest(fsPath, requestPath, req, finalConfig);
|
|
1148
1116
|
}
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
const bunHMRPlugin = {
|
|
1161
|
-
name: "hmr",
|
|
1162
|
-
target: "browser",
|
|
1163
|
-
setup(build) {
|
|
1164
|
-
const entryPoints = [];
|
|
1165
|
-
const addedEnryPoints = new Set;
|
|
1166
|
-
build.config.entrypoints.forEach((entry) => {
|
|
1167
|
-
let entryPath = entry.replace(/^\.*/, "");
|
|
1168
|
-
if (process.platform === "win32") {
|
|
1169
|
-
entryPath = entryPath.replace(/\//g, "\\");
|
|
1170
|
-
}
|
|
1171
|
-
entryPoints.push(entryPath);
|
|
1172
|
-
});
|
|
1173
|
-
build.onLoad({ filter: /\.m?tsx?/ }, async (args) => {
|
|
1174
|
-
const contents = await readFile(args.path, { encoding: "utf-8" });
|
|
1175
|
-
const isTSx = /\.m?tsx$/.test(args.path);
|
|
1176
|
-
const isJSx = /\.m?jsx$/.test(args.path);
|
|
1177
|
-
const isJS = /\.m?js$/.test(args.path);
|
|
1178
|
-
const isTS = /\.m?ts$/.test(args.path);
|
|
1179
|
-
const loader = isTSx ? "tsx" : isJSx ? "jsx" : isTS ? "ts" : isJS ? "js" : "text";
|
|
1180
|
-
const isEntry = entryPoints.some((entry) => args.path.endsWith(entry));
|
|
1181
|
-
if (!addedEnryPoints.has(args.path) && isEntry) {
|
|
1182
|
-
addedEnryPoints.add(args.path);
|
|
1183
|
-
return { contents: `import "bun-hot-reload"
|
|
1184
|
-
` + contents, loader };
|
|
1185
|
-
}
|
|
1186
|
-
return { contents, loader };
|
|
1187
|
-
});
|
|
1188
|
-
build.onLoad({ filter: /./, namespace: "bun-hot-reload" }, async (args) => {
|
|
1189
|
-
return { contents: `(${bunHotReload(config)})()
|
|
1190
|
-
`, loader: "ts" };
|
|
1191
|
-
});
|
|
1192
|
-
build.onResolve({ filter: /^bun-hot-reload$/ }, (args) => {
|
|
1193
|
-
return { path: args.path, namespace: "bun-hot-reload" };
|
|
1194
|
-
});
|
|
1117
|
+
async function handleFileRequest(fsPath, req, finalConfig) {
|
|
1118
|
+
try {
|
|
1119
|
+
const fl = Bun.file(fsPath);
|
|
1120
|
+
finalConfig.logRequests && console.log(`${200} ${req.url}`);
|
|
1121
|
+
return withCORSHeaders(new Response(fl), req);
|
|
1122
|
+
} catch (e) {
|
|
1123
|
+
if (e?.code === "ENOENT") {
|
|
1124
|
+
finalConfig.logRequests && console.log(`${404} ${req.url}`);
|
|
1125
|
+
return withCORSHeaders(new Response("", { status: 404 }), req);
|
|
1126
|
+
} else {
|
|
1127
|
+
return handleErrorResponse(req, e);
|
|
1195
1128
|
}
|
|
1196
|
-
};
|
|
1197
|
-
return bunHMRPlugin;
|
|
1198
|
-
}
|
|
1199
|
-
function getBunHMRFooter(config) {
|
|
1200
|
-
return `;(${bunHotReload(config)})();`;
|
|
1201
|
-
}
|
|
1202
|
-
|
|
1203
|
-
// src/bunManifest.ts
|
|
1204
|
-
var {write, pathToFileURL } = globalThis.Bun;
|
|
1205
|
-
function writeManifest(output, outdir, withHash = false, manifestName = "bun_server_manifest.json") {
|
|
1206
|
-
const entryPoints = output.outputs.filter((o) => o.kind === "entry-point");
|
|
1207
|
-
const epTable = [];
|
|
1208
|
-
for (const ep of entryPoints) {
|
|
1209
|
-
const basePathUrl = pathToFileURL(outdir);
|
|
1210
|
-
const epUrl = pathToFileURL(ep.path);
|
|
1211
|
-
const relativePath = epUrl.href.replace(`${basePathUrl.href}/`, "");
|
|
1212
|
-
const hashedImport = `${relativePath}${withHash ? `?${ep.hash}` : ``}`;
|
|
1213
|
-
epTable.push(hashedImport);
|
|
1214
1129
|
}
|
|
1215
|
-
const outObj = { js: epTable };
|
|
1216
|
-
write(`${outdir}/${manifestName}`, JSON.stringify(outObj));
|
|
1217
1130
|
}
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
message: ""
|
|
1225
|
-
};
|
|
1226
|
-
async function performTSC(finalConfig, importMeta) {
|
|
1227
|
-
if (finalConfig.enableTSC) {
|
|
1228
|
-
console.log("Performing TSC check");
|
|
1229
|
-
const tsc = await $`tsc --noEmit --noErrorTruncation -p ${finalConfig.tscConfigPath}`.cwd(importMeta.dir).quiet().nothrow();
|
|
1230
|
-
if (tsc.exitCode === 0) {
|
|
1231
|
-
console.log(import_picocolors.default.bgGreen("\u2714 [SUCCESS]"), "TSC check passed");
|
|
1232
|
-
return success;
|
|
1233
|
-
} else {
|
|
1234
|
-
const errOutput = tsc.stdout.toString();
|
|
1235
|
-
console.log(import_picocolors.default.bgRed("\u2718 [ERROR]"), `\r
|
|
1236
|
-
${errOutput}`);
|
|
1131
|
+
async function handleDirectoryRequest(fsPath, requestPath, req, finalConfig) {
|
|
1132
|
+
try {
|
|
1133
|
+
const allEntries = await readdir(fsPath, {
|
|
1134
|
+
withFileTypes: true
|
|
1135
|
+
});
|
|
1136
|
+
const dirs = allEntries.filter((entry) => entry.isDirectory()).map((entry) => {
|
|
1237
1137
|
return {
|
|
1238
|
-
|
|
1239
|
-
|
|
1138
|
+
requestPath: requestPath === "/" ? "" : requestPath,
|
|
1139
|
+
name: entry.name
|
|
1240
1140
|
};
|
|
1241
|
-
}
|
|
1141
|
+
});
|
|
1142
|
+
const files = allEntries.filter((entry) => entry.isFile()).map((entry) => {
|
|
1143
|
+
return {
|
|
1144
|
+
requestPath: requestPath === "/" ? "" : requestPath,
|
|
1145
|
+
name: entry.name
|
|
1146
|
+
};
|
|
1147
|
+
});
|
|
1148
|
+
const templatePath = requestPath === "/" ? "" : requestPath;
|
|
1149
|
+
const rnd = import_ejs.render(finalConfig.serveOutputEjs, {
|
|
1150
|
+
dirs,
|
|
1151
|
+
files,
|
|
1152
|
+
requestPath: templatePath
|
|
1153
|
+
});
|
|
1154
|
+
finalConfig.logRequests && console.log(`${200} ${req.url}`);
|
|
1155
|
+
return withCORSHeaders(new Response(rnd, { headers: { "Content-Type": "text/html" } }), req);
|
|
1156
|
+
} catch (err) {
|
|
1157
|
+
return handleErrorResponse(req, err);
|
|
1242
1158
|
}
|
|
1243
|
-
return success;
|
|
1244
1159
|
}
|
|
1245
1160
|
|
|
1161
|
+
// src/staticAssets.ts
|
|
1162
|
+
import { resolve } from "path";
|
|
1163
|
+
|
|
1164
|
+
// src/static/serveOutputStyles.css
|
|
1165
|
+
var serveOutputStyles_default = "./assets/serveOutputStyles.css";
|
|
1166
|
+
|
|
1167
|
+
// src/static/file.svg
|
|
1168
|
+
var file_default = "./assets/file.svg";
|
|
1169
|
+
|
|
1170
|
+
// src/static/folder.svg
|
|
1171
|
+
var folder_default = "./assets/folder.svg";
|
|
1172
|
+
|
|
1173
|
+
// src/static/parent.svg
|
|
1174
|
+
var parent_default = "./assets/parent.svg";
|
|
1175
|
+
|
|
1176
|
+
// src/staticAssets.ts
|
|
1177
|
+
var staticAssets = {
|
|
1178
|
+
listingStyle: Bun.file(resolve(import.meta.dir, serveOutputStyles_default)),
|
|
1179
|
+
fileSvg: Bun.file(resolve(import.meta.dir, file_default)),
|
|
1180
|
+
folderSvg: Bun.file(resolve(import.meta.dir, folder_default)),
|
|
1181
|
+
parentSvg: Bun.file(resolve(import.meta.dir, parent_default))
|
|
1182
|
+
};
|
|
1183
|
+
var staticAssetRoutes = {
|
|
1184
|
+
"/__bun_dev_server__/serveOutputStyles.css": staticAssets.listingStyle,
|
|
1185
|
+
"/__bun_dev_server__/file.svg": staticAssets.fileSvg,
|
|
1186
|
+
"/__bun_dev_server__/folder.svg": staticAssets.folderSvg,
|
|
1187
|
+
"/__bun_dev_server__/parent.svg": staticAssets.parentSvg
|
|
1188
|
+
};
|
|
1189
|
+
|
|
1190
|
+
// src/fileWatcher.ts
|
|
1191
|
+
import { watch } from "fs/promises";
|
|
1192
|
+
|
|
1193
|
+
// src/buildManager.ts
|
|
1194
|
+
var import_ejs2 = __toESM(require_ejs(), 1);
|
|
1195
|
+
var {build } = globalThis.Bun;
|
|
1196
|
+
|
|
1246
1197
|
// node_modules/eventemitter3/index.mjs
|
|
1247
1198
|
var import__ = __toESM(require_eventemitter3(), 1);
|
|
1248
1199
|
|
|
1249
1200
|
// node_modules/p-timeout/index.js
|
|
1250
1201
|
class TimeoutError extends Error {
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
}
|
|
1256
|
-
|
|
1257
|
-
class AbortError extends Error {
|
|
1258
|
-
constructor(message) {
|
|
1259
|
-
super();
|
|
1260
|
-
this.name = "AbortError";
|
|
1261
|
-
this.message = message;
|
|
1202
|
+
name = "TimeoutError";
|
|
1203
|
+
constructor(message, options) {
|
|
1204
|
+
super(message, options);
|
|
1205
|
+
Error.captureStackTrace?.(this, TimeoutError);
|
|
1262
1206
|
}
|
|
1263
1207
|
}
|
|
1264
|
-
var
|
|
1265
|
-
var getAbortedReason = (signal) => {
|
|
1266
|
-
const reason = signal.reason === undefined ? getDOMException("This operation was aborted.") : signal.reason;
|
|
1267
|
-
return reason instanceof Error ? reason : getDOMException(reason);
|
|
1268
|
-
};
|
|
1208
|
+
var getAbortedReason = (signal) => signal.reason ?? new DOMException("This operation was aborted.", "AbortError");
|
|
1269
1209
|
function pTimeout(promise, options) {
|
|
1270
1210
|
const {
|
|
1271
1211
|
milliseconds,
|
|
1272
1212
|
fallback,
|
|
1273
1213
|
message,
|
|
1274
|
-
customTimers = { setTimeout, clearTimeout }
|
|
1214
|
+
customTimers = { setTimeout, clearTimeout },
|
|
1215
|
+
signal
|
|
1275
1216
|
} = options;
|
|
1276
1217
|
let timer;
|
|
1277
1218
|
let abortHandler;
|
|
1278
|
-
const wrappedPromise = new Promise((
|
|
1219
|
+
const wrappedPromise = new Promise((resolve2, reject) => {
|
|
1279
1220
|
if (typeof milliseconds !== "number" || Math.sign(milliseconds) !== 1) {
|
|
1280
1221
|
throw new TypeError(`Expected \`milliseconds\` to be a positive number, got \`${milliseconds}\``);
|
|
1281
1222
|
}
|
|
1282
|
-
if (
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1223
|
+
if (signal?.aborted) {
|
|
1224
|
+
reject(getAbortedReason(signal));
|
|
1225
|
+
return;
|
|
1226
|
+
}
|
|
1227
|
+
if (signal) {
|
|
1287
1228
|
abortHandler = () => {
|
|
1288
1229
|
reject(getAbortedReason(signal));
|
|
1289
1230
|
};
|
|
1290
1231
|
signal.addEventListener("abort", abortHandler, { once: true });
|
|
1291
1232
|
}
|
|
1233
|
+
promise.then(resolve2, reject);
|
|
1292
1234
|
if (milliseconds === Number.POSITIVE_INFINITY) {
|
|
1293
|
-
promise.then(resolve, reject);
|
|
1294
1235
|
return;
|
|
1295
1236
|
}
|
|
1296
1237
|
const timeoutError = new TimeoutError;
|
|
1297
1238
|
timer = customTimers.setTimeout.call(undefined, () => {
|
|
1298
1239
|
if (fallback) {
|
|
1299
1240
|
try {
|
|
1300
|
-
|
|
1241
|
+
resolve2(fallback());
|
|
1301
1242
|
} catch (error) {
|
|
1302
1243
|
reject(error);
|
|
1303
1244
|
}
|
|
@@ -1307,7 +1248,7 @@ function pTimeout(promise, options) {
|
|
|
1307
1248
|
promise.cancel();
|
|
1308
1249
|
}
|
|
1309
1250
|
if (message === false) {
|
|
1310
|
-
|
|
1251
|
+
resolve2();
|
|
1311
1252
|
} else if (message instanceof Error) {
|
|
1312
1253
|
reject(message);
|
|
1313
1254
|
} else {
|
|
@@ -1315,18 +1256,11 @@ function pTimeout(promise, options) {
|
|
|
1315
1256
|
reject(timeoutError);
|
|
1316
1257
|
}
|
|
1317
1258
|
}, milliseconds);
|
|
1318
|
-
(async () => {
|
|
1319
|
-
try {
|
|
1320
|
-
resolve(await promise);
|
|
1321
|
-
} catch (error) {
|
|
1322
|
-
reject(error);
|
|
1323
|
-
}
|
|
1324
|
-
})();
|
|
1325
1259
|
});
|
|
1326
1260
|
const cancelablePromise = wrappedPromise.finally(() => {
|
|
1327
1261
|
cancelablePromise.clear();
|
|
1328
|
-
if (abortHandler &&
|
|
1329
|
-
|
|
1262
|
+
if (abortHandler && signal) {
|
|
1263
|
+
signal.removeEventListener("abort", abortHandler);
|
|
1330
1264
|
}
|
|
1331
1265
|
});
|
|
1332
1266
|
cancelablePromise.clear = () => {
|
|
@@ -1357,16 +1291,13 @@ function lowerBound(array, value, comparator) {
|
|
|
1357
1291
|
class PriorityQueue {
|
|
1358
1292
|
#queue = [];
|
|
1359
1293
|
enqueue(run, options) {
|
|
1360
|
-
|
|
1361
|
-
priority: 0,
|
|
1362
|
-
...options
|
|
1363
|
-
};
|
|
1294
|
+
const { priority = 0, id } = options ?? {};
|
|
1364
1295
|
const element = {
|
|
1365
|
-
priority
|
|
1366
|
-
id
|
|
1296
|
+
priority,
|
|
1297
|
+
id,
|
|
1367
1298
|
run
|
|
1368
1299
|
};
|
|
1369
|
-
if (this.size === 0 || this.#queue[this.size - 1].priority >=
|
|
1300
|
+
if (this.size === 0 || this.#queue[this.size - 1].priority >= priority) {
|
|
1370
1301
|
this.#queue.push(element);
|
|
1371
1302
|
return;
|
|
1372
1303
|
}
|
|
@@ -1395,12 +1326,15 @@ class PriorityQueue {
|
|
|
1395
1326
|
|
|
1396
1327
|
// node_modules/p-queue/dist/index.js
|
|
1397
1328
|
class PQueue extends import__.default {
|
|
1398
|
-
#
|
|
1329
|
+
#carryoverIntervalCount;
|
|
1399
1330
|
#isIntervalIgnored;
|
|
1400
1331
|
#intervalCount = 0;
|
|
1401
1332
|
#intervalCap;
|
|
1333
|
+
#rateLimitedInInterval = false;
|
|
1334
|
+
#rateLimitFlushScheduled = false;
|
|
1402
1335
|
#interval;
|
|
1403
1336
|
#intervalEnd = 0;
|
|
1337
|
+
#lastExecutionTime = 0;
|
|
1404
1338
|
#intervalId;
|
|
1405
1339
|
#timeoutId;
|
|
1406
1340
|
#queue;
|
|
@@ -1408,13 +1342,13 @@ class PQueue extends import__.default {
|
|
|
1408
1342
|
#pending = 0;
|
|
1409
1343
|
#concurrency;
|
|
1410
1344
|
#isPaused;
|
|
1411
|
-
#throwOnTimeout;
|
|
1412
1345
|
#idAssigner = 1n;
|
|
1346
|
+
#runningTasks = new Map;
|
|
1413
1347
|
timeout;
|
|
1414
1348
|
constructor(options) {
|
|
1415
1349
|
super();
|
|
1416
1350
|
options = {
|
|
1417
|
-
|
|
1351
|
+
carryoverIntervalCount: false,
|
|
1418
1352
|
intervalCap: Number.POSITIVE_INFINITY,
|
|
1419
1353
|
interval: 0,
|
|
1420
1354
|
concurrency: Number.POSITIVE_INFINITY,
|
|
@@ -1428,16 +1362,19 @@ class PQueue extends import__.default {
|
|
|
1428
1362
|
if (options.interval === undefined || !(Number.isFinite(options.interval) && options.interval >= 0)) {
|
|
1429
1363
|
throw new TypeError(`Expected \`interval\` to be a finite number >= 0, got \`${options.interval?.toString() ?? ""}\` (${typeof options.interval})`);
|
|
1430
1364
|
}
|
|
1431
|
-
this.#
|
|
1365
|
+
this.#carryoverIntervalCount = options.carryoverIntervalCount ?? options.carryoverConcurrencyCount ?? false;
|
|
1432
1366
|
this.#isIntervalIgnored = options.intervalCap === Number.POSITIVE_INFINITY || options.interval === 0;
|
|
1433
1367
|
this.#intervalCap = options.intervalCap;
|
|
1434
1368
|
this.#interval = options.interval;
|
|
1435
1369
|
this.#queue = new options.queueClass;
|
|
1436
1370
|
this.#queueClass = options.queueClass;
|
|
1437
1371
|
this.concurrency = options.concurrency;
|
|
1372
|
+
if (options.timeout !== undefined && !(Number.isFinite(options.timeout) && options.timeout > 0)) {
|
|
1373
|
+
throw new TypeError(`Expected \`timeout\` to be a positive finite number, got \`${options.timeout}\` (${typeof options.timeout})`);
|
|
1374
|
+
}
|
|
1438
1375
|
this.timeout = options.timeout;
|
|
1439
|
-
this.#throwOnTimeout = options.throwOnTimeout === true;
|
|
1440
1376
|
this.#isPaused = options.autoStart === false;
|
|
1377
|
+
this.#setupRateLimitTracking();
|
|
1441
1378
|
}
|
|
1442
1379
|
get #doesIntervalAllowAnother() {
|
|
1443
1380
|
return this.#isIntervalIgnored || this.#intervalCount < this.#intervalCap;
|
|
@@ -1447,6 +1384,9 @@ class PQueue extends import__.default {
|
|
|
1447
1384
|
}
|
|
1448
1385
|
#next() {
|
|
1449
1386
|
this.#pending--;
|
|
1387
|
+
if (this.#pending === 0) {
|
|
1388
|
+
this.emit("pendingZero");
|
|
1389
|
+
}
|
|
1450
1390
|
this.#tryToStartAnother();
|
|
1451
1391
|
this.emit("next");
|
|
1452
1392
|
}
|
|
@@ -1460,46 +1400,70 @@ class PQueue extends import__.default {
|
|
|
1460
1400
|
if (this.#intervalId === undefined) {
|
|
1461
1401
|
const delay = this.#intervalEnd - now;
|
|
1462
1402
|
if (delay < 0) {
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
}
|
|
1403
|
+
if (this.#lastExecutionTime > 0) {
|
|
1404
|
+
const timeSinceLastExecution = now - this.#lastExecutionTime;
|
|
1405
|
+
if (timeSinceLastExecution < this.#interval) {
|
|
1406
|
+
this.#createIntervalTimeout(this.#interval - timeSinceLastExecution);
|
|
1407
|
+
return true;
|
|
1408
|
+
}
|
|
1469
1409
|
}
|
|
1410
|
+
this.#intervalCount = this.#carryoverIntervalCount ? this.#pending : 0;
|
|
1411
|
+
} else {
|
|
1412
|
+
this.#createIntervalTimeout(delay);
|
|
1470
1413
|
return true;
|
|
1471
1414
|
}
|
|
1472
1415
|
}
|
|
1473
1416
|
return false;
|
|
1474
1417
|
}
|
|
1418
|
+
#createIntervalTimeout(delay) {
|
|
1419
|
+
if (this.#timeoutId !== undefined) {
|
|
1420
|
+
return;
|
|
1421
|
+
}
|
|
1422
|
+
this.#timeoutId = setTimeout(() => {
|
|
1423
|
+
this.#onResumeInterval();
|
|
1424
|
+
}, delay);
|
|
1425
|
+
}
|
|
1426
|
+
#clearIntervalTimer() {
|
|
1427
|
+
if (this.#intervalId) {
|
|
1428
|
+
clearInterval(this.#intervalId);
|
|
1429
|
+
this.#intervalId = undefined;
|
|
1430
|
+
}
|
|
1431
|
+
}
|
|
1432
|
+
#clearTimeoutTimer() {
|
|
1433
|
+
if (this.#timeoutId) {
|
|
1434
|
+
clearTimeout(this.#timeoutId);
|
|
1435
|
+
this.#timeoutId = undefined;
|
|
1436
|
+
}
|
|
1437
|
+
}
|
|
1475
1438
|
#tryToStartAnother() {
|
|
1476
1439
|
if (this.#queue.size === 0) {
|
|
1477
|
-
|
|
1478
|
-
clearInterval(this.#intervalId);
|
|
1479
|
-
}
|
|
1480
|
-
this.#intervalId = undefined;
|
|
1440
|
+
this.#clearIntervalTimer();
|
|
1481
1441
|
this.emit("empty");
|
|
1482
1442
|
if (this.#pending === 0) {
|
|
1443
|
+
this.#clearTimeoutTimer();
|
|
1483
1444
|
this.emit("idle");
|
|
1484
1445
|
}
|
|
1485
1446
|
return false;
|
|
1486
1447
|
}
|
|
1448
|
+
let taskStarted = false;
|
|
1487
1449
|
if (!this.#isPaused) {
|
|
1488
1450
|
const canInitializeInterval = !this.#isIntervalPaused;
|
|
1489
1451
|
if (this.#doesIntervalAllowAnother && this.#doesConcurrentAllowAnother) {
|
|
1490
1452
|
const job = this.#queue.dequeue();
|
|
1491
|
-
if (!
|
|
1492
|
-
|
|
1453
|
+
if (!this.#isIntervalIgnored) {
|
|
1454
|
+
this.#intervalCount++;
|
|
1455
|
+
this.#scheduleRateLimitUpdate();
|
|
1493
1456
|
}
|
|
1494
1457
|
this.emit("active");
|
|
1458
|
+
this.#lastExecutionTime = Date.now();
|
|
1495
1459
|
job();
|
|
1496
1460
|
if (canInitializeInterval) {
|
|
1497
1461
|
this.#initializeIntervalIfNeeded();
|
|
1498
1462
|
}
|
|
1499
|
-
|
|
1463
|
+
taskStarted = true;
|
|
1500
1464
|
}
|
|
1501
1465
|
}
|
|
1502
|
-
return
|
|
1466
|
+
return taskStarted;
|
|
1503
1467
|
}
|
|
1504
1468
|
#initializeIntervalIfNeeded() {
|
|
1505
1469
|
if (this.#isIntervalIgnored || this.#intervalId !== undefined) {
|
|
@@ -1512,11 +1476,11 @@ class PQueue extends import__.default {
|
|
|
1512
1476
|
}
|
|
1513
1477
|
#onInterval() {
|
|
1514
1478
|
if (this.#intervalCount === 0 && this.#pending === 0 && this.#intervalId) {
|
|
1515
|
-
|
|
1516
|
-
this.#intervalId = undefined;
|
|
1479
|
+
this.#clearIntervalTimer();
|
|
1517
1480
|
}
|
|
1518
|
-
this.#intervalCount = this.#
|
|
1481
|
+
this.#intervalCount = this.#carryoverIntervalCount ? this.#pending : 0;
|
|
1519
1482
|
this.#processQueue();
|
|
1483
|
+
this.#scheduleRateLimitUpdate();
|
|
1520
1484
|
}
|
|
1521
1485
|
#processQueue() {
|
|
1522
1486
|
while (this.#tryToStartAnother()) {
|
|
@@ -1540,40 +1504,58 @@ class PQueue extends import__.default {
|
|
|
1540
1504
|
});
|
|
1541
1505
|
}
|
|
1542
1506
|
setPriority(id, priority) {
|
|
1507
|
+
if (typeof priority !== "number" || !Number.isFinite(priority)) {
|
|
1508
|
+
throw new TypeError(`Expected \`priority\` to be a finite number, got \`${priority}\` (${typeof priority})`);
|
|
1509
|
+
}
|
|
1543
1510
|
this.#queue.setPriority(id, priority);
|
|
1544
1511
|
}
|
|
1545
1512
|
async add(function_, options = {}) {
|
|
1546
1513
|
options.id ??= (this.#idAssigner++).toString();
|
|
1547
1514
|
options = {
|
|
1548
1515
|
timeout: this.timeout,
|
|
1549
|
-
throwOnTimeout: this.#throwOnTimeout,
|
|
1550
1516
|
...options
|
|
1551
1517
|
};
|
|
1552
|
-
return new Promise((
|
|
1518
|
+
return new Promise((resolve2, reject) => {
|
|
1519
|
+
const taskSymbol = Symbol(`task-${options.id}`);
|
|
1553
1520
|
this.#queue.enqueue(async () => {
|
|
1554
1521
|
this.#pending++;
|
|
1555
|
-
this.#
|
|
1522
|
+
this.#runningTasks.set(taskSymbol, {
|
|
1523
|
+
id: options.id,
|
|
1524
|
+
priority: options.priority ?? 0,
|
|
1525
|
+
startTime: Date.now(),
|
|
1526
|
+
timeout: options.timeout
|
|
1527
|
+
});
|
|
1556
1528
|
try {
|
|
1557
|
-
|
|
1529
|
+
try {
|
|
1530
|
+
options.signal?.throwIfAborted();
|
|
1531
|
+
} catch (error) {
|
|
1532
|
+
if (!this.#isIntervalIgnored) {
|
|
1533
|
+
this.#intervalCount--;
|
|
1534
|
+
}
|
|
1535
|
+
this.#runningTasks.delete(taskSymbol);
|
|
1536
|
+
throw error;
|
|
1537
|
+
}
|
|
1558
1538
|
let operation = function_({ signal: options.signal });
|
|
1559
1539
|
if (options.timeout) {
|
|
1560
|
-
operation = pTimeout(Promise.resolve(operation), {
|
|
1540
|
+
operation = pTimeout(Promise.resolve(operation), {
|
|
1541
|
+
milliseconds: options.timeout,
|
|
1542
|
+
message: `Task timed out after ${options.timeout}ms (queue has ${this.#pending} running, ${this.#queue.size} waiting)`
|
|
1543
|
+
});
|
|
1561
1544
|
}
|
|
1562
1545
|
if (options.signal) {
|
|
1563
1546
|
operation = Promise.race([operation, this.#throwOnAbort(options.signal)]);
|
|
1564
1547
|
}
|
|
1565
1548
|
const result = await operation;
|
|
1566
|
-
|
|
1549
|
+
resolve2(result);
|
|
1567
1550
|
this.emit("completed", result);
|
|
1568
1551
|
} catch (error) {
|
|
1569
|
-
if (error instanceof TimeoutError && !options.throwOnTimeout) {
|
|
1570
|
-
resolve();
|
|
1571
|
-
return;
|
|
1572
|
-
}
|
|
1573
1552
|
reject(error);
|
|
1574
1553
|
this.emit("error", error);
|
|
1575
1554
|
} finally {
|
|
1576
|
-
this.#
|
|
1555
|
+
this.#runningTasks.delete(taskSymbol);
|
|
1556
|
+
queueMicrotask(() => {
|
|
1557
|
+
this.#next();
|
|
1558
|
+
});
|
|
1577
1559
|
}
|
|
1578
1560
|
}, options);
|
|
1579
1561
|
this.emit("add");
|
|
@@ -1596,6 +1578,7 @@ class PQueue extends import__.default {
|
|
|
1596
1578
|
}
|
|
1597
1579
|
clear() {
|
|
1598
1580
|
this.#queue = new this.#queueClass;
|
|
1581
|
+
this.#updateRateLimitState();
|
|
1599
1582
|
}
|
|
1600
1583
|
async onEmpty() {
|
|
1601
1584
|
if (this.#queue.size === 0) {
|
|
@@ -1615,14 +1598,41 @@ class PQueue extends import__.default {
|
|
|
1615
1598
|
}
|
|
1616
1599
|
await this.#onEvent("idle");
|
|
1617
1600
|
}
|
|
1601
|
+
async onPendingZero() {
|
|
1602
|
+
if (this.#pending === 0) {
|
|
1603
|
+
return;
|
|
1604
|
+
}
|
|
1605
|
+
await this.#onEvent("pendingZero");
|
|
1606
|
+
}
|
|
1607
|
+
async onRateLimit() {
|
|
1608
|
+
if (this.isRateLimited) {
|
|
1609
|
+
return;
|
|
1610
|
+
}
|
|
1611
|
+
await this.#onEvent("rateLimit");
|
|
1612
|
+
}
|
|
1613
|
+
async onRateLimitCleared() {
|
|
1614
|
+
if (!this.isRateLimited) {
|
|
1615
|
+
return;
|
|
1616
|
+
}
|
|
1617
|
+
await this.#onEvent("rateLimitCleared");
|
|
1618
|
+
}
|
|
1619
|
+
async onError() {
|
|
1620
|
+
return new Promise((_resolve, reject) => {
|
|
1621
|
+
const handleError = (error) => {
|
|
1622
|
+
this.off("error", handleError);
|
|
1623
|
+
reject(error);
|
|
1624
|
+
};
|
|
1625
|
+
this.on("error", handleError);
|
|
1626
|
+
});
|
|
1627
|
+
}
|
|
1618
1628
|
async#onEvent(event, filter) {
|
|
1619
|
-
return new Promise((
|
|
1629
|
+
return new Promise((resolve2) => {
|
|
1620
1630
|
const listener = () => {
|
|
1621
1631
|
if (filter && !filter()) {
|
|
1622
1632
|
return;
|
|
1623
1633
|
}
|
|
1624
1634
|
this.off(event, listener);
|
|
1625
|
-
|
|
1635
|
+
resolve2();
|
|
1626
1636
|
};
|
|
1627
1637
|
this.on(event, listener);
|
|
1628
1638
|
});
|
|
@@ -1639,136 +1649,104 @@ class PQueue extends import__.default {
|
|
|
1639
1649
|
get isPaused() {
|
|
1640
1650
|
return this.#isPaused;
|
|
1641
1651
|
}
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
async function startBunDevServer(serverConfig, importMeta) {
|
|
1646
|
-
const defaultConfig = {
|
|
1647
|
-
port: 3000,
|
|
1648
|
-
websocketPath: DEFAULT_HMR_PATH,
|
|
1649
|
-
serveOutputEjs: serveOutputTemplate_default,
|
|
1650
|
-
serveIndexHtmlEjs: indexHTMLTemplate_default,
|
|
1651
|
-
createIndexHTML: true,
|
|
1652
|
-
tscConfigPath: resolve(importMeta.dir, "./tsconfig.json"),
|
|
1653
|
-
broadcastBuildOutputToConsole: true,
|
|
1654
|
-
broadcastBuildOutputToClient: true
|
|
1655
|
-
};
|
|
1656
|
-
const finalConfig = { ...defaultConfig, ...serverConfig };
|
|
1657
|
-
if (serverConfig.tscConfigPath) {
|
|
1658
|
-
finalConfig.tscConfigPath = resolve(importMeta.dir, serverConfig.tscConfigPath);
|
|
1659
|
-
}
|
|
1660
|
-
if (!finalConfig.watchDir) {
|
|
1661
|
-
throw new Error("watchDir must be set");
|
|
1662
|
-
}
|
|
1663
|
-
const servePart = finalConfig.buildConfig.outdir ?? finalConfig.servePath ?? "./dist";
|
|
1664
|
-
const serveDestination = resolve(importMeta.dir, servePart);
|
|
1665
|
-
const watchDestination = resolve(importMeta.dir, finalConfig.watchDir);
|
|
1666
|
-
const allEntries = serverConfig.buildConfig.entrypoints.splice(0, serverConfig.buildConfig.entrypoints.length);
|
|
1667
|
-
const resolvedEntries = allEntries.map((e) => resolve(importMeta.dir, e));
|
|
1668
|
-
serverConfig.buildConfig.entrypoints = resolvedEntries;
|
|
1669
|
-
const destinationPath = serveDestination;
|
|
1670
|
-
const srcWatch = watchDestination;
|
|
1671
|
-
try {
|
|
1672
|
-
await readdir(destinationPath);
|
|
1673
|
-
} catch (e) {
|
|
1674
|
-
if (e.code === "ENOENT") {
|
|
1675
|
-
console.log("Directory not found, creating it...");
|
|
1676
|
-
try {
|
|
1677
|
-
await $2`mkdir ${destinationPath}`;
|
|
1678
|
-
} catch (e2) {
|
|
1679
|
-
console.error("Unable to create directory", e2);
|
|
1680
|
-
}
|
|
1681
|
-
} else {
|
|
1682
|
-
throw e;
|
|
1652
|
+
#setupRateLimitTracking() {
|
|
1653
|
+
if (this.#isIntervalIgnored) {
|
|
1654
|
+
return;
|
|
1683
1655
|
}
|
|
1656
|
+
this.on("add", () => {
|
|
1657
|
+
if (this.#queue.size > 0) {
|
|
1658
|
+
this.#scheduleRateLimitUpdate();
|
|
1659
|
+
}
|
|
1660
|
+
});
|
|
1661
|
+
this.on("next", () => {
|
|
1662
|
+
this.#scheduleRateLimitUpdate();
|
|
1663
|
+
});
|
|
1684
1664
|
}
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
outdir: destinationPath
|
|
1689
|
-
};
|
|
1690
|
-
if (finalConfig.hotReload === "footer") {
|
|
1691
|
-
if (!buildCfg.footer) {
|
|
1692
|
-
buildCfg.footer = "";
|
|
1665
|
+
#scheduleRateLimitUpdate() {
|
|
1666
|
+
if (this.#isIntervalIgnored || this.#rateLimitFlushScheduled) {
|
|
1667
|
+
return;
|
|
1693
1668
|
}
|
|
1694
|
-
|
|
1669
|
+
this.#rateLimitFlushScheduled = true;
|
|
1670
|
+
queueMicrotask(() => {
|
|
1671
|
+
this.#rateLimitFlushScheduled = false;
|
|
1672
|
+
this.#updateRateLimitState();
|
|
1673
|
+
});
|
|
1695
1674
|
}
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1675
|
+
#updateRateLimitState() {
|
|
1676
|
+
const previous = this.#rateLimitedInInterval;
|
|
1677
|
+
const shouldBeRateLimited = !this.#isIntervalIgnored && this.#intervalCount >= this.#intervalCap && this.#queue.size > 0;
|
|
1678
|
+
if (shouldBeRateLimited !== previous) {
|
|
1679
|
+
this.#rateLimitedInInterval = shouldBeRateLimited;
|
|
1680
|
+
this.emit(shouldBeRateLimited ? "rateLimit" : "rateLimitCleared");
|
|
1699
1681
|
}
|
|
1700
|
-
buildCfg.plugins.push(bunHotReloadPlugin(buncfg));
|
|
1701
1682
|
}
|
|
1702
|
-
|
|
1703
|
-
|
|
1683
|
+
get isRateLimited() {
|
|
1684
|
+
return this.#rateLimitedInInterval;
|
|
1704
1685
|
}
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
const queue = getThrottledBuildAndNotify(finalConfig);
|
|
1739
|
-
await queue.add(async () => {
|
|
1740
|
-
await cleanBuildAndNotify(importMeta, finalConfig, destinationPath, buildCfg, bunServer, { filename: "Initial", eventType: "change" });
|
|
1741
|
-
});
|
|
1742
|
-
const watcher = watch(srcWatch, { recursive: true });
|
|
1743
|
-
for await (const event of watcher) {
|
|
1744
|
-
if (queue.pending > 0) {
|
|
1745
|
-
continue;
|
|
1746
|
-
}
|
|
1747
|
-
try {
|
|
1748
|
-
if (queue.size > 0) {
|
|
1749
|
-
queue.clear();
|
|
1750
|
-
}
|
|
1751
|
-
queue.add(async () => {
|
|
1752
|
-
await cleanBuildAndNotify(importMeta, finalConfig, destinationPath, buildCfg, bunServer, event);
|
|
1753
|
-
});
|
|
1754
|
-
} catch (e) {
|
|
1755
|
-
console.error("Error while processing file change", e);
|
|
1686
|
+
get isSaturated() {
|
|
1687
|
+
return this.#pending === this.#concurrency && this.#queue.size > 0 || this.isRateLimited && this.#queue.size > 0;
|
|
1688
|
+
}
|
|
1689
|
+
get runningTasks() {
|
|
1690
|
+
return [...this.#runningTasks.values()].map((task) => ({ ...task }));
|
|
1691
|
+
}
|
|
1692
|
+
}
|
|
1693
|
+
|
|
1694
|
+
// src/buildManager.ts
|
|
1695
|
+
var import_picocolors2 = __toESM(require_picocolors(), 1);
|
|
1696
|
+
|
|
1697
|
+
// src/tsChecker.ts
|
|
1698
|
+
var import_picocolors = __toESM(require_picocolors(), 1);
|
|
1699
|
+
var {$: $2 } = globalThis.Bun;
|
|
1700
|
+
var success = {
|
|
1701
|
+
error: false,
|
|
1702
|
+
message: ""
|
|
1703
|
+
};
|
|
1704
|
+
async function performTSC(finalConfig, importMeta) {
|
|
1705
|
+
if (finalConfig.enableTSC) {
|
|
1706
|
+
console.log("Performing TSC check");
|
|
1707
|
+
const tsc = await $2`tsc --noEmit --noErrorTruncation -p ${finalConfig.tscConfigPath}`.cwd(importMeta.dir).quiet().nothrow();
|
|
1708
|
+
if (tsc.exitCode === 0) {
|
|
1709
|
+
console.log(import_picocolors.default.bgGreen("\u2714 [SUCCESS]"), "TSC check passed");
|
|
1710
|
+
return success;
|
|
1711
|
+
} else {
|
|
1712
|
+
const errOutput = tsc.stdout.toString();
|
|
1713
|
+
console.log(import_picocolors.default.bgRed("\u2718 [ERROR]"), `\r
|
|
1714
|
+
${errOutput}`);
|
|
1715
|
+
return {
|
|
1716
|
+
error: true,
|
|
1717
|
+
message: errOutput
|
|
1718
|
+
};
|
|
1756
1719
|
}
|
|
1757
1720
|
}
|
|
1721
|
+
return success;
|
|
1758
1722
|
}
|
|
1759
|
-
|
|
1760
|
-
|
|
1723
|
+
|
|
1724
|
+
// src/bunManifest.ts
|
|
1725
|
+
var {write, pathToFileURL } = globalThis.Bun;
|
|
1726
|
+
function writeManifest(output, outdir, withHash = false, manifestName = "bun_server_manifest.json") {
|
|
1727
|
+
const entryPoints = output.outputs.filter((o) => o.kind === "entry-point");
|
|
1728
|
+
const epTable = [];
|
|
1729
|
+
for (const ep of entryPoints) {
|
|
1730
|
+
const basePathUrl = pathToFileURL(outdir);
|
|
1731
|
+
const epUrl = pathToFileURL(ep.path);
|
|
1732
|
+
const relativePath = epUrl.href.replace(`${basePathUrl.href}/`, "");
|
|
1733
|
+
const hashedImport = `${relativePath}${withHash ? `?${ep.hash}` : ``}`;
|
|
1734
|
+
epTable.push(hashedImport);
|
|
1735
|
+
}
|
|
1736
|
+
const outObj = { js: epTable };
|
|
1737
|
+
write(`${outdir}/${manifestName}`, JSON.stringify(outObj));
|
|
1738
|
+
}
|
|
1739
|
+
|
|
1740
|
+
// src/buildManager.ts
|
|
1741
|
+
function getThrottledBuildQueue(serverConfig) {
|
|
1742
|
+
return new PQueue({
|
|
1761
1743
|
concurrency: 1,
|
|
1762
1744
|
intervalCap: 1,
|
|
1763
1745
|
interval: serverConfig.watchDelay ?? 1000,
|
|
1764
1746
|
carryoverConcurrencyCount: true
|
|
1765
1747
|
});
|
|
1766
|
-
return anotherThrottle;
|
|
1767
1748
|
}
|
|
1768
1749
|
async function cleanBuildAndNotify(importerMeta, finalConfig, destinationPath, buildCfg, bunServer, event) {
|
|
1769
|
-
if (finalConfig.cleanServePath) {
|
|
1770
|
-
await cleanDirectory(destinationPath);
|
|
1771
|
-
}
|
|
1772
1750
|
const buildEnv = {
|
|
1773
1751
|
importerMeta,
|
|
1774
1752
|
finalConfig,
|
|
@@ -1779,6 +1757,12 @@ async function cleanBuildAndNotify(importerMeta, finalConfig, destinationPath, b
|
|
|
1779
1757
|
};
|
|
1780
1758
|
await finalConfig.beforeBuild?.(buildEnv);
|
|
1781
1759
|
try {
|
|
1760
|
+
if (buildCfg.outdir) {
|
|
1761
|
+
const outdirExists = await checkObjectExists(buildCfg.outdir);
|
|
1762
|
+
if (!outdirExists) {
|
|
1763
|
+
return;
|
|
1764
|
+
}
|
|
1765
|
+
}
|
|
1782
1766
|
const output = await build(buildCfg);
|
|
1783
1767
|
publishOutputLogs(bunServer, output, finalConfig, event);
|
|
1784
1768
|
if (finalConfig.createIndexHTML) {
|
|
@@ -1799,17 +1783,19 @@ async function cleanBuildAndNotify(importerMeta, finalConfig, destinationPath, b
|
|
|
1799
1783
|
bunServer.publish("message", JSON.stringify({ type: "tscerror", message: tscSuccess.message }));
|
|
1800
1784
|
}
|
|
1801
1785
|
} catch (e) {
|
|
1786
|
+
const errorMessage = e instanceof Error ? e.message : String(e);
|
|
1787
|
+
if (errorMessage.includes("test-file-watcher") || errorMessage.includes("test-build") || errorMessage.includes("test-watch-dir")) {
|
|
1788
|
+
return;
|
|
1789
|
+
}
|
|
1802
1790
|
console.error(e);
|
|
1803
1791
|
}
|
|
1804
1792
|
}
|
|
1805
|
-
function handleErrorResponse(req, err) {
|
|
1806
|
-
const msg = `Error while processing request ${req.url}`;
|
|
1807
|
-
console.error(msg, err);
|
|
1808
|
-
return withCORSHeaders(new Response(msg, { status: 500 }), req);
|
|
1809
|
-
}
|
|
1810
1793
|
function publishOutputLogs(bunServer, output, config, event) {
|
|
1811
1794
|
output.logs.forEach(console.log);
|
|
1812
|
-
bunServer.publish("message", JSON.stringify({
|
|
1795
|
+
bunServer.publish("message", JSON.stringify({
|
|
1796
|
+
type: "message",
|
|
1797
|
+
message: `[Bun HMR] ${event.filename} ${event.eventType}`
|
|
1798
|
+
}));
|
|
1813
1799
|
const outTable = output.outputs.filter((o) => o.kind !== "sourcemap").map((o) => {
|
|
1814
1800
|
const a = Bun.pathToFileURL(o.path);
|
|
1815
1801
|
const distPath = Bun.pathToFileURL(config.buildConfig.outdir ?? "./dist");
|
|
@@ -1824,7 +1810,7 @@ function publishOutputLogs(bunServer, output, config, event) {
|
|
|
1824
1810
|
};
|
|
1825
1811
|
});
|
|
1826
1812
|
if (config.broadcastBuildOutputToConsole) {
|
|
1827
|
-
|
|
1813
|
+
printPrettyBuildOutput(outTable);
|
|
1828
1814
|
}
|
|
1829
1815
|
if (config.broadcastBuildOutputToClient) {
|
|
1830
1816
|
bunServer.publish("message", JSON.stringify({ type: "output", message: outTable }));
|
|
@@ -1833,114 +1819,348 @@ function publishOutputLogs(bunServer, output, config, event) {
|
|
|
1833
1819
|
function publishIndexHTML(destinationPath, template, output, _event) {
|
|
1834
1820
|
const eps = output.outputs.filter((o) => o.kind === "entry-point");
|
|
1835
1821
|
const hashedImports = [];
|
|
1822
|
+
const cssFiles = [];
|
|
1823
|
+
const basePathUrl = Bun.pathToFileURL(destinationPath);
|
|
1836
1824
|
for (const ep of eps) {
|
|
1837
|
-
const basePathUrl = Bun.pathToFileURL(destinationPath);
|
|
1838
1825
|
const epUrl = Bun.pathToFileURL(ep.path);
|
|
1839
1826
|
const hashedImport = `${epUrl.href.replace(basePathUrl.href, "")}?${ep.hash}`;
|
|
1840
1827
|
hashedImports.push(hashedImport);
|
|
1841
1828
|
}
|
|
1842
|
-
|
|
1829
|
+
const cssOutputs = output.outputs.filter((o) => o.path.endsWith(".css"));
|
|
1830
|
+
for (const cssFile of cssOutputs) {
|
|
1831
|
+
const cssUrl = Bun.pathToFileURL(cssFile.path);
|
|
1832
|
+
const hashedCss = `${cssUrl.href.replace(basePathUrl.href, "")}?${cssFile.hash}`;
|
|
1833
|
+
cssFiles.push(hashedCss);
|
|
1834
|
+
}
|
|
1835
|
+
Bun.write(destinationPath + "/index.html", import_ejs2.render(template, { hashedImports, cssFiles }));
|
|
1843
1836
|
}
|
|
1844
|
-
function
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1837
|
+
function printPrettyBuildOutput(outTable) {
|
|
1838
|
+
if (outTable.length === 0)
|
|
1839
|
+
return;
|
|
1840
|
+
const totalFiles = outTable.length;
|
|
1841
|
+
const fileWord = totalFiles === 1 ? "file" : "files";
|
|
1842
|
+
console.log(`
|
|
1843
|
+
` + import_picocolors2.default.bold(import_picocolors2.default.cyan(`\uD83D\uDCE6 Build Output (${totalFiles} ${fileWord})`)));
|
|
1844
|
+
outTable.forEach((row) => {
|
|
1845
|
+
const checkmark = import_picocolors2.default.green(" \u2713");
|
|
1846
|
+
const filePath = import_picocolors2.default.white(row.path);
|
|
1847
|
+
const size = import_picocolors2.default.dim(`(${row.size})`);
|
|
1848
|
+
console.log(`${checkmark} ${filePath} ${size}`);
|
|
1849
|
+
});
|
|
1850
|
+
console.log("");
|
|
1850
1851
|
}
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1852
|
+
|
|
1853
|
+
// src/fileWatcher.ts
|
|
1854
|
+
async function startFileWatcher(srcWatch, importMeta, finalConfig, destinationPath, buildCfg, bunServer) {
|
|
1855
|
+
const queue = getThrottledBuildQueue(finalConfig);
|
|
1856
|
+
await queue.add(async () => {
|
|
1857
|
+
await cleanBuildAndNotify(importMeta, finalConfig, destinationPath, buildCfg, bunServer, { filename: "Initial", eventType: "change" });
|
|
1858
|
+
});
|
|
1859
|
+
const watcher = watch(srcWatch, { recursive: true });
|
|
1860
|
+
try {
|
|
1861
|
+
for await (const event of watcher) {
|
|
1862
|
+
if (queue.pending > 0) {
|
|
1863
|
+
continue;
|
|
1864
|
+
}
|
|
1865
|
+
try {
|
|
1866
|
+
if (queue.size > 0) {
|
|
1867
|
+
queue.clear();
|
|
1868
|
+
}
|
|
1869
|
+
queue.add(async () => {
|
|
1870
|
+
await cleanBuildAndNotify(importMeta, finalConfig, destinationPath, buildCfg, bunServer, event);
|
|
1871
|
+
});
|
|
1872
|
+
} catch (e) {
|
|
1873
|
+
console.error("Error while processing file change", e);
|
|
1874
|
+
}
|
|
1875
|
+
}
|
|
1876
|
+
} catch (e) {
|
|
1877
|
+
if (e?.code !== "EPERM") {
|
|
1878
|
+
console.error("Error in file watcher", e);
|
|
1858
1879
|
}
|
|
1859
1880
|
}
|
|
1860
1881
|
}
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1882
|
+
|
|
1883
|
+
// src/configManager.ts
|
|
1884
|
+
var {$: $3 } = globalThis.Bun;
|
|
1885
|
+
import { readdir as readdir2 } from "fs/promises";
|
|
1886
|
+
import { resolve as resolve2 } from "path";
|
|
1887
|
+
|
|
1888
|
+
// src/templates/output.ejs
|
|
1889
|
+
var output_default = "./assets/output.ejs";
|
|
1890
|
+
|
|
1891
|
+
// src/templates/index.ejs
|
|
1892
|
+
var templates_default = "./assets/index.ejs";
|
|
1893
|
+
|
|
1894
|
+
// src/bunClientHmr.ts
|
|
1895
|
+
function hotReload() {
|
|
1896
|
+
if (!window.BUN_DEV_SERVER) {
|
|
1897
|
+
window.BUN_DEV_SERVER = [];
|
|
1865
1898
|
}
|
|
1866
|
-
const
|
|
1867
|
-
const
|
|
1868
|
-
|
|
1869
|
-
|
|
1899
|
+
const devServer = "[Bun Dev Server]";
|
|
1900
|
+
const connectAddress = "[REPLACE_ENDPOINT]";
|
|
1901
|
+
let foundServer = window.BUN_DEV_SERVER.find((server) => server.url === connectAddress);
|
|
1902
|
+
if (!foundServer) {
|
|
1903
|
+
foundServer = { url: connectAddress, socket: null };
|
|
1870
1904
|
}
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1905
|
+
if (foundServer.socket) {
|
|
1906
|
+
return;
|
|
1907
|
+
}
|
|
1908
|
+
console.log(devServer, "Connecting to Bun Dev Server at", connectAddress);
|
|
1909
|
+
foundServer.socket = new WebSocket(connectAddress);
|
|
1910
|
+
window.BUN_DEV_SERVER.push(foundServer);
|
|
1911
|
+
function errorHandler(err) {
|
|
1912
|
+
console.error(devServer, "ERROR", err);
|
|
1913
|
+
}
|
|
1914
|
+
function messageHandler(msg) {
|
|
1915
|
+
let parsed = msg.data;
|
|
1878
1916
|
try {
|
|
1879
|
-
|
|
1917
|
+
parsed = JSON.parse(msg.data);
|
|
1880
1918
|
} catch (e) {
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1919
|
+
}
|
|
1920
|
+
if (parsed?.type === "message") {
|
|
1921
|
+
console.log(devServer, parsed.message);
|
|
1922
|
+
return;
|
|
1923
|
+
}
|
|
1924
|
+
if (parsed?.type === "output") {
|
|
1925
|
+
console.table(devServer, parsed.message);
|
|
1926
|
+
return;
|
|
1927
|
+
}
|
|
1928
|
+
if (parsed?.type === "reload") {
|
|
1929
|
+
window.location.reload();
|
|
1930
|
+
return;
|
|
1931
|
+
}
|
|
1932
|
+
if (parsed?.type === "tscerror") {
|
|
1933
|
+
console.error(parsed.message);
|
|
1934
|
+
return;
|
|
1935
|
+
}
|
|
1936
|
+
if (parsed?.type === "error") {
|
|
1937
|
+
console.error(parsed.message);
|
|
1938
|
+
let newDiv = window.document.getElementById("bun-hmr-error");
|
|
1939
|
+
const divExists = !!newDiv;
|
|
1940
|
+
if (!newDiv) {
|
|
1941
|
+
newDiv = window.document.createElement("div");
|
|
1942
|
+
}
|
|
1943
|
+
newDiv.id = "bun-hmr-error";
|
|
1944
|
+
newDiv.innerText += parsed.message;
|
|
1945
|
+
if (!divExists) {
|
|
1946
|
+
window.document.body.appendChild(newDiv);
|
|
1885
1947
|
}
|
|
1948
|
+
return;
|
|
1886
1949
|
}
|
|
1887
|
-
}
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1950
|
+
}
|
|
1951
|
+
function closeHandler(ev) {
|
|
1952
|
+
console.warn(devServer, "Connection closed. Will retry in 5 seconds...");
|
|
1953
|
+
foundServer.socket?.removeEventListener("error", errorHandler);
|
|
1954
|
+
foundServer.socket?.removeEventListener("message", messageHandler);
|
|
1955
|
+
foundServer.socket?.removeEventListener("open", messageHandler);
|
|
1956
|
+
foundServer.socket?.removeEventListener("close", closeHandler);
|
|
1957
|
+
foundServer.socket = null;
|
|
1958
|
+
setTimeout(function() {
|
|
1959
|
+
console.log(devServer, "Attempting to reconnect...");
|
|
1960
|
+
hotReload();
|
|
1961
|
+
}, 5000);
|
|
1962
|
+
}
|
|
1963
|
+
function openHandler(ev) {
|
|
1964
|
+
console.log(devServer, "Connected to Bun Dev Server");
|
|
1965
|
+
}
|
|
1966
|
+
foundServer.socket.addEventListener("error", errorHandler);
|
|
1967
|
+
foundServer.socket.addEventListener("message", messageHandler);
|
|
1968
|
+
foundServer.socket.addEventListener("close", closeHandler);
|
|
1969
|
+
foundServer.socket.addEventListener("open", openHandler);
|
|
1970
|
+
}
|
|
1971
|
+
var DEFAULT_HMR_PATH = "/hmr-ws";
|
|
1972
|
+
function bunHotReload(bunServerConfig) {
|
|
1973
|
+
const socketPath = bunServerConfig.websocketPath || DEFAULT_HMR_PATH;
|
|
1974
|
+
const endPath = socketPath.startsWith("/") ? socketPath : `/${socketPath}`;
|
|
1975
|
+
const path = `${bunServerConfig.secure ? "wss" : "ws"}://localhost:${bunServerConfig.port}${endPath}`;
|
|
1976
|
+
return hotReload.toString().replace("[REPLACE_ENDPOINT]", path);
|
|
1977
|
+
}
|
|
1978
|
+
|
|
1979
|
+
// src/bunHmrPlugin.ts
|
|
1980
|
+
import { readFile as readFile2 } from "fs/promises";
|
|
1981
|
+
function bunHotReloadPlugin(config) {
|
|
1982
|
+
const bunHMRPlugin = {
|
|
1983
|
+
name: "hmr",
|
|
1984
|
+
target: "browser",
|
|
1985
|
+
setup(build2) {
|
|
1986
|
+
const entryPoints = [];
|
|
1987
|
+
const addedEnryPoints = new Set;
|
|
1988
|
+
build2.config.entrypoints.forEach((entry) => {
|
|
1989
|
+
let entryPath = entry.replace(/^\.*/, "");
|
|
1990
|
+
if (process.platform === "win32") {
|
|
1991
|
+
entryPath = entryPath.replace(/\//g, "\\");
|
|
1992
|
+
}
|
|
1993
|
+
entryPoints.push(entryPath);
|
|
1994
|
+
});
|
|
1995
|
+
build2.onLoad({ filter: /\.m?(t|j)sx?/ }, async (args) => {
|
|
1996
|
+
const contents = await readFile2(args.path, { encoding: "utf-8" });
|
|
1997
|
+
const isTSx = /\.m?tsx$/.test(args.path);
|
|
1998
|
+
const isJSx = /\.m?jsx$/.test(args.path);
|
|
1999
|
+
const isJS = /\.m?js$/.test(args.path);
|
|
2000
|
+
const isTS = /\.m?ts$/.test(args.path);
|
|
2001
|
+
const loader = isTSx ? "tsx" : isJSx ? "jsx" : isTS ? "ts" : isJS ? "js" : "text";
|
|
2002
|
+
const isEntry = entryPoints.some((entry) => args.path.endsWith(entry));
|
|
2003
|
+
if (!addedEnryPoints.has(args.path) && isEntry) {
|
|
2004
|
+
addedEnryPoints.add(args.path);
|
|
2005
|
+
return { contents: `import "bun-hot-reload"
|
|
2006
|
+
` + contents, loader };
|
|
2007
|
+
}
|
|
2008
|
+
return { contents, loader };
|
|
2009
|
+
});
|
|
2010
|
+
build2.onLoad({ filter: /./, namespace: "bun-hot-reload" }, async (args) => {
|
|
2011
|
+
return { contents: `(${bunHotReload(config)})()
|
|
2012
|
+
`, loader: "ts" };
|
|
2013
|
+
});
|
|
2014
|
+
build2.onResolve({ filter: /^bun-hot-reload$/ }, (args) => {
|
|
2015
|
+
return { path: args.path, namespace: "bun-hot-reload" };
|
|
2016
|
+
});
|
|
1891
2017
|
}
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
2018
|
+
};
|
|
2019
|
+
return bunHMRPlugin;
|
|
2020
|
+
}
|
|
2021
|
+
function getBunHMRFooter(config) {
|
|
2022
|
+
return `;(${bunHotReload(config)})();`;
|
|
2023
|
+
}
|
|
2024
|
+
|
|
2025
|
+
// src/configManager.ts
|
|
2026
|
+
var serveTemplate = await Bun.file(Bun.fileURLToPath(import.meta.resolve(output_default))).text();
|
|
2027
|
+
var indexTemplate = await Bun.file(Bun.fileURLToPath(import.meta.resolve(templates_default))).text();
|
|
2028
|
+
async function prepareConfiguration(serverConfig, importMeta) {
|
|
2029
|
+
const defaultConfig = {
|
|
2030
|
+
port: 3000,
|
|
2031
|
+
websocketPath: DEFAULT_HMR_PATH,
|
|
2032
|
+
serveOutputEjs: serveTemplate,
|
|
2033
|
+
serveIndexHtmlEjs: indexTemplate,
|
|
2034
|
+
createIndexHTML: true,
|
|
2035
|
+
tscConfigPath: resolve2(importMeta.dir, "./tsconfig.json"),
|
|
2036
|
+
broadcastBuildOutputToConsole: true,
|
|
2037
|
+
broadcastBuildOutputToClient: true
|
|
2038
|
+
};
|
|
2039
|
+
const finalConfig = { ...defaultConfig, ...serverConfig };
|
|
2040
|
+
if (serverConfig.tscConfigPath) {
|
|
2041
|
+
finalConfig.tscConfigPath = resolve2(importMeta.dir, serverConfig.tscConfigPath);
|
|
1895
2042
|
}
|
|
1896
|
-
if (!
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
2043
|
+
if (!finalConfig.watchDir) {
|
|
2044
|
+
throw new Error("watchDir must be set");
|
|
2045
|
+
}
|
|
2046
|
+
const servePart = finalConfig.buildConfig.outdir ?? finalConfig.servePath ?? "./dist";
|
|
2047
|
+
const serveDestination = resolve2(importMeta.dir, servePart);
|
|
2048
|
+
const watchDestination = resolve2(importMeta.dir, finalConfig.watchDir);
|
|
2049
|
+
const allEntries = serverConfig.buildConfig.entrypoints.splice(0, serverConfig.buildConfig.entrypoints.length);
|
|
2050
|
+
const resolvedEntries = allEntries.map((e) => resolve2(importMeta.dir, e));
|
|
2051
|
+
serverConfig.buildConfig.entrypoints = resolvedEntries;
|
|
2052
|
+
const destinationPath = serveDestination;
|
|
2053
|
+
const srcWatch = watchDestination;
|
|
2054
|
+
await ensureDestinationDirectory(destinationPath);
|
|
2055
|
+
const buncfg = {
|
|
2056
|
+
port: finalConfig.port,
|
|
2057
|
+
tls: finalConfig.tls,
|
|
2058
|
+
websocketPath: finalConfig.websocketPath,
|
|
2059
|
+
secure: finalConfig.tls !== undefined
|
|
2060
|
+
};
|
|
2061
|
+
const buildCfg = {
|
|
2062
|
+
...serverConfig.buildConfig,
|
|
2063
|
+
outdir: destinationPath
|
|
2064
|
+
};
|
|
2065
|
+
if (finalConfig.hotReload === "footer") {
|
|
2066
|
+
if (!buildCfg.footer) {
|
|
2067
|
+
buildCfg.footer = "";
|
|
1908
2068
|
}
|
|
2069
|
+
buildCfg.footer += getBunHMRFooter(buncfg);
|
|
1909
2070
|
}
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
}
|
|
1914
|
-
|
|
1915
|
-
return {
|
|
1916
|
-
requestPath: requestPath === "/" ? "" : requestPath,
|
|
1917
|
-
name: entry.name
|
|
1918
|
-
};
|
|
1919
|
-
});
|
|
1920
|
-
const files = allEntries.filter((entry) => entry.isFile()).map((entry) => {
|
|
1921
|
-
return {
|
|
1922
|
-
requestPath: requestPath === "/" ? "" : requestPath,
|
|
1923
|
-
name: entry.name
|
|
1924
|
-
};
|
|
1925
|
-
});
|
|
1926
|
-
const rnd = import_ejs.render(finalConfig.serveOutputEjs, { dirs, files });
|
|
1927
|
-
finalConfig.logRequests && console.log(`${200} ${req.url}`);
|
|
1928
|
-
return withCORSHeaders(new Response(rnd, { headers: { "Content-Type": "text/html" } }), req);
|
|
1929
|
-
} catch (err) {
|
|
1930
|
-
return handleErrorResponse(req, err);
|
|
2071
|
+
if (finalConfig.hotReload === "plugin") {
|
|
2072
|
+
if (!buildCfg.plugins) {
|
|
2073
|
+
buildCfg.plugins = [];
|
|
2074
|
+
}
|
|
2075
|
+
buildCfg.plugins.push(bunHotReloadPlugin(buncfg));
|
|
1931
2076
|
}
|
|
2077
|
+
const userBeforeBuild = finalConfig.beforeBuild;
|
|
2078
|
+
finalConfig.beforeBuild = async (env) => {
|
|
2079
|
+
if (serverConfig.cleanServePath) {
|
|
2080
|
+
await cleanDirectory(env.destinationPath);
|
|
2081
|
+
}
|
|
2082
|
+
await userBeforeBuild?.(env);
|
|
2083
|
+
};
|
|
2084
|
+
return {
|
|
2085
|
+
finalConfig,
|
|
2086
|
+
destinationPath,
|
|
2087
|
+
srcWatch,
|
|
2088
|
+
buildCfg
|
|
2089
|
+
};
|
|
1932
2090
|
}
|
|
1933
|
-
async function
|
|
2091
|
+
async function ensureDestinationDirectory(destinationPath) {
|
|
1934
2092
|
try {
|
|
1935
|
-
await
|
|
1936
|
-
return true;
|
|
2093
|
+
await readdir2(destinationPath);
|
|
1937
2094
|
} catch (e) {
|
|
1938
|
-
if (e
|
|
1939
|
-
|
|
2095
|
+
if (e.code === "ENOENT") {
|
|
2096
|
+
console.log("Directory not found, creating it...");
|
|
2097
|
+
try {
|
|
2098
|
+
await $3`mkdir ${destinationPath}`;
|
|
2099
|
+
} catch (e2) {
|
|
2100
|
+
console.error("Unable to create directory", e2);
|
|
2101
|
+
}
|
|
2102
|
+
} else {
|
|
2103
|
+
throw e;
|
|
1940
2104
|
}
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
2105
|
+
}
|
|
2106
|
+
}
|
|
2107
|
+
|
|
2108
|
+
// src/server.ts
|
|
2109
|
+
async function startBunDevServer(serverConfig, importMeta) {
|
|
2110
|
+
const { finalConfig, destinationPath, srcWatch, buildCfg } = await prepareConfiguration(serverConfig, importMeta);
|
|
2111
|
+
const protocol = finalConfig.tls ? "https" : "http";
|
|
2112
|
+
const serverUrl = `${protocol}://localhost:${finalConfig.port}`;
|
|
2113
|
+
const isTestMode = process.env.BUN_TEST === "true" || typeof Bun !== "undefined" && Bun.main.includes("test");
|
|
2114
|
+
if (!isTestMode) {
|
|
2115
|
+
console.log(import_picocolors3.default.bold(import_picocolors3.default.green("\uD83D\uDE80 Server running at")) + " " + import_picocolors3.default.cyan(import_picocolors3.default.underline(serverUrl)));
|
|
2116
|
+
}
|
|
2117
|
+
let bunServer;
|
|
2118
|
+
try {
|
|
2119
|
+
bunServer = Bun.serve({
|
|
2120
|
+
port: finalConfig.port,
|
|
2121
|
+
development: {
|
|
2122
|
+
console: true,
|
|
2123
|
+
hmr: true,
|
|
2124
|
+
chromeDevToolsAutomaticWorkspaceFolders: true
|
|
2125
|
+
},
|
|
2126
|
+
tls: finalConfig.tls,
|
|
2127
|
+
routes: {
|
|
2128
|
+
"/favicon.ico": withCORSHeaders(new Response("", { status: 404 })),
|
|
2129
|
+
...finalConfig.routes,
|
|
2130
|
+
...staticAssetRoutes
|
|
2131
|
+
},
|
|
2132
|
+
async fetch(req, server) {
|
|
2133
|
+
if (req.method === "OPTIONS") {
|
|
2134
|
+
finalConfig.logRequests && console.log(`${200} ${req.url} OPTIONS`);
|
|
2135
|
+
return withCORSHeaders(new Response("", { status: 200 }), req);
|
|
2136
|
+
}
|
|
2137
|
+
if (req.url.toLowerCase().endsWith(finalConfig.websocketPath)) {
|
|
2138
|
+
finalConfig.logRequests && console.log(`${req.url} Socket Upgrade`);
|
|
2139
|
+
if (server.upgrade(req)) {
|
|
2140
|
+
return withCORSHeaders(new Response("", { status: 200 }), req);
|
|
2141
|
+
}
|
|
2142
|
+
}
|
|
2143
|
+
const url = new URL(req.url);
|
|
2144
|
+
let requestPath = url.pathname;
|
|
2145
|
+
return handlePathRequest(requestPath, req, finalConfig, destinationPath);
|
|
2146
|
+
},
|
|
2147
|
+
websocket: {
|
|
2148
|
+
open(ws) {
|
|
2149
|
+
ws.subscribe("message");
|
|
2150
|
+
},
|
|
2151
|
+
message(ws, message) {
|
|
2152
|
+
},
|
|
2153
|
+
sendPings: true
|
|
2154
|
+
}
|
|
2155
|
+
});
|
|
2156
|
+
} catch (error) {
|
|
2157
|
+
if (isTestMode) {
|
|
2158
|
+
return;
|
|
2159
|
+
}
|
|
2160
|
+
throw error;
|
|
2161
|
+
}
|
|
2162
|
+
if (bunServer) {
|
|
2163
|
+
await startFileWatcher(srcWatch, importMeta, finalConfig, destinationPath, buildCfg, bunServer);
|
|
1944
2164
|
}
|
|
1945
2165
|
}
|
|
1946
2166
|
export {
|