diff --git a/src/__tests__/functions/server/grab-web-meta-html.test.ts b/src/__tests__/functions/server/grab-web-meta-html.test.ts index 38fe516..41a59d8 100644 --- a/src/__tests__/functions/server/grab-web-meta-html.test.ts +++ b/src/__tests__/functions/server/grab-web-meta-html.test.ts @@ -1,108 +1,109 @@ import { describe, it, expect } from "bun:test"; +import { renderToString } from "react-dom/server"; import grabWebMetaHTML from "../../../functions/server/web-pages/grab-web-meta-html"; +function render(meta: Parameters[0]["meta"]) { + return renderToString(grabWebMetaHTML({ meta })); +} + describe("grabWebMetaHTML", () => { it("returns empty string for empty meta object", () => { - expect(grabWebMetaHTML({ meta: {} })).toBe(""); + expect(render({})).toBe(""); }); it("generates a title tag", () => { - const html = grabWebMetaHTML({ meta: { title: "My Page" } }); - expect(html).toContain("My Page"); + expect(render({ title: "My Page" })).toContain("My Page"); }); it("generates a description meta tag", () => { - const html = grabWebMetaHTML({ meta: { description: "A description" } }); - expect(html).toContain(' { - const html = grabWebMetaHTML({ - meta: { keywords: ["react", "bun", "ssr"] }, - }); - expect(html).toContain('content="react, bun, ssr"'); + expect(render({ keywords: ["react", "bun", "ssr"] })).toContain( + 'content="react, bun, ssr"', + ); }); it("uses string keywords directly", () => { - const html = grabWebMetaHTML({ meta: { keywords: "react, bun" } }); - expect(html).toContain('content="react, bun"'); + expect(render({ keywords: "react, bun" })).toContain( + 'content="react, bun"', + ); }); it("generates author meta tag", () => { - const html = grabWebMetaHTML({ meta: { author: "Alice" } }); - expect(html).toContain(' { - const html = grabWebMetaHTML({ meta: { robots: "noindex" } }); - expect(html).toContain(' { - const html = grabWebMetaHTML({ - meta: { canonical: "https://example.com/page" }, - }); - expect(html).toContain(' { - const html = grabWebMetaHTML({ meta: { themeColor: "#ff0000" } }); - expect(html).toContain(' { - const html = grabWebMetaHTML({ - meta: { - og: { - title: "OG Title", - description: "OG Desc", - image: "https://example.com/img.png", - url: "https://example.com", - type: "website", - siteName: "Example", - locale: "en_US", - }, + const html = render({ + og: { + title: "OG Title", + description: "OG Desc", + image: "https://example.com/img.png", + url: "https://example.com", + type: "website", + siteName: "Example", + locale: "en_US", }, }); - expect(html).toContain(' { - const html = grabWebMetaHTML({ - meta: { - twitter: { - card: "summary_large_image", - title: "Tweet Title", - description: "Tweet Desc", - image: "https://example.com/tw.png", - site: "@example", - creator: "@alice", - }, + const html = render({ + twitter: { + card: "summary_large_image", + title: "Tweet Title", + description: "Tweet Desc", + image: "https://example.com/tw.png", + site: "@example", + creator: "@alice", }, }); - expect(html).toContain(' { - const html = grabWebMetaHTML({ meta: { og: { title: "Only Title" } } }); + const html = render({ og: { title: "Only Title" } }); expect(html).toContain("og:title"); expect(html).not.toContain("og:description"); expect(html).not.toContain("og:image"); }); it("does not emit tags for missing fields", () => { - const html = grabWebMetaHTML({ meta: { title: "Hello" } }); + const html = render({ title: "Hello" }); expect(html).not.toContain("description"); expect(html).not.toContain("og:"); expect(html).not.toContain("twitter:"); diff --git a/src/commands/build/index.ts b/src/commands/build/index.ts index eb1976d..de7fceb 100644 --- a/src/commands/build/index.ts +++ b/src/commands/build/index.ts @@ -1,10 +1,11 @@ import { Command } from "commander"; import { log } from "../../utils/log"; import init from "../../functions/init"; -import rewritePagesModule from "../../utils/rewrite-pages-module"; +// import rewritePagesModule from "../../utils/rewrite-pages-module"; import allPagesBunBundler from "../../functions/bundler/all-pages-bun-bundler"; import grabDirNames from "../../utils/grab-dir-names"; import { rmSync } from "fs"; +import allPagesBundler from "../../functions/bundler/all-pages-bundler"; const { HYDRATION_DST_DIR, BUNX_CWD_PAGES_REWRITE_DIR } = grabDirNames(); @@ -20,7 +21,9 @@ export default function () { rmSync(BUNX_CWD_PAGES_REWRITE_DIR, { recursive: true }); } catch (error) {} - await rewritePagesModule(); + global.SKIPPED_BROWSER_MODULES = new Set(); + + // await rewritePagesModule(); await init(); log.banner(); diff --git a/src/commands/dev/index.ts b/src/commands/dev/index.ts index 1f4f294..8db7ccc 100644 --- a/src/commands/dev/index.ts +++ b/src/commands/dev/index.ts @@ -2,7 +2,7 @@ import { Command } from "commander"; import startServer from "../../functions/server/start-server"; import { log } from "../../utils/log"; import bunextInit from "../../functions/bunext-init"; -import rewritePagesModule from "../../utils/rewrite-pages-module"; +// import rewritePagesModule from "../../utils/rewrite-pages-module"; import grabDirNames from "../../utils/grab-dir-names"; import { rmSync } from "fs"; @@ -21,7 +21,7 @@ export default function () { rmSync(BUNX_CWD_PAGES_REWRITE_DIR, { recursive: true }); } catch (error) {} - await rewritePagesModule(); + // await rewritePagesModule(); await bunextInit(); await startServer(); diff --git a/src/functions/bundler/all-pages-bun-bundler.ts b/src/functions/bundler/all-pages-bun-bundler.ts index 33530b5..440a413 100644 --- a/src/functions/bundler/all-pages-bun-bundler.ts +++ b/src/functions/bundler/all-pages-bun-bundler.ts @@ -8,6 +8,7 @@ import path from "path"; import grabClientHydrationScript from "./grab-client-hydration-script"; import { mkdirSync, rmSync } from "fs"; import recordArtifacts from "./record-artifacts"; +import BunSkipNonBrowserPlugin from "./plugins/bun-skip-browser-plugin"; const { HYDRATION_DST_DIR, BUNX_HYDRATION_SRC_DIR, BUNX_TMP_DIR } = grabDirNames(); diff --git a/src/functions/bundler/all-pages-bundler.ts b/src/functions/bundler/all-pages-bundler.ts index a91bca4..0e49121 100644 --- a/src/functions/bundler/all-pages-bundler.ts +++ b/src/functions/bundler/all-pages-bundler.ts @@ -9,6 +9,7 @@ import grabArtifactsFromBundledResults from "./grab-artifacts-from-bundled-resul import { writeFileSync } from "fs"; import type { BundlerCTXMap } from "../../types"; import recordArtifacts from "./record-artifacts"; +import stripServerSideLogic from "./strip-server-side-logic"; const { HYDRATION_DST_DIR, HYDRATION_DST_DIR_MAP_JSON_FILE } = grabDirNames(); @@ -39,7 +40,7 @@ export default async function allPagesBundler(params?: Params) { const dev = isDevelopment(); for (const page of target_pages) { - const key = page.transformed_path; + const key = page.local_path; const txt = await grabClientHydrationScript({ page_local_path: page.local_path, @@ -51,6 +52,13 @@ export default async function allPagesBundler(params?: Params) { if (!txt) continue; + // const final_tsx = stripServerSideLogic({ + // txt_code: txt, + // file_path: key, + // }); + + // console.log("final_tsx", final_tsx); + virtualEntries[key] = txt; } @@ -94,6 +102,31 @@ export default async function allPagesBundler(params?: Params) { const entryPoints = Object.keys(virtualEntries).map((k) => `virtual:${k}`); + // let alias: any = {}; + // const excludes = [ + // "bun:sqlite", + // "path", + // "url", + // "events", + // "util", + // "crypto", + // "net", + // "tls", + // "fs", + // "node:path", + // "node:url", + // "node:process", + // "node:fs", + // "node:timers/promises", + // ]; + + // for (let i = 0; i < excludes.length; i++) { + // const exclude = excludes[i]; + // alias[exclude] = "./empty.js"; + // } + + // console.log("alias", alias); + const result = await esbuild.build({ entryPoints, outdir: HYDRATION_DST_DIR, @@ -119,6 +152,7 @@ export default async function allPagesBundler(params?: Params) { "react-dom/client", "react/jsx-runtime", ], + // alias, }); if (result.errors.length > 0) { diff --git a/src/functions/bundler/grab-client-hydration-script.tsx b/src/functions/bundler/grab-client-hydration-script.tsx index 6317089..22f254b 100644 --- a/src/functions/bundler/grab-client-hydration-script.tsx +++ b/src/functions/bundler/grab-client-hydration-script.tsx @@ -23,24 +23,24 @@ export default async function grabClientHydrationScript({ const { root_file_path } = grabRootFilePath(); - const target_path = pagePathTransform({ page_path: page_local_path }); - const target_root_path = root_file_path - ? pagePathTransform({ page_path: root_file_path }) - : undefined; + // const target_path = pagePathTransform({ page_path: page_local_path }); + // const target_root_path = root_file_path + // ? pagePathTransform({ page_path: root_file_path }) + // : undefined; let txt = ``; txt += `import { hydrateRoot } from "react-dom/client";\n`; - if (target_root_path) { - txt += `import Root from "${target_root_path}";\n`; + if (root_file_path) { + txt += `import Root from "${root_file_path}";\n`; } - txt += `import Page from "${target_path}";\n\n`; + txt += `import Page from "${page_local_path}";\n\n`; txt += `const pageProps = window.${ClientWindowPagePropsName} || {};\n`; - if (target_root_path) { - txt += `const component = \n`; + if (root_file_path) { + txt += `const component = \n`; } else { - txt += `const component = \n`; + txt += `const component = \n`; } txt += `if (window.${ClientRootComponentWindowName}?.render) {\n`; diff --git a/src/functions/bundler/plugins/bun-skip-browser-plugin.ts b/src/functions/bundler/plugins/bun-skip-browser-plugin.ts index 23db2e7..8052c4c 100644 --- a/src/functions/bundler/plugins/bun-skip-browser-plugin.ts +++ b/src/functions/bundler/plugins/bun-skip-browser-plugin.ts @@ -1,33 +1,84 @@ +import { log } from "../../../utils/log"; + const BunSkipNonBrowserPlugin: Bun.BunPlugin = { name: "skip-non-browser", setup(build) { - build.onResolve({ filter: /^(bun:|node:)/ }, (args) => { - return { path: args.path, external: true }; + const skipFilter = + /^(bun:|node:|fs$|path$|os$|crypto$|net$|events$|util$|tls$|url$|process$)/; + + // const skipped_modules = new Set(); + + build.onResolve({ filter: skipFilter }, (args) => { + global.SKIPPED_BROWSER_MODULES.add(args.path); + return { + path: args.path, + namespace: "skipped", + // external: true, + }; }); - build.onResolve({ filter: /^[^./]/ }, (args) => { - // If it's a built-in like 'fs' or 'path', skip it immediately - const excludes = [ - "fs", - "path", - "os", - "crypto", - "net", - "events", - "util", - ]; + // build.onEnd(() => { + // log.warn(`global.SKIPPED_BROWSER_MODULES`, [ + // ...global.SKIPPED_BROWSER_MODULES, + // ]); + // }); - if (excludes.includes(args.path) || args.path.startsWith("node:")) { - return { path: args.path, external: true }; - } + // build.onResolve({ filter: /^[^./]/ }, (args) => { + // // If it's a built-in like 'fs' or 'path', skip it immediately + // const excludes = [ + // "fs", + // "path", + // "os", + // "crypto", + // "net", + // "events", + // "util", + // "tls", + // ]; - try { - Bun.resolveSync(args.path, args.importer || process.cwd()); - return null; - } catch (e) { - console.warn(`[Skip] Mark as external: ${args.path}`); - return { path: args.path, external: true }; - } + // if (excludes.includes(args.path) || args.path.startsWith("node:")) { + // return { + // path: args.path, + // // namespace: "skipped", + // external: true, + // }; + // } + + // try { + // Bun.resolveSync(args.path, args.importer || process.cwd()); + // return null; + // } catch (e) { + // console.warn(`[Skip] Mark as external: ${args.path}`); + // return { + // path: args.path, + // // namespace: "skipped", + // external: true, + // }; + // } + // }); + + build.onLoad({ filter: /.*/, namespace: "skipped" }, (args) => { + return { + contents: ` + const proxy = new Proxy(() => proxy, { + get: () => proxy, + construct: () => proxy, + }); + + export const Database = proxy; + export const join = proxy; + export const fileURLToPath = proxy; + export const arch = proxy; + export const platform = proxy; + export const statSync = proxy; + + export const $H = proxy; + export const _ = proxy; + + export default proxy; + `, + loader: "js", + }; }); }, }; diff --git a/src/functions/bunext-init.ts b/src/functions/bunext-init.ts index f95fb1c..1272cdb 100644 --- a/src/functions/bunext-init.ts +++ b/src/functions/bunext-init.ts @@ -35,6 +35,7 @@ declare global { var CURRENT_VERSION: string | undefined; var PAGE_FILES: PageFiles[]; var ROOT_FILE_UPDATED: boolean; + var SKIPPED_BROWSER_MODULES: Set; // var BUNDLER_CTX: BuildContext | undefined; } @@ -47,6 +48,7 @@ export default async function bunextInit() { global.BUNDLER_CTX_MAP = {}; global.BUNDLER_REBUILDS = 0; global.PAGE_FILES = []; + global.SKIPPED_BROWSER_MODULES = new Set(); await init(); log.banner(); diff --git a/src/functions/server/watcher.ts b/src/functions/server/watcher.ts index 63cd86b..cb86a0e 100644 --- a/src/functions/server/watcher.ts +++ b/src/functions/server/watcher.ts @@ -3,7 +3,7 @@ import path from "path"; import grabDirNames from "../../utils/grab-dir-names"; import rebuildBundler from "./rebuild-bundler"; import { log } from "../../utils/log"; -import rewritePagesModule from "../../utils/rewrite-pages-module"; +// import rewritePagesModule from "../../utils/rewrite-pages-module"; const { ROOT_DIR } = grabDirNames(); @@ -86,7 +86,7 @@ async function fullRebuild(params?: { msg?: string }) { (hmr) => hmr.target_map?.local_path, ).filter((f) => typeof f == "string"); - await rewritePagesModule(); + // await rewritePagesModule(); if (msg) { log.watch(msg); diff --git a/src/functions/server/web-pages/generate-web-html.tsx b/src/functions/server/web-pages/generate-web-html.tsx index ad1fe5e..fd24a08 100644 --- a/src/functions/server/web-pages/generate-web-html.tsx +++ b/src/functions/server/web-pages/generate-web-html.tsx @@ -79,6 +79,13 @@ export default async function genWebHTML({ }, }); + // let skipped_modules_import_map: { [k: string]: string } = {}; + + // [...global.SKIPPED_BROWSER_MODULES].forEach((sk) => { + // skipped_modules_import_map[sk] = + // "data:text/javascript,export default {}"; + // }); + let final_component = ( @@ -101,6 +108,16 @@ export default async function genWebHTML({ }} /> + {/* {global.SKIPPED_BROWSER_MODULES ? ( +