wip fix
This commit is contained in:
parent
43745f20df
commit
972fbd6ffd
|
|
@ -1,7 +1,7 @@
|
||||||
import { dir } from "dir";
|
import { dir } from "dir";
|
||||||
import { apiContext } from "../../../pkgs/core/server/api/api-ctx";
|
import { apiContext } from "../../../pkgs/core/server/api/api-ctx";
|
||||||
import { g } from "utils/global";
|
import { g } from "utils/global";
|
||||||
import { code } from "../ws/sync/editor/code/util";
|
import { code } from "../ws/sync/editor/code/util-code";
|
||||||
|
|
||||||
export const _ = {
|
export const _ = {
|
||||||
url: "/nova-load/:mode/:id/**",
|
url: "/nova-load/:mode/:id/**",
|
||||||
|
|
|
||||||
|
|
@ -65,12 +65,12 @@ export const SyncActions = {
|
||||||
bin: Uint8Array
|
bin: Uint8Array
|
||||||
) => ({}) as { diff: Uint8Array; sv: Uint8Array } | void,
|
) => ({}) as { diff: Uint8Array; sv: Uint8Array } | void,
|
||||||
diff_local: async (
|
diff_local: async (
|
||||||
mode: "page" | "comp" | "site",
|
mode: "page" | "comp" | "site" | "code",
|
||||||
id: string,
|
id: string,
|
||||||
bin: Uint8Array
|
bin: Uint8Array
|
||||||
) => {},
|
) => {},
|
||||||
sv_remote: async (
|
sv_remote: async (
|
||||||
mode: "page" | "comp" | "site",
|
mode: "page" | "comp" | "site" | "code",
|
||||||
id: string,
|
id: string,
|
||||||
sv: Uint8Array,
|
sv: Uint8Array,
|
||||||
diff: Uint8Array
|
diff: Uint8Array
|
||||||
|
|
@ -84,7 +84,13 @@ export const SyncActions = {
|
||||||
load: async (id: string, type: "src" | "build") =>
|
load: async (id: string, type: "src" | "build") =>
|
||||||
({}) as {
|
({}) as {
|
||||||
id: string;
|
id: string;
|
||||||
snapshot: null | Uint8Array;
|
snapshot: null | Record<
|
||||||
|
string,
|
||||||
|
{
|
||||||
|
id_doc: number;
|
||||||
|
bin: Uint8Array;
|
||||||
|
}
|
||||||
|
>;
|
||||||
},
|
},
|
||||||
edit: async (
|
edit: async (
|
||||||
arg:
|
arg:
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { SAction } from "../actions";
|
import { SAction } from "../actions";
|
||||||
import { getCode, prepDCode } from "../editor/code/prep-code";
|
import { prepCodeSnapshot } from "../editor/code/prep-code";
|
||||||
import { SyncConnection } from "../type";
|
import { SyncConnection } from "../type";
|
||||||
|
|
||||||
export const code_load: SAction["code"]["load"] = async function (
|
export const code_load: SAction["code"]["load"] = async function (
|
||||||
|
|
@ -7,13 +7,9 @@ export const code_load: SAction["code"]["load"] = async function (
|
||||||
site_id,
|
site_id,
|
||||||
type
|
type
|
||||||
) {
|
) {
|
||||||
const code = await getCode(site_id, "site");
|
const snap = await prepCodeSnapshot(site_id, "site");
|
||||||
|
if (snap && snap.type === "code") {
|
||||||
if (code) {
|
return { id: site_id, snapshot: snap.build };
|
||||||
const prep = await prepDCode(site_id);
|
|
||||||
if (prep) {
|
|
||||||
return { id: site_id, snapshot: prep.bin[type] };
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return { id: site_id, snapshot: null };
|
return { id: site_id, snapshot: null };
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ import { ESite } from "../../../../web/src/nova/ed/logic/ed-global";
|
||||||
import { SAction } from "../actions";
|
import { SAction } from "../actions";
|
||||||
import { prepCodeSnapshot } from "../editor/code/prep-code";
|
import { prepCodeSnapshot } from "../editor/code/prep-code";
|
||||||
import { SyncConnection } from "../type";
|
import { SyncConnection } from "../type";
|
||||||
|
import { gzipAsync } from "../entity/zlib";
|
||||||
|
|
||||||
export const site_load: SAction["site"]["load"] = async function (
|
export const site_load: SAction["site"]["load"] = async function (
|
||||||
this: SyncConnection,
|
this: SyncConnection,
|
||||||
|
|
@ -27,7 +28,13 @@ export const site_load: SAction["site"]["load"] = async function (
|
||||||
select: { id: true },
|
select: { id: true },
|
||||||
});
|
});
|
||||||
|
|
||||||
await prepCodeSnapshot(site_id, "site");
|
const snap = await prepCodeSnapshot(site_id, "site");
|
||||||
|
const compressed: any = {};
|
||||||
|
if (snap) {
|
||||||
|
for (const [key, value] of Object.entries(snap.build)) {
|
||||||
|
compressed[key] = { bin: await gzipAsync(value.bin) };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: site.id,
|
id: site.id,
|
||||||
|
|
@ -38,7 +45,10 @@ export const site_load: SAction["site"]["load"] = async function (
|
||||||
responsive: site.responsive as ESite["responsive"],
|
responsive: site.responsive as ESite["responsive"],
|
||||||
js_compiled: site.js_compiled || "",
|
js_compiled: site.js_compiled || "",
|
||||||
layout: { id: layout?.id || "", snapshot: null, meta: undefined },
|
layout: { id: layout?.id || "", snapshot: null, meta: undefined },
|
||||||
code: { snapshot: null, mode: site.code_mode as "old" | "vsc" },
|
code: {
|
||||||
|
snapshot: compressed,
|
||||||
|
mode: site.code_mode as "old" | "vsc",
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,11 @@ export const yjs_sv_remote: SAction["yjs"]["sv_remote"] = async function (
|
||||||
console.log(`sv_remote not found`, mode, id);
|
console.log(`sv_remote not found`, mode, id);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const doc = docs[mode][id].doc;
|
let doc = null;
|
||||||
|
if (mode !== "code") doc = docs[mode][id].doc;
|
||||||
|
else {
|
||||||
|
doc = docs.code[id].build.site;
|
||||||
|
}
|
||||||
const diff_local = Y.encodeStateAsUpdate(doc as any, await gunzipAsync(sv));
|
const diff_local = Y.encodeStateAsUpdate(doc as any, await gunzipAsync(sv));
|
||||||
Y.applyUpdate(doc as any, await gunzipAsync(diff), "local");
|
Y.applyUpdate(doc as any, await gunzipAsync(diff), "local");
|
||||||
return { diff: await gzipAsync(diff_local) };
|
return { diff: await gzipAsync(diff_local) };
|
||||||
|
|
|
||||||
|
|
@ -1,50 +1,78 @@
|
||||||
import { build, context } from "esbuild";
|
|
||||||
import { Code } from "./watcher";
|
|
||||||
import { g } from "utils/global";
|
|
||||||
import { dir } from "dir";
|
|
||||||
import globalExternals from "@fal-works/esbuild-plugin-global-externals";
|
import globalExternals from "@fal-works/esbuild-plugin-global-externals";
|
||||||
import { style } from "@hyrious/esbuild-plugin-style";
|
import { style } from "@hyrious/esbuild-plugin-style";
|
||||||
import { sendWS } from "../../sync-handler";
|
import { dir } from "dir";
|
||||||
import { SyncType } from "../../type";
|
import { context } from "esbuild";
|
||||||
import { gzipAsync } from "../../entity/zlib";
|
import { existsAsync, dirAsync, removeAsync, writeAsync } from "fs-jetpack";
|
||||||
import { ServerWebSocket } from "bun";
|
import { CodeMode, code } from "./util-code";
|
||||||
import { WSData } from "../../../../../../pkgs/core/server/create";
|
|
||||||
import { user } from "../../entity/user";
|
import { user } from "../../entity/user";
|
||||||
import { conns } from "../../entity/conn";
|
import { docs } from "../../entity/docs";
|
||||||
import { CodeMode, code } from "./util";
|
import { DCode } from "../../../../../web/src/utils/types/root";
|
||||||
|
import { readDirectoryRecursively } from "../../../../api/site-export";
|
||||||
|
|
||||||
const encoder = new TextEncoder();
|
const encoder = new TextEncoder();
|
||||||
export const codeBuild = async (id_site: any, mode: CodeMode) => {
|
export const codeBuild = async (id_site: any, mode: CodeMode) => {
|
||||||
const src_path = code.path(id_site, mode, "src");
|
const src_path = code.path(id_site, mode, "src");
|
||||||
|
if (!(await existsAsync(src_path))) return;
|
||||||
const build_path = code.path(id_site, mode, "build");
|
const build_path = code.path(id_site, mode, "build");
|
||||||
const build_file = dir.path(`${build_path}/index.js`);
|
|
||||||
|
await removeAsync(build_path);
|
||||||
|
await dirAsync(build_path);
|
||||||
|
const build_file = `${build_path}/index.js`;
|
||||||
|
await writeAsync(build_file, "");
|
||||||
|
|
||||||
if (!code.esbuild[id_site]) {
|
if (!code.esbuild[id_site]) {
|
||||||
code.esbuild[id_site] = { site: null, ssr: null };
|
code.esbuild[id_site] = { site: null, ssr: null };
|
||||||
}
|
}
|
||||||
|
|
||||||
code.esbuild[id_site][mode] = await context({
|
if (!code.esbuild[id_site][mode]) {
|
||||||
absWorkingDir: src_path,
|
code.esbuild[id_site][mode] = await context({
|
||||||
entryPoints: ["index.tsx"],
|
absWorkingDir: src_path,
|
||||||
bundle: true,
|
entryPoints: ["index.tsx"],
|
||||||
outfile: build_file,
|
bundle: true,
|
||||||
minify: true,
|
outfile: build_file,
|
||||||
treeShaking: true,
|
minify: true,
|
||||||
sourcemap: true,
|
treeShaking: true,
|
||||||
plugins: [
|
format: "cjs",
|
||||||
style(),
|
sourcemap: true,
|
||||||
globalExternals({
|
plugins: [
|
||||||
react: {
|
style(),
|
||||||
varName: "window.React",
|
globalExternals({
|
||||||
type: "cjs",
|
react: {
|
||||||
|
varName: "window.React",
|
||||||
|
type: "cjs",
|
||||||
|
},
|
||||||
|
"react-dom": {
|
||||||
|
varName: "window.ReactDOM",
|
||||||
|
type: "cjs",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
name: "prasi",
|
||||||
|
setup(setup) {
|
||||||
|
setup.onEnd((res) => {
|
||||||
|
const cdoc = docs.code[id_site];
|
||||||
|
if (cdoc) {
|
||||||
|
const doc = cdoc.build[mode];
|
||||||
|
const build_dir = code.path(id_site, mode, "build");
|
||||||
|
|
||||||
|
if (doc) {
|
||||||
|
codeApplyChanges(build_dir, doc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
},
|
},
|
||||||
"react-dom": {
|
],
|
||||||
varName: "window.ReactDOM",
|
});
|
||||||
type: "cjs",
|
const esbuild = code.esbuild[id_site][mode];
|
||||||
},
|
esbuild?.watch();
|
||||||
}),
|
}
|
||||||
],
|
const esbuild = code.esbuild[id_site][mode];
|
||||||
});
|
if (esbuild) {
|
||||||
|
try {
|
||||||
|
await esbuild.rebuild();
|
||||||
|
} catch (e) {}
|
||||||
|
}
|
||||||
|
|
||||||
const out = Bun.file(build_file);
|
const out = Bun.file(build_file);
|
||||||
const src = (await out.text()).replace(
|
const src = (await out.text()).replace(
|
||||||
|
|
@ -53,3 +81,25 @@ export const codeBuild = async (id_site: any, mode: CodeMode) => {
|
||||||
);
|
);
|
||||||
await Bun.write(out, src);
|
await Bun.write(out, src);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const codeApplyChanges = (path: string, doc: DCode) => {
|
||||||
|
const map = doc.getMap("map");
|
||||||
|
|
||||||
|
const files = map.get("files");
|
||||||
|
|
||||||
|
const dirs = readDirectoryRecursively(path);
|
||||||
|
doc.transact(() => {
|
||||||
|
files?.forEach((v, k) => {
|
||||||
|
if (!dirs[k]) {
|
||||||
|
files?.delete(k);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
for (const [k, v] of Object.entries(dirs)) {
|
||||||
|
if (files) {
|
||||||
|
files.set(k, v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return doc;
|
||||||
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,75 +0,0 @@
|
||||||
import { spawn } from "bun";
|
|
||||||
import { sendWS } from "../../sync-handler";
|
|
||||||
import { SyncType } from "../../type";
|
|
||||||
import { Code } from "./watcher";
|
|
||||||
|
|
||||||
const decoder = new TextDecoder();
|
|
||||||
export const codePkgInstall = async (id_site: string, mode: string) => {
|
|
||||||
try {
|
|
||||||
const proc = spawn({
|
|
||||||
cmd: ["bun", "i"],
|
|
||||||
cwd: Code.path(code.id_site, code.id),
|
|
||||||
stderr: "pipe",
|
|
||||||
stdout: "pipe",
|
|
||||||
});
|
|
||||||
|
|
||||||
const broadcast = (content: string) => {
|
|
||||||
activity.site
|
|
||||||
.room(code.id_site)
|
|
||||||
.findAll({ site_js: code.name })
|
|
||||||
.forEach((item, ws) => {
|
|
||||||
sendWS(ws, {
|
|
||||||
type: SyncType.Event,
|
|
||||||
event: "code",
|
|
||||||
data: {
|
|
||||||
name: code.name,
|
|
||||||
id: code.id,
|
|
||||||
content,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
(async () => {
|
|
||||||
for await (const chunk of proc.stdout) {
|
|
||||||
broadcast(decoder.decode(chunk));
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
(async () => {
|
|
||||||
for await (const chunk of proc.stderr) {
|
|
||||||
broadcast(decoder.decode(chunk));
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
await proc.exited;
|
|
||||||
|
|
||||||
activity.site
|
|
||||||
.room(code.id_site)
|
|
||||||
.findAll({ site_js: code.name })
|
|
||||||
.forEach((item, ws) => {
|
|
||||||
sendWS(ws, {
|
|
||||||
type: SyncType.Event,
|
|
||||||
event: "code",
|
|
||||||
data: {
|
|
||||||
name: code.name,
|
|
||||||
id: code.id,
|
|
||||||
event: "code-done",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
|
||||||
} catch (e: any) {
|
|
||||||
activity.site
|
|
||||||
.room(code.id_site)
|
|
||||||
.findAll({ site_js: code.name })
|
|
||||||
.forEach((item, ws) => {
|
|
||||||
sendWS(ws, {
|
|
||||||
type: SyncType.Event,
|
|
||||||
event: "code",
|
|
||||||
data: {
|
|
||||||
name: code.name,
|
|
||||||
id: code.id,
|
|
||||||
event: "code-done",
|
|
||||||
content: `ERROR: ${e.message}`,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
@ -1,11 +1,15 @@
|
||||||
import { existsAsync } from "fs-jetpack";
|
|
||||||
import { Doc } from "yjs";
|
import { Doc } from "yjs";
|
||||||
import { DCode } from "../../../../../web/src/utils/types/root";
|
import { DCode } from "../../../../../web/src/utils/types/root";
|
||||||
import { readDirectoryRecursively } from "../../../../api/site-export";
|
import { readDirectoryRecursively } from "../../../../api/site-export";
|
||||||
import { docs } from "../../entity/docs";
|
import { docs } from "../../entity/docs";
|
||||||
import { snapshot } from "../../entity/snapshot";
|
import { snapshot } from "../../entity/snapshot";
|
||||||
|
import { gzipAsync } from "../../entity/zlib";
|
||||||
import { codeBuild } from "./build-code";
|
import { codeBuild } from "./build-code";
|
||||||
import { CodeMode, code } from "./util";
|
import { CodeMode, code } from "./util-code";
|
||||||
|
import { user } from "../../entity/user";
|
||||||
|
import { SyncType } from "../../type";
|
||||||
|
import { sendWS } from "../../sync-handler";
|
||||||
|
import { conns } from "../../entity/conn";
|
||||||
|
|
||||||
export const prepCodeSnapshot = async (id_site: string, mode: CodeMode) => {
|
export const prepCodeSnapshot = async (id_site: string, mode: CodeMode) => {
|
||||||
await code
|
await code
|
||||||
|
|
@ -17,23 +21,47 @@ export const prepCodeSnapshot = async (id_site: string, mode: CodeMode) => {
|
||||||
)
|
)
|
||||||
.await();
|
.await();
|
||||||
|
|
||||||
let doc = docs.code[id_site];
|
let dcode = docs.code[id_site];
|
||||||
if (!docs.code[id_site]) {
|
if (!docs.code[id_site]) {
|
||||||
docs.code[id_site] = {
|
docs.code[id_site] = {
|
||||||
id: id_site,
|
id: id_site,
|
||||||
build: {},
|
build: {},
|
||||||
};
|
};
|
||||||
doc = docs.code[id_site];
|
dcode = docs.code[id_site];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (doc) {
|
if (dcode) {
|
||||||
if (!doc.build[mode]) {
|
if (!dcode.build[mode]) {
|
||||||
const build_dir = code.path(id_site, mode, "build");
|
const build_dir = code.path(id_site, mode, "build");
|
||||||
|
await codeBuild(id_site, mode);
|
||||||
|
dcode.build[mode] = codeLoad(id_site, build_dir);
|
||||||
|
const doc = dcode.build[mode] as Doc;
|
||||||
|
if (doc) {
|
||||||
|
doc.on("update", async (e, origin) => {
|
||||||
|
const bin = Y.encodeStateAsUpdate(doc);
|
||||||
|
|
||||||
if (!(await existsAsync(build_dir))) {
|
if (snap && snap.type === "code") {
|
||||||
await codeBuild(id_site, mode);
|
snap.build[mode].bin = bin;
|
||||||
|
snapshot.update({
|
||||||
|
id: id_site,
|
||||||
|
type: "code",
|
||||||
|
build: snap.build,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const sv_local = await gzipAsync(bin);
|
||||||
|
user.active.findAll({ site_id: id_site }).map((e) => {
|
||||||
|
const ws = conns.get(e.client_id)?.ws;
|
||||||
|
if (ws) {
|
||||||
|
sendWS(ws, {
|
||||||
|
type: SyncType.Event,
|
||||||
|
event: "remote_svlocal",
|
||||||
|
data: { type: "code", sv_local, id: id_site },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
doc.build[mode] = codeLoad(id_site, build_dir);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const build: Record<
|
const build: Record<
|
||||||
|
|
@ -43,9 +71,10 @@ export const prepCodeSnapshot = async (id_site: string, mode: CodeMode) => {
|
||||||
bin: Uint8Array;
|
bin: Uint8Array;
|
||||||
}
|
}
|
||||||
> = {};
|
> = {};
|
||||||
for (const [k, v] of Object.entries(doc.build)) {
|
|
||||||
|
for (const [k, v] of Object.entries(dcode.build)) {
|
||||||
const bin = Y.encodeStateAsUpdate(v as Doc);
|
const bin = Y.encodeStateAsUpdate(v as Doc);
|
||||||
build[k] = { bin, id_doc: v.clientID };
|
build[k] = { bin: bin, id_doc: v.clientID };
|
||||||
}
|
}
|
||||||
|
|
||||||
let snap = await snapshot.getOrCreate({
|
let snap = await snapshot.getOrCreate({
|
||||||
|
|
@ -54,7 +83,9 @@ export const prepCodeSnapshot = async (id_site: string, mode: CodeMode) => {
|
||||||
build,
|
build,
|
||||||
});
|
});
|
||||||
|
|
||||||
return snap;
|
if (snap.type === "code") {
|
||||||
|
return snap;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -66,7 +97,7 @@ const codeLoad = (id: string, path: string) => {
|
||||||
|
|
||||||
const dirs = readDirectoryRecursively(path);
|
const dirs = readDirectoryRecursively(path);
|
||||||
for (const [k, v] of Object.entries(dirs)) {
|
for (const [k, v] of Object.entries(dirs)) {
|
||||||
files.set(k, new Y.Text(v));
|
files.set(k, v);
|
||||||
}
|
}
|
||||||
|
|
||||||
doc.transact(() => {
|
doc.transact(() => {
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,17 @@
|
||||||
import { dir } from "dir";
|
import { dir } from "dir";
|
||||||
import { g } from "utils/global";
|
|
||||||
import { dirAsync, writeAsync } from "fs-jetpack";
|
|
||||||
import { dirname } from "path";
|
|
||||||
import { BuildContext } from "esbuild";
|
import { BuildContext } from "esbuild";
|
||||||
|
import { dirAsync, existsAsync, writeAsync } from "fs-jetpack";
|
||||||
|
import { dirname } from "path";
|
||||||
|
import { g } from "utils/global";
|
||||||
|
|
||||||
export type CodeMode = "site" | "ssr";
|
export type CodeMode = "site" | "ssr";
|
||||||
export const code = {
|
export const code = {
|
||||||
path(id_site: string, mode: CodeMode, type: "src" | "build", path?: string) {
|
path(id_site: string, mode: CodeMode, type: "src" | "build", path?: string) {
|
||||||
return dir.path(`${g.datadir}/code/${id_site}/${mode}/${type}`);
|
let file_path = "";
|
||||||
|
if (path) {
|
||||||
|
file_path = path[0] === "/" ? path : `/${path}`;
|
||||||
|
}
|
||||||
|
return dir.path(`${g.datadir}/code/${id_site}/${mode}/${type}${file_path}`);
|
||||||
},
|
},
|
||||||
esbuild: {} as Record<string, Record<CodeMode, null | BuildContext>>,
|
esbuild: {} as Record<string, Record<CodeMode, null | BuildContext>>,
|
||||||
prep(id_site: string, mode: CodeMode) {
|
prep(id_site: string, mode: CodeMode) {
|
||||||
|
|
@ -17,15 +21,21 @@ export const code = {
|
||||||
});
|
});
|
||||||
const promises: Promise<void>[] = [];
|
const promises: Promise<void>[] = [];
|
||||||
return {
|
return {
|
||||||
path(type: "src" | "build", path?: string) {
|
path(type: "src" | "build", path: string) {
|
||||||
return dir.path(`${g.datadir}/code/${id_site}/${mode}/${type}`);
|
return dir.path(
|
||||||
|
`${g.datadir}/code/${id_site}/${mode}/${type}${
|
||||||
|
path[0] === "/" ? path : `/${path}`
|
||||||
|
}`
|
||||||
|
);
|
||||||
},
|
},
|
||||||
new_file(path: string, content: string) {
|
new_file(path: string, content: string) {
|
||||||
promises.push(
|
promises.push(
|
||||||
new Promise(async (done) => {
|
new Promise(async (done) => {
|
||||||
const full_path = this.path("src", path);
|
const full_path = this.path("src", path);
|
||||||
await dirAsync(dirname(full_path));
|
if (!(await existsAsync(full_path))) {
|
||||||
await writeAsync(full_path, content);
|
await dirAsync(dirname(full_path));
|
||||||
|
await writeAsync(full_path, content);
|
||||||
|
}
|
||||||
done();
|
done();
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
@ -85,8 +85,9 @@ export const snapshot = {
|
||||||
async getOrCreate(data: DocSnapshot) {
|
async getOrCreate(data: DocSnapshot) {
|
||||||
const id = `${data.type}-${data.id}`;
|
const id = `${data.type}-${data.id}`;
|
||||||
let res = this.db.get(id);
|
let res = this.db.get(id);
|
||||||
if (!res) {
|
|
||||||
await this.db.put(id, structuredClone(emptySnapshot as DocSnapshot));
|
if (!res || !res.id) {
|
||||||
|
await this.db.put(id, structuredClone(data as DocSnapshot));
|
||||||
res = this.db.get(id);
|
res = this.db.get(id);
|
||||||
}
|
}
|
||||||
return res as DocSnapshot;
|
return res as DocSnapshot;
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import { SAction } from "../../../../../srv/ws/sync/actions";
|
||||||
import { parseJs } from "../../../../../srv/ws/sync/editor/parser/parse-js";
|
import { parseJs } from "../../../../../srv/ws/sync/editor/parser/parse-js";
|
||||||
import { clientStartSync } from "../../../utils/sync/ws-client";
|
import { clientStartSync } from "../../../utils/sync/ws-client";
|
||||||
import { IItem } from "../../../utils/types/item";
|
import { IItem } from "../../../utils/types/item";
|
||||||
import { DComp, DPage } from "../../../utils/types/root";
|
import { DCode, DComp, DPage } from "../../../utils/types/root";
|
||||||
import { GenMetaP, IMeta as LogicMeta } from "../../vi/utils/types";
|
import { GenMetaP, IMeta as LogicMeta } from "../../vi/utils/types";
|
||||||
|
|
||||||
export type IMeta = LogicMeta;
|
export type IMeta = LogicMeta;
|
||||||
|
|
@ -24,7 +24,15 @@ export const EmptySite = {
|
||||||
meta: undefined as void | Record<string, IMeta>,
|
meta: undefined as void | Record<string, IMeta>,
|
||||||
},
|
},
|
||||||
code: {
|
code: {
|
||||||
snapshot: null as null | Uint8Array,
|
snapshot: {} as
|
||||||
|
| undefined
|
||||||
|
| Record<
|
||||||
|
string,
|
||||||
|
{
|
||||||
|
id_doc: number;
|
||||||
|
bin: Uint8Array;
|
||||||
|
}
|
||||||
|
>,
|
||||||
mode: "old" as "old" | "vsc",
|
mode: "old" as "old" | "vsc",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
@ -183,7 +191,8 @@ export const EDGlobal = {
|
||||||
>,
|
>,
|
||||||
group: {} as Record<string, Awaited<ReturnType<SAction["comp"]["group"]>>>,
|
group: {} as Record<string, Awaited<ReturnType<SAction["comp"]["group"]>>>,
|
||||||
},
|
},
|
||||||
global_prop: [] as string[],
|
code: {} as Record<string, { doc: null | DCode }>,
|
||||||
|
global_prop: [] as string[],
|
||||||
ui: {
|
ui: {
|
||||||
zoom: localStorage.zoom || "100%",
|
zoom: localStorage.zoom || "100%",
|
||||||
should_render: false,
|
should_render: false,
|
||||||
|
|
@ -211,7 +220,6 @@ export const EDGlobal = {
|
||||||
code: {
|
code: {
|
||||||
init: false,
|
init: false,
|
||||||
open: false,
|
open: false,
|
||||||
id: "",
|
|
||||||
name: "site",
|
name: "site",
|
||||||
log: "",
|
log: "",
|
||||||
loading: false,
|
loading: false,
|
||||||
|
|
|
||||||
|
|
@ -1,57 +1,59 @@
|
||||||
import { viLoadLegacy } from "../../vi/load/load-legacy";
|
import { viLoadLegacy } from "../../vi/load/load-legacy";
|
||||||
|
import { viLoadSnapshot } from "../../vi/load/load-snapshot";
|
||||||
import { ESite, PG } from "./ed-global";
|
import { ESite, PG } from "./ed-global";
|
||||||
import { reloadPage } from "./ed-route";
|
import { reloadPage } from "./ed-route";
|
||||||
|
|
||||||
export const loadSite = async (p: PG, site: ESite, note: string) => {
|
export const loadSite = async (p: PG, site: ESite, note: string) => {
|
||||||
const old_layout_id = p.site.layout.id;
|
const old_layout_id = p.site.layout.id;
|
||||||
const layout_changed = p.site.layout.id !== site.layout.id;
|
const layout_changed = p.site.layout.id !== site.layout.id;
|
||||||
p.site = site;
|
p.site = site;
|
||||||
|
|
||||||
p.mode = "desktop";
|
p.mode = "desktop";
|
||||||
if (p.site.responsive === "mobile-only") {
|
if (p.site.responsive === "mobile-only") {
|
||||||
p.mode = "mobile";
|
p.mode = "mobile";
|
||||||
} else if (p.site.responsive === "desktop-only") {
|
} else if (p.site.responsive === "desktop-only") {
|
||||||
p.mode = "desktop";
|
p.mode = "desktop";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (layout_changed) {
|
if (layout_changed) {
|
||||||
const old_layout = p.page.list[old_layout_id];
|
const old_layout = p.page.list[old_layout_id];
|
||||||
|
|
||||||
if (old_layout && old_layout.on_update) {
|
if (old_layout && old_layout.on_update) {
|
||||||
old_layout.doc.off("update", old_layout.on_update);
|
old_layout.doc.off("update", old_layout.on_update);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!p.script.db && !p.script.api) {
|
if (!p.script.db && !p.script.api) {
|
||||||
if (p.site.code.mode === "old") {
|
if (p.site.code.mode === "old") {
|
||||||
await viLoadLegacy({
|
await viLoadLegacy({
|
||||||
site: {
|
site: {
|
||||||
api_url: p.site.config.api_url,
|
api_url: p.site.config.api_url,
|
||||||
id: p.site.id,
|
id: p.site.id,
|
||||||
api: {
|
api: {
|
||||||
get() {
|
get() {
|
||||||
return p.script.api;
|
return p.script.api;
|
||||||
},
|
},
|
||||||
set(val) {
|
set(val) {
|
||||||
p.script.api = val;
|
p.script.api = val;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
db: {
|
db: {
|
||||||
get() {
|
get() {
|
||||||
return p.script.db;
|
return p.script.db;
|
||||||
},
|
},
|
||||||
set(val) {
|
set(val) {
|
||||||
p.script.db = val;
|
p.script.db = val;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
render: () => {},
|
render: () => {},
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
}
|
await viLoadSnapshot(p);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (site.layout.id) {
|
if (site.layout.id) {
|
||||||
await reloadPage(p, site.layout.id, "load-layout");
|
await reloadPage(p, site.layout.id, "load-layout");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -64,47 +64,11 @@ export const edInitSync = (p: PG) => {
|
||||||
site_id: params.site_id,
|
site_id: params.site_id,
|
||||||
page_id: params.page_id,
|
page_id: params.page_id,
|
||||||
events: {
|
events: {
|
||||||
code(arg) {
|
|
||||||
p.ui.popup.code.error = false;
|
|
||||||
|
|
||||||
if (arg.event === "code-loading") {
|
|
||||||
p.ui.popup.code.log = "";
|
|
||||||
p.ui.popup.code.loading = true;
|
|
||||||
p.render();
|
|
||||||
} else if (arg.event === "code-done") {
|
|
||||||
if (typeof arg.content === "string") {
|
|
||||||
if (arg.content !== "OK") {
|
|
||||||
p.ui.popup.code.error = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
p.ui.popup.code.log += arg.content;
|
|
||||||
}
|
|
||||||
p.ui.popup.code.loading = false;
|
|
||||||
|
|
||||||
if (arg.src) {
|
|
||||||
const w = window as any;
|
|
||||||
const module = evalCJS(decoder.decode(decompress(arg.src)));
|
|
||||||
p.global_prop = Object.keys(module);
|
|
||||||
if (typeof module === "object") {
|
|
||||||
for (const [k, v] of Object.entries(module)) {
|
|
||||||
w[k] = v;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
p.render();
|
|
||||||
} else {
|
|
||||||
if (typeof arg.content === "string")
|
|
||||||
p.ui.popup.code.log += arg.content;
|
|
||||||
p.render();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
activity(arg) {},
|
|
||||||
opened() {
|
opened() {
|
||||||
if (w.offline) {
|
if (w.offline) {
|
||||||
console.log("reconnected!");
|
console.log("reconnected!");
|
||||||
w.offline = false;
|
w.offline = false;
|
||||||
p.ui.syncing = true;
|
p.ui.syncing = true;
|
||||||
p.sync.activity("site", { type: "join", id: params.site_id });
|
|
||||||
p.render();
|
p.render();
|
||||||
} else {
|
} else {
|
||||||
w.offline = false;
|
w.offline = false;
|
||||||
|
|
@ -158,6 +122,8 @@ export const edInitSync = (p: PG) => {
|
||||||
doc = p.page.doc as Y.Doc;
|
doc = p.page.doc as Y.Doc;
|
||||||
} else if (data.type === "comp" && p.comp.list[data.id]) {
|
} else if (data.type === "comp" && p.comp.list[data.id]) {
|
||||||
doc = p.comp.list[data.id].doc;
|
doc = p.comp.list[data.id].doc;
|
||||||
|
} else if (data.type === "code") {
|
||||||
|
doc = p.code.site.doc;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (doc) {
|
if (doc) {
|
||||||
|
|
@ -179,7 +145,7 @@ export const edInitSync = (p: PG) => {
|
||||||
Y.applyUpdate(doc, decompress(res.diff), "sv_remote");
|
Y.applyUpdate(doc, decompress(res.diff), "sv_remote");
|
||||||
if (data.type === "page") {
|
if (data.type === "page") {
|
||||||
await treeRebuild(p, { note: "sv_remote" });
|
await treeRebuild(p, { note: "sv_remote" });
|
||||||
} else {
|
} else if (data.type === "comp") {
|
||||||
const updated = await updateComponentMeta(p, doc, data.id);
|
const updated = await updateComponentMeta(p, doc, data.id);
|
||||||
if (updated) {
|
if (updated) {
|
||||||
p.comp.list[data.id].meta = updated.meta;
|
p.comp.list[data.id].meta = updated.meta;
|
||||||
|
|
|
||||||
|
|
@ -7,11 +7,11 @@ import { Popover } from "../../../../../utils/ui/popover";
|
||||||
import { Tooltip } from "../../../../../utils/ui/tooltip";
|
import { Tooltip } from "../../../../../utils/ui/tooltip";
|
||||||
import { EDGlobal } from "../../../logic/ed-global";
|
import { EDGlobal } from "../../../logic/ed-global";
|
||||||
import {
|
import {
|
||||||
iconChevronDown,
|
iconChevronDown,
|
||||||
iconLoading,
|
iconLoading,
|
||||||
iconLog,
|
iconLog,
|
||||||
iconNewTab,
|
iconNewTab,
|
||||||
iconTrash
|
iconTrash,
|
||||||
} from "./icons";
|
} from "./icons";
|
||||||
import { CodeNameItem, CodeNameList, codeName } from "./name-list";
|
import { CodeNameItem, CodeNameList, codeName } from "./name-list";
|
||||||
|
|
||||||
|
|
@ -116,7 +116,6 @@ const CodeBody = () => {
|
||||||
onPick={async (e) => {
|
onPick={async (e) => {
|
||||||
local.namePicker = false;
|
local.namePicker = false;
|
||||||
p.ui.popup.code.name = e.name;
|
p.ui.popup.code.name = e.name;
|
||||||
p.ui.popup.code.id = "";
|
|
||||||
p.render();
|
p.render();
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
@ -142,41 +141,6 @@ const CodeBody = () => {
|
||||||
></div>
|
></div>
|
||||||
</Popover>
|
</Popover>
|
||||||
|
|
||||||
{p.ui.popup.code.name !== "site" &&
|
|
||||||
p.ui.popup.code.name !== "SSR" && (
|
|
||||||
<>
|
|
||||||
<Tooltip
|
|
||||||
content={"Delete Code Module"}
|
|
||||||
className="flex items-center border-l relative hover:bg-red-50 cursor-pointer px-2 transition-all text-red-500"
|
|
||||||
placement="bottom"
|
|
||||||
onClick={async () => {
|
|
||||||
if (
|
|
||||||
prompt(
|
|
||||||
"Are you sure want to delete this code?\ntype 'yes' to confirm:"
|
|
||||||
) === "yes"
|
|
||||||
) {
|
|
||||||
await db.code.delete({
|
|
||||||
where: { id: p.ui.popup.code.id },
|
|
||||||
});
|
|
||||||
|
|
||||||
codeName.list = codeName.list.filter(
|
|
||||||
(e) => e.id !== p.ui.popup.code.id
|
|
||||||
);
|
|
||||||
|
|
||||||
p.ui.popup.code.name = codeName.list[0].name;
|
|
||||||
p.ui.popup.code.id = codeName.list[0].id;
|
|
||||||
p.render();
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
dangerouslySetInnerHTML={{
|
|
||||||
__html: iconTrash,
|
|
||||||
}}
|
|
||||||
></div>
|
|
||||||
</Tooltip>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
<Tooltip
|
<Tooltip
|
||||||
content="STDOUT Log"
|
content="STDOUT Log"
|
||||||
delay={0}
|
delay={0}
|
||||||
|
|
@ -211,9 +175,7 @@ const CodeBody = () => {
|
||||||
placement="bottom"
|
placement="bottom"
|
||||||
className={cx("flex items-stretch relative")}
|
className={cx("flex items-stretch relative")}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
window.open(
|
window.open(`${vscode_url}folder=/site/${p.site.id}/site/src`);
|
||||||
`${vscode_url}folder=/site/code/${p.site.id}/${p.ui.popup.code.id}`
|
|
||||||
);
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
|
@ -271,13 +233,13 @@ const CodeBody = () => {
|
||||||
|
|
||||||
{code_mode === "vsc" ? (
|
{code_mode === "vsc" ? (
|
||||||
<div className="flex flex-1 relative">
|
<div className="flex flex-1 relative">
|
||||||
{!p.ui.popup.code.open || !p.ui.popup.code.id ? (
|
{!p.ui.popup.code.open ? (
|
||||||
<Loading backdrop={false} />
|
<Loading backdrop={false} />
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<iframe
|
<iframe
|
||||||
className="flex flex-1 absolute inset-0 w-full h-full z-10"
|
className="flex flex-1 absolute inset-0 w-full h-full z-10"
|
||||||
src={`${vscode_url}folder=/site/code/${p.site.id}/${p.ui.popup.code.id}`}
|
src={`${vscode_url}folder=/site/${p.site.id}/site/src`}
|
||||||
></iframe>
|
></iframe>
|
||||||
<div className="flex flex-1 absolute inset-0 z-0 items-center justify-center">
|
<div className="flex flex-1 absolute inset-0 z-0 items-center justify-center">
|
||||||
Loading VSCode...
|
Loading VSCode...
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,57 @@
|
||||||
|
import { decompress } from "wasm-gzip";
|
||||||
|
import { loadApiProxyDef } from "../../../base/load/api/api-proxy-def";
|
||||||
|
import { PG } from "../../ed/logic/ed-global";
|
||||||
|
import { evalCJS } from "../../ed/logic/ed-sync";
|
||||||
|
import { treeRebuild } from "../../ed/logic/tree/build";
|
||||||
|
|
||||||
|
const decoder = new TextDecoder();
|
||||||
|
|
||||||
|
export const viLoadSnapshot = async (p: PG) => {
|
||||||
|
let api_url = p.site.config.api_url;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const apiURL = new URL(api_url);
|
||||||
|
if (api_url && apiURL.hostname) {
|
||||||
|
try {
|
||||||
|
await loadApiProxyDef(api_url, true);
|
||||||
|
} catch (e) {
|
||||||
|
console.warn("Failed to load API:", api_url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {}
|
||||||
|
|
||||||
|
if (p.site.code.snapshot) {
|
||||||
|
for (const [name, build] of Object.entries(p.site.code.snapshot)) {
|
||||||
|
const doc = new Y.Doc();
|
||||||
|
Y.applyUpdate(doc, decompress(build.bin));
|
||||||
|
|
||||||
|
p.code[name] = { doc: doc as any };
|
||||||
|
const code = p.code[name].doc;
|
||||||
|
if (code) {
|
||||||
|
const src = code.getMap("map").get("files")?.get("index.js");
|
||||||
|
applyEnv(p, src);
|
||||||
|
treeRebuild(p);
|
||||||
|
p.render();
|
||||||
|
code.on("update", (e, origin) => {
|
||||||
|
const src = code.getMap("map").get("files")?.get("index.js");
|
||||||
|
applyEnv(p, src);
|
||||||
|
treeRebuild(p);
|
||||||
|
p.render();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const applyEnv = (p: PG, src?: string) => {
|
||||||
|
if (src) {
|
||||||
|
const w = window as any;
|
||||||
|
const module = evalCJS(src);
|
||||||
|
p.global_prop = Object.keys(module);
|
||||||
|
if (typeof module === "object") {
|
||||||
|
for (const [k, v] of Object.entries(module)) {
|
||||||
|
w[k] = v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
@ -77,28 +77,9 @@ export const clientStartSync = async (arg: {
|
||||||
site_id?: string;
|
site_id?: string;
|
||||||
page_id?: string;
|
page_id?: string;
|
||||||
events: {
|
events: {
|
||||||
code: (arg: {
|
|
||||||
name: string;
|
|
||||||
id: string;
|
|
||||||
event: "code-loading" | "code-done";
|
|
||||||
content?: string;
|
|
||||||
src?: Uint8Array;
|
|
||||||
}) => void;
|
|
||||||
activity: (arg: {
|
|
||||||
activity: string;
|
|
||||||
room_id: string;
|
|
||||||
clients: {
|
|
||||||
user: {
|
|
||||||
client_id: string;
|
|
||||||
user_id: string;
|
|
||||||
username: string;
|
|
||||||
};
|
|
||||||
data: any;
|
|
||||||
}[];
|
|
||||||
}) => void;
|
|
||||||
editor_start: (arg: UserConf) => void;
|
editor_start: (arg: UserConf) => void;
|
||||||
remote_svlocal: (arg: {
|
remote_svlocal: (arg: {
|
||||||
type: "page" | "comp";
|
type: "page" | "comp" | "code";
|
||||||
id: string;
|
id: string;
|
||||||
sv_local: Uint8Array;
|
sv_local: Uint8Array;
|
||||||
}) => void;
|
}) => void;
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,6 @@ export type DComp = TypedDoc<{
|
||||||
export type DCode = TypedDoc<{
|
export type DCode = TypedDoc<{
|
||||||
map: TypedMap<{
|
map: TypedMap<{
|
||||||
id: string;
|
id: string;
|
||||||
files: TypedMap<Record<string, Y.Text>>;
|
files: TypedMap<Record<string, string>>;
|
||||||
}>;
|
}>;
|
||||||
}>;
|
}>;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue