diff --git a/bun.lock b/bun.lock index 72d8d47..22c7b53 100644 --- a/bun.lock +++ b/bun.lock @@ -28,6 +28,8 @@ "happy-dom": "^20.8.4", }, "peerDependencies": { + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0", "typescript": "^5.0.0", }, }, @@ -285,10 +287,16 @@ "pretty-format": ["pretty-format@27.5.1", "", { "dependencies": { "ansi-regex": "^5.0.1", "ansi-styles": "^5.0.0", "react-is": "^17.0.1" } }, "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ=="], + "react": ["react@19.2.4", "", {}, "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ=="], + + "react-dom": ["react-dom@19.2.4", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.4" } }, "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ=="], + "react-is": ["react-is@17.0.2", "", {}, "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="], "restore-cursor": ["restore-cursor@5.1.0", "", { "dependencies": { "onetime": "^7.0.0", "signal-exit": "^4.1.0" } }, "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA=="], + "scheduler": ["scheduler@0.27.0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="], + "signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="], "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="], diff --git a/dist/commands/index.js b/dist/commands/index.js old mode 100644 new mode 100755 diff --git a/package.json b/package.json index 32650f6..6df4640 100644 --- a/package.json +++ b/package.json @@ -38,15 +38,13 @@ "@types/micromatch": "^4.0.10", "happy-dom": "^20.8.4" }, - "peerDependencies": { - "typescript": "^5.0.0", - "react": "^18.0.0 || ^19.0.0", - "react-dom": "^18.0.0 || ^19.0.0" - }, "publishConfig": { "registry": "https://npm.pkg.github.com" }, "dependencies": { + "typescript": "^5.0.0", + "react": "^19.0.0", + "react-dom": "^19.0.0", "@tailwindcss/postcss": "^4.2.2", "@types/bun": "latest", "@types/node": "^24.10.0", diff --git a/src/commands/build/index.ts b/src/commands/build/index.ts index e4cc262..c9fef32 100644 --- a/src/commands/build/index.ts +++ b/src/commands/build/index.ts @@ -27,8 +27,8 @@ export default function () { log.banner(); log.build("Building Project ..."); - // await allPagesBunBundler(); + await allPagesBunBundler(); - allPagesBundler(); + // await allPagesBundler(); }); } diff --git a/src/functions/bundler/all-pages-bun-bundler.ts b/src/functions/bundler/all-pages-bun-bundler.ts index a777909..ebe0c6d 100644 --- a/src/functions/bundler/all-pages-bun-bundler.ts +++ b/src/functions/bundler/all-pages-bun-bundler.ts @@ -3,24 +3,62 @@ import grabDirNames from "../../utils/grab-dir-names"; import isDevelopment from "../../utils/is-development"; import { log } from "../../utils/log"; import tailwindcss from "bun-plugin-tailwind"; +import type { BundlerCTXMap, PageFiles } from "../../types"; +import path from "path"; +import grabClientHydrationScript from "./grab-client-hydration-script"; +import { mkdirSync, rmSync } from "fs"; +import recordArtifacts from "./record-artifacts"; -const { HYDRATION_DST_DIR } = grabDirNames(); +const { HYDRATION_DST_DIR, BUNX_HYDRATION_SRC_DIR, BUNX_TMP_DIR } = + grabDirNames(); type Params = { target?: "bun" | "browser"; + page_file_paths?: string[]; }; export default async function allPagesBunBundler(params?: Params) { - const { target = "browser" } = params || {}; + const { target = "browser", page_file_paths } = params || {}; const pages = grabAllPages({ exclude_api: true }); + + const target_pages = page_file_paths?.[0] + ? pages.filter((p) => page_file_paths.includes(p.local_path)) + : pages; + + if (!page_file_paths) { + global.PAGE_FILES = pages; + try { + rmSync(BUNX_HYDRATION_SRC_DIR, { recursive: true }); + } catch {} + } + mkdirSync(BUNX_HYDRATION_SRC_DIR, { recursive: true }); + const dev = isDevelopment(); - let buildStart = 0; - buildStart = performance.now(); + const entryToPage = new Map(); - const build = await Bun.build({ - entrypoints: pages.map((p) => p.transformed_path), + for (const page of target_pages) { + const txt = await grabClientHydrationScript({ + page_local_path: page.local_path, + }); + if (!txt) continue; + + const entryFile = path.join( + BUNX_HYDRATION_SRC_DIR, + `${page.url_path}.tsx`, + ); + await Bun.write(entryFile, txt, { createPath: true }); + entryToPage.set(path.resolve(entryFile), page); + } + + if (entryToPage.size === 0) return; + + const buildStart = performance.now(); + + const result = await Bun.build({ + entrypoints: [...entryToPage.keys()], outdir: HYDRATION_DST_DIR, + root: BUNX_HYDRATION_SRC_DIR, minify: true, format: "esm", define: { @@ -29,32 +67,69 @@ export default async function allPagesBunBundler(params?: Params) { ), }, naming: { - entry: "[name]/[hash].[ext]", - chunk: "chunks/[name]-[hash].[ext]", + entry: "[dir]/[hash].[ext]", + chunk: "chunks/[hash].[ext]", }, - plugins: [ - tailwindcss, - { - name: "post-build", - setup(build) { - build.onEnd((result) => { - console.log("result", result); - }); - }, - }, - ], - // plugins: [ - - // ], + plugins: [tailwindcss], splitting: true, target, - external: ["bun"], + metafile: true, + external: [ + "react", + "react-dom", + "react-dom/client", + "react/jsx-runtime", + ], }); - console.log("build", build); + Bun.write( + path.join(BUNX_TMP_DIR, "bundle.json"), + JSON.stringify(result, null, 4), + { createPath: true }, + ); - if (build.success) { - const elapsed = (performance.now() - buildStart).toFixed(0); - log.success(`[Built] in ${elapsed}ms`); + if (!result.success) { + for (const entry of result.logs) { + log.error(`[Build] ${entry.message}`); + } + return; } + + const artifacts: BundlerCTXMap[] = []; + + for (const [outputPath, outputInfo] of Object.entries( + result.metafile!.outputs, + )) { + const entryPoint = outputInfo.entryPoint; + const cssBundle = outputInfo.cssBundle; + if (!entryPoint) continue; + if (outputPath.match(/\.css$/)) continue; + + const page = entryToPage.get(path.resolve(entryPoint)); + if (!page) continue; + + artifacts.push({ + path: path.join(".bunext/public/pages", outputPath), + hash: path.basename(outputPath, path.extname(outputPath)), + type: outputPath.endsWith(".css") ? "text/css" : "text/javascript", + entrypoint: entryPoint, + css_path: cssBundle + ? path.join(".bunext/public/pages", cssBundle) + : undefined, + file_name: page.file_name, + local_path: page.local_path, + url_path: page.url_path, + }); + } + + if (artifacts?.[0]) { + await recordArtifacts({ artifacts }); + } + + const elapsed = (performance.now() - buildStart).toFixed(0); + log.success(`[Built] in ${elapsed}ms`); + + global.RECOMPILING = false; + + return artifacts; } diff --git a/src/functions/bundler/all-pages-bundler.ts b/src/functions/bundler/all-pages-bundler.ts index 4389c4c..a91bca4 100644 --- a/src/functions/bundler/all-pages-bundler.ts +++ b/src/functions/bundler/all-pages-bundler.ts @@ -7,6 +7,8 @@ import tailwindEsbuildPlugin from "../server/web-pages/tailwind-esbuild-plugin"; import grabClientHydrationScript from "./grab-client-hydration-script"; import grabArtifactsFromBundledResults from "./grab-artifacts-from-bundled-result"; import { writeFileSync } from "fs"; +import type { BundlerCTXMap } from "../../types"; +import recordArtifacts from "./record-artifacts"; const { HYDRATION_DST_DIR, HYDRATION_DST_DIR_MAP_JSON_FILE } = grabDirNames(); @@ -43,6 +45,10 @@ export default async function allPagesBundler(params?: Params) { page_local_path: page.local_path, }); + // if (page.url_path == "/index") { + // console.log("txt", txt); + // } + if (!txt) continue; virtualEntries[key] = txt; @@ -64,11 +70,11 @@ export default async function allPagesBundler(params?: Params) { }, }; + let buildStart = 0; + const artifactTracker: esbuild.Plugin = { name: "artifact-tracker", setup(build) { - let buildStart = 0; - build.onStart(() => { build_starts++; buildStart = performance.now(); @@ -80,50 +86,15 @@ export default async function allPagesBundler(params?: Params) { } }); - build.onEnd((result) => { - if (result.errors.length > 0) { - for (const error of result.errors) { - const loc = error.location; - const location = loc - ? ` ${loc.file}:${loc.line}:${loc.column}` - : ""; - log.error(`[Build]${location} ${error.text}`); - } - return; - } + // build.onEnd((result) => { - const artifacts = grabArtifactsFromBundledResults({ - pages: target_pages, - result, - }); - - if (artifacts?.[0] && artifacts.length > 0) { - for (let i = 0; i < artifacts.length; i++) { - const artifact = artifacts[i]; - global.BUNDLER_CTX_MAP[artifact.local_path] = artifact; - } - - // params?.post_build_fn?.({ artifacts }); - - writeFileSync( - HYDRATION_DST_DIR_MAP_JSON_FILE, - JSON.stringify(artifacts), - ); - } - - const elapsed = (performance.now() - buildStart).toFixed(0); - log.success(`[Built] in ${elapsed}ms`); - - global.RECOMPILING = false; - - build_starts = 0; - }); + // }); }, }; const entryPoints = Object.keys(virtualEntries).map((k) => `virtual:${k}`); - await esbuild.build({ + const result = await esbuild.build({ entryPoints, outdir: HYDRATION_DST_DIR, bundle: true, @@ -136,11 +107,44 @@ export default async function allPagesBundler(params?: Params) { dev ? "development" : "production", ), }, - entryNames: "[dir]/[name]/[hash]", + entryNames: "[dir]/[hash]", metafile: true, plugins: [tailwindEsbuildPlugin, virtualPlugin, artifactTracker], jsx: "automatic", - splitting: true, + // splitting: true, // logLevel: "silent", + external: [ + "react", + "react-dom", + "react-dom/client", + "react/jsx-runtime", + ], }); + + if (result.errors.length > 0) { + for (const error of result.errors) { + const loc = error.location; + const location = loc + ? ` ${loc.file}:${loc.line}:${loc.column}` + : ""; + log.error(`[Build]${location} ${error.text}`); + } + return; + } + + const artifacts = grabArtifactsFromBundledResults({ + pages: target_pages, + result, + }); + + if (artifacts?.[0]) { + await recordArtifacts({ artifacts }); + } + + const elapsed = (performance.now() - buildStart).toFixed(0); + log.success(`[Built] in ${elapsed}ms`); + + global.RECOMPILING = false; + + build_starts = 0; } diff --git a/src/functions/bundler/all-pages-esbuild-context-bundler.ts b/src/functions/bundler/all-pages-esbuild-context-bundler.ts new file mode 100644 index 0000000..86ba26a --- /dev/null +++ b/src/functions/bundler/all-pages-esbuild-context-bundler.ts @@ -0,0 +1,152 @@ +import * as esbuild from "esbuild"; +import grabAllPages from "../../utils/grab-all-pages"; +import grabDirNames from "../../utils/grab-dir-names"; +import isDevelopment from "../../utils/is-development"; +import { log } from "../../utils/log"; +import tailwindEsbuildPlugin from "../server/web-pages/tailwind-esbuild-plugin"; +import grabClientHydrationScript from "./grab-client-hydration-script"; +import grabArtifactsFromBundledResults from "./grab-artifacts-from-bundled-result"; +import { writeFileSync } from "fs"; + +const { HYDRATION_DST_DIR, HYDRATION_DST_DIR_MAP_JSON_FILE } = grabDirNames(); + +let build_starts = 0; +const MAX_BUILD_STARTS = 10; + +type Params = { + post_build_fn?: (params: { artifacts: any[] }) => Promise | void; +}; + +export default async function allPagesESBuildContextBundler(params?: Params) { + const pages = grabAllPages({ exclude_api: true }); + + global.PAGE_FILES = pages; + + const virtualEntries: Record = {}; + const dev = isDevelopment(); + + for (const page of pages) { + const key = page.transformed_path; + + const txt = await grabClientHydrationScript({ + page_local_path: page.local_path, + }); + + // if (page.url_path == "/index") { + // console.log("txt", txt); + // } + + if (!txt) continue; + + virtualEntries[key] = txt; + } + + const virtualPlugin: esbuild.Plugin = { + name: "virtual-entrypoints", + setup(build) { + build.onResolve({ filter: /^virtual:/ }, (args) => ({ + path: args.path.replace("virtual:", ""), + namespace: "virtual", + })); + + build.onLoad({ filter: /.*/, namespace: "virtual" }, (args) => ({ + contents: virtualEntries[args.path], + loader: "tsx", + resolveDir: process.cwd(), + })); + }, + }; + + let buildStart = 0; + + const artifactTracker: esbuild.Plugin = { + name: "artifact-tracker", + setup(build) { + build.onStart(() => { + build_starts++; + buildStart = performance.now(); + + if (build_starts == MAX_BUILD_STARTS) { + const error_msg = `Build Failed. Please check all your components and imports.`; + log.error(error_msg); + process.exit(1); + } + }); + + build.onEnd((result) => { + if (result.errors.length > 0) { + for (const error of result.errors) { + const loc = error.location; + const location = loc + ? ` ${loc.file}:${loc.line}:${loc.column}` + : ""; + log.error(`[Build]${location} ${error.text}`); + } + return; + } + + const artifacts = grabArtifactsFromBundledResults({ + pages, + result, + }); + + if (artifacts?.[0] && artifacts.length > 0) { + for (let i = 0; i < artifacts.length; i++) { + const artifact = artifacts[i]; + if (artifact?.local_path && global.BUNDLER_CTX_MAP) { + global.BUNDLER_CTX_MAP[artifact.local_path] = + artifact; + } + } + + params?.post_build_fn?.({ artifacts }); + + writeFileSync( + HYDRATION_DST_DIR_MAP_JSON_FILE, + JSON.stringify(artifacts, null, 4), + ); + } + + const elapsed = (performance.now() - buildStart).toFixed(0); + log.success(`[Built] in ${elapsed}ms`); + + global.RECOMPILING = false; + + build_starts = 0; + }); + }, + }; + + const entryPoints = Object.keys(virtualEntries).map((k) => `virtual:${k}`); + + const ctx = await esbuild.context({ + entryPoints, + outdir: HYDRATION_DST_DIR, + bundle: true, + minify: true, + format: "esm", + target: "es2020", + platform: "browser", + define: { + "process.env.NODE_ENV": JSON.stringify( + dev ? "development" : "production", + ), + }, + entryNames: "[dir]/[hash]", + metafile: true, + plugins: [tailwindEsbuildPlugin, virtualPlugin, artifactTracker], + jsx: "automatic", + splitting: true, + // logLevel: "silent", + external: [ + "react", + "react-dom", + "react-dom/client", + "react/jsx-runtime", + ], + }); + + await ctx.rebuild(); + + // global.BUNDLER_CTX = ctx; +} diff --git a/src/functions/bundler/grab-client-hydration-script.tsx b/src/functions/bundler/grab-client-hydration-script.tsx index 124c0a2..b297e54 100644 --- a/src/functions/bundler/grab-client-hydration-script.tsx +++ b/src/functions/bundler/grab-client-hydration-script.tsx @@ -31,7 +31,7 @@ export default async function grabClientHydrationScript({ let txt = ``; - txt += `import { hydrateRoot, createElement } from "react-dom/client";\n`; + txt += `import { hydrateRoot } from "react-dom/client";\n`; if (does_root_exist) { txt += `import Root from "${root_component_path}";\n`; } diff --git a/src/functions/bundler/record-artifacts.ts b/src/functions/bundler/record-artifacts.ts new file mode 100644 index 0000000..6736cf4 --- /dev/null +++ b/src/functions/bundler/record-artifacts.ts @@ -0,0 +1,27 @@ +import grabDirNames from "../../utils/grab-dir-names"; +import type { BundlerCTXMap } from "../../types"; + +const { HYDRATION_DST_DIR_MAP_JSON_FILE } = grabDirNames(); + +type Params = { + artifacts: BundlerCTXMap[]; +}; + +export default async function recordArtifacts({ artifacts }: Params) { + const artifacts_map: { [k: string]: BundlerCTXMap } = {}; + + for (const artifact of artifacts) { + if (artifact?.local_path) { + artifacts_map[artifact.local_path] = artifact; + } + } + + if (global.BUNDLER_CTX_MAP) { + global.BUNDLER_CTX_MAP = artifacts_map; + } + + await Bun.write( + HYDRATION_DST_DIR_MAP_JSON_FILE, + JSON.stringify(artifacts_map, null, 4), + ); +} diff --git a/src/functions/bunext-init.ts b/src/functions/bunext-init.ts index 4b2a206..f95fb1c 100644 --- a/src/functions/bunext-init.ts +++ b/src/functions/bunext-init.ts @@ -15,6 +15,7 @@ import watcher from "./server/watcher"; import { log } from "../utils/log"; import cron from "./server/cron"; import EJSON from "../utils/ejson"; +import allPagesBunBundler from "./bundler/all-pages-bun-bundler"; /** * # Declare Global Variables @@ -28,12 +29,13 @@ declare global { var ROUTER: FileSystemRouter; var HMR_CONTROLLERS: GlobalHMRControllerObject[]; var LAST_BUILD_TIME: number; - var BUNDLER_CTX_MAP: { [k: string]: BundlerCTXMap }; + var BUNDLER_CTX_MAP: { [k: string]: BundlerCTXMap } | undefined; var BUNDLER_REBUILDS: 0; var PAGES_SRC_WATCHER: FSWatcher | undefined; var CURRENT_VERSION: string | undefined; var PAGE_FILES: PageFiles[]; var ROOT_FILE_UPDATED: boolean; + // var BUNDLER_CTX: BuildContext | undefined; } const { PAGES_DIR, HYDRATION_DST_DIR_MAP_JSON_FILE } = grabDirNames(); @@ -59,13 +61,14 @@ export default async function bunextInit() { const is_dev = isDevelopment(); if (is_dev) { - await allPagesBundler(); + // await allPagesBundler(); + await allPagesBunBundler(); watcher(); } else { const artifacts = EJSON.parse( readFileSync(HYDRATION_DST_DIR_MAP_JSON_FILE, "utf-8"), ) as { [k: string]: BundlerCTXMap } | undefined; - if (!artifacts?.[0]) { + if (!artifacts) { log.error("Please build first."); process.exit(1); } diff --git a/src/functions/server/bunext-req-handler.ts b/src/functions/server/bunext-req-handler.ts index ace3b76..52cb371 100644 --- a/src/functions/server/bunext-req-handler.ts +++ b/src/functions/server/bunext-req-handler.ts @@ -5,6 +5,7 @@ import grabConstants from "../../utils/grab-constants"; import handleHmr from "./handle-hmr"; import handlePublic from "./handle-public"; import handleFiles from "./handle-files"; +import handleBunextPublicAssets from "./handle-bunext-public-assets"; type Params = { req: Request; }; @@ -39,6 +40,8 @@ export default async function bunextRequestHandler({ if (url.pathname === "/__hmr" && is_dev) { response = await handleHmr({ req }); + } else if (url.pathname.startsWith("/.bunext/public/pages")) { + response = await handleBunextPublicAssets({ req }); } else if (url.pathname.startsWith("/api/")) { response = await handleRoutes({ req }); } else if (url.pathname.startsWith("/public/")) { diff --git a/src/functions/server/cleanup-artifacts.tsx b/src/functions/server/cleanup-artifacts.tsx new file mode 100644 index 0000000..fada23f --- /dev/null +++ b/src/functions/server/cleanup-artifacts.tsx @@ -0,0 +1,52 @@ +import { log } from "../../utils/log"; +import path from "path"; +import grabDirNames from "../../utils/grab-dir-names"; +import { existsSync, readdirSync, statSync, unlinkSync } from "fs"; +import type { BundlerCTXMap } from "../../types"; + +type Params = { + new_artifacts: BundlerCTXMap[]; +}; + +const { ROOT_DIR, HYDRATION_DST_DIR_MAP_JSON_FILE_NAME } = grabDirNames(); + +export default async function cleanupArtifacts({ new_artifacts }: Params) { + try { + for (let i = 0; i < new_artifacts.length; i++) { + const new_artifact = new_artifacts[i]; + + const artifact_public_dir = path.dirname( + path.join(ROOT_DIR, new_artifact.path), + ); + + const dir_content = readdirSync(artifact_public_dir); + + for (let d = 0; d < dir_content.length; d++) { + const dir_or_file = dir_content[d]; + const full_path = path.join(artifact_public_dir, dir_or_file); + + const file_or_path_stats = statSync(full_path); + + if ( + file_or_path_stats.isDirectory() || + dir_or_file == HYDRATION_DST_DIR_MAP_JSON_FILE_NAME + ) { + continue; + } + + if ( + new_artifact.path.includes(dir_or_file) || + new_artifact.css_path?.includes(dir_or_file) + ) { + continue; + } + + if (existsSync(full_path)) { + unlinkSync(full_path); + } + } + } + } catch (error: any) { + log.error(error); + } +} diff --git a/src/functions/server/handle-bunext-public-assets.ts b/src/functions/server/handle-bunext-public-assets.ts new file mode 100644 index 0000000..4856650 --- /dev/null +++ b/src/functions/server/handle-bunext-public-assets.ts @@ -0,0 +1,34 @@ +import grabDirNames from "../../utils/grab-dir-names"; +import path from "path"; +import isDevelopment from "../../utils/is-development"; +import { existsSync } from "fs"; + +const { HYDRATION_DST_DIR } = grabDirNames(); + +type Params = { + req: Request; +}; + +export default async function ({ req }: Params): Promise { + try { + const is_dev = isDevelopment(); + const url = new URL(req.url); + const file_path = path.join( + HYDRATION_DST_DIR, + url.pathname.replace(/\/\.bunext\/public\/pages\//, ""), + ); + + if (!existsSync(file_path)) { + return new Response(`File Doesn't Exist`, { + status: 404, + }); + } + + const file = Bun.file(file_path); + return new Response(file); + } catch (error) { + return new Response(`File Not Found`, { + status: 404, + }); + } +} diff --git a/src/functions/server/handle-hmr.ts b/src/functions/server/handle-hmr.ts index ca7784e..fcb9004 100644 --- a/src/functions/server/handle-hmr.ts +++ b/src/functions/server/handle-hmr.ts @@ -7,7 +7,7 @@ export default async function ({ req }: Params): Promise { const match = global.ROUTER.match(referer_url.pathname); const target_map = match?.filePath - ? global.BUNDLER_CTX_MAP[match.filePath] + ? global.BUNDLER_CTX_MAP?.[match.filePath] : undefined; let controller: ReadableStreamDefaultController; diff --git a/src/functions/server/rebuild-bundler.tsx b/src/functions/server/rebuild-bundler.tsx index bb5459e..e1b80b5 100644 --- a/src/functions/server/rebuild-bundler.tsx +++ b/src/functions/server/rebuild-bundler.tsx @@ -1,6 +1,7 @@ -import allPagesBundler from "../bundler/all-pages-bundler"; import serverPostBuildFn from "./server-post-build-fn"; import { log } from "../../utils/log"; +import allPagesBunBundler from "../bundler/all-pages-bun-bundler"; +import cleanupArtifacts from "./cleanup-artifacts"; type Params = { target_file_paths?: string[]; @@ -13,11 +14,15 @@ export default async function rebuildBundler(params?: Params) { // await global.BUNDLER_CTX?.dispose(); // global.BUNDLER_CTX = undefined; - await allPagesBundler({ + const new_artifacts = await allPagesBunBundler({ page_file_paths: params?.target_file_paths, }); await serverPostBuildFn(); + + if (new_artifacts?.[0]) { + cleanupArtifacts({ new_artifacts }); + } } catch (error: any) { log.error(error); } diff --git a/src/functions/server/watcher.ts b/src/functions/server/watcher.ts index a453b6f..8bcec42 100644 --- a/src/functions/server/watcher.ts +++ b/src/functions/server/watcher.ts @@ -8,8 +8,6 @@ import rewritePagesModule from "../../utils/rewrite-pages-module"; const { ROOT_DIR } = grabDirNames(); export default async function watcher() { - await Bun.sleep(1000); - const pages_src_watcher = watch( ROOT_DIR, { @@ -88,7 +86,9 @@ async function fullRebuild(params?: { msg?: string }) { (hmr) => hmr.target_map?.local_path, ).filter((f) => typeof f == "string"); - await rewritePagesModule({ page_file_path: target_file_paths }); + await rewritePagesModule({ + page_file_path: target_file_paths, + }); 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 4e37d09..4f89b2d 100644 --- a/src/functions/server/web-pages/generate-web-html.tsx +++ b/src/functions/server/web-pages/generate-web-html.tsx @@ -7,6 +7,18 @@ import grabWebPageHydrationScript from "./grab-web-page-hydration-script"; import grabWebMetaHTML from "./grab-web-meta-html"; import { log } from "../../../utils/log"; import { AppData } from "../../../data/app-data"; +import { readFileSync } from "fs"; +import path from "path"; + +let _reactVersion = "19"; +try { + _reactVersion = JSON.parse( + readFileSync( + path.join(process.cwd(), "node_modules/react/package.json"), + "utf-8", + ), + ).version; +} catch {} export default async function genWebHTML({ component, @@ -54,6 +66,18 @@ export default async function genWebHTML({ }\n`; if (bundledMap?.path) { + const dev = isDevelopment(); + const devSuffix = dev ? "?dev" : ""; + const importMap = JSON.stringify({ + imports: { + react: `https://esm.sh/react@${_reactVersion}${devSuffix}`, + "react-dom": `https://esm.sh/react-dom@${_reactVersion}${devSuffix}`, + "react-dom/client": `https://esm.sh/react-dom@${_reactVersion}/client${devSuffix}`, + "react/jsx-runtime": `https://esm.sh/react@${_reactVersion}/jsx-runtime${devSuffix}`, + "react/jsx-dev-runtime": `https://esm.sh/react@${_reactVersion}/jsx-dev-runtime${devSuffix}`, + }, + }); + html += ` \n`; html += ` \n`; } diff --git a/src/functions/server/web-pages/grab-page-component.tsx b/src/functions/server/web-pages/grab-page-component.tsx index b1713c4..805f333 100644 --- a/src/functions/server/web-pages/grab-page-component.tsx +++ b/src/functions/server/web-pages/grab-page-component.tsx @@ -62,7 +62,7 @@ export default async function grabPageComponent({ throw new Error(errMsg); } - const bundledMap = global.BUNDLER_CTX_MAP[file_path]; + const bundledMap = global.BUNDLER_CTX_MAP?.[file_path]; if (!bundledMap?.path) { const errMsg = `No Bundled File Path for this request path!`; diff --git a/src/functions/server/web-pages/grab-page-error-component.tsx b/src/functions/server/web-pages/grab-page-error-component.tsx index 1c1bcb7..b07f34e 100644 --- a/src/functions/server/web-pages/grab-page-error-component.tsx +++ b/src/functions/server/web-pages/grab-page-error-component.tsx @@ -33,7 +33,7 @@ export default async function grabPageErrorComponent({ const filePath = match?.filePath || presetComponent; const bundledMap = match?.filePath - ? global.BUNDLER_CTX_MAP[match.filePath] + ? global.BUNDLER_CTX_MAP?.[match.filePath] : ({} as BundlerCTXMap); const module: BunextPageModule = await import(filePath); diff --git a/src/utils/grab-all-pages.ts b/src/utils/grab-all-pages.ts index 59cd31f..39db52d 100644 --- a/src/utils/grab-all-pages.ts +++ b/src/utils/grab-all-pages.ts @@ -66,7 +66,13 @@ function grabPageDirRecursively({ page_dir }: { page_dir: string }) { } } - return pages_files; + return pages_files.sort((a, b) => { + if (a.url_path == "/index") { + return -1; + } + + return 1; + }); } function grabPageFileObject({ diff --git a/src/utils/grab-dir-names.ts b/src/utils/grab-dir-names.ts index a508bb2..accda12 100644 --- a/src/utils/grab-dir-names.ts +++ b/src/utils/grab-dir-names.ts @@ -6,13 +6,6 @@ export default function grabDirNames() { const PAGES_DIR = path.join(SRC_DIR, "pages"); const API_DIR = path.join(PAGES_DIR, "api"); const PUBLIC_DIR = path.join(ROOT_DIR, "public"); - const BUNEXT_PUBLIC_DIR = path.join(PUBLIC_DIR, "__bunext"); - const HYDRATION_DST_DIR = path.join(BUNEXT_PUBLIC_DIR, "pages"); - const BUNEXT_CACHE_DIR = path.join(BUNEXT_PUBLIC_DIR, "cache"); - const HYDRATION_DST_DIR_MAP_JSON_FILE = path.join( - HYDRATION_DST_DIR, - "map.json", - ); const CONFIG_FILE = path.join(ROOT_DIR, "bunext.config.ts"); const BUNX_CWD_DIR = path.resolve(ROOT_DIR, ".bunext"); @@ -28,6 +21,15 @@ export default function grabDirNames() { "hydration-src", ); + const BUNEXT_PUBLIC_DIR = path.join(BUNX_CWD_DIR, "public"); + const HYDRATION_DST_DIR = path.join(BUNEXT_PUBLIC_DIR, "pages"); + const BUNEXT_CACHE_DIR = path.join(BUNEXT_PUBLIC_DIR, "cache"); + const HYDRATION_DST_DIR_MAP_JSON_FILE_NAME = "map.json"; + const HYDRATION_DST_DIR_MAP_JSON_FILE = path.join( + HYDRATION_DST_DIR, + HYDRATION_DST_DIR_MAP_JSON_FILE_NAME, + ); + const BUNX_ROOT_DIR = path.resolve(__dirname, "../../"); const BUNX_ROOT_SRC_DIR = path.join(BUNX_ROOT_DIR, "src"); const BUNX_ROOT_PRESETS_DIR = path.join(BUNX_ROOT_SRC_DIR, "presets"); @@ -65,5 +67,6 @@ export default function grabDirNames() { BUNEXT_CACHE_DIR, BUNX_CWD_MODULE_CACHE_DIR, BUNX_CWD_PAGES_REWRITE_DIR, + HYDRATION_DST_DIR_MAP_JSON_FILE_NAME, }; }