From 49e1569b07f4fc77d3830e917f6f5053bfcec820 Mon Sep 17 00:00:00 2001 From: Rizky Date: Wed, 25 Oct 2023 14:03:17 +0700 Subject: [PATCH] fix export --- app/srv/api/npm-bundle.ts | 171 +-------------- app/srv/api/npm.ts | 2 +- app/srv/api/site-export.ts | 38 +++- app/srv/util/build-npm.ts | 194 ++++++++++++++++++ .../src/render/ed/panel/popup/site-user.tsx | 20 ++ 5 files changed, 251 insertions(+), 174 deletions(-) create mode 100644 app/srv/util/build-npm.ts create mode 100644 app/web/src/render/ed/panel/popup/site-user.tsx diff --git a/app/srv/api/npm-bundle.ts b/app/srv/api/npm-bundle.ts index 60378f6f..d409fa0e 100644 --- a/app/srv/api/npm-bundle.ts +++ b/app/srv/api/npm-bundle.ts @@ -11,179 +11,12 @@ import { g } from "utils/global"; import { validate } from "uuid"; import { glb } from "../global"; import { eg } from "../ws/edit/edit-global"; - -export type NPMImportAs = { - main: { mode: "default" | "*"; name: string }; - names: string[]; - custom?: string; -}; +import { buildNpm } from "../util/build-npm"; export const _ = { url: "/npm-bundle/:mode/:id", async api(mode: "site" | "page", id: string) { const {} = apiContext(this); - if (!validate(id)) return "-"; - - let items = [] as npm_page[] | npm_site[]; - - if (mode === "site") { - items = await db.npm_site.findMany({ where: { id_site: id } }); - } else { - items = await db.npm_page.findMany({ where: { id_page: id } }); - } - const packages: Record = {}; - - const imports = items - .map((e) => { - const import_as = e.import_as as NPMImportAs; - - packages[e.module] = e.version; - if (import_as.main.name || import_as.names.length > 0) { - let main = ""; - let names = import_as.names.map((e) => `${e} as __${e}`); - - if (import_as.main.name) { - main = - import_as.main.mode === "default" - ? `__${import_as.main.name}` - : `* as __${import_as.main.name}`; - } - - const imports = [ - main.trim(), - (names.length > 0 ? `{ ${names.join(",")} }` : "").trim(), - ].filter((e) => e); - - let final = ""; - if (imports.length > 0) { - final = `import ${imports.join(",")} from "${e.module}";`; - } - - if (import_as.custom) { - final = final + "\n" + import_as.custom; - } - return final; - } - return ""; - }) - .filter((e) => !!e) - .join("\n"); - - const exports = items - .map((e) => { - const import_as = e.import_as as NPMImportAs; - const res: string[] = []; - - if (import_as.main.name || import_as.names.length > 0) { - let main = ""; - let names = import_as.names; - - if (import_as.main.name) { - main = import_as.main.name; - } - - if (main) { - res.push(`window.exports.${main} = __${main};`); - } - - if (names.length > 0) { - names.forEach((e) => { - res.push(`window.exports.${e} = __${e};`); - }); - } - } - - return res.join("\n").trim(); - }) - .filter((e) => !!e) - .join("\n"); - - const src = `\ -${imports} -${exports} -`.trim(); - await dirAsync(dir.path(`${g.datadir}/npm/${mode}/${id}`)); - await writeAsync(dir.path(`${g.datadir}/npm/${mode}/${id}/input.js`), src); - packages["react"] = "18.2.0"; - packages["react-dom"] = "18.2.0"; - await writeAsync(dir.path(`${g.datadir}/npm/${mode}/${id}/package.json`), { - dependencies: packages, - }); - await writeAsync( - dir.path(`${g.datadir}/npm/${mode}/${id}/pnpm-workspace.yaml`), - `\ -packages: - - ./*` - ); - try { - await $({ - cwd: dir.path(`${g.datadir}/npm/${mode}/${id}`), - })`pnpm i`; - - await build({ - absWorkingDir: dir.path(`${g.datadir}/npm/${mode}/${id}`), - entryPoints: ["input.js"], - bundle: true, - outfile: "index.js", - minify: true, - treeShaking: true, - sourcemap: true, - plugins: [ - style(), - globalExternals({ - react: { - varName: "window.React", - type: "cjs", - }, - "react-dom": { - varName: "window.ReactDOM", - type: "cjs", - }, - }), - ], - logLevel: "silent", - }); - } catch (e) { - return e; - } - - try { - const s = await stat(dir.path(`${g.datadir}/npm/${mode}/${id}/index.js`)); - - if (mode === "page") { - delete glb.npm.page[id]; - await db.npm_page.updateMany({ - where: { - id_page: id, - }, - data: { bundled: true }, - }); - const p = eg.edit.page[id]; - if (p) { - await db.page.update({ - where: { - id, - }, - data: { - updated_at: new Date(), - }, - }); - p.doc.getMap("map").set("updated_at", new Date().toISOString()); - } - } else if (mode === "site") { - delete glb.npm.site[id]; - - await db.npm_site.updateMany({ - where: { - id_site: id, - }, - data: { bundled: true }, - }); - } - - return s.size.toString(); - } catch (e) { - return "-"; - } + return await buildNpm({ id, mode }); }, }; diff --git a/app/srv/api/npm.ts b/app/srv/api/npm.ts index c974a737..f16e3652 100644 --- a/app/srv/api/npm.ts +++ b/app/srv/api/npm.ts @@ -48,7 +48,7 @@ export const _ = { } } - if (path.length > dir.path(`../npm/${mode}/${id}`).length) { + if (path.length > dir.path(`${g.datadir}/npm/${mode}/${id}`).length) { const file = await readAsync(path, "buffer"); if (file) { diff --git a/app/srv/api/site-export.ts b/app/srv/api/site-export.ts index c6f00299..493c1a4d 100644 --- a/app/srv/api/site-export.ts +++ b/app/srv/api/site-export.ts @@ -5,6 +5,8 @@ import fs from "fs"; import { exists } from "fs-jetpack"; import { gzipSync } from "zlib"; import path from "path"; +import { g } from "utils/global"; +import { buildNpm } from "../util/build-npm"; export const _ = { url: "/site-export/:site_id", @@ -19,7 +21,7 @@ export const _ = { is_deleted: false, name: { not: { startsWith: "layout:" } }, }, - }); + }); if (site) { const layout = await db.page.findFirst({ @@ -67,12 +69,39 @@ export const _ = { site: {} as Record, pages: {} as Record>, }; - npm.site = readDirectoryRecursively(dir.path(`../npm/site/${site_id}`)); + const page_ids = await db.page.findMany({ + where: { id_site: site_id, is_deleted: false }, + select: { id: true }, + }); + const npm_page = await db.npm_page.findMany({ + where: { id_page: { in: page_ids.map((e) => e.id) } }, + }); + + if (!exists(dir.path(`${g.datadir}/npm/site/${site_id}`))) { + await buildNpm({ id: site_id, mode: "site" }); + } + const npm_page_ids = {} as Record; + for (const np of npm_page) { + if (!npm_page_ids[np.id_page]) { + npm_page_ids[np.id_page] = []; + } + npm_page_ids[np.id_page].push(np); + } + + for (const [k, v] of Object.entries(npm_page_ids)) { + if (!exists(dir.path(`${g.datadir}/npm/page/${k}`))) { + await buildNpm({ id: k, mode: "page", _items: v }); + } + } + + npm.site = readDirectoryRecursively( + dir.path(`${g.datadir}/npm/site/${site_id}`) + ); for (const page of pages) { - if (exists(dir.path(`../npm/page/${page.id}`))) { + if (exists(dir.path(`${g.datadir}/npm/page/${page.id}`))) { npm.pages[page.id] = readDirectoryRecursively( - dir.path(`../npm/page/${page.id}`) + dir.path(`${g.datadir}/npm/page/${page.id}`) ); } } @@ -99,6 +128,7 @@ function readDirectoryRecursively( result[[...(baseDir || []), item].join("/")] = content; } else if (stats.isDirectory()) { if (item !== "node_modules") { + console.log(itemPath); const subdirResult = readDirectoryRecursively(itemPath, [ ...(baseDir || []), item, diff --git a/app/srv/util/build-npm.ts b/app/srv/util/build-npm.ts new file mode 100644 index 00000000..a39d774f --- /dev/null +++ b/app/srv/util/build-npm.ts @@ -0,0 +1,194 @@ +import globalExternals from "@fal-works/esbuild-plugin-global-externals"; +import { style } from "@hyrious/esbuild-plugin-style"; +import { npm_page, npm_site } from "dbgen"; +import { dir } from "dir"; +import { build } from "esbuild"; +import { $ } from "execa"; +import { dirAsync, writeAsync } from "fs-jetpack"; +import { stat } from "fs/promises"; +import { apiContext } from "service-srv"; +import { g } from "utils/global"; +import { validate } from "uuid"; +import { glb } from "../global"; +import { eg } from "../ws/edit/edit-global"; + +export type NPMImportAs = { + main: { mode: "default" | "*"; name: string }; + names: string[]; + custom?: string; +}; + +export const buildNpm = async ({ + id, + mode, + _items, +}: { + id: string; + mode: "site" | "page"; + _items?: npm_page[] | npm_site[]; +}) => { + if (!validate(id)) return "-"; + + let items = _items; + if (!items) { + if (mode === "site") { + items = await db.npm_site.findMany({ where: { id_site: id } }); + } else { + items = await db.npm_page.findMany({ where: { id_page: id } }); + } + } + const packages: Record = {}; + + const imports = items + .map((e) => { + const import_as = e.import_as as NPMImportAs; + + packages[e.module] = e.version; + if (import_as.main.name || import_as.names.length > 0) { + let main = ""; + let names = import_as.names.map((e) => `${e} as __${e}`); + + if (import_as.main.name) { + main = + import_as.main.mode === "default" + ? `__${import_as.main.name}` + : `* as __${import_as.main.name}`; + } + + const imports = [ + main.trim(), + (names.length > 0 ? `{ ${names.join(",")} }` : "").trim(), + ].filter((e) => e); + + let final = ""; + if (imports.length > 0) { + final = `import ${imports.join(",")} from "${e.module}";`; + } + + if (import_as.custom) { + final = final + "\n" + import_as.custom; + } + return final; + } + return ""; + }) + .filter((e) => !!e) + .join("\n"); + + const exports = items + .map((e) => { + const import_as = e.import_as as NPMImportAs; + const res: string[] = []; + + if (import_as.main.name || import_as.names.length > 0) { + let main = ""; + let names = import_as.names; + + if (import_as.main.name) { + main = import_as.main.name; + } + + if (main) { + res.push(`window.exports.${main} = __${main};`); + } + + if (names.length > 0) { + names.forEach((e) => { + res.push(`window.exports.${e} = __${e};`); + }); + } + } + + return res.join("\n").trim(); + }) + .filter((e) => !!e) + .join("\n"); + + const src = `\ +${imports} +${exports} +`.trim(); + await dirAsync(dir.path(`${g.datadir}/npm/${mode}/${id}`)); + await writeAsync(dir.path(`${g.datadir}/npm/${mode}/${id}/input.js`), src); + packages["react"] = "18.2.0"; + packages["react-dom"] = "18.2.0"; + await writeAsync(dir.path(`${g.datadir}/npm/${mode}/${id}/package.json`), { + dependencies: packages, + }); + await writeAsync( + dir.path(`${g.datadir}/npm/${mode}/${id}/pnpm-workspace.yaml`), + `\ +packages: +- ./*` + ); + try { + await $({ + cwd: dir.path(`${g.datadir}/npm/${mode}/${id}`), + })`pnpm i`; + + await build({ + absWorkingDir: dir.path(`${g.datadir}/npm/${mode}/${id}`), + entryPoints: ["input.js"], + bundle: true, + outfile: "index.js", + minify: true, + treeShaking: true, + sourcemap: true, + plugins: [ + style(), + globalExternals({ + react: { + varName: "window.React", + type: "cjs", + }, + "react-dom": { + varName: "window.ReactDOM", + type: "cjs", + }, + }), + ], + logLevel: "silent", + }); + } catch (e) { + return e; + } + + try { + const s = await stat(dir.path(`${g.datadir}/npm/${mode}/${id}/index.js`)); + + if (mode === "page") { + delete glb.npm.page[id]; + await db.npm_page.updateMany({ + where: { + id_page: id, + }, + data: { bundled: true }, + }); + const p = eg.edit.page[id]; + if (p) { + await db.page.update({ + where: { + id, + }, + data: { + updated_at: new Date(), + }, + }); + p.doc.getMap("map").set("updated_at", new Date().toISOString()); + } + } else if (mode === "site") { + delete glb.npm.site[id]; + + await db.npm_site.updateMany({ + where: { + id_site: id, + }, + data: { bundled: true }, + }); + } + + return s.size.toString(); + } catch (e) { + return "-"; + } +}; diff --git a/app/web/src/render/ed/panel/popup/site-user.tsx b/app/web/src/render/ed/panel/popup/site-user.tsx new file mode 100644 index 00000000..4e36e163 --- /dev/null +++ b/app/web/src/render/ed/panel/popup/site-user.tsx @@ -0,0 +1,20 @@ +import { Popover } from "../../../../utils/ui/popover"; + +export const EdPopUser = (users: { id: string; username: string }[]) => { + return ( + +
+
+ {users.map((user) => ( +
+
+ +
+
{user.username}
+
+ ))} +
+
+
+ ); +};