From 993951eb898791406edfb2ae634c0515d931d9e6 Mon Sep 17 00:00:00 2001 From: Rizky Date: Sun, 5 May 2024 19:27:54 +0700 Subject: [PATCH] checkpoint --- app/srv/api/prod.ts | 6 +- app/srv/ws/sync/code/code.ts | 3 +- app/srv/ws/sync/code/parts/init/frontend.ts | 115 +++++++-- app/srv/ws/sync/editor/code/build-code.ts | 261 -------------------- app/srv/ws/sync/editor/code/server-main.ts | 76 +++--- pkgs/core/utils/global.ts | 2 +- 6 files changed, 141 insertions(+), 322 deletions(-) delete mode 100644 app/srv/ws/sync/editor/code/build-code.ts diff --git a/app/srv/api/prod.ts b/app/srv/api/prod.ts index d3b17182..8a79f050 100644 --- a/app/srv/api/prod.ts +++ b/app/srv/api/prod.ts @@ -4,6 +4,7 @@ import { validate } from "uuid"; import { prodIndex } from "../util/prod-index"; import { gzipAsync } from "../ws/sync/entity/zlib"; import { code } from "../ws/sync/code/code"; +import { initFrontEnd } from "../ws/sync/code/parts/init/frontend"; export const _ = { url: "/prod/:site_id/**", @@ -30,8 +31,11 @@ export const _ = { const build_path = code.path(site_id, "site", "build", codepath); const file = Bun.file(build_path); - if (!(await file.exists())) + if (!(await file.exists())) { + const root = `/code/${site_id}/site/src`; + await initFrontEnd(root, site_id); return new Response("Code file not found", { status: 403 }); + } return new Response(file); } diff --git a/app/srv/ws/sync/code/code.ts b/app/srv/ws/sync/code/code.ts index 1920c086..6107dea9 100644 --- a/app/srv/ws/sync/code/code.ts +++ b/app/srv/ws/sync/code/code.ts @@ -1,10 +1,9 @@ import { dir } from "dir"; import { initFrontEnd } from "./parts/init/frontend"; import { initServer } from "./parts/init/server"; -import { buildTypes } from "./parts/init/typings"; import { codeInternal } from "./parts/internal"; -import { ensureLib } from "./utlis/ensure-lib"; import { ensureFiles } from "./utlis/ensure-files"; +import { ensureLib } from "./utlis/ensure-lib"; export const code = { internal: codeInternal, diff --git a/app/srv/ws/sync/code/parts/init/frontend.ts b/app/srv/ws/sync/code/parts/init/frontend.ts index 071f0c2d..c61298e8 100644 --- a/app/srv/ws/sync/code/parts/init/frontend.ts +++ b/app/srv/ws/sync/code/parts/init/frontend.ts @@ -1,12 +1,12 @@ import globalExternals from "@fal-works/esbuild-plugin-global-externals"; import style from "@hyrious/esbuild-plugin-style"; import { dir } from "dir"; -import { context } from "esbuild"; +import { BuildOptions, BuildResult, context } from "esbuild"; import { removeAsync } from "fs-jetpack"; import isEqual from "lodash.isequal"; +import { appendFile } from "node:fs/promises"; import { code } from "../../code"; import { buildTypes } from "./typings"; -import { appendFile } from "node:fs/promises"; const decoder = new TextDecoder(); export const initFrontEnd = async (root: string, id_site: string) => { @@ -21,13 +21,12 @@ export const initFrontEnd = async (root: string, id_site: string) => { try { await isInstalling(id_site); - - const build_path = dir.data(`code/${id_site}/site/build`); - await removeAsync(build_path); + const out_dir = dir.data(`code/${id_site}/site/build`); + await removeAsync(out_dir); const existing = await context({ absWorkingDir: dir.data(root), entryPoints: ["index.tsx"], - outdir: build_path, + outdir: out_dir, format: "esm", bundle: true, minify: true, @@ -52,6 +51,23 @@ export const initFrontEnd = async (root: string, id_site: string) => { name: "prasi", async setup(setup) { try { + await codeError(id_site, "Building..."); + setup.onStart(async () => { + if (!(await isInstalling(id_site))) + await codeError(id_site, "Building..."); + }); + setup.onEnd(async (res) => { + if (res.errors.length > 0) { + if (!(await installDeps(root, res, id_site))) { + await codeError( + id_site, + res.errors.map((e) => e.text).join("\n\n") + ); + } + } else { + await buildTypes(root, id_site); + } + }); } catch (e) { console.log("ERROR"); } @@ -61,9 +77,9 @@ export const initFrontEnd = async (root: string, id_site: string) => { }); code.internal.frontend[id_site] = existing; await existing.watch(); - } catch (e) { + } catch (e: any) { + console.error("Error building front end", id_site); delete code.internal.frontend[id_site]; - console.log("front end error", e); } }; @@ -82,8 +98,10 @@ const isInstalling = async (id_site: string) => { const file = Bun.file(path); try { const text = await file.text(); - if (text.startsWith("Installing dependencies")) return true; + if (typeof text === "string" && text.startsWith("Installing dependencies")) + return true; } catch (e) {} + return false; }; @@ -91,18 +109,81 @@ const readPackageJSON = async (id_site: string) => { const file = Bun.file(code.path(id_site, "site", "src", "package.json")); const deps = new Set(); - const json = await file.json(); + if (await file.exists()) { + const json = await file.json(); - if (json.dependencies) { - for (const k of Object.keys(json.dependencies)) { - deps.add(k); + if (json.dependencies) { + for (const k of Object.keys(json.dependencies)) { + deps.add(k); + } } - } - if (json.devDependencies) { - for (const k of Object.keys(json.devDependencies)) { - deps.add(k); + if (json.devDependencies) { + for (const k of Object.keys(json.devDependencies)) { + deps.add(k); + } } } return deps; }; + +const installDeps = async ( + root: string, + res: BuildResult, + id_site: string +) => { + const pkgjson = await readPackageJSON(id_site); + const imports = new Set(); + + if (!(await isInstalling(id_site))) await codeError(id_site, ""); + + if (res.errors.length > 0) { + for (const err of res.errors) { + if (err.notes?.[0].text.startsWith("You can mark the path ")) { + let im = err.notes?.[0].text.split('"')[1]; + + if (!im.startsWith("@")) { + im = im.split("/").shift() || ""; + } + + imports.add(im); + } + } + } + + if (res.metafile) { + for (const [_, file] of Object.entries(res.metafile?.inputs || {})) { + for (const im of file.imports) { + if (im.kind === "import-statement" && im.external) { + if (!im.path.startsWith(".") && !im.path.startsWith("@/")) + imports.add(im.path); + } + } + } + } + + if (!isEqual(imports, pkgjson)) { + await codeError( + id_site, + "Installing dependencies:\n " + [...imports].join("\n ") + ); + let proc = Bun.spawn([`npm`, `install`, ...imports], { + stdio: ["inherit", "pipe", "pipe"], + cwd: dir.data(root), + }); + + async function print(generator: ReadableStream, prefix: any) { + for await (let value of generator) { + const str = decoder.decode(value); + await codeError(id_site, `${prefix} ${str}`, true); + } + } + + print(proc.stdout, "stdout:"); + print(proc.stderr, "stderr:"); + + await proc.exited; + await codeError(id_site, ""); + return true; + } +}; diff --git a/app/srv/ws/sync/editor/code/build-code.ts b/app/srv/ws/sync/editor/code/build-code.ts deleted file mode 100644 index 36eb5c99..00000000 --- a/app/srv/ws/sync/editor/code/build-code.ts +++ /dev/null @@ -1,261 +0,0 @@ -import { ServerWebSocket } from "bun"; -import { Packr } from "msgpackr"; -import { WSData } from "../../../../../../pkgs/core/server/create"; -import { code } from "../../code/code"; - -const packr = new Packr({ structuredClone: true }); - -const sendWS = (ws: ServerWebSocket, msg: any) => { - ws.sendBinary(packr.pack(msg)); -}; - -export const codeBuild = async (id_site: any) => { - return; - // const src_path = code.path(id_site, "site", "src"); - // if (!(await existsAsync(src_path))) return; - // if (!code.esbuild[id_site]) { - // code.esbuild[id_site] = { site: null, server: null, site_ts: Date.now() }; - // } - - // if (!code.esbuild[id_site].server) { - // const server_main = code.path(id_site, "site", "src", "server.ts"); - // if (!(await existsAsync(server_main))) { - // await writeAsync( - // server_main, - // `\ - // import type {} from "./typings/global"; - - // export const server: PrasiServer = { - // async http({ req, handle, mode, url, index, server }) { - // return await handle(req); - // } - // }; - // ` - // ); - // const bun_types = Bun.spawn({ - // cmd: ["npm", "i", "-D", "@types/bun"], - // cwd: code.path(id_site, "site", "src"), - // }); - // await bun_types.exited; - // } - - // const build_path = code.path(id_site, "server", "build"); - // await removeAsync(build_path); - // await dirAsync(build_path); - - // const build_file = `${build_path}/index.js`; - // if (!(await existsAsync(build_file))) { - // await writeAsync(build_file, ""); - // } - - // code.esbuild[id_site].server = await context({ - // absWorkingDir: src_path, - // entryPoints: ["server.ts"], - // bundle: true, - // outfile: build_file, - // platform: "node", - // treeShaking: true, - // format: "cjs", - // logLevel: "silent", - // banner: { - // js: `\ - // const _fs = require('node:fs/promises'); - // const console = - // typeof global.server_hook === "function" - // ? { ...global.console } - // : global.console; - - // let db = new Proxy({}, { - // get(_, key) { - // const runtime = global.server_runtime["${id_site}"]; - // if (runtime && runtime.db) { - // return runtime.db[key]; - // } - // } - // }); - // let api = {}; - // if (typeof global.server_hook === "function") { - // const log = global.console.log; - // console.log = function (...arg) { - // const out = "${code.path(id_site, "site", "src", "server.log")}"; - // _fs.appendFile(out, arg.map((e)=>{ - // const ancestors = []; - // if (typeof e === 'object') return JSON.stringify(e, function (key, val) { - // if (val) { - // if (typeof val === 'function') { - // return '[function]'; - // } - // if (typeof val === 'object') { - // while (ancestors.length > 0 && ancestors.at(-1) !== this) { - // ancestors.pop(); - // } - // if (ancestors.includes(val)) { - // return "[circular]"; - // } - // ancestors.push(val); - - // if (val.constructor && - // !['Object', 'Array'].includes(val.constructor.name)) { - // if (val.constructor.name === 'Error') { - // return '[Error] ' + val.message; - // } - // return '[Class] ' + val.constructor.name; - // } - // } - // } - // return val; - // }, 2); - // return e; - // }).join(" ") + "\\n"); - // }.bind(console); - // } else { - // db = global.db; - // api = global.api; - // }`, - // }, - // plugins: [ - // style(), - // globalExternals({ - // react: { - // varName: "window.React", - // type: "cjs", - // }, - // "react-dom": { - // varName: "window.ReactDOM", - // type: "cjs", - // }, - // }), - // { - // name: "prasi", - // setup(setup) { - // const reinit = () => { - // setup.onEnd((res) => { - // if (res.errors.length > 0) { - // Bun.write( - // Bun.file(code.path(id_site, "site", "src", "server.log")), - // JSON.stringify(res.errors, null, 2) - // ); - // } - // server.init(id_site); - // }); - // }; - // reinit(); - // }, - // }, - // ], - // }); - - // const esbuild = code.esbuild[id_site].server; - // esbuild?.watch(); - // } - - // if (!code.esbuild[id_site].site) { - // const build_path = code.path(id_site, "site", "build_cache"); - // await removeAsync(build_path); - // await dirAsync(build_path); - // const build_file = `${build_path}/index.js`; - // await writeAsync(build_file, ""); - - // const index_path = code.path(id_site, "site", "src", "index.tsx"); - // if (!(await existsAsync(index_path))) { - // await writeAsync(index_path, 'export const hello = "world"'); - // } - // console.log("running "); - - // code.esbuild[id_site].site = await context({ - // absWorkingDir: src_path, - // entryPoints: ["index.tsx"], - // bundle: true, - // outdir: build_path, - // minify: true, - // treeShaking: true, - // format: "esm", - // splitting: true, - // logLevel: "silent", - // sourcemap: true, - // plugins: [ - // style(), - // globalExternals({ - // react: { - // varName: "window.React", - // type: "cjs", - // }, - // "react-dom": { - // varName: "window.ReactDOM", - // type: "cjs", - // }, - // }), - // { - // name: "prasi", - // setup(setup) { - // setup.onEnd(async (res) => { - // if (res.errors.length > 0) { - // await codeError( - // id_site, - // res.errors.map((e) => e.text).join("\n\n"), - // "site" - // ); - // } else { - // codeBuildType(id_site); - // await removeAsync(code.path(id_site, "site", "build")); - // await moveAsync( - // code.path(id_site, "site", "build_cache"), - // code.path(id_site, "site", "build") - // ); - // await removeAsync( - // code.path(id_site, "site", "src", "index.log") - // ); - - // code.esbuild[id_site].site_ts = Date.now(); - // const client_ids = new Set(); - // user.active.findAll({ site_id: id_site }).forEach((e) => { - // client_ids.add(e.client_id); - // }); - - // client_ids.forEach((client_id) => { - // const ws = conns.get(client_id)?.ws; - // if (ws) { - // sendWS(ws, { - // type: SyncType.Event, - // event: "code_changes", - // data: { ts: code.esbuild[id_site].site_ts }, - // }); - // } - // }); - // } - // }); - // }, - // }, - // ], - // }); - // const esbuild = code.esbuild[id_site].site; - // esbuild?.watch(); - // } - // for (const _mode of ["site", "server"]) { - // const mode = _mode as "site" | "server"; - - // const esbuild = code.esbuild[id_site][mode]; - // if (esbuild) { - // try { - // await esbuild.rebuild(); - // } catch (e: any) { - // await codeError(id_site, e.message, mode); - // } - // } - // } -}; - -const codeError = async ( - id_site: string, - error: string, - mode: "server" | "site" -) => { - const path = code.path( - id_site, - "site", - "src", - mode === "server" ? "server.log" : "index.log" - ); - - await Bun.write(path, error); -}; diff --git a/app/srv/ws/sync/editor/code/server-main.ts b/app/srv/ws/sync/editor/code/server-main.ts index 2ed999ed..fae8f7af 100644 --- a/app/srv/ws/sync/editor/code/server-main.ts +++ b/app/srv/ws/sync/editor/code/server-main.ts @@ -29,46 +29,42 @@ const serverMain = () => ({ init(site_id: string) { clearTimeout(this.init_timeout); this.init_timeout = setTimeout(async () => { - const server_src_path = code.path(site_id, "server", "build", "index.js"); - try { - delete require.cache[server_src_path]; - const svr = require(server_src_path); - - if (svr && typeof svr.server === "object") { - this.handler[site_id] = svr.server; - this.handler[site_id].site_id = site_id; - - if (typeof svr.server.init === "function") { - svr.server.init({}); - } - - Bun.write( - Bun.file(code.path(site_id, "site", "src", "server.log")), - "" - ); - } else { - const file = await Bun.file(server_src_path).text(); - const log_path = code.path(site_id, "site", "src", "server.log"); - - if (file.length === 0) { - await Bun.write(Bun.file(log_path), "server.ts is empty"); - } else { - await Bun.write( - Bun.file(log_path), - "server.ts does not return server object" - ); - } - } - } catch (e: any) { - const file = await Bun.file(server_src_path).text(); - const log_path = code.path(site_id, "site", "src", "server.log"); - if (file.length === 0) { - await Bun.write(Bun.file(log_path), "server.ts is empty"); - } else { - await Bun.write(Bun.file(log_path), e.message); - console.log(`Failed to init server ${site_id}\n`, log_path); - } - } + // const server_src_path = code.path(site_id, "server", "build", "index.js"); + // try { + // delete require.cache[server_src_path]; + // const svr = require(server_src_path); + // if (svr && typeof svr.server === "object") { + // this.handler[site_id] = svr.server; + // this.handler[site_id].site_id = site_id; + // if (typeof svr.server.init === "function") { + // svr.server.init({}); + // } + // Bun.write( + // Bun.file(code.path(site_id, "site", "src", "server.log")), + // "" + // ); + // } else { + // const file = await Bun.file(server_src_path).text(); + // const log_path = code.path(site_id, "site", "src", "server.log"); + // if (file.length === 0) { + // await Bun.write(Bun.file(log_path), "server.ts is empty"); + // } else { + // await Bun.write( + // Bun.file(log_path), + // "server.ts does not return server object" + // ); + // } + // } + // } catch (e: any) { + // const file = await Bun.file(server_src_path).text(); + // const log_path = code.path(site_id, "site", "src", "server.log"); + // if (file.length === 0) { + // await Bun.write(Bun.file(log_path), "server.ts is empty"); + // } else { + // await Bun.write(Bun.file(log_path), e.message); + // console.log(`Failed to init server ${site_id}\n`, log_path); + // } + // } }, 10); }, async http( diff --git a/pkgs/core/utils/global.ts b/pkgs/core/utils/global.ts index f7ada923..ad02fa32 100644 --- a/pkgs/core/utils/global.ts +++ b/pkgs/core/utils/global.ts @@ -3,6 +3,7 @@ import { Logger } from "pino"; import { RadixRouter } from "radix3"; import { syncronize } from "y-pojo"; import type * as Y from "yjs"; +//@ts-ignore import { PrismaClient } from "../../../app/db/db"; import { WSData } from "../server/create"; import { @@ -14,7 +15,6 @@ import { dbProxy } from "../../../app/web/src/base/load/db/db-proxy"; type SingleRoute = { url: string; args: string[]; - raw: boolean; fn: (...arg: any[]) => Promise; path: string; };