fix live reload
This commit is contained in:
parent
07d0005958
commit
3ccb5bb087
|
|
@ -1 +0,0 @@
|
||||||
var g=Object.create;var e=Object.defineProperty;var h=Object.getOwnPropertyDescriptor;var i=Object.getOwnPropertyNames;var j=Object.getPrototypeOf,k=Object.prototype.hasOwnProperty;var m=(b,a)=>()=>(a||b((a={exports:{}}).exports,a),a.exports),n=(b,a)=>{for(var c in a)e(b,c,{get:a[c],enumerable:!0})},l=(b,a,c,f)=>{if(a&&typeof a=="object"||typeof a=="function")for(let d of i(a))!k.call(b,d)&&d!==c&&e(b,d,{get:()=>a[d],enumerable:!(f=h(a,d))||f.enumerable});return b};var o=(b,a,c)=>(c=b!=null?g(j(b)):{},l(a||!b||!b.__esModule?e(c,"default",{value:b,enumerable:!0}):c,b));export{m as a,n as b,o as c};
|
|
||||||
File diff suppressed because one or more lines are too long
|
|
@ -0,0 +1 @@
|
||||||
|
import{d as o,e as f,g as s}from"./chunk-ACJPWHEH.js";o();s();f();
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
import"./chunk-5TBO732O.js";
|
|
||||||
File diff suppressed because one or more lines are too long
|
|
@ -10,6 +10,7 @@ import { gzipAsync } from "../entity/zlib";
|
||||||
import { sendWS } from "../sync-handler";
|
import { sendWS } from "../sync-handler";
|
||||||
import { SyncConnection, SyncType } from "../type";
|
import { SyncConnection, SyncType } from "../type";
|
||||||
import { validate } from "uuid";
|
import { validate } from "uuid";
|
||||||
|
|
||||||
export const page_load: SAction["page"]["load"] = async function (
|
export const page_load: SAction["page"]["load"] = async function (
|
||||||
this: SyncConnection,
|
this: SyncConnection,
|
||||||
id: string
|
id: string
|
||||||
|
|
@ -45,24 +46,48 @@ export const page_load: SAction["page"]["load"] = async function (
|
||||||
delete g.route_cache[snap.id_site];
|
delete g.route_cache[snap.id_site];
|
||||||
}
|
}
|
||||||
|
|
||||||
const client_ids = new Set<string>();
|
const found = user.active.findAll({ page_id: id });
|
||||||
user.active.findAll({ page_id: id }).forEach((e) => {
|
|
||||||
client_ids.add(e.client_id);
|
|
||||||
});
|
|
||||||
|
|
||||||
client_ids.forEach((client_id) => {
|
if (!g.preview_page_timeout) g.preview_page_timeout = {};
|
||||||
|
clearTimeout(g.preview_page_timeout[id]);
|
||||||
|
g.preview_page_timeout[id] = setTimeout(() => {
|
||||||
|
let json = doc.toJSON();
|
||||||
|
for (const f of found) {
|
||||||
|
const client_id = f.client_id;
|
||||||
|
const ws = conns.get(client_id)?.ws;
|
||||||
|
|
||||||
|
if (client_id && ws) {
|
||||||
|
if (!f.user_id) {
|
||||||
|
console.log(client_id);
|
||||||
|
sendWS(ws, {
|
||||||
|
type: SyncType.Event,
|
||||||
|
event: "page_changed",
|
||||||
|
data: json,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 1000);
|
||||||
|
|
||||||
|
for (const f of found) {
|
||||||
|
const client_id = f.client_id;
|
||||||
|
const ws = conns.get(client_id)?.ws;
|
||||||
|
if (client_id && ws) {
|
||||||
|
if (!!f.user_id) {
|
||||||
|
if (ws) {
|
||||||
if (origin !== um) {
|
if (origin !== um) {
|
||||||
if (client_id === origin) return;
|
if (client_id === origin) return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ws = conns.get(client_id)?.ws;
|
|
||||||
if (ws)
|
|
||||||
sendWS(ws, {
|
sendWS(ws, {
|
||||||
type: SyncType.Event,
|
type: SyncType.Event,
|
||||||
event: "remote_svlocal",
|
event: "remote_svlocal",
|
||||||
data: { type: "page", sv_local, id },
|
data: { type: "page", sv_local, id },
|
||||||
});
|
});
|
||||||
});
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -85,6 +85,9 @@ export const initFrontEnd = async (
|
||||||
async setup(setup) {
|
async setup(setup) {
|
||||||
try {
|
try {
|
||||||
setup.onEnd(async (res) => {
|
setup.onEnd(async (res) => {
|
||||||
|
const client_ids = user.active
|
||||||
|
.findAll({ site_id: id_site })
|
||||||
|
.map((e) => e.client_id);
|
||||||
if (res.errors.length > 0) {
|
if (res.errors.length > 0) {
|
||||||
await codeError(
|
await codeError(
|
||||||
id_site,
|
id_site,
|
||||||
|
|
@ -92,13 +95,6 @@ export const initFrontEnd = async (
|
||||||
"\n\n"
|
"\n\n"
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
} else {
|
|
||||||
await codeError(id_site, "");
|
|
||||||
|
|
||||||
const client_ids = new Set<string>();
|
|
||||||
user.active.findAll({ site_id: id_site }).forEach((e) => {
|
|
||||||
client_ids.add(e.client_id);
|
|
||||||
});
|
|
||||||
|
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
client_ids.forEach((client_id) => {
|
client_ids.forEach((client_id) => {
|
||||||
|
|
@ -107,13 +103,26 @@ export const initFrontEnd = async (
|
||||||
sendWS(ws, {
|
sendWS(ws, {
|
||||||
type: SyncType.Event,
|
type: SyncType.Event,
|
||||||
event: "code_changes",
|
event: "code_changes",
|
||||||
data: { ts: now, mode: "frontend" },
|
data: { ts: now, mode: "frontend", status: "error" },
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
await codeError(id_site, "");
|
||||||
|
|
||||||
await $`rm -rf ${out_dir_switch}`.quiet();
|
await $`rm -rf ${out_dir_switch}`.quiet();
|
||||||
await $`mv ${out_dir} ${out_dir_switch}`.quiet();
|
await $`mv ${out_dir} ${out_dir_switch}`.quiet();
|
||||||
await $`mv ${out_dir_temp} ${out_dir}`.quiet();
|
await $`mv ${out_dir_temp} ${out_dir}`.quiet();
|
||||||
|
|
||||||
|
const now = Date.now();
|
||||||
|
client_ids.forEach((client_id) => {
|
||||||
|
const ws = conns.get(client_id)?.ws;
|
||||||
|
if (ws)
|
||||||
|
sendWS(ws, {
|
||||||
|
type: SyncType.Event,
|
||||||
|
event: "code_changes",
|
||||||
|
data: { ts: now, mode: "frontend", status: "ok" },
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
@ -123,6 +132,24 @@ export const initFrontEnd = async (
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
const broadcastLoading = async () => {
|
||||||
|
const client_ids = user.active
|
||||||
|
.findAll({ site_id: id_site })
|
||||||
|
.map((e) => e.client_id);
|
||||||
|
|
||||||
|
const now = Date.now();
|
||||||
|
|
||||||
|
client_ids.forEach((client_id) => {
|
||||||
|
const ws = conns.get(client_id)?.ws;
|
||||||
|
if (ws)
|
||||||
|
sendWS(ws, {
|
||||||
|
type: SyncType.Event,
|
||||||
|
event: "code_changes",
|
||||||
|
data: { ts: now, mode: "frontend", status: "building" },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
code.internal.frontend[id_site] = {
|
code.internal.frontend[id_site] = {
|
||||||
ctx: build_ctx,
|
ctx: build_ctx,
|
||||||
timeout: null,
|
timeout: null,
|
||||||
|
|
@ -135,7 +162,7 @@ export const initFrontEnd = async (
|
||||||
async (event, filename) => {
|
async (event, filename) => {
|
||||||
const fe = code.internal.frontend[id_site];
|
const fe = code.internal.frontend[id_site];
|
||||||
const srv = code.internal.server[id_site];
|
const srv = code.internal.server[id_site];
|
||||||
if (filename?.startsWith("node_modules")) return;
|
if (filename?.startsWith("node_modules") || filename?.startsWith("typings")) return;
|
||||||
if (
|
if (
|
||||||
filename?.endsWith(".tsx") ||
|
filename?.endsWith(".tsx") ||
|
||||||
filename?.endsWith(".ts") ||
|
filename?.endsWith(".ts") ||
|
||||||
|
|
@ -144,10 +171,14 @@ export const initFrontEnd = async (
|
||||||
) {
|
) {
|
||||||
if (typeof fe !== "undefined" && !fe.rebuilding) {
|
if (typeof fe !== "undefined" && !fe.rebuilding) {
|
||||||
fe.rebuilding = true;
|
fe.rebuilding = true;
|
||||||
|
clearTimeout(fe.timeout);
|
||||||
|
fe.timeout = setTimeout(async () => {
|
||||||
try {
|
try {
|
||||||
|
broadcastLoading();
|
||||||
await fe.ctx.rebuild();
|
await fe.ctx.rebuild();
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
fe.rebuilding = false;
|
fe.rebuilding = false;
|
||||||
|
}, 500);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof srv !== "undefined" && !srv.rebuilding && srv.ctx) {
|
if (typeof srv !== "undefined" && !srv.rebuilding && srv.ctx) {
|
||||||
|
|
|
||||||
|
|
@ -28,12 +28,9 @@ export const loadComponent = async (comp_id: string, sync?: SyncConnection) => {
|
||||||
|
|
||||||
const sv_local = await gzipAsync(update);
|
const sv_local = await gzipAsync(update);
|
||||||
|
|
||||||
user.active.findAll({ comp_id: comp_id });
|
const client_ids = user.active
|
||||||
|
.findAll({ comp_id: comp_id })
|
||||||
const client_ids = new Set<string>();
|
.map((e) => e.client_id);
|
||||||
user.active.findAll({ comp_id: comp_id }).forEach((e) => {
|
|
||||||
client_ids.add(e.client_id);
|
|
||||||
});
|
|
||||||
|
|
||||||
client_ids.forEach((client_id) => {
|
client_ids.forEach((client_id) => {
|
||||||
if (origin !== um) {
|
if (origin !== um) {
|
||||||
|
|
|
||||||
|
|
@ -1,53 +1,85 @@
|
||||||
import { dir } from "dir";
|
|
||||||
import { IndexedMap } from "../../../../web/src/utils/sync/idx-map";
|
|
||||||
|
|
||||||
const defaultConf = {
|
const defaultConf = {
|
||||||
site_id: "",
|
site_id: "",
|
||||||
page_id: "",
|
page_id: "",
|
||||||
};
|
};
|
||||||
export type UserConf = typeof defaultConf;
|
export type UserConf = typeof defaultConf;
|
||||||
|
|
||||||
export const user = {
|
export type TUser = {
|
||||||
active: IndexedMap.create<
|
select?: "" | "comp" | "item" | "section" | "text";
|
||||||
{
|
client_id: string;
|
||||||
user_id: string;
|
user_id: string;
|
||||||
site_id: string;
|
site_id: string;
|
||||||
page_id: string;
|
page_id: string;
|
||||||
comp_id?: string;
|
comp_id: string | string[];
|
||||||
client_id: string;
|
};
|
||||||
},
|
|
||||||
"client_id"
|
|
||||||
>("client_id"),
|
|
||||||
conf: {
|
|
||||||
_db: {} as any,
|
|
||||||
init() {
|
|
||||||
return this._db;
|
|
||||||
},
|
|
||||||
async getOrCreate(id: string) {
|
|
||||||
let res = this._db[id];
|
|
||||||
|
|
||||||
if (!res || !res.id) {
|
const g = global as unknown as { _active_user: any; _conf_user: any };
|
||||||
this._db[id] = structuredClone(defaultConf);
|
|
||||||
res = this._db[id];
|
if (!g._conf_user) {
|
||||||
|
g._conf_user = {};
|
||||||
}
|
}
|
||||||
return res as UserConf;
|
if (!g._active_user) {
|
||||||
|
g._active_user = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
export const user = {
|
||||||
|
conf: {
|
||||||
|
db: g._conf_user as Record<string, UserConf>,
|
||||||
|
init() {},
|
||||||
|
async getOrCreate(user_id: string) {
|
||||||
|
if (!this.db[user_id]) this.db[user_id] = { ...defaultConf };
|
||||||
|
return this.db[user_id];
|
||||||
|
},
|
||||||
|
|
||||||
|
async set(user_id: string, key: keyof UserConf, value: any) {
|
||||||
|
const conf = await this.getOrCreate(user_id);
|
||||||
|
conf[key] = value;
|
||||||
},
|
},
|
||||||
get(user_id: string) {
|
get(user_id: string) {
|
||||||
return this._db[user_id];
|
if (!this.db[user_id]) this.db[user_id] = { ...defaultConf };
|
||||||
|
return this.db[user_id];
|
||||||
},
|
},
|
||||||
async set<T extends keyof UserConf>(
|
},
|
||||||
user_id: string,
|
active: {
|
||||||
key: T,
|
db: g._active_user as TUser[],
|
||||||
value: UserConf[T]
|
comps: {} as Record<string, Set<string>>,
|
||||||
) {
|
findAll(where: Partial<TUser>) {
|
||||||
let current = this.get(user_id);
|
return this.db.filter((e: any) => {
|
||||||
if (!current) {
|
for (const [k, v] of Object.entries(where)) {
|
||||||
this._db[user_id] = structuredClone(defaultConf)
|
if (k === "comp_id" && where.client_id) {
|
||||||
current = this.get(user_id);
|
if (!this.comps[where.client_id].has(v as any)) return false;
|
||||||
|
} else if (e[k] !== v) return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
delAll(where: Partial<TUser>) {
|
||||||
|
const res = this.findAll(where);
|
||||||
|
this.db = this.db.filter((e) => !res.includes(e));
|
||||||
|
},
|
||||||
|
add(user: Partial<TUser>) {
|
||||||
|
let found = null;
|
||||||
|
if (user.client_id) {
|
||||||
|
found = this.db.find((e: any) => {
|
||||||
|
return e.client_id === user.client_id;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (current) {
|
if (found) {
|
||||||
this._db[user_id] = { ...current, [key]: value }
|
for (const [k, v] of Object.entries(user)) {
|
||||||
|
(found as any)[k] = v;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.db.push(user as any);
|
||||||
|
found = user;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof found.comp_id === "string") {
|
||||||
|
let cid = found.client_id || "";
|
||||||
|
if (cid && !this.comps[cid]) {
|
||||||
|
this.comps[cid] = new Set();
|
||||||
|
}
|
||||||
|
this.comps[cid].add(found.comp_id);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
import { ServerWebSocket } from "bun";
|
||||||
|
import { WSData } from "../../../../../pkgs/core/server/create";
|
||||||
|
import { user } from "../entity/user";
|
||||||
|
|
||||||
|
export const previewLiveReload = (
|
||||||
|
ws: ServerWebSocket<WSData>,
|
||||||
|
msg:
|
||||||
|
| { mode: "init"; data: { client_id: string; site_id: string } }
|
||||||
|
| {
|
||||||
|
mode: "listen";
|
||||||
|
data: { type: "page"; id: string; client_id: string };
|
||||||
|
}
|
||||||
|
) => {
|
||||||
|
if (msg.mode === "init") {
|
||||||
|
user.active.add({
|
||||||
|
client_id: msg.data.client_id,
|
||||||
|
site_id: msg.data.site_id,
|
||||||
|
});
|
||||||
|
} else if (msg.mode === "listen") {
|
||||||
|
if (msg.data.type === "page") {
|
||||||
|
user.active.add({
|
||||||
|
client_id: msg.data.client_id,
|
||||||
|
page_id: msg.data.id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
@ -10,6 +10,7 @@ import { loadSitePage } from "./editor/load-sitepage";
|
||||||
import { conns, wconns } from "./entity/conn";
|
import { conns, wconns } from "./entity/conn";
|
||||||
import { UserConf, user } from "./entity/user";
|
import { UserConf, user } from "./entity/user";
|
||||||
import { SyncType } from "./type";
|
import { SyncType } from "./type";
|
||||||
|
import { previewLiveReload } from "./preview/live-reload";
|
||||||
const packr = new Packr({ structuredClone: true });
|
const packr = new Packr({ structuredClone: true });
|
||||||
|
|
||||||
export const sendWS = (ws: ServerWebSocket<WSData>, msg: any) => {
|
export const sendWS = (ws: ServerWebSocket<WSData>, msg: any) => {
|
||||||
|
|
@ -43,6 +44,9 @@ export const syncHandler: WebSocketHandler<WSData> = {
|
||||||
const conn = conns.get(conn_id);
|
const conn = conns.get(conn_id);
|
||||||
if (conn) {
|
if (conn) {
|
||||||
const msg = packr.unpack(Buffer.from(raw));
|
const msg = packr.unpack(Buffer.from(raw));
|
||||||
|
if (msg.type === "preview") {
|
||||||
|
previewLiveReload(ws, msg);
|
||||||
|
}
|
||||||
if (msg.type === SyncType.UserID) {
|
if (msg.type === SyncType.UserID) {
|
||||||
const { user_id, page_id, site_id } = msg;
|
const { user_id, page_id, site_id } = msg;
|
||||||
conn.user_id = user_id;
|
conn.user_id = user_id;
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
import getTime from "date-fns/getTime";
|
|
||||||
import { PG } from "./ed-global";
|
|
||||||
import { format } from "date-fns";
|
import { format } from "date-fns";
|
||||||
|
import { PG } from "./ed-global";
|
||||||
|
|
||||||
export const loadFrontEnd = async (p: PG, ts?: number) => {
|
export const loadFrontEnd = async (p: PG, ts?: number) => {
|
||||||
|
|
||||||
const id_site = p.site.id;
|
const id_site = p.site.id;
|
||||||
const url = `/prod/${id_site}/_prasi/code/index.js?ts=${ts}`;
|
const url = `/prod/${id_site}/_prasi/code/index.js?ts=${ts}`;
|
||||||
const fn = new Function(
|
const fn = new Function(
|
||||||
|
|
@ -17,12 +17,12 @@ import("${url}")
|
||||||
try {
|
try {
|
||||||
fn((exports: any) => {
|
fn((exports: any) => {
|
||||||
const w = window as any;
|
const w = window as any;
|
||||||
|
|
||||||
for (const [k, v] of Object.entries(exports)) {
|
for (const [k, v] of Object.entries(exports)) {
|
||||||
w[k] = v;
|
w[k] = v;
|
||||||
p.site_exports[k] = v;
|
p.site_exports[k] = v;
|
||||||
}
|
}
|
||||||
resolve(exports);
|
resolve(exports);
|
||||||
console.log(`🚧 Code updated from vscode - ${format(Date.now(), "HH:mm:ss")}`);
|
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log("Failed to load site code", e);
|
console.log("Failed to load site code", e);
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ import { EmptySite, PG } from "./ed-global";
|
||||||
import { reloadPage } from "./ed-route";
|
import { reloadPage } from "./ed-route";
|
||||||
import { loadSite } from "./ed-site";
|
import { loadSite } from "./ed-site";
|
||||||
import { treeRebuild } from "./tree/build";
|
import { treeRebuild } from "./tree/build";
|
||||||
|
import { format } from "date-fns";
|
||||||
|
|
||||||
const decoder = new TextDecoder();
|
const decoder = new TextDecoder();
|
||||||
|
|
||||||
|
|
@ -151,6 +152,7 @@ export const edInitSync = (p: PG) => {
|
||||||
},
|
},
|
||||||
shakehand(client_id) {
|
shakehand(client_id) {
|
||||||
p.user.client_id = client_id;
|
p.user.client_id = client_id;
|
||||||
|
console.log(`Prasi connected: ${client_id}`);
|
||||||
},
|
},
|
||||||
disconnected() {
|
disconnected() {
|
||||||
console.log("offline, reconnecting...");
|
console.log("offline, reconnecting...");
|
||||||
|
|
@ -185,9 +187,23 @@ export const edInitSync = (p: PG) => {
|
||||||
}
|
}
|
||||||
p.render();
|
p.render();
|
||||||
},
|
},
|
||||||
async code_changes({ ts, mode }) {
|
async code_changes({ ts, mode, status }) {
|
||||||
if (mode === "frontend") {
|
if (mode === "frontend") {
|
||||||
|
if (status === "ok") {
|
||||||
|
console.clear();
|
||||||
|
console.log(
|
||||||
|
`${format(Date.now(), "HH:mm:ss")} 🚧 Code updated from vscode `
|
||||||
|
);
|
||||||
|
|
||||||
await loadFrontEnd(p, ts);
|
await loadFrontEnd(p, ts);
|
||||||
|
} else if (status === "building") {
|
||||||
|
console.log(
|
||||||
|
`${format(
|
||||||
|
Date.now(),
|
||||||
|
"HH:mm:ss"
|
||||||
|
)} ⏳ Code changed from vscode, rebuilding...`
|
||||||
|
);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
await loadTypings(p);
|
await loadTypings(p);
|
||||||
if (p.ui.monaco) {
|
if (p.ui.monaco) {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,164 @@
|
||||||
|
import { format } from "date-fns";
|
||||||
|
import { get } from "idb-keyval";
|
||||||
|
import { Packr } from "msgpackr";
|
||||||
|
import { SyncType } from "../../../../../../srv/ws/sync/type";
|
||||||
|
import { w } from "../../../../utils/types/general";
|
||||||
|
import { base } from "../base";
|
||||||
|
import { scanComponent } from "../component";
|
||||||
|
import { rebuildMeta } from "../route";
|
||||||
|
const packr = new Packr({ structuredClone: true });
|
||||||
|
|
||||||
|
/** CONSTANT */
|
||||||
|
const WS_CONFIG = {
|
||||||
|
debug: !!localStorage.getItem("prasi-ws-debug"),
|
||||||
|
reconnectTimeout: 1000,
|
||||||
|
id_client: "",
|
||||||
|
id_site: "",
|
||||||
|
ws: null as any,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const initDevLiveReload = () => {
|
||||||
|
if (
|
||||||
|
location.host === "localhost:4550" ||
|
||||||
|
location.host === "prasi.avolut.com"
|
||||||
|
) {
|
||||||
|
const patharr = location.pathname.split("/");
|
||||||
|
const id_site = patharr[2];
|
||||||
|
const retry = () => {
|
||||||
|
const url = new URL(w.basehost || location.href);
|
||||||
|
url.pathname = "/sync";
|
||||||
|
url.protocol = url.protocol === "http:" ? "ws:" : "wss:";
|
||||||
|
|
||||||
|
const ws = new WebSocket(`${url.protocol}//${url.host}${url.pathname}`);
|
||||||
|
|
||||||
|
let timeout = setTimeout(() => {
|
||||||
|
clearTimeout(timeout);
|
||||||
|
ws.close();
|
||||||
|
retry();
|
||||||
|
}, 2000);
|
||||||
|
|
||||||
|
WS_CONFIG.ws = ws;
|
||||||
|
WS_CONFIG.id_site = id_site;
|
||||||
|
ws.onopen = () => {
|
||||||
|
clearTimeout(timeout);
|
||||||
|
w.offline = false;
|
||||||
|
w.editorRender?.();
|
||||||
|
};
|
||||||
|
ws.onmessage = async (e) => {
|
||||||
|
const raw = e.data as Blob;
|
||||||
|
const msg = packr.unpack(Buffer.from(await raw.arrayBuffer()));
|
||||||
|
|
||||||
|
if (WS_CONFIG.debug)
|
||||||
|
console.log(`%c⬇`, `color:red`, formatBytes(raw.size, 0), msg);
|
||||||
|
|
||||||
|
if (msg.type === SyncType.ClientID) {
|
||||||
|
WS_CONFIG.id_client = msg.client_id;
|
||||||
|
send(ws, {
|
||||||
|
type: "preview",
|
||||||
|
mode: "init",
|
||||||
|
data: {
|
||||||
|
site_id: id_site,
|
||||||
|
client_id: msg.client_id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} else if (msg.type === SyncType.Event) {
|
||||||
|
if (msg.event === "page_changed") {
|
||||||
|
const id = msg.data.map.id;
|
||||||
|
const page = base.page.cache[id];
|
||||||
|
const root = msg.data.map.root;
|
||||||
|
page.root = root;
|
||||||
|
const p = {
|
||||||
|
id: page.id,
|
||||||
|
url: page.url,
|
||||||
|
root,
|
||||||
|
meta: {},
|
||||||
|
};
|
||||||
|
await scanComponent(root.childs, true);
|
||||||
|
rebuildMeta(p.meta, root);
|
||||||
|
base.page.cache[p.id] = p;
|
||||||
|
w.prasiContext.render();
|
||||||
|
} else if (msg.event === "code_changes") {
|
||||||
|
const { mode, ts, status } = msg.data;
|
||||||
|
if (mode === "frontend") {
|
||||||
|
if (status === "ok") {
|
||||||
|
console.clear();
|
||||||
|
console.log(
|
||||||
|
`${format(
|
||||||
|
Date.now(),
|
||||||
|
"HH:mm:ss"
|
||||||
|
)} 🚧 Code updated from vscode `
|
||||||
|
);
|
||||||
|
|
||||||
|
const url = `/prod/${id_site}/_prasi/code/index.js?ts=${ts}`;
|
||||||
|
const fn = new Function(
|
||||||
|
"callback",
|
||||||
|
`
|
||||||
|
import("${url}")
|
||||||
|
.catch((e) => console.error("Failed to load site code\\n\\n", e))
|
||||||
|
.then(callback)`
|
||||||
|
);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await new Promise<any>((resolve) => {
|
||||||
|
try {
|
||||||
|
fn((exports: any) => {
|
||||||
|
const w = window as any;
|
||||||
|
for (const [k, v] of Object.entries(exports)) {
|
||||||
|
w[k] = v;
|
||||||
|
}
|
||||||
|
resolve(exports);
|
||||||
|
w.prasiContext.render();
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
console.log("Failed to load site code", e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (e) {}
|
||||||
|
} else if (status === "building") {
|
||||||
|
console.log(
|
||||||
|
`${format(
|
||||||
|
Date.now(),
|
||||||
|
"HH:mm:ss"
|
||||||
|
)} ⏳ Code changed from vscode, rebuilding...`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
retry();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const listenChanges = (
|
||||||
|
arg: { type: "page"; id: string } | { type: "comp"; ids: string[] }
|
||||||
|
) => {
|
||||||
|
if (WS_CONFIG.ws) {
|
||||||
|
send(WS_CONFIG.ws, {
|
||||||
|
type: "preview",
|
||||||
|
mode: "listen",
|
||||||
|
data: {
|
||||||
|
...arg,
|
||||||
|
client_id: WS_CONFIG.id_client,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const send = (ws: WebSocket, msg: any) => {
|
||||||
|
const raw = packr.pack(msg);
|
||||||
|
if (WS_CONFIG.debug)
|
||||||
|
console.log(`%c⬆`, "color:blue", formatBytes(raw.length, 0), msg);
|
||||||
|
ws.send(raw);
|
||||||
|
};
|
||||||
|
|
||||||
|
function formatBytes(bytes: number, decimals: number) {
|
||||||
|
if (bytes == 0) return "0 Bytes";
|
||||||
|
var k = 1024,
|
||||||
|
dm = decimals || 2,
|
||||||
|
sizes = ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"],
|
||||||
|
i = Math.floor(Math.log(bytes) / Math.log(k));
|
||||||
|
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + " " + sizes[i];
|
||||||
|
}
|
||||||
|
|
@ -48,6 +48,7 @@ export const loadPages = (page_ids: string[]) => {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_done) {
|
if (is_done) {
|
||||||
done(result);
|
done(result);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,13 @@
|
||||||
import { createRoot } from "react-dom/client";
|
import { createRoot } from "react-dom/client";
|
||||||
import { defineReact, defineWindow } from "web-utils";
|
import { defineReact, defineWindow } from "web-utils";
|
||||||
import { initBaseConfig } from "./base/base";
|
import { initBaseConfig } from "./base/base";
|
||||||
|
import { initDevLiveReload } from "./base/live-reload/dev-live-reload";
|
||||||
import { Root, isPreview } from "./root";
|
import { Root, isPreview } from "./root";
|
||||||
import { w } from "./w";
|
import { w } from "./w";
|
||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
import("./font");
|
import("./font");
|
||||||
|
initDevLiveReload();
|
||||||
initBaseConfig();
|
initBaseConfig();
|
||||||
const div = document.getElementById("root");
|
const div = document.getElementById("root");
|
||||||
if (div) {
|
if (div) {
|
||||||
|
|
@ -35,6 +37,5 @@ import { w } from "./w";
|
||||||
if (document.body.classList.contains("opacity-0")) {
|
if (document.body.classList.contains("opacity-0")) {
|
||||||
document.body.classList.remove("opacity-0");
|
document.body.classList.remove("opacity-0");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ import { detectResponsiveMode } from "./base/responsive";
|
||||||
import { initBaseRoute, rebuildMeta } from "./base/route";
|
import { initBaseRoute, rebuildMeta } from "./base/route";
|
||||||
import { w } from "./w";
|
import { w } from "./w";
|
||||||
import { MatchedRoute } from "radix3";
|
import { MatchedRoute } from "radix3";
|
||||||
|
import { listenChanges } from "./base/live-reload/dev-live-reload";
|
||||||
|
|
||||||
export const isPreview = () => {
|
export const isPreview = () => {
|
||||||
return (
|
return (
|
||||||
|
|
@ -129,6 +130,8 @@ export const Root = () => {
|
||||||
|
|
||||||
if (page.id !== local.page_id) {
|
if (page.id !== local.page_id) {
|
||||||
base.init_local_effect = {};
|
base.init_local_effect = {};
|
||||||
|
|
||||||
|
listenChanges({ type: "page", id: page.id });
|
||||||
}
|
}
|
||||||
|
|
||||||
w.params = page.params || {};
|
w.params = page.params || {};
|
||||||
|
|
|
||||||
|
|
@ -1,70 +0,0 @@
|
||||||
import hash_sum from "hash-sum";
|
|
||||||
|
|
||||||
const match = (item: any, where: any) => {
|
|
||||||
for (const [k, v] of Object.entries(where)) {
|
|
||||||
if (item[k] !== v) return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const IndexedMap = {
|
|
||||||
create: <OBJ extends Record<string, any>, KEY extends string>(id: KEY) => {
|
|
||||||
const all = {} as Record<KEY, Record<string, OBJ>>;
|
|
||||||
|
|
||||||
return {
|
|
||||||
add(item: OBJ & Record<KEY, any>) {
|
|
||||||
const pk = item[id] as KEY;
|
|
||||||
|
|
||||||
if (!all[pk]) {
|
|
||||||
all[pk] = {};
|
|
||||||
}
|
|
||||||
const _id = hash_sum(item) as any;
|
|
||||||
|
|
||||||
const items = all[pk] as Record<string, OBJ>;
|
|
||||||
items[_id] = item;
|
|
||||||
|
|
||||||
return pk;
|
|
||||||
},
|
|
||||||
findAll(where: Partial<OBJ & Record<KEY, string>>, withId?: boolean) {
|
|
||||||
const founds = [];
|
|
||||||
if (where[id]) {
|
|
||||||
const _id = where[id] as KEY;
|
|
||||||
|
|
||||||
if (all[_id]) {
|
|
||||||
for (const [k, item] of Object.entries(all[_id])) {
|
|
||||||
if (match(item, where)) {
|
|
||||||
if (withId) {
|
|
||||||
founds.push({ ...item, _id: k });
|
|
||||||
} else {
|
|
||||||
founds.push(item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (const _items of Object.values(all)) {
|
|
||||||
const items = _items as Record<string, OBJ>;
|
|
||||||
|
|
||||||
for (const [k, item] of Object.entries(items)) {
|
|
||||||
if (match(item, where)) {
|
|
||||||
if (withId) {
|
|
||||||
founds.push({ ...item, _id: k });
|
|
||||||
} else {
|
|
||||||
founds.push(item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return founds;
|
|
||||||
},
|
|
||||||
delAll(where: Partial<OBJ & Record<KEY, string>>) {
|
|
||||||
for (const item of this.findAll(where, true)) {
|
|
||||||
delete all[item[id]][item._id];
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
@ -97,7 +97,11 @@ export const clientStartSync = async (arg: {
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
) => void;
|
) => void;
|
||||||
code_changes: (arg: { ts: number; mode: "frontend" | "typings" }) => void;
|
code_changes: (arg: {
|
||||||
|
ts: number;
|
||||||
|
mode: "frontend" | "typings";
|
||||||
|
status: "error" | "ok" | "building";
|
||||||
|
}) => void;
|
||||||
disconnected: () => { reconnect: boolean };
|
disconnected: () => { reconnect: boolean };
|
||||||
opened: () => void;
|
opened: () => void;
|
||||||
shakehand: (client_id: string) => void;
|
shakehand: (client_id: string) => void;
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,9 @@ import { $ } from "bun";
|
||||||
import { dir } from "dir";
|
import { dir } from "dir";
|
||||||
import { context } from "esbuild";
|
import { context } from "esbuild";
|
||||||
import { removeAsync } from "fs-jetpack";
|
import { removeAsync } from "fs-jetpack";
|
||||||
|
import { polyfillNode } from "esbuild-plugin-polyfill-node";
|
||||||
|
|
||||||
await removeAsync(dir.path('/app/srv/core'))
|
await removeAsync(dir.path("/app/srv/core"));
|
||||||
await $`bun tailwindcss -i src/nova/prod/tailwind.css -m -o ../srv/core/index.css`
|
await $`bun tailwindcss -i src/nova/prod/tailwind.css -m -o ../srv/core/index.css`
|
||||||
.cwd(dir.path(`/app/web`))
|
.cwd(dir.path(`/app/web`))
|
||||||
.quiet();
|
.quiet();
|
||||||
|
|
@ -24,6 +25,61 @@ const ctx = await context({
|
||||||
define: {
|
define: {
|
||||||
"process.env.NODE_ENV": `"production"`,
|
"process.env.NODE_ENV": `"production"`,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
plugins: [
|
||||||
|
polyfillNode({
|
||||||
|
polyfills: {
|
||||||
|
buffer: true,
|
||||||
|
_stream_duplex: false,
|
||||||
|
_stream_passthrough: false,
|
||||||
|
_stream_readable: false,
|
||||||
|
_stream_transform: false,
|
||||||
|
_stream_writable: false,
|
||||||
|
assert: false,
|
||||||
|
async_hooks: false,
|
||||||
|
child_process: false,
|
||||||
|
cluster: false,
|
||||||
|
console: false,
|
||||||
|
constants: false,
|
||||||
|
crypto: false,
|
||||||
|
dgram: false,
|
||||||
|
diagnostics_channel: false,
|
||||||
|
dns: false,
|
||||||
|
domain: false,
|
||||||
|
events: false,
|
||||||
|
fs: false,
|
||||||
|
http: false,
|
||||||
|
http2: false,
|
||||||
|
https: false,
|
||||||
|
module: false,
|
||||||
|
net: false,
|
||||||
|
os: false,
|
||||||
|
path: false,
|
||||||
|
perf_hooks: false,
|
||||||
|
process: false,
|
||||||
|
punycode: false,
|
||||||
|
querystring: false,
|
||||||
|
readline: false,
|
||||||
|
repl: false,
|
||||||
|
stream: false,
|
||||||
|
string_decoder: false,
|
||||||
|
sys: false,
|
||||||
|
timers: false,
|
||||||
|
tls: false,
|
||||||
|
tty: false,
|
||||||
|
url: false,
|
||||||
|
util: false,
|
||||||
|
v8: false,
|
||||||
|
vm: false,
|
||||||
|
wasi: false,
|
||||||
|
worker_threads: false,
|
||||||
|
zlib: false,
|
||||||
|
"assert/strict": false,
|
||||||
|
"fs/promises": false,
|
||||||
|
"timers/promises": false,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
ctx.watch();
|
ctx.watch();
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@
|
||||||
"acorn-walk": "^8.3.3",
|
"acorn-walk": "^8.3.3",
|
||||||
"brotli-wasm": "^3.0.1",
|
"brotli-wasm": "^3.0.1",
|
||||||
"esbuild": "^0.21.5",
|
"esbuild": "^0.21.5",
|
||||||
|
"esbuild-plugin-polyfill-node": "^0.3.0",
|
||||||
"execa": "^8.0.1",
|
"execa": "^8.0.1",
|
||||||
"fs-jetpack": "^5.1.0",
|
"fs-jetpack": "^5.1.0",
|
||||||
"lmdb": "^2.8.5",
|
"lmdb": "^2.8.5",
|
||||||
|
|
|
||||||
|
|
@ -66,4 +66,5 @@ export const g = global as unknown as {
|
||||||
route_cache: Record<string, { br?: any; gzip?: any }>;
|
route_cache: Record<string, { br?: any; gzip?: any }>;
|
||||||
main_cache: Record<string, { content: any; type: string }>;
|
main_cache: Record<string, { content: any; type: string }>;
|
||||||
br: BrotliWasmType;
|
br: BrotliWasmType;
|
||||||
|
preview_page_timeout: Record<string, Timer>
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue