import * as esbuild from "npm:esbuild@0.20.2"; import { ReactCompilerEsbuildPlugin } from "./reactcompileresbuild.ts"; import { cache } from "npm:esbuild-plugin-cache"; import tailwindconfig from "../tailwind.config.ts"; import tailwindcss from "https://esm.sh/tailwindcss@3"; import postcss from "https://esm.sh/postcss@8"; import autoprefixer from "https://esm.sh/autoprefixer@10"; import { renderToString } from "https://esm.sh/react-dom@19.1.1/server"; import { createElement } from "https://esm.sh/react@19.1.1"; import { Root } from "./browser/landing-shared.tsx"; // helper build function async function build({ entry, initialData, }: { entry: "index" | "view"; initialData?: { config: { inviteOnly: boolean; //port: number; did: string; host: string; indexPriority?: string[]; }; users: { did: string; role: string; registrationdate: string; onboardingstatus: string; pfp?: string; displayname: string; handle: string; }[]; }; }) { const template = await Deno.readTextFile( `./shared-landing/template-${entry}.html` ); const importmap = { imports: { "react/jsx-runtime": "https://esm.sh/react@19.1.1/jsx-runtime", "react/compiler-runtime": "https://esm.sh/react@19.1.1/compiler-runtime", }, }; const result = await esbuild.build({ entryPoints: [`./shared-landing/browser/landing-${entry}.tsx`], bundle: true, format: "esm", jsx: "transform", write: false, // keep in memory loader: { ".tsx": "tsx" }, jsxFactory: "React.createElement", jsxFragment: "React.Fragment", platform: "neutral", //"browser", external: ["react/jsx-runtime", "react/compiler-runtime"], plugins: [ cache({ importmap, directory: "./cache" }), ReactCompilerEsbuildPlugin({ filter: /\.tsx?$/, sourceMaps: true, runtimeModulePath: "https://esm.sh/react@19.1.1/jsx-runtime", }), ], }); const rawcss = await Deno.readTextFile("./shared-landing/app.css"); // @ts-ignore its fiiine const cssResult = await postcss([ tailwindcss(tailwindconfig), autoprefixer(), ]).process(rawcss, { from: "./shared-landing/app.css", map: false, }); const js = result.outputFiles[0].text; const jshash = hashString(js); const csshash = hashString(cssResult.css); const html = template.replace( "", ` ` ); // const html = template.replace( // "", // ` // ` // ); const ssr = renderToString( createElement( () => Root({ type: entry, initialData }), null ) ); const ssrhtml = html.replace("", `${ssr}`); return { js, html: ssrhtml, css: cssResult.css }; } // public compile function export async function compile({ target, initialData }: { target: "index" | "view"; initialData?: { config: { inviteOnly: boolean; //port: number; did: string; host: string; indexPriority?: string[]; }; users: { did: string; role: string; registrationdate: string; onboardingstatus: string; pfp?: string; displayname: string; handle: string; }[]; }; }) { return await build({entry: target, initialData}); } // watch loop export async function devWatch({target,initialData,onBuild}:{ target: "index" | "view", initialData?: { config: { inviteOnly: boolean; //port: number; did: string; host: string; indexPriority?: string[]; }; users: { did: string; role: string; registrationdate: string; onboardingstatus: string; pfp?: string; displayname: string; handle: string; }[]; }, onBuild: (data: { js: string; html: string; css: string }) => void } ) { for await (const event of Deno.watchFs(".")) { if (event.paths.some((p) => p.endsWith(".tsx"))) { console.log("Rebuilding bundle…"); const data = await compile({target,initialData}); onBuild(data); } } } async function hashString(content: string): Promise { const encoder = new TextEncoder(); const data = encoder.encode(content); // SHA-256 hash const hashBuffer = await crypto.subtle.digest("SHA-256", data); // Convert buffer to hex string return Array.from(new Uint8Array(hashBuffer)) .map((b) => b.toString(16).padStart(2, "0")) .join("") .slice(0, 8); // optional: shorten hash for filenames }