@antdevx/vite-plugin-hmr-sync
Version:
A Vite plugin for synchronizing HMR across multiple workspaces in a monorepo setup.
229 lines (228 loc) • 6.77 kB
JavaScript
import { exec as w } from "node:child_process";
import { readFileSync as O } from "fs";
import { resolve as v } from "path";
import N from "node:http";
import b from "node:https";
import { URL as _ } from "node:url";
const C = "/on-child-rebuild", S = { type: "full-reload", path: "*" }, h = "vite-plugin-notify-on-rebuild", y = "vite-plugin-listen-for-remote-rebuilds", I = "vite-plugin-listen-for-remote-rebuilds-error";
function g(e, t = !1) {
const o = `[${h}][${e}]`;
return {
log: (r) => !t && console.log(`${o} ${r}`),
info: (r) => !t && console.info(`${o} ${r}`),
warn: (r) => !t && console.warn(`${o} ${r}`),
error: (r, n) => !t && console.error(`${o} ${r}`, n)
};
}
function T(e) {
const t = g(e.appName || "unknown");
return new Promise((o, r) => {
t.log("Starting build...");
const { buildCommand: n } = e, i = `${n} ${process.cwd()}/dist/spa`;
w(i, (a, d, s) => {
if (a) {
t.error(`Error during build: ${a.message}`), r(a);
return;
}
s && t.warn(s.toString()), t.log(d.toString()), t.log("Build completed successfully!"), o(d);
});
});
}
function A(e) {
const t = g(e.appName || "unknown");
return new Promise((o, r) => {
t.log("Starting server...");
const { port: n, cache: i = "0", cors: a = !0 } = e, d = `quasar serve ${process.cwd()}/dist/spa ${n ? "--port " + n : ""} ${i ? "--cache " + i : ""} ${a ? "--cors" : ""}`, s = w(d, (l, c, f) => {
if (l)
return t.error(`Error starting server: ${l.message}`), r(l);
f && t.warn(f.toString()), t.log(`Server started: ${c}`);
});
s.stdout && s.stdout.on("data", (l) => {
t.log(`Server log: ${l}`);
}), s.stderr && s.stderr.on("data", (l) => {
t.error(`Server error: ${l}`);
}), o(s);
});
}
function P(e, t, o, r, n) {
const {
onRebuild: i,
hotPayload: a = S,
allowedApps: d,
suppressLogs: s = !1
} = r, l = g("host", s);
try {
const c = t.headers.host ? `http://${t.headers.host}` : "http://localhost", u = new URL(t.url || "", c).searchParams.get("app") || "unknown";
if (Array.isArray(d) && !d.includes(u)) {
s || l.warn(`[${n}] ⚠️ Rebuild from unlisted app "${u}" ignored.`), o.writeHead(403, { "Content-Type": "text/plain" }), o.end(`[${n}] App "${u}" not allowed`);
return;
}
s || l.log(`🔁 [${n}] Received rebuild signal from "${u}"`), e.ws.send(a), o.writeHead(200, { "Content-Type": "text/plain" }), o.end(`[${n}] Reload triggered`), i == null || i(u, e);
} catch (c) {
s || l.error(`[${n}] ❌ Error handling request:`, c), o.writeHead(500, { "Content-Type": "text/plain" }), o.end(`[${n}] Internal error`);
return;
}
}
function q() {
const e = v(process.cwd(), "nodemon.json");
try {
const t = O(e, "utf-8");
return JSON.parse(t).hmrSync || {};
} catch {
return console.warn("[vite-plugin-hmr-sync] Failed to read nodemon.json config"), {};
}
}
function L(e) {
const t = typeof e == "string" ? { appName: e } : e;
if (!t.appName)
throw new Error("`appName` is required");
return {
appName: t.appName,
hostUrl: t.hostUrl || "http://localhost:5000",
endpoint: t.endpoint || "/on-child-rebuild",
method: t.method || "GET",
notifyOnSuccessOnly: t.notifyOnSuccessOnly ?? !0,
suppressLogs: t.suppressLogs ?? !1
};
}
function B(e) {
const { endpoint: t, hotPayload: o, allowedApps: r, onRebuild: n, suppressLogs: i } = e;
return {
hotPayload: o || S,
endpoint: t || C,
onRebuild: n,
allowedApps: r || [],
suppressLogs: i || !1
};
}
function F(e) {
const {
port: t = "5000",
cors: o = !0,
cache: r = "0",
notify: n = !0,
hostUrl: i = "http://localhost:5000",
appName: a = "my-app",
buildCommand: d = "quasar build",
serveCommand: s = "quasar serve"
} = e;
return {
port: t,
cors: o,
cache: r,
notify: n,
hostUrl: i,
appName: a,
buildCommand: d,
serveCommand: s
};
}
function R(e, t) {
const { appName: o, hostUrl: r, endpoint: n, method: i, notifyOnSuccessOnly: a, suppressLogs: d } = e, s = g(o, d);
if (a && t) {
s.error("Build failed, skipping notification.", (t == null ? void 0 : t.message) || t);
return;
}
const l = `${r}${n}?app=${encodeURIComponent(o)}`;
let c;
try {
c = new _(l);
} catch (p) {
s.error(`Invalid URL: "${l}"`, p);
return;
}
const f = c.protocol === "https:" ? b : N, u = {
method: i.toUpperCase(),
hostname: c.hostname,
port: c.port || (c.protocol === "https:" ? 443 : 80),
path: c.pathname + c.search,
headers: {
"User-Agent": `vite-plugin-notify-on-rebuild/${o}`
}
};
s.info(`🔔 Sending ${i.toUpperCase()} request to ${c.href}`);
const $ = f.request(u, (p) => {
let m = "";
p.on("data", (E) => {
m += E;
}), p.on("end", () => {
p.statusCode && p.statusCode >= 200 && p.statusCode < 300 ? s.info(`✅ Host notified (${p.statusCode})`) : s.warn(`⚠️ Host responded with ${p.statusCode}. Response: ${m.slice(0, 200)}${m.length > 200 ? "..." : ""}`);
});
});
$.on("error", (p) => s.error("Notification failed:", p)), $.end();
}
const U = "1.1.1";
function j(e) {
let t;
try {
t = L(e);
} catch (o) {
return g("unknown").error(`[${h}] ❌`, o), { name: I };
}
return {
api: {
get options() {
return t;
},
version: U
},
enforce: "post",
name: h,
buildEnd(o) {
R(t, o);
}
};
}
function z(e = {}) {
const t = B(e), { hotPayload: o, endpoint: r } = t;
return {
api: {
get options() {
return t;
},
version: U
},
enforce: "post",
name: y,
configureServer(n) {
n.middlewares.use(t.endpoint, (i, a) => {
P(
n,
i,
a,
{ ...e, hotPayload: o },
y
);
});
}
};
}
async function M() {
const e = q(), t = F(e), { appName: o, hostUrl: r } = t;
await T(t), await A(t).then((n) => {
var i;
return (i = n.stdout) == null || i.on("data", (a) => {
if (console.log(a.toString()), a.includes("Listening at") && e.notify) {
console.log("Server is running, sending notification...");
const d = L({
appName: o,
hostUrl: r || "http://localhost:5000"
});
R(d);
}
}), console.log("Server started successfully!"), n;
});
}
export {
F as createBuildAndServeContext,
P as handleRemoteRebuildRequest,
z as listenForRemoteRebuilds,
B as normalizeListenOptions,
L as normalizeNotifyOptions,
j as notifyOnRebuild,
q as readNodemonHmrSyncConfig,
R as sendNotification,
T as startBuild,
M as startBuildAndServer,
A as startServer
};