diff --git a/dist/commands/dev/dev-spawn.js b/dist/commands/dev/dev-spawn.js index 1e195a1..3c29423 100644 --- a/dist/commands/dev/dev-spawn.js +++ b/dist/commands/dev/dev-spawn.js @@ -4,11 +4,22 @@ import bunextInit from "../../functions/bunext-init"; import grabDirNames from "../../utils/grab-dir-names"; import { rmSync } from "fs"; const { HYDRATION_DST_DIR, BUNX_CWD_PAGES_REWRITE_DIR } = grabDirNames(); +process.on("uncaughtException", (error) => { + log.error(`Uncaught exception: ${error}`); +}); +process.on("unhandledRejection", (reason) => { + log.error(`Unhandled rejection: ${reason}`); +}); log.info("Running development server ..."); try { rmSync(HYDRATION_DST_DIR, { recursive: true }); rmSync(BUNX_CWD_PAGES_REWRITE_DIR, { recursive: true }); } catch (error) { } -await bunextInit(); -await startServer(); +try { + await bunextInit(); + await startServer(); +} +catch (error) { + log.error(`Failed to start development server: ${error}`); +} diff --git a/dist/functions/bundler/pages-ssr-bundler.js b/dist/functions/bundler/pages-ssr-bundler.js index f934dd3..0b1dd97 100644 --- a/dist/functions/bundler/pages-ssr-bundler.js +++ b/dist/functions/bundler/pages-ssr-bundler.js @@ -48,41 +48,46 @@ export default async function pagesSSRBundler(params) { writeFileSync(path.join(BUNX_TMP_DIR, "ssr-entrypoints.json"), JSON.stringify(entryPoints, null, 4)); } catch (error) { } - await esbuild.build({ - entryPoints, - outdir: BUNX_CWD_MODULE_CACHE_DIR, - bundle: true, - minify: !dev, - format: "esm", - target: "esnext", - platform: "node", - define: { - "process.env.NODE_ENV": JSON.stringify(dev ? "development" : "production"), - }, - entryNames: "[dir]/[hash]", - metafile: true, - plugins: [ - tailwindEsbuildPlugin, - ssrVirtualFilesPlugin({ - entryToPage, - }), - ssrCTXArtifactTracker({ - entryToPage, - post_build_fn: params?.post_build_fn, - }), - ], - jsx: "automatic", - external: [ - "react", - "react-dom", - "react/jsx-runtime", - "react/jsx-dev-runtime", - "bun:*", - "sqlite-vec", - "better-sqlite3", - ...(config.ssr_compiler_excludes || []), - ], - splitting: true, - // logLevel: "silent", - }); + try { + await esbuild.build({ + entryPoints, + outdir: BUNX_CWD_MODULE_CACHE_DIR, + bundle: true, + minify: !dev, + format: "esm", + target: "esnext", + platform: "node", + define: { + "process.env.NODE_ENV": JSON.stringify(dev ? "development" : "production"), + }, + entryNames: "[dir]/[hash]", + metafile: true, + plugins: [ + tailwindEsbuildPlugin, + ssrVirtualFilesPlugin({ + entryToPage, + }), + ssrCTXArtifactTracker({ + entryToPage, + post_build_fn: params?.post_build_fn, + }), + ], + jsx: "automatic", + external: [ + "react", + "react-dom", + "react/jsx-runtime", + "react/jsx-dev-runtime", + "bun:*", + "sqlite-vec", + "better-sqlite3", + ...(config.ssr_compiler_excludes || []), + ], + splitting: true, + }); + } + catch (error) { + global.SSR_BUNDLER_CTX_DISPOSED = true; + log.error(`SSR Bundler Error: ${error}`); + } } diff --git a/dist/functions/bundler/plugins/esbuild-ctx-artifact-tracker.js b/dist/functions/bundler/plugins/esbuild-ctx-artifact-tracker.js index 6d43972..e65a4a6 100644 --- a/dist/functions/bundler/plugins/esbuild-ctx-artifact-tracker.js +++ b/dist/functions/bundler/plugins/esbuild-ctx-artifact-tracker.js @@ -56,7 +56,12 @@ export default function esbuildCTXArtifactTracker({ entryToPage, post_build_fn, _.merge(global.BUNDLER_CTX_MAP[artifact.local_path], artifact); } } - post_build_fn?.({ artifacts }); + try { + await post_build_fn?.({ artifacts }); + } + catch (error) { + log.error(`Post-build Error: ${error}`); + } } const elapsed = (performance.now() - build_start).toFixed(0); log.success(`[Built] in ${elapsed}ms`); @@ -82,7 +87,7 @@ export default function esbuildCTXArtifactTracker({ entryToPage, post_build_fn, } else { try { - pagesSSRBundler(); + await pagesSSRBundler(); } catch (error) { log.error(`SSR Bundler Error: ${error}`); diff --git a/dist/functions/bundler/plugins/ssr-ctx-artifact-tracker.js b/dist/functions/bundler/plugins/ssr-ctx-artifact-tracker.js index 0ddbd31..b601bee 100644 --- a/dist/functions/bundler/plugins/ssr-ctx-artifact-tracker.js +++ b/dist/functions/bundler/plugins/ssr-ctx-artifact-tracker.js @@ -23,9 +23,15 @@ export default function ssrCTXArtifactTracker({ entryToPage, post_build_fn, }) { build.onEnd(async (result) => { if (result.errors.length > 0) { global.SSR_BUNDLER_CTX_DISPOSED = true; - await global.SSR_BUNDLER_CTX?.dispose(); + try { + await global.SSR_BUNDLER_CTX?.dispose(); + } + catch { } + global.SSR_BUNDLER_CTX = undefined; build_starts = 0; - console.log("SSR Build errors:", result.errors); + for (const err of result.errors) { + console.error(`SSR Build error: ${err.text}${err.location ? ` (${err.location.file}:${err.location.line}:${err.location.column})` : ""}`); + } return; } const artifacts = grabArtifactsFromBundledResults({ diff --git a/dist/functions/server/chokidar-watcher-esbuild-ctx.js b/dist/functions/server/chokidar-watcher-esbuild-ctx.js index 281156e..1949e71 100644 --- a/dist/functions/server/chokidar-watcher-esbuild-ctx.js +++ b/dist/functions/server/chokidar-watcher-esbuild-ctx.js @@ -1,17 +1,17 @@ import chokidar from "chokidar"; import path from "path"; -import { existsSync, statSync } from "fs"; +import { existsSync } from "fs"; import grabDirNames from "../../utils/grab-dir-names"; import fullRebuild from "./full-rebuild"; import { AppData } from "../../data/app-data"; import checkExcludedPatterns from "../../utils/check-excluded-patterns"; import pagesSSRBundler from "../bundler/pages-ssr-bundler"; +import { log } from "../../utils/log"; const { ROOT_DIR, BUNX_BUNDLER_ERROR_EXIT_FILE } = grabDirNames(); export default async function chokadirWatcherEsbuildCTX() { - // Define ignored patterns directly in Chokidar for better performance const watcher = chokidar.watch(ROOT_DIR, { ignored: [ - /(^|[\/\\])\../, // ignore dotfiles + /(^|[\/\\])\../, /node_modules/, /public/, /\.bunext/, @@ -25,78 +25,97 @@ export default async function chokadirWatcherEsbuildCTX() { depth: 99, }); const handleEvent = async (event, filePath) => { - const filename = path.relative(ROOT_DIR, filePath); - if (existsSync(BUNX_BUNDLER_ERROR_EXIT_FILE)) { - await fullRebuild(); - return; - } - if (global.BUNDLER_CTX_DISPOSED) { - await fullRebuild({ msg: `Restarting Bundler ...` }); - } - if (global.SSR_BUNDLER_CTX_DISPOSED) { - pagesSSRBundler(); - } - if (filename.match(/\/styles$/) || filename === "styles") { - global.RECOMPILING = true; - await Bun.sleep(1000); - await fullRebuild({ - msg: `Detected new \`styles\` directory. Rebuilding ...`, - }); - return; - } - if (filename.match(/bunext.config\.ts/)) { - await fullRebuild({ - msg: `bunext.config.ts file changed. Rebuilding server ...`, - }); - return; - } - const target_files_match = /\.(tsx?|jsx?|css)$/; - if (event === "change") { - if (filename.match(target_files_match)) { + let owns_recompile = false; + try { + const filename = path.relative(ROOT_DIR, filePath); + if (existsSync(BUNX_BUNDLER_ERROR_EXIT_FILE)) { + await fullRebuild(); + return; + } + if (global.BUNDLER_CTX_DISPOSED) { + await fullRebuild({ msg: `Restarting Bundler ...` }); + } + if (global.SSR_BUNDLER_CTX_DISPOSED) { + await pagesSSRBundler().catch((error) => { + log.error(`SSR Bundler Error: ${error}`); + }); + } + if (filename.match(/\/styles$/) || filename === "styles") { + owns_recompile = true; + global.RECOMPILING = true; + await Bun.sleep(1000); + await fullRebuild({ + msg: `Detected new \`styles\` directory. Rebuilding ...`, + }); + return; + } + if (filename.match(/bunext.config\.ts/)) { + await fullRebuild({ + msg: `bunext.config.ts file changed. Rebuilding server ...`, + }); + return; + } + const target_files_match = /\.(tsx?|jsx?|css)$/; + if (event === "change") { + if (filename.match(target_files_match)) { + if (global.RECOMPILING) + return; + owns_recompile = true; + global.RECOMPILING = true; + if (filename.match(/.*\.server\.tsx?/)) { + global.IS_SERVER_COMPONENT = true; + } + if (global.BUNDLER_CTX) { + await global.BUNDLER_CTX.rebuild(); + } + if (filename.match(/(404|500)\.tsx?/)) { + for (let i = global.HMR_CONTROLLERS.length - 1; i >= 0; i--) { + const controller = global.HMR_CONTROLLERS[i]; + try { + controller?.controller?.enqueue(`event: update\ndata: ${JSON.stringify({ reload: true })}\n\n`); + } + catch { + global.HMR_CONTROLLERS.splice(i, 1); + } + } + } + } + return; + } + if (["add", "unlink", "addDir", "unlinkDir"].includes(event)) { + const is_file_of_interest = !!filename.match(target_files_match) || + event.includes("Dir"); + if (!is_file_of_interest) + return; + if (!filename.match(/^src\/pages\/|\.css$/) || + checkExcludedPatterns({ path: filename }) || + filename.includes(" ")) { + return reloadWatcher(); + } if (global.RECOMPILING) return; - global.RECOMPILING = true; - if (filename.match(/.*\.server\.tsx?/)) { - global.IS_SERVER_COMPONENT = true; - } - if (global.BUNDLER_CTX) { - await global.BUNDLER_CTX.rebuild(); - } - // HMR for error pages - if (filename.match(/(404|500)\.tsx?/)) { - global.HMR_CONTROLLERS.forEach((controller) => { - controller?.controller?.enqueue(`event: update\ndata: ${JSON.stringify({ reload: true })}\n\n`); - }); - } + owns_recompile = true; + const action = event.startsWith("add") ? "created" : "deleted"; + const type = filename.match(/\.css$/) + ? "Stylesheet" + : event.includes("Dir") + ? "Directory" + : filename.match(/\/pages\/api\//) + ? "API Route" + : "Page"; + await fullRebuild({ + msg: `${type} ${action}: ${filename}. Rebuilding ...`, + }); } - return; } - // Handle Structural Changes (Add/Delete) - if (["add", "unlink", "addDir", "unlinkDir"].includes(event)) { - const is_file_of_interest = !!filename.match(target_files_match) || event.includes("Dir"); - if (!is_file_of_interest) - return; - // Validation logic - if (!filename.match(/^src\/pages\/|\.css$/) || - checkExcludedPatterns({ path: filename }) || - filename.includes(" ")) { - // With chokidar, you rarely need to "reload" the whole watcher. - // But we keep the logic for consistency. - return reloadWatcher(); + catch (error) { + log.error(`Watcher rebuild failed: ${error}`); + } + finally { + if (owns_recompile) { + global.RECOMPILING = false; + global.IS_SERVER_COMPONENT = false; } - if (global.RECOMPILING) - return; - const action = event.startsWith("add") ? "created" : "deleted"; - const type = filename.match(/\.css$/) - ? "Stylesheet" - : event.includes("Dir") - ? "Directory" - : filename.match(/\/pages\/api\//) - ? "API Route" - : "Page"; - await fullRebuild({ - msg: `${type} ${action}: ${filename}. Rebuilding ...`, - }); } }; watcher @@ -105,7 +124,6 @@ export default async function chokadirWatcherEsbuildCTX() { .on("unlink", (path) => handleEvent("unlink", path)) .on("addDir", (path) => handleEvent("addDir", path)) .on("unlinkDir", (path) => handleEvent("unlinkDir", path)); - // global.PAGES_SRC_WATCHER = watcher; } function reloadWatcher() { if (global.PAGES_SRC_WATCHER) { diff --git a/dist/functions/server/full-rebuild.js b/dist/functions/server/full-rebuild.js index f08b5eb..b2aeffb 100644 --- a/dist/functions/server/full-rebuild.js +++ b/dist/functions/server/full-rebuild.js @@ -17,7 +17,6 @@ export default async function fullRebuild(params) { global.SSR_BUNDLER_CTX = undefined; } catch (error) { } - // await pagesSSRBundler(); await allPagesESBuildContextBundler({ post_build_fn: () => { serverPostBuildFn(); @@ -25,9 +24,11 @@ export default async function fullRebuild(params) { }); } catch (error) { + log.error(error); + } + finally { global.RECOMPILING = false; global.IS_SERVER_COMPONENT = false; - log.error(error); } if (global.PAGES_SRC_WATCHER) { global.PAGES_SRC_WATCHER.close(); diff --git a/dist/functions/server/server-post-build-fn.js b/dist/functions/server/server-post-build-fn.js index d179fa2..6b909c0 100644 --- a/dist/functions/server/server-post-build-fn.js +++ b/dist/functions/server/server-post-build-fn.js @@ -22,12 +22,22 @@ export default async function serverPostBuildFn(params) { continue; } if (params?.reload_all_controllers) { - controller.controller.enqueue(reload_enqueue); + try { + controller.controller.enqueue(reload_enqueue); + } + catch { + global.HMR_CONTROLLERS.splice(i, 1); + } continue; } const target_artifact = global.BUNDLER_CTX_MAP[controller.target_map.local_path]; - if (!target_artifact.local_path) { - controller.controller.enqueue(reload_enqueue); + if (!target_artifact?.local_path) { + try { + controller.controller.enqueue(reload_enqueue); + } + catch { + global.HMR_CONTROLLERS.splice(i, 1); + } continue; } const mock_req = target_artifact.req_url diff --git a/dist/functions/server/watcher-esbuild-ctx.js b/dist/functions/server/watcher-esbuild-ctx.js index f84460a..b228af7 100644 --- a/dist/functions/server/watcher-esbuild-ctx.js +++ b/dist/functions/server/watcher-esbuild-ctx.js @@ -5,99 +5,121 @@ import fullRebuild from "./full-rebuild"; import { AppData } from "../../data/app-data"; import checkExcludedPatterns from "../../utils/check-excluded-patterns"; import pagesSSRBundler from "../bundler/pages-ssr-bundler"; +import { log } from "../../utils/log"; const { ROOT_DIR, BUNX_BUNDLER_ERROR_EXIT_FILE } = grabDirNames(); export default async function watcherEsbuildCTX() { const pages_src_watcher = watch(ROOT_DIR, { recursive: true, persistent: true, }, async (event, filename) => { - if (!filename) - return; - if (existsSync(BUNX_BUNDLER_ERROR_EXIT_FILE)) { - await fullRebuild(); - return; - } - if (filename.match(/^\.\w+/)) { - return; - } - if (global.BUNDLER_CTX_DISPOSED) { - await fullRebuild({ msg: `Restarting Bundler ...` }); - return; - } - if (global.SSR_BUNDLER_CTX_DISPOSED) { - pagesSSRBundler(); - } - if (filename.endsWith(AppData["BunextTmpFileExt"])) { - return; - } - const full_file_path = path.join(ROOT_DIR, filename); - const does_file_exist = existsSync(full_file_path); - const file_stat = does_file_exist - ? statSync(full_file_path) - : undefined; - if (full_file_path.match(/\/styles$/)) { - global.RECOMPILING = true; - await Bun.sleep(1000); - await fullRebuild({ - msg: `Detected new \`styles\` directory. Rebuilding ...`, - }); - return; - } - const excluded_match = /node_modules\/|^public\/|^\.bunext\/|^\.git\/|^\.?dist\/|bun\.lockb$/; - if (filename.match(excluded_match)) - return; - if (filename.match(/bunext.config\.ts/)) { - await fullRebuild({ - msg: `bunext.config.ts file changed. Rebuilding server ...`, - }); - return; - } - const target_files_match = /\.(tsx?|jsx?|css)$/; - // const rebuild_skip_paths = /\/pages\/api\//; - if (event !== "rename") { - if (filename.match(target_files_match)) { - if (global.RECOMPILING) - return; + let owns_recompile = false; + try { + if (!filename) + return; + if (existsSync(BUNX_BUNDLER_ERROR_EXIT_FILE)) { + await fullRebuild(); + return; + } + if (filename.match(/^\.\w+/)) { + return; + } + if (global.BUNDLER_CTX_DISPOSED) { + await fullRebuild({ msg: `Restarting Bundler ...` }); + return; + } + if (global.SSR_BUNDLER_CTX_DISPOSED) { + await pagesSSRBundler().catch((error) => { + log.error(`SSR Bundler Error: ${error}`); + }); + } + if (filename.endsWith(AppData["BunextTmpFileExt"])) { + return; + } + const full_file_path = path.join(ROOT_DIR, filename); + const does_file_exist = existsSync(full_file_path); + const file_stat = does_file_exist + ? statSync(full_file_path) + : undefined; + if (full_file_path.match(/\/styles$/)) { + owns_recompile = true; global.RECOMPILING = true; - if (filename.match(/.*\.server\.tsx?/)) { - global.IS_SERVER_COMPONENT = true; - } - if (global.BUNDLER_CTX) { - await global.BUNDLER_CTX.rebuild(); - } - if (filename.match(/(404|500)\.tsx?/)) { - for (let i = global.HMR_CONTROLLERS.length - 1; i >= 0; i--) { - const controller = global.HMR_CONTROLLERS[i]; - controller?.controller?.enqueue(`event: update\ndata: ${JSON.stringify({ reload: true })}\n\n`); + await Bun.sleep(1000); + await fullRebuild({ + msg: `Detected new \`styles\` directory. Rebuilding ...`, + }); + return; + } + const excluded_match = /node_modules\/|^public\/|^\.bunext\/|^\.git\/|^\.?dist\/|bun\.lockb$/; + if (filename.match(excluded_match)) + return; + if (filename.match(/bunext.config\.ts/)) { + await fullRebuild({ + msg: `bunext.config.ts file changed. Rebuilding server ...`, + }); + return; + } + const target_files_match = /\.(tsx?|jsx?|css)$/; + if (event !== "rename") { + if (filename.match(target_files_match)) { + if (global.RECOMPILING) + return; + owns_recompile = true; + global.RECOMPILING = true; + if (filename.match(/.*\.server\.tsx?/)) { + global.IS_SERVER_COMPONENT = true; + } + if (global.BUNDLER_CTX) { + await global.BUNDLER_CTX.rebuild(); + } + if (filename.match(/(404|500)\.tsx?/)) { + for (let i = global.HMR_CONTROLLERS.length - 1; i >= 0; i--) { + const controller = global.HMR_CONTROLLERS[i]; + try { + controller?.controller?.enqueue(`event: update\ndata: ${JSON.stringify({ reload: true })}\n\n`); + } + catch { + global.HMR_CONTROLLERS.splice(i, 1); + } + } } } + return; } - return; + const is_file_of_interest = Boolean(filename.match(target_files_match)) || + file_stat?.isDirectory(); + if (!is_file_of_interest) { + return; + } + if (!filename.match(/^src\/pages\/|\.css$/)) + return reloadWatcher(); + if (checkExcludedPatterns({ path: filename })) + return reloadWatcher(); + if (filename.match(/ /)) + return reloadWatcher(); + if (global.RECOMPILING) + return; + owns_recompile = true; + const action = does_file_exist ? "created" : "deleted"; + const type = filename.match(/\.css$/) + ? "Sylesheet" + : file_stat?.isDirectory() + ? "Directory" + : filename.match(/\/pages\/api\//) + ? "API Route" + : "Page"; + await fullRebuild({ + msg: `${type} ${action}: ${filename}. Rebuilding ...`, + }); } - const is_file_of_interest = Boolean(filename.match(target_files_match)) || - file_stat?.isDirectory(); - if (!is_file_of_interest) { - return; + catch (error) { + log.error(`Watcher rebuild failed: ${error}`); + } + finally { + if (owns_recompile) { + global.RECOMPILING = false; + global.IS_SERVER_COMPONENT = false; + } } - if (!filename.match(/^src\/pages\/|\.css$/)) - return reloadWatcher(); - if (checkExcludedPatterns({ path: filename })) - return reloadWatcher(); - if (filename.match(/ /)) - return reloadWatcher(); - if (global.RECOMPILING) - return; - const action = does_file_exist ? "created" : "deleted"; - const type = filename.match(/\.css$/) - ? "Sylesheet" - : file_stat?.isDirectory() - ? "Directory" - : filename.match(/\/pages\/api\//) - ? "API Route" - : "Page"; - await fullRebuild({ - msg: `${type} ${action}: ${filename}. Rebuilding ...`, - }); }); global.PAGES_SRC_WATCHER = pages_src_watcher; } diff --git a/src/commands/dev/dev-spawn.ts b/src/commands/dev/dev-spawn.ts index 79ad16d..3baae27 100644 --- a/src/commands/dev/dev-spawn.ts +++ b/src/commands/dev/dev-spawn.ts @@ -6,6 +6,14 @@ import { rmSync } from "fs"; const { HYDRATION_DST_DIR, BUNX_CWD_PAGES_REWRITE_DIR } = grabDirNames(); +process.on("uncaughtException", (error) => { + log.error(`Uncaught exception: ${error}`); +}); + +process.on("unhandledRejection", (reason) => { + log.error(`Unhandled rejection: ${reason}`); +}); + log.info("Running development server ..."); try { @@ -13,6 +21,9 @@ try { rmSync(BUNX_CWD_PAGES_REWRITE_DIR, { recursive: true }); } catch (error) {} -await bunextInit(); - -await startServer(); +try { + await bunextInit(); + await startServer(); +} catch (error) { + log.error(`Failed to start development server: ${error}`); +} diff --git a/src/functions/bundler/pages-ssr-bundler.ts b/src/functions/bundler/pages-ssr-bundler.ts index 98f8e8d..0b3303d 100644 --- a/src/functions/bundler/pages-ssr-bundler.ts +++ b/src/functions/bundler/pages-ssr-bundler.ts @@ -75,43 +75,47 @@ export default async function pagesSSRBundler(params?: Params) { ); } catch (error) {} - await esbuild.build({ - entryPoints, - outdir: BUNX_CWD_MODULE_CACHE_DIR, - bundle: true, - minify: !dev, - format: "esm", - target: "esnext", - platform: "node", - define: { - "process.env.NODE_ENV": JSON.stringify( - dev ? "development" : "production", - ), - }, - entryNames: "[dir]/[hash]", - metafile: true, - plugins: [ - tailwindEsbuildPlugin, - ssrVirtualFilesPlugin({ - entryToPage, - }), - ssrCTXArtifactTracker({ - entryToPage, - post_build_fn: params?.post_build_fn, - }), - ], - jsx: "automatic", - external: [ - "react", - "react-dom", - "react/jsx-runtime", - "react/jsx-dev-runtime", - "bun:*", - "sqlite-vec", - "better-sqlite3", - ...(config.ssr_compiler_excludes || []), - ], - splitting: true, - // logLevel: "silent", - }); + try { + await esbuild.build({ + entryPoints, + outdir: BUNX_CWD_MODULE_CACHE_DIR, + bundle: true, + minify: !dev, + format: "esm", + target: "esnext", + platform: "node", + define: { + "process.env.NODE_ENV": JSON.stringify( + dev ? "development" : "production", + ), + }, + entryNames: "[dir]/[hash]", + metafile: true, + plugins: [ + tailwindEsbuildPlugin, + ssrVirtualFilesPlugin({ + entryToPage, + }), + ssrCTXArtifactTracker({ + entryToPage, + post_build_fn: params?.post_build_fn, + }), + ], + jsx: "automatic", + external: [ + "react", + "react-dom", + "react/jsx-runtime", + "react/jsx-dev-runtime", + "bun:*", + "sqlite-vec", + "better-sqlite3", + ...(config.ssr_compiler_excludes || []), + ], + splitting: true, + }); + } catch (error) { + global.SSR_BUNDLER_CTX_DISPOSED = true; + log.error(`SSR Bundler Error: ${error}`); + } } diff --git a/src/functions/bundler/plugins/esbuild-ctx-artifact-tracker.ts b/src/functions/bundler/plugins/esbuild-ctx-artifact-tracker.ts index aec8b6c..09f71eb 100644 --- a/src/functions/bundler/plugins/esbuild-ctx-artifact-tracker.ts +++ b/src/functions/bundler/plugins/esbuild-ctx-artifact-tracker.ts @@ -98,7 +98,11 @@ export default function esbuildCTXArtifactTracker({ } } - post_build_fn?.({ artifacts }); + try { + await post_build_fn?.({ artifacts }); + } catch (error) { + log.error(`Post-build Error: ${error}`); + } } const elapsed = (performance.now() - build_start).toFixed(0); @@ -131,7 +135,7 @@ export default function esbuildCTXArtifactTracker({ await fullRebuild(); } else { try { - pagesSSRBundler(); + await pagesSSRBundler(); } catch (error) { log.error(`SSR Bundler Error: ${error}`); } diff --git a/src/functions/bundler/plugins/ssr-ctx-artifact-tracker.ts b/src/functions/bundler/plugins/ssr-ctx-artifact-tracker.ts index 97d05b2..7252406 100644 --- a/src/functions/bundler/plugins/ssr-ctx-artifact-tracker.ts +++ b/src/functions/bundler/plugins/ssr-ctx-artifact-tracker.ts @@ -41,9 +41,16 @@ export default function ssrCTXArtifactTracker({ build.onEnd(async (result) => { if (result.errors.length > 0) { global.SSR_BUNDLER_CTX_DISPOSED = true; - await global.SSR_BUNDLER_CTX?.dispose(); + try { + await global.SSR_BUNDLER_CTX?.dispose(); + } catch {} + global.SSR_BUNDLER_CTX = undefined; build_starts = 0; - console.log("SSR Build errors:", result.errors); + for (const err of result.errors) { + console.error( + `SSR Build error: ${err.text}${err.location ? ` (${err.location.file}:${err.location.line}:${err.location.column})` : ""}`, + ); + } return; } diff --git a/src/functions/server/chokidar-watcher-esbuild-ctx.ts b/src/functions/server/chokidar-watcher-esbuild-ctx.ts index aadb59b..5131958 100644 --- a/src/functions/server/chokidar-watcher-esbuild-ctx.ts +++ b/src/functions/server/chokidar-watcher-esbuild-ctx.ts @@ -1,19 +1,19 @@ import chokidar from "chokidar"; import path from "path"; -import { existsSync, statSync } from "fs"; +import { existsSync } from "fs"; import grabDirNames from "../../utils/grab-dir-names"; import fullRebuild from "./full-rebuild"; import { AppData } from "../../data/app-data"; import checkExcludedPatterns from "../../utils/check-excluded-patterns"; import pagesSSRBundler from "../bundler/pages-ssr-bundler"; +import { log } from "../../utils/log"; const { ROOT_DIR, BUNX_BUNDLER_ERROR_EXIT_FILE } = grabDirNames(); export default async function chokadirWatcherEsbuildCTX() { - // Define ignored patterns directly in Chokidar for better performance const watcher = chokidar.watch(ROOT_DIR, { ignored: [ - /(^|[\/\\])\../, // ignore dotfiles + /(^|[\/\\])\../, /node_modules/, /public/, /\.bunext/, @@ -31,96 +31,117 @@ export default async function chokadirWatcherEsbuildCTX() { event: "add" | "change" | "unlink" | "addDir" | "unlinkDir", filePath: string, ) => { - const filename = path.relative(ROOT_DIR, filePath); + let owns_recompile = false; - if (existsSync(BUNX_BUNDLER_ERROR_EXIT_FILE)) { - await fullRebuild(); - return; - } + try { + const filename = path.relative(ROOT_DIR, filePath); - if (global.BUNDLER_CTX_DISPOSED) { - await fullRebuild({ msg: `Restarting Bundler ...` }); - } + if (existsSync(BUNX_BUNDLER_ERROR_EXIT_FILE)) { + await fullRebuild(); + return; + } - if (global.SSR_BUNDLER_CTX_DISPOSED) { - pagesSSRBundler(); - } + if (global.BUNDLER_CTX_DISPOSED) { + await fullRebuild({ msg: `Restarting Bundler ...` }); + } - if (filename.match(/\/styles$/) || filename === "styles") { - global.RECOMPILING = true; - await Bun.sleep(1000); - await fullRebuild({ - msg: `Detected new \`styles\` directory. Rebuilding ...`, - }); - return; - } + if (global.SSR_BUNDLER_CTX_DISPOSED) { + await pagesSSRBundler().catch((error) => { + log.error(`SSR Bundler Error: ${error}`); + }); + } - if (filename.match(/bunext.config\.ts/)) { - await fullRebuild({ - msg: `bunext.config.ts file changed. Rebuilding server ...`, - }); - return; - } - - const target_files_match = /\.(tsx?|jsx?|css)$/; - - if (event === "change") { - if (filename.match(target_files_match)) { - if (global.RECOMPILING) return; + if (filename.match(/\/styles$/) || filename === "styles") { + owns_recompile = true; global.RECOMPILING = true; - - if (filename.match(/.*\.server\.tsx?/)) { - global.IS_SERVER_COMPONENT = true; - } - - if (global.BUNDLER_CTX) { - await global.BUNDLER_CTX.rebuild(); - } - - // HMR for error pages - if (filename.match(/(404|500)\.tsx?/)) { - global.HMR_CONTROLLERS.forEach((controller) => { - controller?.controller?.enqueue( - `event: update\ndata: ${JSON.stringify({ reload: true })}\n\n`, - ); - }); - } - } - return; - } - - // Handle Structural Changes (Add/Delete) - if (["add", "unlink", "addDir", "unlinkDir"].includes(event)) { - const is_file_of_interest = - !!filename.match(target_files_match) || event.includes("Dir"); - - if (!is_file_of_interest) return; - - // Validation logic - if ( - !filename.match(/^src\/pages\/|\.css$/) || - checkExcludedPatterns({ path: filename }) || - filename.includes(" ") - ) { - // With chokidar, you rarely need to "reload" the whole watcher. - // But we keep the logic for consistency. - return reloadWatcher(); + await Bun.sleep(1000); + await fullRebuild({ + msg: `Detected new \`styles\` directory. Rebuilding ...`, + }); + return; } - if (global.RECOMPILING) return; + if (filename.match(/bunext.config\.ts/)) { + await fullRebuild({ + msg: `bunext.config.ts file changed. Rebuilding server ...`, + }); + return; + } - const action = event.startsWith("add") ? "created" : "deleted"; - const type = filename.match(/\.css$/) - ? "Stylesheet" - : event.includes("Dir") - ? "Directory" - : filename.match(/\/pages\/api\//) - ? "API Route" - : "Page"; + const target_files_match = /\.(tsx?|jsx?|css)$/; - await fullRebuild({ - msg: `${type} ${action}: ${filename}. Rebuilding ...`, - }); + if (event === "change") { + if (filename.match(target_files_match)) { + if (global.RECOMPILING) return; + owns_recompile = true; + global.RECOMPILING = true; + + if (filename.match(/.*\.server\.tsx?/)) { + global.IS_SERVER_COMPONENT = true; + } + + if (global.BUNDLER_CTX) { + await global.BUNDLER_CTX.rebuild(); + } + + if (filename.match(/(404|500)\.tsx?/)) { + for ( + let i = global.HMR_CONTROLLERS.length - 1; + i >= 0; + i-- + ) { + const controller = global.HMR_CONTROLLERS[i]; + try { + controller?.controller?.enqueue( + `event: update\ndata: ${JSON.stringify({ reload: true })}\n\n`, + ); + } catch { + global.HMR_CONTROLLERS.splice(i, 1); + } + } + } + } + return; + } + + if (["add", "unlink", "addDir", "unlinkDir"].includes(event)) { + const is_file_of_interest = + !!filename.match(target_files_match) || + event.includes("Dir"); + + if (!is_file_of_interest) return; + + if ( + !filename.match(/^src\/pages\/|\.css$/) || + checkExcludedPatterns({ path: filename }) || + filename.includes(" ") + ) { + return reloadWatcher(); + } + + if (global.RECOMPILING) return; + + owns_recompile = true; + const action = event.startsWith("add") ? "created" : "deleted"; + const type = filename.match(/\.css$/) + ? "Stylesheet" + : event.includes("Dir") + ? "Directory" + : filename.match(/\/pages\/api\//) + ? "API Route" + : "Page"; + + await fullRebuild({ + msg: `${type} ${action}: ${filename}. Rebuilding ...`, + }); + } + } catch (error) { + log.error(`Watcher rebuild failed: ${error}`); + } finally { + if (owns_recompile) { + global.RECOMPILING = false; + global.IS_SERVER_COMPONENT = false; + } } }; @@ -130,8 +151,6 @@ export default async function chokadirWatcherEsbuildCTX() { .on("unlink", (path) => handleEvent("unlink", path)) .on("addDir", (path) => handleEvent("addDir", path)) .on("unlinkDir", (path) => handleEvent("unlinkDir", path)); - - // global.PAGES_SRC_WATCHER = watcher; } function reloadWatcher() { diff --git a/src/functions/server/full-rebuild.ts b/src/functions/server/full-rebuild.ts index b2d1cef..d17e854 100644 --- a/src/functions/server/full-rebuild.ts +++ b/src/functions/server/full-rebuild.ts @@ -23,17 +23,16 @@ export default async function fullRebuild(params?: { msg?: string }) { global.SSR_BUNDLER_CTX = undefined; } catch (error) {} - // await pagesSSRBundler(); - await allPagesESBuildContextBundler({ post_build_fn: () => { serverPostBuildFn(); }, }); } catch (error: any) { + log.error(error); + } finally { global.RECOMPILING = false; global.IS_SERVER_COMPONENT = false; - log.error(error); } if (global.PAGES_SRC_WATCHER) { diff --git a/src/functions/server/server-post-build-fn.ts b/src/functions/server/server-post-build-fn.ts index bed8651..7cf56aa 100644 --- a/src/functions/server/server-post-build-fn.ts +++ b/src/functions/server/server-post-build-fn.ts @@ -33,15 +33,23 @@ export default async function serverPostBuildFn(params?: Params) { } if (params?.reload_all_controllers) { - controller.controller.enqueue(reload_enqueue); + try { + controller.controller.enqueue(reload_enqueue); + } catch { + global.HMR_CONTROLLERS.splice(i, 1); + } continue; } const target_artifact = global.BUNDLER_CTX_MAP[controller.target_map.local_path]; - if (!target_artifact.local_path) { - controller.controller.enqueue(reload_enqueue); + if (!target_artifact?.local_path) { + try { + controller.controller.enqueue(reload_enqueue); + } catch { + global.HMR_CONTROLLERS.splice(i, 1); + } continue; } diff --git a/src/functions/server/watcher-esbuild-ctx.ts b/src/functions/server/watcher-esbuild-ctx.ts index 40cb14a..8d56e7a 100644 --- a/src/functions/server/watcher-esbuild-ctx.ts +++ b/src/functions/server/watcher-esbuild-ctx.ts @@ -5,6 +5,7 @@ import fullRebuild from "./full-rebuild"; import { AppData } from "../../data/app-data"; import checkExcludedPatterns from "../../utils/check-excluded-patterns"; import pagesSSRBundler from "../bundler/pages-ssr-bundler"; +import { log } from "../../utils/log"; const { ROOT_DIR, BUNX_BUNDLER_ERROR_EXIT_FILE } = grabDirNames(); @@ -16,116 +17,136 @@ export default async function watcherEsbuildCTX() { persistent: true, }, async (event, filename) => { - if (!filename) return; + let owns_recompile = false; - if (existsSync(BUNX_BUNDLER_ERROR_EXIT_FILE)) { - await fullRebuild(); - return; - } + try { + if (!filename) return; - if (filename.match(/^\.\w+/)) { - return; - } + if (existsSync(BUNX_BUNDLER_ERROR_EXIT_FILE)) { + await fullRebuild(); + return; + } - if (global.BUNDLER_CTX_DISPOSED) { - await fullRebuild({ msg: `Restarting Bundler ...` }); - return; - } + if (filename.match(/^\.\w+/)) { + return; + } - if (global.SSR_BUNDLER_CTX_DISPOSED) { - pagesSSRBundler(); - } + if (global.BUNDLER_CTX_DISPOSED) { + await fullRebuild({ msg: `Restarting Bundler ...` }); + return; + } - if (filename.endsWith(AppData["BunextTmpFileExt"])) { - return; - } + if (global.SSR_BUNDLER_CTX_DISPOSED) { + await pagesSSRBundler().catch((error) => { + log.error(`SSR Bundler Error: ${error}`); + }); + } - const full_file_path = path.join(ROOT_DIR, filename); - const does_file_exist = existsSync(full_file_path); - const file_stat = does_file_exist - ? statSync(full_file_path) - : undefined; + if (filename.endsWith(AppData["BunextTmpFileExt"])) { + return; + } - if (full_file_path.match(/\/styles$/)) { - global.RECOMPILING = true; - await Bun.sleep(1000); - await fullRebuild({ - msg: `Detected new \`styles\` directory. Rebuilding ...`, - }); - return; - } + const full_file_path = path.join(ROOT_DIR, filename); + const does_file_exist = existsSync(full_file_path); + const file_stat = does_file_exist + ? statSync(full_file_path) + : undefined; - const excluded_match = - /node_modules\/|^public\/|^\.bunext\/|^\.git\/|^\.?dist\/|bun\.lockb$/; - - if (filename.match(excluded_match)) return; - - if (filename.match(/bunext.config\.ts/)) { - await fullRebuild({ - msg: `bunext.config.ts file changed. Rebuilding server ...`, - }); - return; - } - - const target_files_match = /\.(tsx?|jsx?|css)$/; - // const rebuild_skip_paths = /\/pages\/api\//; - - if (event !== "rename") { - if (filename.match(target_files_match)) { - if (global.RECOMPILING) return; + if (full_file_path.match(/\/styles$/)) { + owns_recompile = true; global.RECOMPILING = true; + await Bun.sleep(1000); + await fullRebuild({ + msg: `Detected new \`styles\` directory. Rebuilding ...`, + }); + return; + } - if (filename.match(/.*\.server\.tsx?/)) { - global.IS_SERVER_COMPONENT = true; - } + const excluded_match = + /node_modules\/|^public\/|^\.bunext\/|^\.git\/|^\.?dist\/|bun\.lockb$/; - if (global.BUNDLER_CTX) { - await global.BUNDLER_CTX.rebuild(); - } + if (filename.match(excluded_match)) return; - if (filename.match(/(404|500)\.tsx?/)) { - for ( - let i = global.HMR_CONTROLLERS.length - 1; - i >= 0; - i-- - ) { - const controller = global.HMR_CONTROLLERS[i]; - controller?.controller?.enqueue( - `event: update\ndata: ${JSON.stringify({ reload: true })}\n\n`, - ); + if (filename.match(/bunext.config\.ts/)) { + await fullRebuild({ + msg: `bunext.config.ts file changed. Rebuilding server ...`, + }); + return; + } + + const target_files_match = /\.(tsx?|jsx?|css)$/; + + if (event !== "rename") { + if (filename.match(target_files_match)) { + if (global.RECOMPILING) return; + owns_recompile = true; + global.RECOMPILING = true; + + if (filename.match(/.*\.server\.tsx?/)) { + global.IS_SERVER_COMPONENT = true; + } + + if (global.BUNDLER_CTX) { + await global.BUNDLER_CTX.rebuild(); + } + + if (filename.match(/(404|500)\.tsx?/)) { + for ( + let i = global.HMR_CONTROLLERS.length - 1; + i >= 0; + i-- + ) { + const controller = global.HMR_CONTROLLERS[i]; + try { + controller?.controller?.enqueue( + `event: update\ndata: ${JSON.stringify({ reload: true })}\n\n`, + ); + } catch { + global.HMR_CONTROLLERS.splice(i, 1); + } + } } } + return; + } + + const is_file_of_interest = + Boolean(filename.match(target_files_match)) || + file_stat?.isDirectory(); + + if (!is_file_of_interest) { + return; + } + + if (!filename.match(/^src\/pages\/|\.css$/)) + return reloadWatcher(); + if (checkExcludedPatterns({ path: filename })) + return reloadWatcher(); + if (filename.match(/ /)) return reloadWatcher(); + + if (global.RECOMPILING) return; + + owns_recompile = true; + const action = does_file_exist ? "created" : "deleted"; + const type = filename.match(/\.css$/) + ? "Sylesheet" + : file_stat?.isDirectory() + ? "Directory" + : filename.match(/\/pages\/api\//) + ? "API Route" + : "Page"; + + await fullRebuild({ + msg: `${type} ${action}: ${filename}. Rebuilding ...`, + }); + } catch (error) { + log.error(`Watcher rebuild failed: ${error}`); + } finally { + if (owns_recompile) { + global.RECOMPILING = false; + global.IS_SERVER_COMPONENT = false; } - return; } - - const is_file_of_interest = - Boolean(filename.match(target_files_match)) || - file_stat?.isDirectory(); - - if (!is_file_of_interest) { - return; - } - - if (!filename.match(/^src\/pages\/|\.css$/)) return reloadWatcher(); - if (checkExcludedPatterns({ path: filename })) - return reloadWatcher(); - if (filename.match(/ /)) return reloadWatcher(); - - if (global.RECOMPILING) return; - - const action = does_file_exist ? "created" : "deleted"; - const type = filename.match(/\.css$/) - ? "Sylesheet" - : file_stat?.isDirectory() - ? "Directory" - : filename.match(/\/pages\/api\//) - ? "API Route" - : "Page"; - - await fullRebuild({ - msg: `${type} ${action}: ${filename}. Rebuilding ...`, - }); }, );