import isDevelopment from "../../../utils/is-development"; import * as esbuild from "esbuild"; import grabDirNames from "../../../utils/grab-dir-names"; import path from "path"; import tailwindEsbuildPlugin from "./tailwind-esbuild-plugin"; import { existsSync, rmSync, unlinkSync } from "fs"; const { PAGES_DIR, BUNX_CWD_MODULE_CACHE_DIR, ROOT_DIR } = grabDirNames(); function toModPath(page_file_path) { return path.join(BUNX_CWD_MODULE_CACHE_DIR, page_file_path.replace(PAGES_DIR, "").replace(/\.(t|j)sx?$/, ".js")); } function isBatch(params) { return "tsx_map" in params; } async function buildEntries({ entries, clean_cache }) { const dev = isDevelopment(); const toBuild = []; for (const entry of entries) { const mod_file_path = toModPath(entry.page_file_path); // try { // if (clean_cache && existsSync(mod_file_path)) { // console.log(`Removing ${mod_file_path}`); // await Bun.file(mod_file_path).delete(); // } // } catch (error) {} const does_mod_file_path_exists = existsSync(mod_file_path); if (!does_mod_file_path_exists) { toBuild.push({ tsx: entry.tsx, mod_file_path, }); } } if (toBuild.length === 0) return; const virtualEntries = {}; for (const { tsx, mod_file_path } of toBuild) { virtualEntries[mod_file_path] = tsx; } const virtualPlugin = { name: "virtual-tsx-entries", setup(build) { const entryPaths = new Set(Object.keys(virtualEntries)); build.onResolve({ filter: /.*/ }, (args) => { if (entryPaths.has(args.path)) { return { path: args.path, namespace: "virtual", }; } }); build.onLoad({ filter: /.*/, namespace: "virtual" }, (args) => ({ contents: virtualEntries[args.path], resolveDir: process.cwd(), loader: "tsx", })); build.onEnd((result) => { if (result.errors.length > 0) { console.log(`Build Errors =>`, result.errors); return; } // const artifacts: any[] = Object.entries( // result.metafile!.outputs, // ) // .filter(([, meta]) => meta.entryPoint) // .map(([outputPath, meta]) => { // return { // path: outputPath, // hash: path.basename( // outputPath, // path.extname(outputPath), // ), // type: outputPath.endsWith(".css") // ? "text/css" // : "text/javascript", // entrypoint: meta.entryPoint, // css_path: meta.cssBundle, // }; // }); // console.log("artifacts", artifacts); }); }, }; const entryPoints = Object.keys(virtualEntries); const build = await esbuild.build({ entryPoints, bundle: true, format: "esm", target: "es2020", platform: "node", external: [ "react", "react-dom", "react/jsx-runtime", "react/jsx-dev-runtime", ], minify: !dev, define: { "process.env.NODE_ENV": JSON.stringify(dev ? "development" : "production"), }, jsx: "automatic", outdir: BUNX_CWD_MODULE_CACHE_DIR, plugins: [virtualPlugin, tailwindEsbuildPlugin], metafile: true, // logLevel: "silent", }); } async function loadEntry(page_file_path) { const now = Date.now(); const mod_file_path = toModPath(page_file_path); const mod_css_path = mod_file_path.replace(/\.js$/, ".css"); if (global.REACT_DOM_MODULE_CACHE.has(page_file_path)) { return global.REACT_DOM_MODULE_CACHE.get(page_file_path)?.main; } const mod = await import(`${mod_file_path}?t=${now}`); global.REACT_DOM_MODULE_CACHE.set(page_file_path, { main: mod, css: mod_css_path, }); return mod; } export default async function grabTsxStringModule(params) { if (isBatch(params)) { try { await buildEntries({ entries: params.tsx_map }); } catch (error) { console.error(`SSR Batch Build Error\n`); console.log(error); } return Promise.all(params.tsx_map.map((entry) => loadEntry(entry.page_file_path))); } try { await buildEntries({ entries: [params], clean_cache: true, }); } catch (error) { console.error(`SSR Single Build Error\n`); console.log(error); } return loadEntry(params.page_file_path); } // export default async function grabTsxStringModule({ // tsx, // }: Params): Promise { // const dev = isDevelopment(); // const now = Date.now(); // const { BUNX_CWD_MODULE_CACHE_DIR } = grabDirNames(); // const target_cache_file_path = path.join( // BUNX_CWD_MODULE_CACHE_DIR, // `server-render-${now}.js`, // ); // await esbuild.build({ // stdin: { // contents: dev ? tsx + `\n// v_${now}` : tsx, // resolveDir: process.cwd(), // loader: "tsx", // }, // bundle: true, // format: "esm", // target: "es2020", // platform: "node", // external: [ // "react", // "react-dom", // "react/jsx-runtime", // "react/jsx-dev-runtime", // ], // minify: !dev, // define: { // "process.env.NODE_ENV": JSON.stringify( // dev ? "development" : "production", // ), // }, // jsx: "automatic", // outfile: target_cache_file_path, // plugins: [tailwindEsbuildPlugin], // }); // Loader.registry.delete(target_cache_file_path); // const mod = await import(`${target_cache_file_path}?t=${now}`); // return mod as T; // } // if (!dev) { // const now = Date.now(); // const final_tsx = dev ? tsx + `\n// v_${now}` : tsx; // const result = await esbuild.transform(final_tsx, { // loader: "tsx", // format: "esm", // jsx: "automatic", // minify: !dev, // }); // const blob = new Blob([result.code], { type: "text/javascript" }); // const url = URL.createObjectURL(blob); // const mod = await import(url); // URL.revokeObjectURL(url); // return mod as T; // }