adding brotli sync ws
This commit is contained in:
parent
e0984794de
commit
2d53709630
|
|
@ -0,0 +1,182 @@
|
|||
import { syncronize } from "y-pojo";
|
||||
import { IItem, MItem } from "../../web/src/utils/types/item";
|
||||
import { eg } from "../ws/edit/edit-global";
|
||||
|
||||
export const _ = {
|
||||
url: "/comp-create",
|
||||
async api(arg: {
|
||||
site_id: string;
|
||||
page_id?: string;
|
||||
item_id: string;
|
||||
comp_id?: string;
|
||||
group_id?: string;
|
||||
}) {
|
||||
const { page_id, site_id, item_id, comp_id, group_id } = arg;
|
||||
let element = undefined as MItem | undefined;
|
||||
const walk = (el: MItem): MItem | undefined => {
|
||||
if (el.get("id") === item_id) {
|
||||
return el;
|
||||
}
|
||||
let final = null;
|
||||
|
||||
const props = el.get("component")?.get("props");
|
||||
if (props) {
|
||||
props.forEach((e) => {
|
||||
const content = e.get("content");
|
||||
if (content) {
|
||||
const result = walk(content);
|
||||
if (result) final = result;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const childs = el.get("childs");
|
||||
childs?.forEach((e: any) => {
|
||||
const result = walk(e);
|
||||
if (result) final = result;
|
||||
});
|
||||
if (final) return final;
|
||||
};
|
||||
|
||||
const page = eg.edit.page[page_id || ""];
|
||||
if (page_id) {
|
||||
if (page) {
|
||||
const root = page.doc.getMap("map").get("content_tree");
|
||||
if (root) {
|
||||
element = walk(root as any);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const comp = eg.edit.comp[comp_id || ""];
|
||||
if (comp_id) {
|
||||
if (comp) {
|
||||
const root = comp.doc.getMap("map").get("content_tree");
|
||||
if (root) {
|
||||
element = walk(root as any);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let gid = group_id;
|
||||
if (!gid) {
|
||||
let group = await db.component_group.findFirst({
|
||||
where: {
|
||||
component_site: {
|
||||
some: {
|
||||
id_site: site_id,
|
||||
},
|
||||
},
|
||||
name: {
|
||||
not: {
|
||||
equals: "__TRASH__",
|
||||
},
|
||||
},
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (!group) {
|
||||
group = await db.component_group.create({
|
||||
data: {
|
||||
name: "All",
|
||||
component_site: {
|
||||
create: {
|
||||
id_site: site_id,
|
||||
},
|
||||
},
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
gid = group.id;
|
||||
}
|
||||
if (element) {
|
||||
const newcomp = await db.component.create({
|
||||
data: {
|
||||
name: element.get("name") || "",
|
||||
content_tree: element.toJSON(),
|
||||
component_group: {
|
||||
connect: {
|
||||
id: gid,
|
||||
},
|
||||
},
|
||||
},
|
||||
select: {
|
||||
content_tree: true,
|
||||
id: true,
|
||||
},
|
||||
});
|
||||
if (newcomp) {
|
||||
const content_tree = {
|
||||
...(newcomp.content_tree as any),
|
||||
component: {
|
||||
id: newcomp.id,
|
||||
group: {
|
||||
id: gid,
|
||||
},
|
||||
},
|
||||
};
|
||||
await db.component.update({
|
||||
data: {
|
||||
content_tree: content_tree,
|
||||
},
|
||||
where: {
|
||||
id: newcomp.id,
|
||||
},
|
||||
});
|
||||
|
||||
const json = element.toJSON() as IItem;
|
||||
syncronize(
|
||||
element as any,
|
||||
{
|
||||
...json,
|
||||
childs: [],
|
||||
component: {
|
||||
id: newcomp.id,
|
||||
name: "",
|
||||
props: {},
|
||||
},
|
||||
} as IItem
|
||||
);
|
||||
|
||||
if (comp_id) {
|
||||
await db.component.update({
|
||||
where: {
|
||||
id: comp_id,
|
||||
},
|
||||
data: {
|
||||
content_tree: comp.doc
|
||||
.getMap("map")
|
||||
.get("content_tree")
|
||||
?.toJSON(),
|
||||
},
|
||||
});
|
||||
} else if (page && page.id) {
|
||||
await db.page.update({
|
||||
where: {
|
||||
id: page.id,
|
||||
},
|
||||
data: {
|
||||
content_tree: page.doc
|
||||
.getMap("map")
|
||||
.get("content_tree")
|
||||
?.toJSON(),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
id: newcomp.id,
|
||||
group_id: gid,
|
||||
};
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
|
@ -7,6 +7,7 @@
|
|||
"@node-rs/argon2": "^1.5.2",
|
||||
"@paralleldrive/cuid2": "^2.2.2",
|
||||
"@types/mime-types": "^2.1.2",
|
||||
"brotli-wasm": "^2.0.1",
|
||||
"esbuild": "^0.19.4",
|
||||
"lz-string": "^1.5.0",
|
||||
"mime-types": "^2.1.35",
|
||||
|
|
|
|||
|
|
@ -1,15 +1,16 @@
|
|||
import { createId } from "@paralleldrive/cuid2";
|
||||
import brotliPromise from "brotli-wasm";
|
||||
import { WebSocketHandler } from "bun";
|
||||
import { decompress } from "lz-string";
|
||||
import { WSData } from "../../../pkgs/core/server/create";
|
||||
import { WS_MSG } from "../../web/src/utils/types/ws";
|
||||
import { eg } from "./edit/edit-global";
|
||||
import { decompress } from "lz-string";
|
||||
import { getPage } from "./edit/action/get-page";
|
||||
import { getComp } from "./edit/action/get-comp";
|
||||
import { svLocal } from "./edit/action/sv-local";
|
||||
import { diffLocal } from "./edit/action/diff-local";
|
||||
import { getComp } from "./edit/action/get-comp";
|
||||
import { getPage } from "./edit/action/get-page";
|
||||
import { svLocal } from "./edit/action/sv-local";
|
||||
import { svdiffRemote } from "./edit/action/svdiff-remote";
|
||||
import { redo, undo } from "./edit/action/undo-redo";
|
||||
import { eg } from "./edit/edit-global";
|
||||
|
||||
eg.edit = {
|
||||
site: {},
|
||||
|
|
@ -21,7 +22,25 @@ const site = {
|
|||
saveTimeout: null as any,
|
||||
};
|
||||
|
||||
const brotli = await brotliPromise;
|
||||
export const wsHandler: Record<string, WebSocketHandler<WSData>> = {
|
||||
"/live": {
|
||||
async open(ws) {
|
||||
ws.send(
|
||||
brotli.compress(
|
||||
Buffer.from(
|
||||
JSON.stringify(
|
||||
await db.page.findFirst({
|
||||
where: { id: "324dde34-1e01-46ff-929c-124e5e01f585" },
|
||||
})
|
||||
)
|
||||
),
|
||||
{ quality: 11 }
|
||||
)
|
||||
);
|
||||
},
|
||||
message(ws, message) {},
|
||||
},
|
||||
"/edit": {
|
||||
open(ws) {
|
||||
eg.edit.ws.set(ws, {
|
||||
|
|
|
|||
|
|
@ -6,6 +6,10 @@
|
|||
"...",
|
||||
"@tinijs/parcel-reporter-copy-public"
|
||||
],
|
||||
"packagers": {
|
||||
"*.wasm": "@parcel/packager-wasm"
|
||||
},
|
||||
|
||||
"transformers": {
|
||||
"*.wasm": [
|
||||
"...",
|
||||
|
|
|
|||
|
|
@ -11,9 +11,11 @@
|
|||
"@minoru/react-dnd-treeview": "^3.4.4",
|
||||
"@monaco-editor/react": "^4.6.0",
|
||||
"@paralleldrive/cuid2": "2.2.2",
|
||||
"@parcel/packager-wasm": "^2.10.0",
|
||||
"@parcel/service-worker": "^2.10.0",
|
||||
"@swc/wasm-web": "1.3.94-nightly-20231014.1",
|
||||
"algoliasearch": "^4.20.0",
|
||||
"brotli-dec-wasm": "^2.0.1",
|
||||
"date-fns": "^2.30.0",
|
||||
"dbgen": "workspace:*",
|
||||
"downshift": "^8.2.2",
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ export default page({
|
|||
domain_or_siteid={params.domain}
|
||||
pathname={pathname}
|
||||
loader={devLoader}
|
||||
liveSync
|
||||
/>
|
||||
);
|
||||
},
|
||||
|
|
|
|||
|
|
@ -5,12 +5,14 @@ import { LPage } from "./elements/l-page";
|
|||
import { LiveGlobal, Loader } from "./logic/global";
|
||||
import { initLive, w } from "./logic/init";
|
||||
import { preload, routeLive } from "./logic/route";
|
||||
import { liveSyncWS } from "./logic/ws-sync";
|
||||
|
||||
export const Live: FC<{
|
||||
domain_or_siteid: string;
|
||||
pathname: string;
|
||||
loader: Loader;
|
||||
}> = ({ domain_or_siteid, pathname, loader }) => {
|
||||
liveSync?: boolean;
|
||||
}> = ({ domain_or_siteid, pathname, loader, liveSync }) => {
|
||||
const p = useGlobal(LiveGlobal, "LIVE");
|
||||
p.loader = loader;
|
||||
|
||||
|
|
@ -55,6 +57,9 @@ export const Live: FC<{
|
|||
}, [p.site.responsive]);
|
||||
|
||||
if (p.status === "init") {
|
||||
if (liveSync) {
|
||||
liveSyncWS(p);
|
||||
}
|
||||
initLive(p, domain_or_siteid);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -67,6 +67,11 @@ export type Loader = {
|
|||
comp: (p: PG, id: string) => Promise<PRASI_COMPONENT>;
|
||||
};
|
||||
export const LiveGlobal = {
|
||||
liveSync: {
|
||||
ws: null as null | WebSocket,
|
||||
init: false,
|
||||
decompress: null as null | ((buf: Uint8Array) => Uint8Array),
|
||||
},
|
||||
prod: false,
|
||||
loader: undefined as unknown as Loader,
|
||||
mode: "" as "desktop" | "mobile",
|
||||
|
|
|
|||
|
|
@ -4,8 +4,7 @@ import { type apiClient } from "web-utils";
|
|||
import {
|
||||
createAPI,
|
||||
createDB,
|
||||
initApi,
|
||||
reloadDBAPI,
|
||||
initApi
|
||||
} from "../../../utils/script/init-api";
|
||||
import importModule from "../../editor/tools/dynamic-import";
|
||||
import { LSite, PG } from "./global";
|
||||
|
|
|
|||
|
|
@ -1,12 +1,8 @@
|
|||
import { page } from "dbgen";
|
||||
import { validate } from "uuid";
|
||||
import { w } from "../../../utils/types/general";
|
||||
import { WS_MSG_GET_PAGE } from "../../../utils/types/ws";
|
||||
import importModule from "../../editor/tools/dynamic-import";
|
||||
import { loadComponent } from "./comp";
|
||||
import { LPage, PG } from "./global";
|
||||
import { rebuildTree } from "./tree-logic";
|
||||
import { liveWS, wsend } from "./ws";
|
||||
|
||||
export const routeLive = (p: PG, pathname: string) => {
|
||||
if (p.status !== "loading" && p.status !== "not-found") {
|
||||
|
|
@ -127,7 +123,6 @@ const loadPage = async (p: PG, id: string) => {
|
|||
content_tree: page.content_tree as any,
|
||||
js: (page as any).js_compiled as any,
|
||||
};
|
||||
console.log(p.pages[page.id]);
|
||||
|
||||
const cur = p.pages[page.id];
|
||||
if (cur && cur.content_tree) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,28 @@
|
|||
import { PG } from "./global";
|
||||
|
||||
export const liveSyncWS = async (p: PG) => {
|
||||
if (!p.liveSync.init) {
|
||||
p.liveSync.init = true;
|
||||
|
||||
if (!p.liveSync.decompress) {
|
||||
const brotliPromise = (await import("brotli-dec-wasm")).default;
|
||||
p.liveSync.decompress = (await brotliPromise).decompress;
|
||||
}
|
||||
const decoder = new TextDecoder();
|
||||
const url = new URL(location.href);
|
||||
url.pathname = "/live";
|
||||
url.protocol = url.protocol === "http:" ? "ws:" : "wss:";
|
||||
|
||||
const ws = new WebSocket(url);
|
||||
p.liveSync.ws = ws;
|
||||
ws.onmessage = async (e) => {
|
||||
const decompress = p.liveSync.decompress;
|
||||
if (decompress) {
|
||||
const raw = e.data as Blob;
|
||||
const extracted = decompress(new Uint8Array(await raw.arrayBuffer()));
|
||||
const json = JSON.parse(decoder.decode(extracted));
|
||||
console.log(json);
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
|
@ -1,297 +0,0 @@
|
|||
import throttle from "lodash.throttle";
|
||||
import { compress, decompress } from "lz-string";
|
||||
import * as Y from "yjs";
|
||||
import { createAPI, createDB } from "../../../utils/script/init-api";
|
||||
import { MPage } from "../../../utils/types/general";
|
||||
import {
|
||||
WS_MSG,
|
||||
WS_MSG_DIFF_LOCAL,
|
||||
WS_MSG_SET_PAGE,
|
||||
WS_MSG_SVDIFF_REMOTE,
|
||||
WS_MSG_SV_LOCAL,
|
||||
} from "../../../utils/types/ws";
|
||||
import importModule from "../../editor/tools/dynamic-import";
|
||||
import { PG } from "./global";
|
||||
import { rebuildTree } from "./tree-logic";
|
||||
import { CompDoc } from "../../../base/global/content-editor";
|
||||
import { IItem } from "../../../utils/types/item";
|
||||
import { scanComponent } from "./comp";
|
||||
import { PRASI_COMPONENT } from "../../../utils/types/render";
|
||||
|
||||
export const liveWS = async (p: PG) => {
|
||||
return new Promise<void>(async (resolve) => {
|
||||
const wsurl = new URL(serverurl);
|
||||
wsurl.protocol = wsurl.protocol.startsWith("http:") ? "ws:" : "wss:";
|
||||
|
||||
if (
|
||||
p.wsRetry.localIP &&
|
||||
["localhost", "127.0.0.1"].includes(wsurl.hostname)
|
||||
) {
|
||||
const ips = await api.local_ip();
|
||||
wsurl.hostname = ips[0];
|
||||
}
|
||||
|
||||
wsurl.pathname = "/edit";
|
||||
|
||||
if (p.ws && p.ws.readyState === p.ws.OPEN) {
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
p.ws = new WebSocket(wsurl);
|
||||
const ws = p.ws;
|
||||
|
||||
if (ws) {
|
||||
const retry = (e: any) => {
|
||||
if (p.wsRetry.disabled) return;
|
||||
|
||||
p.wsRetry.reconnecting = true;
|
||||
p.wsRetry.localIP = true;
|
||||
if (p.wsRetry.fast) {
|
||||
liveWS(p);
|
||||
} else {
|
||||
setTimeout(() => {
|
||||
console.log("Reconnecting...");
|
||||
liveWS(p);
|
||||
}, 2000);
|
||||
}
|
||||
};
|
||||
ws.addEventListener("error", retry);
|
||||
ws.addEventListener("close", retry);
|
||||
ws.addEventListener("open", () => {
|
||||
if (p.wsRetry.reconnecting) {
|
||||
p.wsRetry.reconnecting = false;
|
||||
console.log("Connected");
|
||||
}
|
||||
resolve();
|
||||
});
|
||||
ws.addEventListener("message", async (e) => {
|
||||
const msg = JSON.parse(e.data) as WS_MSG;
|
||||
|
||||
switch (msg.type) {
|
||||
case "get_page":
|
||||
break;
|
||||
case "set_page":
|
||||
if (p.mpage) {
|
||||
p.mpage.destroy();
|
||||
}
|
||||
p.mpage = await setPage(msg);
|
||||
p.mpage.on(
|
||||
"update",
|
||||
throttle((e, origin) => {
|
||||
if (p.mpage) {
|
||||
p.page = p.mpage.getMap("map").toJSON() as any;
|
||||
|
||||
console.clear();
|
||||
console.log(
|
||||
`🔥 Page updated: ${p.page
|
||||
?.url} ${new Date().toLocaleString()}`
|
||||
);
|
||||
}
|
||||
})
|
||||
);
|
||||
p.page = p.mpage.getMap("map").toJSON() as any;
|
||||
|
||||
if (p.mpageLoaded) {
|
||||
p.mpageLoaded(p.mpage);
|
||||
p.mpageLoaded = null;
|
||||
}
|
||||
break;
|
||||
case "sv_local":
|
||||
svLocal({ p, bin: extract(msg.sv_local), msg });
|
||||
break;
|
||||
case "svd_remote":
|
||||
svdRemote({ p, bin: extract(msg.diff_remote), msg });
|
||||
rebuildTree(p, { note: "page-changed" });
|
||||
break;
|
||||
case "diff_local":
|
||||
if (msg.mode === "page") {
|
||||
Y.applyUpdate(p.mpage as any, extract(msg.diff_local), "remote");
|
||||
}
|
||||
if (msg.mode === "comp") {
|
||||
Y.applyUpdate(
|
||||
p.comps.doc[msg.id] as any,
|
||||
extract(msg.diff_local),
|
||||
"remote"
|
||||
);
|
||||
}
|
||||
break;
|
||||
case "set_comp":
|
||||
{
|
||||
const callback = p.comps.resolve[msg.comp_id];
|
||||
if (callback) {
|
||||
p.comps.doc[msg.comp_id] = new Y.Doc() as CompDoc;
|
||||
Y.applyUpdate(
|
||||
p.comps.doc[msg.comp_id] as any,
|
||||
extract(msg.changes),
|
||||
"remote"
|
||||
);
|
||||
setTimeout(() => {
|
||||
p.comps.doc[msg.comp_id].on(
|
||||
"update",
|
||||
throttle((e, origin) => {
|
||||
if (origin === "remote") {
|
||||
return;
|
||||
}
|
||||
|
||||
const doc = p.comps.doc[msg.comp_id];
|
||||
if (doc) {
|
||||
if (!origin && origin !== "updated_at") {
|
||||
const id = doc.getMap("map").get("id");
|
||||
if (id) {
|
||||
doc.transact(() => {
|
||||
doc
|
||||
.getMap("map")
|
||||
.set("updated_at", new Date().toISOString());
|
||||
}, "updated_at");
|
||||
|
||||
const sendmsg: WS_MSG_SV_LOCAL = {
|
||||
type: "sv_local",
|
||||
mode: "comp",
|
||||
id,
|
||||
sv_local: compress(
|
||||
Y.encodeStateVector(doc as any).toString()
|
||||
),
|
||||
};
|
||||
wsend(p, JSON.stringify(sendmsg));
|
||||
}
|
||||
}
|
||||
}
|
||||
}, 200)
|
||||
);
|
||||
}, 500);
|
||||
const comp = p.comps.doc[msg.comp_id]
|
||||
.getMap("map")
|
||||
.get("content_tree")
|
||||
?.toJSON() as IItem;
|
||||
|
||||
const ids = new Set<string>();
|
||||
scanComponent(comp, ids);
|
||||
|
||||
callback(
|
||||
p.comps.doc[msg.comp_id]
|
||||
.getMap("map")
|
||||
.toJSON() as PRASI_COMPONENT
|
||||
);
|
||||
delete p.comps.pending[msg.comp_id];
|
||||
delete p.comps.resolve[msg.comp_id];
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "undo":
|
||||
case "redo":
|
||||
case "new_comp":
|
||||
case "get_comp":
|
||||
}
|
||||
});
|
||||
ws.addEventListener("open", () => {
|
||||
p.wsRetry.disabled = false;
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const extract = (str: string) => {
|
||||
return Uint8Array.from(
|
||||
decompress(str)
|
||||
.split(",")
|
||||
.map((x) => parseInt(x, 10))
|
||||
);
|
||||
};
|
||||
|
||||
const svLocal = async (arg: {
|
||||
bin: Uint8Array;
|
||||
msg: {
|
||||
id: string;
|
||||
mode: string;
|
||||
type: string;
|
||||
};
|
||||
p: PG;
|
||||
}) => {
|
||||
const { bin, msg, p } = arg;
|
||||
const { id, mode, type } = msg;
|
||||
|
||||
let doc = null as any;
|
||||
if (mode === "page") doc = p.mpage;
|
||||
if (mode === "comp") doc = p.comps.doc[id];
|
||||
if (!doc) return;
|
||||
|
||||
const diff_remote = Y.encodeStateAsUpdate(doc, bin);
|
||||
const sv_remote = Y.encodeStateVector(doc);
|
||||
|
||||
const sendmsg: any = {
|
||||
diff_remote: compress(diff_remote.toString()),
|
||||
sv_remote: compress(sv_remote.toString()),
|
||||
id: id,
|
||||
mode: mode,
|
||||
type: type,
|
||||
};
|
||||
await wsend(p, JSON.stringify(sendmsg));
|
||||
};
|
||||
|
||||
const svdRemote = async (arg: {
|
||||
bin: Uint8Array;
|
||||
msg: WS_MSG_SVDIFF_REMOTE;
|
||||
p: PG;
|
||||
}) => {
|
||||
const { bin, msg, p } = arg;
|
||||
const { id, mode, type } = msg;
|
||||
const sv_remote = Uint8Array.from(
|
||||
decompress(msg.sv_remote)
|
||||
.split(",")
|
||||
.map((x) => parseInt(x, 10))
|
||||
);
|
||||
const diff_remote = Uint8Array.from(
|
||||
decompress(msg.diff_remote)
|
||||
.split(",")
|
||||
.map((x) => parseInt(x, 10))
|
||||
);
|
||||
|
||||
const sendDoc = async (doc: any) => {
|
||||
const diff_local = Y.encodeStateAsUpdate(doc as any, sv_remote);
|
||||
Y.applyUpdate(doc as any, diff_remote, "local");
|
||||
const sendmsg: WS_MSG_DIFF_LOCAL = {
|
||||
type: "diff_local",
|
||||
mode: msg.mode,
|
||||
id: msg.id,
|
||||
diff_local: compress(diff_local.toString()),
|
||||
};
|
||||
await wsend(p, JSON.stringify(sendmsg));
|
||||
};
|
||||
|
||||
let doc = null as any;
|
||||
if (mode === "page") doc = p.mpage;
|
||||
if (mode === "comp") doc = p.comps.doc[id];
|
||||
if (!doc) return;
|
||||
sendDoc(doc);
|
||||
};
|
||||
|
||||
export const wsend = async (local: PG, payload: string) => {
|
||||
const ws = local.ws;
|
||||
if (ws) {
|
||||
if (ws.readyState !== ws.OPEN) {
|
||||
await new Promise<void>((resolve) => {
|
||||
const ival = setInterval(() => {
|
||||
if (ws.readyState === ws.OPEN) {
|
||||
clearInterval(ival);
|
||||
resolve();
|
||||
}
|
||||
}, 50);
|
||||
});
|
||||
}
|
||||
|
||||
ws.send(payload);
|
||||
}
|
||||
};
|
||||
|
||||
const setPage = async (msg: WS_MSG_SET_PAGE) => {
|
||||
const page = Uint8Array.from(
|
||||
decompress(msg.changes)
|
||||
.split(",")
|
||||
.map((x) => parseInt(x, 10))
|
||||
);
|
||||
|
||||
const doc = new Y.Doc();
|
||||
Y.applyUpdate(doc as any, page, "remote");
|
||||
|
||||
return doc as unknown as MPage;
|
||||
};
|
||||
|
|
@ -11,7 +11,8 @@ const args = [
|
|||
dir.path("node_modules/.bin/parcel"),
|
||||
"build",
|
||||
"./src/index.tsx",
|
||||
"--no-optimize",
|
||||
// "--no-optimize",
|
||||
"--no-scope-hoist",
|
||||
"--dist-dir",
|
||||
dir.path(`app/static`),
|
||||
];
|
||||
|
|
|
|||
Loading…
Reference in New Issue