bun-dev-server 0.4.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bunServeConfig.d.ts +10 -2
- package/dist/index.js +260 -129
- package/dist/tsChecker.d.ts +1 -1
- package/package.json +7 -5
package/dist/bunServeConfig.d.ts
CHANGED
|
@@ -1,13 +1,20 @@
|
|
|
1
1
|
import { type BuildConfig, type TLSOptions } from "bun";
|
|
2
|
-
export interface BunDevServerConfig extends Partial<
|
|
2
|
+
export interface BunDevServerConfig extends Partial<BunServeConfig> {
|
|
3
3
|
port: number;
|
|
4
4
|
buildConfig: BuildConfig;
|
|
5
5
|
watchDir: string;
|
|
6
|
+
/**
|
|
7
|
+
* The delay in milliseconds to wait before starting a new build once a file has changed.
|
|
8
|
+
* Used in debounce function, in case many changes happen in file system at once
|
|
9
|
+
* @default 1000
|
|
10
|
+
*/
|
|
11
|
+
watchDelay?: number;
|
|
6
12
|
enableTSC?: boolean;
|
|
7
13
|
writeManifest?: boolean;
|
|
8
14
|
manifestName?: string;
|
|
9
15
|
manifestWithHash?: boolean;
|
|
10
16
|
hotReload?: "plugin" | "footer";
|
|
17
|
+
logRequests?: boolean;
|
|
11
18
|
reloadOnChange?: boolean;
|
|
12
19
|
/**
|
|
13
20
|
* The path to the directory to serve files from.
|
|
@@ -19,8 +26,9 @@ export interface BunDevServerConfig extends Partial<BunDevServerSocketConfig> {
|
|
|
19
26
|
serveOutputEjs?: string;
|
|
20
27
|
serveOutputHtml?: string;
|
|
21
28
|
}
|
|
22
|
-
export interface
|
|
29
|
+
export interface BunServeConfig {
|
|
23
30
|
port: number;
|
|
24
31
|
tls?: TLSOptions;
|
|
25
32
|
websocketPath?: string;
|
|
33
|
+
static?: Record<`/${string}`, Response>;
|
|
26
34
|
}
|
package/dist/index.js
CHANGED
|
@@ -846,6 +846,84 @@ var require_picocolors = __commonJS((exports, module) => {
|
|
|
846
846
|
module.exports.createColors = createColors;
|
|
847
847
|
});
|
|
848
848
|
|
|
849
|
+
// node_modules/debounce/index.js
|
|
850
|
+
var require_debounce = __commonJS((exports, module) => {
|
|
851
|
+
function debounce(function_, wait = 100, options = {}) {
|
|
852
|
+
if (typeof function_ !== "function") {
|
|
853
|
+
throw new TypeError(`Expected the first parameter to be a function, got \`${typeof function_}\`.`);
|
|
854
|
+
}
|
|
855
|
+
if (wait < 0) {
|
|
856
|
+
throw new RangeError("`wait` must not be negative.");
|
|
857
|
+
}
|
|
858
|
+
const { immediate } = typeof options === "boolean" ? { immediate: options } : options;
|
|
859
|
+
let storedContext;
|
|
860
|
+
let storedArguments;
|
|
861
|
+
let timeoutId;
|
|
862
|
+
let timestamp;
|
|
863
|
+
let result;
|
|
864
|
+
function run() {
|
|
865
|
+
const callContext = storedContext;
|
|
866
|
+
const callArguments = storedArguments;
|
|
867
|
+
storedContext = undefined;
|
|
868
|
+
storedArguments = undefined;
|
|
869
|
+
result = function_.apply(callContext, callArguments);
|
|
870
|
+
return result;
|
|
871
|
+
}
|
|
872
|
+
function later() {
|
|
873
|
+
const last = Date.now() - timestamp;
|
|
874
|
+
if (last < wait && last >= 0) {
|
|
875
|
+
timeoutId = setTimeout(later, wait - last);
|
|
876
|
+
} else {
|
|
877
|
+
timeoutId = undefined;
|
|
878
|
+
if (!immediate) {
|
|
879
|
+
result = run();
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
const debounced = function(...arguments_) {
|
|
884
|
+
if (storedContext && this !== storedContext && Object.getPrototypeOf(this) === Object.getPrototypeOf(storedContext)) {
|
|
885
|
+
throw new Error("Debounced method called with different contexts of the same prototype.");
|
|
886
|
+
}
|
|
887
|
+
storedContext = this;
|
|
888
|
+
storedArguments = arguments_;
|
|
889
|
+
timestamp = Date.now();
|
|
890
|
+
const callNow = immediate && !timeoutId;
|
|
891
|
+
if (!timeoutId) {
|
|
892
|
+
timeoutId = setTimeout(later, wait);
|
|
893
|
+
}
|
|
894
|
+
if (callNow) {
|
|
895
|
+
result = run();
|
|
896
|
+
}
|
|
897
|
+
return result;
|
|
898
|
+
};
|
|
899
|
+
Object.defineProperty(debounced, "isPending", {
|
|
900
|
+
get() {
|
|
901
|
+
return timeoutId !== undefined;
|
|
902
|
+
}
|
|
903
|
+
});
|
|
904
|
+
debounced.clear = () => {
|
|
905
|
+
if (!timeoutId) {
|
|
906
|
+
return;
|
|
907
|
+
}
|
|
908
|
+
clearTimeout(timeoutId);
|
|
909
|
+
timeoutId = undefined;
|
|
910
|
+
};
|
|
911
|
+
debounced.flush = () => {
|
|
912
|
+
if (!timeoutId) {
|
|
913
|
+
return;
|
|
914
|
+
}
|
|
915
|
+
debounced.trigger();
|
|
916
|
+
};
|
|
917
|
+
debounced.trigger = () => {
|
|
918
|
+
result = run();
|
|
919
|
+
debounced.clear();
|
|
920
|
+
};
|
|
921
|
+
return debounced;
|
|
922
|
+
}
|
|
923
|
+
exports.debounce = debounce;
|
|
924
|
+
module.exports = debounce;
|
|
925
|
+
});
|
|
926
|
+
|
|
849
927
|
// src/server.ts
|
|
850
928
|
var import_ejs = __toESM(require_ejs(), 1);
|
|
851
929
|
var Bun = globalThis.Bun;
|
|
@@ -888,30 +966,40 @@ var indexHTMLTemplate_default = `<!DOCTYPE html>\r
|
|
|
888
966
|
`;
|
|
889
967
|
|
|
890
968
|
// src/server.ts
|
|
891
|
-
import { watch, readdir,
|
|
969
|
+
import { watch, readdir, access, readFile as readFile2, constants } from "fs/promises";
|
|
892
970
|
|
|
893
971
|
// src/bunClientHmr.ts
|
|
894
972
|
function hotReload() {
|
|
895
|
-
if (window.
|
|
973
|
+
if (!window.BUN_DEV_SERVER) {
|
|
974
|
+
window.BUN_DEV_SERVER = [];
|
|
975
|
+
}
|
|
976
|
+
const devServer = "[Bun Dev Server]";
|
|
977
|
+
const connectAddress = "[REPLACE_ENDPOINT]";
|
|
978
|
+
let foundServer = window.BUN_DEV_SERVER.find((server) => server.url === connectAddress);
|
|
979
|
+
if (!foundServer) {
|
|
980
|
+
foundServer = { url: connectAddress, socket: null };
|
|
981
|
+
}
|
|
982
|
+
if (foundServer.socket) {
|
|
896
983
|
return;
|
|
897
984
|
}
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
985
|
+
console.log(devServer, "Connecting to Bun Dev Server at", connectAddress);
|
|
986
|
+
foundServer.socket = new WebSocket(connectAddress);
|
|
987
|
+
window.BUN_DEV_SERVER.push(foundServer);
|
|
988
|
+
function errorHandler(err) {
|
|
989
|
+
console.error(devServer, "ERROR", err);
|
|
990
|
+
}
|
|
991
|
+
function messageHandler(msg) {
|
|
904
992
|
let parsed = msg.data;
|
|
905
993
|
try {
|
|
906
994
|
parsed = JSON.parse(msg.data);
|
|
907
995
|
} catch (e) {
|
|
908
996
|
}
|
|
909
997
|
if (parsed?.type === "message") {
|
|
910
|
-
console.log(parsed.message);
|
|
998
|
+
console.log(devServer, parsed.message);
|
|
911
999
|
return;
|
|
912
1000
|
}
|
|
913
1001
|
if (parsed?.type === "output") {
|
|
914
|
-
console.table(parsed.message);
|
|
1002
|
+
console.table(devServer, parsed.message);
|
|
915
1003
|
return;
|
|
916
1004
|
}
|
|
917
1005
|
if (parsed?.type === "reload") {
|
|
@@ -932,7 +1020,26 @@ function hotReload() {
|
|
|
932
1020
|
}
|
|
933
1021
|
return;
|
|
934
1022
|
}
|
|
935
|
-
}
|
|
1023
|
+
}
|
|
1024
|
+
function closeHandler(ev) {
|
|
1025
|
+
console.warn(devServer, "Connection closed. Will retry in 5 seconds...");
|
|
1026
|
+
foundServer.socket?.removeEventListener("error", errorHandler);
|
|
1027
|
+
foundServer.socket?.removeEventListener("message", messageHandler);
|
|
1028
|
+
foundServer.socket?.removeEventListener("open", messageHandler);
|
|
1029
|
+
foundServer.socket?.removeEventListener("close", closeHandler);
|
|
1030
|
+
foundServer.socket = null;
|
|
1031
|
+
setTimeout(function() {
|
|
1032
|
+
console.log(devServer, "Attempting to reconnect...");
|
|
1033
|
+
hotReload();
|
|
1034
|
+
}, 5000);
|
|
1035
|
+
}
|
|
1036
|
+
function openHandler(ev) {
|
|
1037
|
+
console.log(devServer, "Connected to Bun Dev Server");
|
|
1038
|
+
}
|
|
1039
|
+
foundServer.socket.addEventListener("error", errorHandler);
|
|
1040
|
+
foundServer.socket.addEventListener("message", messageHandler);
|
|
1041
|
+
foundServer.socket.addEventListener("close", closeHandler);
|
|
1042
|
+
foundServer.socket.addEventListener("open", openHandler);
|
|
936
1043
|
}
|
|
937
1044
|
var DEFAULT_HMR_PATH = "/hmr-ws";
|
|
938
1045
|
function bunHotReload(bunServerConfig) {
|
|
@@ -1013,14 +1120,19 @@ async function performTSC(finalConfig) {
|
|
|
1013
1120
|
const tsc = await $`tsc`.nothrow().quiet();
|
|
1014
1121
|
if (tsc.exitCode === 0) {
|
|
1015
1122
|
console.log(import_picocolors.default.bgGreen("\u2714 [SUCCESS]"), "TSC check passed");
|
|
1123
|
+
return true;
|
|
1016
1124
|
} else {
|
|
1017
1125
|
console.log(import_picocolors.default.bgRed("\u2718 [ERROR]"), `\r
|
|
1018
1126
|
${tsc.stdout.toString()}`);
|
|
1127
|
+
return false;
|
|
1019
1128
|
}
|
|
1020
1129
|
}
|
|
1130
|
+
return true;
|
|
1021
1131
|
}
|
|
1022
1132
|
|
|
1023
1133
|
// src/server.ts
|
|
1134
|
+
var import_debounce = __toESM(require_debounce(), 1);
|
|
1135
|
+
var watchDelay = 1000;
|
|
1024
1136
|
async function startBunDevServer(serverConfig) {
|
|
1025
1137
|
const defaultConfig = {
|
|
1026
1138
|
port: 3000,
|
|
@@ -1029,20 +1141,23 @@ async function startBunDevServer(serverConfig) {
|
|
|
1029
1141
|
serveOutputHtml: indexHTMLTemplate_default
|
|
1030
1142
|
};
|
|
1031
1143
|
const finalConfig = { ...defaultConfig, ...serverConfig };
|
|
1144
|
+
if (finalConfig.watchDelay) {
|
|
1145
|
+
watchDelay = finalConfig.watchDelay;
|
|
1146
|
+
}
|
|
1032
1147
|
if (!finalConfig.watchDir) {
|
|
1033
1148
|
throw new Error("watchDir must be set");
|
|
1034
1149
|
}
|
|
1035
1150
|
const serveDestination = finalConfig.buildConfig.outdir ?? finalConfig.servePath ?? "dist";
|
|
1036
1151
|
const bunDestinationPath = Bun.pathToFileURL(serveDestination);
|
|
1037
1152
|
const bunWatchDirPath = Bun.pathToFileURL(finalConfig.watchDir);
|
|
1038
|
-
const
|
|
1153
|
+
const destinationPath = process.platform === "win32" ? bunDestinationPath.pathname.substring(1) : bunDestinationPath.pathname;
|
|
1039
1154
|
const srcWatch = process.platform === "win32" ? bunWatchDirPath.pathname.substring(1) : bunWatchDirPath.pathname;
|
|
1040
1155
|
try {
|
|
1041
|
-
await readdir(
|
|
1156
|
+
await readdir(destinationPath);
|
|
1042
1157
|
} catch (e) {
|
|
1043
1158
|
if (e.code === "ENOENT") {
|
|
1044
1159
|
console.log("Directory not found, creating it...");
|
|
1045
|
-
await $2`mkdir ${
|
|
1160
|
+
await $2`mkdir ${destinationPath}`;
|
|
1046
1161
|
} else {
|
|
1047
1162
|
throw e;
|
|
1048
1163
|
}
|
|
@@ -1050,7 +1165,7 @@ async function startBunDevServer(serverConfig) {
|
|
|
1050
1165
|
const buncfg = { port: finalConfig.port, tls: finalConfig.tls, websocketPath: finalConfig.websocketPath };
|
|
1051
1166
|
const buildCfg = {
|
|
1052
1167
|
...serverConfig.buildConfig,
|
|
1053
|
-
outdir:
|
|
1168
|
+
outdir: destinationPath
|
|
1054
1169
|
};
|
|
1055
1170
|
if (finalConfig.hotReload === "footer") {
|
|
1056
1171
|
if (!buildCfg.footer) {
|
|
@@ -1065,89 +1180,34 @@ async function startBunDevServer(serverConfig) {
|
|
|
1065
1180
|
buildCfg.plugins.push(bunHotReloadPlugin(buncfg));
|
|
1066
1181
|
}
|
|
1067
1182
|
if (serverConfig.cleanServePath) {
|
|
1068
|
-
await cleanDirectory(
|
|
1183
|
+
await cleanDirectory(destinationPath);
|
|
1069
1184
|
}
|
|
1070
1185
|
console.log("Starting Bun Dev Server on port", finalConfig.port);
|
|
1071
1186
|
const bunServer = Bun.serve({
|
|
1072
1187
|
port: finalConfig.port,
|
|
1073
1188
|
development: true,
|
|
1074
1189
|
tls: finalConfig.tls,
|
|
1190
|
+
static: {
|
|
1191
|
+
"/favicon.ico": withCORSHeaders(new Response("", { status: 404 })),
|
|
1192
|
+
...finalConfig.static
|
|
1193
|
+
},
|
|
1075
1194
|
async fetch(req, server) {
|
|
1076
1195
|
if (req.method === "OPTIONS") {
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
return response;
|
|
1080
|
-
}
|
|
1081
|
-
if (req.url.toLowerCase().endsWith("/favicon.ico")) {
|
|
1082
|
-
const response = new Response("", { status: 404 });
|
|
1083
|
-
augumentHeaders(req, response);
|
|
1084
|
-
return response;
|
|
1196
|
+
finalConfig.logRequests && console.log(`${200} ${req.url} OPTIONS`);
|
|
1197
|
+
return withCORSHeaders(new Response("", { status: 200 }), req);
|
|
1085
1198
|
}
|
|
1086
1199
|
if (req.url.toLowerCase().endsWith(finalConfig.websocketPath)) {
|
|
1200
|
+
finalConfig.logRequests && console.log(`${req.url} Socket Upgrade`);
|
|
1087
1201
|
if (server.upgrade(req)) {
|
|
1088
1202
|
return;
|
|
1089
1203
|
}
|
|
1090
1204
|
}
|
|
1091
1205
|
const url = new URL(req.url);
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
if (objThere) {
|
|
1096
|
-
try {
|
|
1097
|
-
await readFile2(dst + requestPath);
|
|
1098
|
-
} catch (e) {
|
|
1099
|
-
if (e.code === "EISDIR") {
|
|
1100
|
-
isDirectory = true;
|
|
1101
|
-
} else {
|
|
1102
|
-
throw e;
|
|
1103
|
-
}
|
|
1104
|
-
}
|
|
1105
|
-
} else {
|
|
1106
|
-
const response = new Response("", { status: 404 });
|
|
1107
|
-
augumentHeaders(req, response);
|
|
1108
|
-
return response;
|
|
1109
|
-
}
|
|
1110
|
-
if (!isDirectory) {
|
|
1111
|
-
try {
|
|
1112
|
-
const fl = Bun.file(dst + requestPath);
|
|
1113
|
-
const response = new Response(fl);
|
|
1114
|
-
augumentHeaders(req, response);
|
|
1115
|
-
return response;
|
|
1116
|
-
} catch (e) {
|
|
1117
|
-
if (e.code === "ENOENT") {
|
|
1118
|
-
const response = new Response("", { status: 404 });
|
|
1119
|
-
augumentHeaders(req, response);
|
|
1120
|
-
return response;
|
|
1121
|
-
} else {
|
|
1122
|
-
throw e;
|
|
1123
|
-
}
|
|
1124
|
-
}
|
|
1125
|
-
}
|
|
1126
|
-
try {
|
|
1127
|
-
const allEntries = await readdir(dst + requestPath, {
|
|
1128
|
-
withFileTypes: true
|
|
1129
|
-
});
|
|
1130
|
-
const dirs = allEntries.filter((entry) => entry.isDirectory()).map((entry) => {
|
|
1131
|
-
return {
|
|
1132
|
-
requestPath: requestPath === "/" ? "" : requestPath,
|
|
1133
|
-
name: entry.name
|
|
1134
|
-
};
|
|
1135
|
-
});
|
|
1136
|
-
const files = allEntries.filter((entry) => entry.isFile()).map((entry) => {
|
|
1137
|
-
return {
|
|
1138
|
-
requestPath: requestPath === "/" ? "" : requestPath,
|
|
1139
|
-
name: entry.name
|
|
1140
|
-
};
|
|
1141
|
-
});
|
|
1142
|
-
const rnd = import_ejs.render(finalConfig.serveOutputEjs, { dirs, files });
|
|
1143
|
-
const response = new Response(rnd, { headers: { "Content-Type": "text/html" } });
|
|
1144
|
-
augumentHeaders(req, response);
|
|
1145
|
-
return response;
|
|
1146
|
-
} catch {
|
|
1147
|
-
const response = new Response("Not Found", { status: 404 });
|
|
1148
|
-
augumentHeaders(req, response);
|
|
1149
|
-
return response;
|
|
1206
|
+
let requestPath = url.pathname;
|
|
1207
|
+
if (requestPath.toLowerCase() === "/index.html") {
|
|
1208
|
+
requestPath = "/";
|
|
1150
1209
|
}
|
|
1210
|
+
return handlePathRequest(requestPath, req, finalConfig, destinationPath);
|
|
1151
1211
|
},
|
|
1152
1212
|
websocket: {
|
|
1153
1213
|
open(ws) {
|
|
@@ -1158,60 +1218,63 @@ async function startBunDevServer(serverConfig) {
|
|
|
1158
1218
|
sendPings: true
|
|
1159
1219
|
}
|
|
1160
1220
|
});
|
|
1161
|
-
|
|
1162
|
-
publishOutputLogs(output, { filename: "Initial", eventType: "change" });
|
|
1163
|
-
publishIndexHTML(output, { filename: "Initial", eventType: "change" });
|
|
1164
|
-
if (finalConfig.writeManifest) {
|
|
1165
|
-
writeManifest(output, dst, finalConfig.manifestWithHash, finalConfig.manifestName);
|
|
1166
|
-
}
|
|
1167
|
-
await performTSC(finalConfig);
|
|
1221
|
+
debouncedbuildAndNotify(finalConfig, destinationPath, buildCfg, bunServer, { filename: "Initial", eventType: "change" });
|
|
1168
1222
|
const watcher = watch(srcWatch, { recursive: true });
|
|
1169
1223
|
for await (const event of watcher) {
|
|
1170
|
-
|
|
1171
|
-
await cleanDirectory(dst);
|
|
1172
|
-
}
|
|
1173
|
-
const output2 = await Bun.build(buildCfg);
|
|
1174
|
-
publishOutputLogs(output2, event);
|
|
1175
|
-
publishIndexHTML(output2, event);
|
|
1176
|
-
if (finalConfig.writeManifest) {
|
|
1177
|
-
writeManifest(output2, dst, finalConfig.manifestWithHash, finalConfig.manifestName);
|
|
1178
|
-
}
|
|
1179
|
-
await performTSC(finalConfig);
|
|
1180
|
-
if (finalConfig.reloadOnChange) {
|
|
1181
|
-
bunServer.publish("message", JSON.stringify({ type: "reload" }));
|
|
1182
|
-
}
|
|
1224
|
+
debouncedbuildAndNotify(finalConfig, destinationPath, buildCfg, bunServer, event);
|
|
1183
1225
|
}
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
const a = Bun.pathToFileURL(o.path);
|
|
1189
|
-
const fileName = a.href.substring(a.href.lastIndexOf("/") + 1);
|
|
1190
|
-
return {
|
|
1191
|
-
name: fileName,
|
|
1192
|
-
path: o.path,
|
|
1193
|
-
size: convertBytes(o.size)
|
|
1194
|
-
};
|
|
1195
|
-
});
|
|
1196
|
-
console.table(outTable);
|
|
1197
|
-
bunServer.publish("message", JSON.stringify({ type: "output", message: outTable }));
|
|
1226
|
+
}
|
|
1227
|
+
var debouncedbuildAndNotify = import_debounce.default(async (finalConfig, destinationPath, buildCfg, bunServer, event) => {
|
|
1228
|
+
if (finalConfig.cleanServePath) {
|
|
1229
|
+
await cleanDirectory(destinationPath);
|
|
1198
1230
|
}
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
const epUrl = Bun.pathToFileURL(ep.path);
|
|
1205
|
-
const hashedImport = `${epUrl.href.replace(basePathUrl.href, "")}?${ep.hash}`;
|
|
1206
|
-
hashedImports.push(hashedImport);
|
|
1207
|
-
}
|
|
1208
|
-
Bun.write(dst + "/index.html", import_ejs.render(finalConfig.serveOutputHtml, { hashedImports }));
|
|
1231
|
+
const output = await Bun.build(buildCfg);
|
|
1232
|
+
publishOutputLogs(bunServer, output, event);
|
|
1233
|
+
publishIndexHTML(destinationPath, finalConfig.serveOutputHtml, output, event);
|
|
1234
|
+
if (finalConfig.writeManifest) {
|
|
1235
|
+
writeManifest(output, destinationPath, finalConfig.manifestWithHash, finalConfig.manifestName);
|
|
1209
1236
|
}
|
|
1237
|
+
const tscSuccess = await performTSC(finalConfig);
|
|
1238
|
+
if (finalConfig.reloadOnChange && tscSuccess) {
|
|
1239
|
+
bunServer.publish("message", JSON.stringify({ type: "reload" }));
|
|
1240
|
+
}
|
|
1241
|
+
}, watchDelay, { immediate: true });
|
|
1242
|
+
function handleErrorResponse(req, err) {
|
|
1243
|
+
const msg = `Error while processing request ${req.url}`;
|
|
1244
|
+
console.error(msg, err);
|
|
1245
|
+
return withCORSHeaders(new Response(msg, { status: 500 }), req);
|
|
1246
|
+
}
|
|
1247
|
+
function publishOutputLogs(bunServer, output, event) {
|
|
1248
|
+
output.logs.forEach(console.log);
|
|
1249
|
+
bunServer.publish("message", JSON.stringify({ type: "message", message: `[Bun HMR] ${event.filename} ${event.eventType}` }));
|
|
1250
|
+
const outTable = output.outputs.filter((o) => o.kind !== "sourcemap").map((o) => {
|
|
1251
|
+
const a = Bun.pathToFileURL(o.path);
|
|
1252
|
+
const fileName = a.href.substring(a.href.lastIndexOf("/") + 1);
|
|
1253
|
+
return {
|
|
1254
|
+
name: fileName,
|
|
1255
|
+
path: o.path,
|
|
1256
|
+
size: convertBytes(o.size)
|
|
1257
|
+
};
|
|
1258
|
+
});
|
|
1259
|
+
console.table(outTable);
|
|
1260
|
+
bunServer.publish("message", JSON.stringify({ type: "output", message: outTable }));
|
|
1261
|
+
}
|
|
1262
|
+
function publishIndexHTML(destinationPath, template, output, _event) {
|
|
1263
|
+
const eps = output.outputs.filter((o) => o.kind === "entry-point");
|
|
1264
|
+
const hashedImports = [];
|
|
1265
|
+
for (const ep of eps) {
|
|
1266
|
+
const basePathUrl = Bun.pathToFileURL(destinationPath);
|
|
1267
|
+
const epUrl = Bun.pathToFileURL(ep.path);
|
|
1268
|
+
const hashedImport = `${epUrl.href.replace(basePathUrl.href, "")}?${ep.hash}`;
|
|
1269
|
+
hashedImports.push(hashedImport);
|
|
1270
|
+
}
|
|
1271
|
+
Bun.write(destinationPath + "/index.html", import_ejs.render(template, { hashedImports }));
|
|
1210
1272
|
}
|
|
1211
|
-
function
|
|
1212
|
-
response.headers.set("Access-Control-Allow-Origin", request
|
|
1213
|
-
response.headers.set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
|
|
1273
|
+
function withCORSHeaders(response, request) {
|
|
1274
|
+
response.headers.set("Access-Control-Allow-Origin", request?.headers.get("origin") ?? "*");
|
|
1275
|
+
response.headers.set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, PATCH, OPTIONS");
|
|
1214
1276
|
response.headers.set("Access-Control-Allow-Credentials", "true");
|
|
1277
|
+
return response;
|
|
1215
1278
|
}
|
|
1216
1279
|
async function cleanDirectory(dst) {
|
|
1217
1280
|
const { stderr, exitCode } = await $2`rm -rf ${dst}/*`.nothrow();
|
|
@@ -1235,6 +1298,74 @@ function convertBytes(bytes) {
|
|
|
1235
1298
|
}
|
|
1236
1299
|
return (bytes / Math.pow(1024, i)).toFixed(1) + " " + sizes[i];
|
|
1237
1300
|
}
|
|
1301
|
+
async function handlePathRequest(requestPath, req, finalConfig, destinationPath) {
|
|
1302
|
+
const fsPath = destinationPath + requestPath;
|
|
1303
|
+
const objThere = await checkObjectExists(fsPath, req);
|
|
1304
|
+
let isDirectory = false;
|
|
1305
|
+
if (objThere) {
|
|
1306
|
+
try {
|
|
1307
|
+
await readFile2(fsPath);
|
|
1308
|
+
} catch (e) {
|
|
1309
|
+
if (e.code === "EISDIR") {
|
|
1310
|
+
isDirectory = true;
|
|
1311
|
+
} else {
|
|
1312
|
+
throw e;
|
|
1313
|
+
}
|
|
1314
|
+
}
|
|
1315
|
+
} else {
|
|
1316
|
+
finalConfig.logRequests && console.log(`${404} ${req.url}`);
|
|
1317
|
+
return withCORSHeaders(new Response("", { status: 404 }), req);
|
|
1318
|
+
}
|
|
1319
|
+
if (!isDirectory) {
|
|
1320
|
+
try {
|
|
1321
|
+
const fl = Bun.file(fsPath);
|
|
1322
|
+
finalConfig.logRequests && console.log(`${200} ${req.url}`);
|
|
1323
|
+
return withCORSHeaders(new Response(fl), req);
|
|
1324
|
+
} catch (e) {
|
|
1325
|
+
if (e?.code === "ENOENT") {
|
|
1326
|
+
finalConfig.logRequests && console.log(`${404} ${req.url}`);
|
|
1327
|
+
return withCORSHeaders(new Response("", { status: 404 }), req);
|
|
1328
|
+
} else {
|
|
1329
|
+
return handleErrorResponse(req, e);
|
|
1330
|
+
}
|
|
1331
|
+
}
|
|
1332
|
+
}
|
|
1333
|
+
try {
|
|
1334
|
+
const allEntries = await readdir(fsPath, {
|
|
1335
|
+
withFileTypes: true
|
|
1336
|
+
});
|
|
1337
|
+
const dirs = allEntries.filter((entry) => entry.isDirectory()).map((entry) => {
|
|
1338
|
+
return {
|
|
1339
|
+
requestPath: requestPath === "/" ? "" : requestPath,
|
|
1340
|
+
name: entry.name
|
|
1341
|
+
};
|
|
1342
|
+
});
|
|
1343
|
+
const files = allEntries.filter((entry) => entry.isFile()).map((entry) => {
|
|
1344
|
+
return {
|
|
1345
|
+
requestPath: requestPath === "/" ? "" : requestPath,
|
|
1346
|
+
name: entry.name
|
|
1347
|
+
};
|
|
1348
|
+
});
|
|
1349
|
+
const rnd = import_ejs.render(finalConfig.serveOutputEjs, { dirs, files });
|
|
1350
|
+
finalConfig.logRequests && console.log(`${200} ${req.url}`);
|
|
1351
|
+
return withCORSHeaders(new Response(rnd, { headers: { "Content-Type": "text/html" } }), req);
|
|
1352
|
+
} catch (err) {
|
|
1353
|
+
return handleErrorResponse(req, err);
|
|
1354
|
+
}
|
|
1355
|
+
}
|
|
1356
|
+
async function checkObjectExists(fsPath, req) {
|
|
1357
|
+
try {
|
|
1358
|
+
await access(fsPath, constants.R_OK);
|
|
1359
|
+
return true;
|
|
1360
|
+
} catch (e) {
|
|
1361
|
+
if (e?.code === "ENOENT") {
|
|
1362
|
+
return false;
|
|
1363
|
+
}
|
|
1364
|
+
const msg = `Error while accessing path ${fsPath}`;
|
|
1365
|
+
console.error(msg, e);
|
|
1366
|
+
return false;
|
|
1367
|
+
}
|
|
1368
|
+
}
|
|
1238
1369
|
export {
|
|
1239
1370
|
startBunDevServer,
|
|
1240
1371
|
getBunHMRFooter,
|
package/dist/tsChecker.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import type { BunDevServerConfig } from "./bunServeConfig";
|
|
2
|
-
export declare function performTSC(finalConfig: BunDevServerConfig): Promise<
|
|
2
|
+
export declare function performTSC(finalConfig: BunDevServerConfig): Promise<boolean>;
|
package/package.json
CHANGED
|
@@ -9,6 +9,8 @@
|
|
|
9
9
|
"keywords": [
|
|
10
10
|
"bun",
|
|
11
11
|
"devserver",
|
|
12
|
+
"dev",
|
|
13
|
+
"server",
|
|
12
14
|
"hmr",
|
|
13
15
|
"reload",
|
|
14
16
|
"hot"
|
|
@@ -19,22 +21,22 @@
|
|
|
19
21
|
"exports": {
|
|
20
22
|
".": "./dist/index.js"
|
|
21
23
|
},
|
|
22
|
-
"version": "0.
|
|
24
|
+
"version": "0.6.0",
|
|
23
25
|
"module": "index.ts",
|
|
24
26
|
"type": "module",
|
|
25
27
|
"license": "MIT",
|
|
26
28
|
"scripts": {
|
|
27
|
-
"
|
|
29
|
+
"build": "bun run ./build.ts"
|
|
28
30
|
},
|
|
29
31
|
"devDependencies": {
|
|
30
|
-
"@types/bun": "latest"
|
|
31
|
-
"bun": "^1.1.36"
|
|
32
|
+
"@types/bun": "latest"
|
|
32
33
|
},
|
|
33
34
|
"peerDependencies": {
|
|
34
|
-
"typescript": "^5.7.
|
|
35
|
+
"typescript": "^5.7.3"
|
|
35
36
|
},
|
|
36
37
|
"dependencies": {
|
|
37
38
|
"@types/ejs": "^3.1.5",
|
|
39
|
+
"debounce": "^2.2.0",
|
|
38
40
|
"ejs": "^3.1.10",
|
|
39
41
|
"picocolors": "^1.1.1"
|
|
40
42
|
}
|