bun-dev-server 0.4.0 → 0.5.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 +6 -0
- package/dist/index.js +175 -63
- package/dist/tsChecker.d.ts +1 -1
- package/package.json +5 -3
package/dist/bunServeConfig.d.ts
CHANGED
|
@@ -3,6 +3,12 @@ export interface BunDevServerConfig extends Partial<BunDevServerSocketConfig> {
|
|
|
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;
|
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;
|
|
@@ -892,26 +970,36 @@ import { watch, readdir, exists, readFile as readFile2 } 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,7 +1180,7 @@ 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({
|
|
@@ -1090,11 +1205,11 @@ async function startBunDevServer(serverConfig) {
|
|
|
1090
1205
|
}
|
|
1091
1206
|
const url = new URL(req.url);
|
|
1092
1207
|
const requestPath = url.pathname;
|
|
1093
|
-
const objThere = await exists(
|
|
1208
|
+
const objThere = await exists(destinationPath + requestPath);
|
|
1094
1209
|
let isDirectory = false;
|
|
1095
1210
|
if (objThere) {
|
|
1096
1211
|
try {
|
|
1097
|
-
await readFile2(
|
|
1212
|
+
await readFile2(destinationPath + requestPath);
|
|
1098
1213
|
} catch (e) {
|
|
1099
1214
|
if (e.code === "EISDIR") {
|
|
1100
1215
|
isDirectory = true;
|
|
@@ -1109,7 +1224,7 @@ async function startBunDevServer(serverConfig) {
|
|
|
1109
1224
|
}
|
|
1110
1225
|
if (!isDirectory) {
|
|
1111
1226
|
try {
|
|
1112
|
-
const fl = Bun.file(
|
|
1227
|
+
const fl = Bun.file(destinationPath + requestPath);
|
|
1113
1228
|
const response = new Response(fl);
|
|
1114
1229
|
augumentHeaders(req, response);
|
|
1115
1230
|
return response;
|
|
@@ -1124,7 +1239,7 @@ async function startBunDevServer(serverConfig) {
|
|
|
1124
1239
|
}
|
|
1125
1240
|
}
|
|
1126
1241
|
try {
|
|
1127
|
-
const allEntries = await readdir(
|
|
1242
|
+
const allEntries = await readdir(destinationPath + requestPath, {
|
|
1128
1243
|
withFileTypes: true
|
|
1129
1244
|
});
|
|
1130
1245
|
const dirs = allEntries.filter((entry) => entry.isDirectory()).map((entry) => {
|
|
@@ -1158,55 +1273,52 @@ async function startBunDevServer(serverConfig) {
|
|
|
1158
1273
|
sendPings: true
|
|
1159
1274
|
}
|
|
1160
1275
|
});
|
|
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);
|
|
1276
|
+
debouncedbuildAndNotify(finalConfig, destinationPath, buildCfg, bunServer, { filename: "Initial", eventType: "change" });
|
|
1168
1277
|
const watcher = watch(srcWatch, { recursive: true });
|
|
1169
1278
|
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
|
-
}
|
|
1279
|
+
debouncedbuildAndNotify(finalConfig, destinationPath, buildCfg, bunServer, event);
|
|
1183
1280
|
}
|
|
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 }));
|
|
1281
|
+
}
|
|
1282
|
+
var debouncedbuildAndNotify = import_debounce.default(async (finalConfig, destinationPath, buildCfg, bunServer, event) => {
|
|
1283
|
+
if (finalConfig.cleanServePath) {
|
|
1284
|
+
await cleanDirectory(destinationPath);
|
|
1198
1285
|
}
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
}
|
|
1208
|
-
|
|
1286
|
+
const output = await Bun.build(buildCfg);
|
|
1287
|
+
publishOutputLogs(bunServer, output, event);
|
|
1288
|
+
publishIndexHTML(destinationPath, finalConfig.serveOutputHtml, output, event);
|
|
1289
|
+
if (finalConfig.writeManifest) {
|
|
1290
|
+
writeManifest(output, destinationPath, finalConfig.manifestWithHash, finalConfig.manifestName);
|
|
1291
|
+
}
|
|
1292
|
+
const tscSuccess = await performTSC(finalConfig);
|
|
1293
|
+
if (finalConfig.reloadOnChange && tscSuccess) {
|
|
1294
|
+
bunServer.publish("message", JSON.stringify({ type: "reload" }));
|
|
1295
|
+
}
|
|
1296
|
+
}, watchDelay, { immediate: true });
|
|
1297
|
+
function publishOutputLogs(bunServer, output, event) {
|
|
1298
|
+
output.logs.forEach(console.log);
|
|
1299
|
+
bunServer.publish("message", JSON.stringify({ type: "message", message: `[Bun HMR] ${event.filename} ${event.eventType}` }));
|
|
1300
|
+
const outTable = output.outputs.filter((o) => o.kind !== "sourcemap").map((o) => {
|
|
1301
|
+
const a = Bun.pathToFileURL(o.path);
|
|
1302
|
+
const fileName = a.href.substring(a.href.lastIndexOf("/") + 1);
|
|
1303
|
+
return {
|
|
1304
|
+
name: fileName,
|
|
1305
|
+
path: o.path,
|
|
1306
|
+
size: convertBytes(o.size)
|
|
1307
|
+
};
|
|
1308
|
+
});
|
|
1309
|
+
console.table(outTable);
|
|
1310
|
+
bunServer.publish("message", JSON.stringify({ type: "output", message: outTable }));
|
|
1311
|
+
}
|
|
1312
|
+
function publishIndexHTML(destinationPath, template, output, _event) {
|
|
1313
|
+
const eps = output.outputs.filter((o) => o.kind === "entry-point");
|
|
1314
|
+
const hashedImports = [];
|
|
1315
|
+
for (const ep of eps) {
|
|
1316
|
+
const basePathUrl = Bun.pathToFileURL(destinationPath);
|
|
1317
|
+
const epUrl = Bun.pathToFileURL(ep.path);
|
|
1318
|
+
const hashedImport = `${epUrl.href.replace(basePathUrl.href, "")}?${ep.hash}`;
|
|
1319
|
+
hashedImports.push(hashedImport);
|
|
1209
1320
|
}
|
|
1321
|
+
Bun.write(destinationPath + "/index.html", import_ejs.render(template, { hashedImports }));
|
|
1210
1322
|
}
|
|
1211
1323
|
function augumentHeaders(request, response) {
|
|
1212
1324
|
response.headers.set("Access-Control-Allow-Origin", request.headers.get("origin") ?? "*");
|
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,7 +21,7 @@
|
|
|
19
21
|
"exports": {
|
|
20
22
|
".": "./dist/index.js"
|
|
21
23
|
},
|
|
22
|
-
"version": "0.
|
|
24
|
+
"version": "0.5.0",
|
|
23
25
|
"module": "index.ts",
|
|
24
26
|
"type": "module",
|
|
25
27
|
"license": "MIT",
|
|
@@ -27,14 +29,14 @@
|
|
|
27
29
|
"serve": "bun --hot ./serve.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
35
|
"typescript": "^5.7.2"
|
|
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
|
}
|