Switch watcher to chokidar

This commit is contained in:
2026-04-20 16:12:24 +01:00
parent c06cb73181
commit e2b8b95a4b
20 changed files with 344 additions and 65 deletions
@@ -4,6 +4,13 @@ export default async function buildOnstartErrorHandler(params?: Params) {
// const error_msg = `Build Failed. Please check all your components and imports.`;
// log.error(error_msg);
if (global.BUNDLER_CTX_DISPOSED) {
return;
}
console.log(`Killing Bundler ...`);
console.log(`global.BUNDLER_CTX_DISPOSED`, global.BUNDLER_CTX_DISPOSED);
global.BUNDLER_CTX_DISPOSED = true;
global.RECOMPILING = false;
@@ -14,7 +14,6 @@ import cleanupLogsDirs from "../../cleanup-logs-dir";
const { BUNX_BUNDLER_ERROR_EXIT_FILE, BUNX_ERROR_LOGS_DIR } = grabDirNames();
let build_start = 0;
let build_starts = 0;
const MAX_BUILD_STARTS = 2;
type Params = {
@@ -35,7 +34,7 @@ export default function esbuildCTXArtifactTracker({
name: "artifact-tracker",
setup(build) {
build.onStart(async () => {
build_starts++;
global.MAIN_CTX_BUILD_STARTS++;
build_start = performance.now();
const does_error_file_exist = existsSync(
@@ -43,7 +42,7 @@ export default function esbuildCTXArtifactTracker({
);
if (
build_starts >= MAX_BUILD_STARTS &&
global.MAIN_CTX_BUILD_STARTS >= MAX_BUILD_STARTS &&
!does_error_file_exist
) {
await buildOnstartErrorHandler();
@@ -54,14 +53,19 @@ export default function esbuildCTXArtifactTracker({
if (result.errors.length > 0) {
global.RECOMPILING = false;
global.IS_SERVER_COMPONENT = false;
build_starts = 0;
log.error(`Build errors:`);
for (const err of result.errors) {
log.error(` ${err.text}${err.location ? ` (${err.location.file}:${err.location.line}:${err.location.column})` : ""}`);
log.error(
` ${err.text}${err.location ? ` (${err.location.file}:${err.location.line}:${err.location.column})` : ""}`,
);
}
for (let i = global.HMR_CONTROLLERS.length - 1; i >= 0; i--) {
for (
let i = global.HMR_CONTROLLERS.length - 1;
i >= 0;
i--
) {
const controller = global.HMR_CONTROLLERS[i];
try {
controller?.controller?.enqueue(
@@ -101,7 +105,8 @@ export default function esbuildCTXArtifactTracker({
global.RECOMPILING = false;
global.IS_SERVER_COMPONENT = false;
build_starts = 0;
global.MAIN_CTX_BUILD_STARTS = 0;
global.BUNDLER_CTX_DISPOSED = false;
const does_error_file_exist = existsSync(
BUNX_BUNDLER_ERROR_EXIT_FILE,
@@ -38,9 +38,10 @@ export default function ssrCTXArtifactTracker({
}
});
build.onEnd((result) => {
build.onEnd(async (result) => {
if (result.errors.length > 0) {
global.SSR_BUNDLER_CTX_DISPOSED = false;
global.SSR_BUNDLER_CTX_DISPOSED = true;
await global.SSR_BUNDLER_CTX?.dispose();
build_starts = 0;
console.log("SSR Build errors:", result.errors);
return;
@@ -1,7 +1,6 @@
import type { Plugin } from "esbuild";
import path from "path";
import type { PageFiles } from "../../../types";
import { log } from "../../../utils/log";
type Params = {
entryToPage: Map<
+5 -3
View File
@@ -6,17 +6,17 @@ import type {
} from "../types";
import type { FileSystemRouter, Server } from "bun";
import grabDirNames, { type DirNames } from "../utils/grab-dir-names";
import { type FSWatcher } from "fs";
import init from "./init";
import isDevelopment from "../utils/is-development";
import { log } from "../utils/log";
import cron from "./server/cron";
import type { BuildContext } from "esbuild";
import watcherEsbuildCTX from "./server/watcher-esbuild-ctx";
import allPagesESBuildContextBundler from "./bundler/all-pages-esbuild-context-bundler";
import serverPostBuildFn from "./server/server-post-build-fn";
import reactModulesBundler from "./bundler/react-modules-bundler";
import grabConstants from "../utils/grab-constants";
import type { FSWatcher } from "chokidar";
import chokadirWatcherEsbuildCTX from "./server/chokidar-watcher-esbuild-ctx";
/**
* # Declare Global Variables
@@ -52,6 +52,7 @@ declare global {
var REBUILD_RETRIES: number;
var IS_404_PAGE: boolean;
var CONSTANTS: ReturnType<typeof grabConstants>;
var MAIN_CTX_BUILD_STARTS: number;
}
const dirNames = grabDirNames();
@@ -69,6 +70,7 @@ export default async function bunextInit() {
global.DIR_NAMES = dirNames;
global.REACT_IMPORTS_MAP = { imports: {} };
global.REACT_DOM_MODULE_CACHE = new Map<string, any>();
global.MAIN_CTX_BUILD_STARTS = 0;
await init();
log.banner();
@@ -93,7 +95,7 @@ export default async function bunextInit() {
serverPostBuildFn();
},
});
watcherEsbuildCTX();
chokadirWatcherEsbuildCTX();
} else {
log.build(`Building Modules ...`);
await allPagesESBuildContextBundler();
@@ -0,0 +1,142 @@
import chokidar from "chokidar";
import path from "path";
import { existsSync, statSync } 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";
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/,
/\.git/,
/dist/,
/bun\.lockb/,
(path: string) => path.endsWith(AppData["BunextTmpFileExt"]),
],
persistent: true,
ignoreInitial: true,
depth: 99,
});
const handleEvent = async (
event: "add" | "change" | "unlink" | "addDir" | "unlinkDir",
filePath: string,
) => {
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)) {
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`,
);
});
}
}
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();
}
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
.on("add", (path) => handleEvent("add", path))
.on("change", (path) => handleEvent("change", path))
.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) {
global.PAGES_SRC_WATCHER.close();
chokadirWatcherEsbuildCTX();
}
}
+9 -8
View File
@@ -1,8 +1,7 @@
import { log } from "../../utils/log";
import allPagesESBuildContextBundler from "../bundler/all-pages-esbuild-context-bundler";
import pagesSSRBundler from "../bundler/pages-ssr-bundler";
import chokadirWatcherEsbuildCTX from "./chokidar-watcher-esbuild-ctx";
import serverPostBuildFn from "./server-post-build-fn";
import watcherEsbuildCTX from "./watcher-esbuild-ctx";
export default async function fullRebuild(params?: { msg?: string }) {
try {
@@ -16,13 +15,15 @@ export default async function fullRebuild(params?: { msg?: string }) {
global.ROUTER.reload();
await global.BUNDLER_CTX?.dispose();
global.BUNDLER_CTX = undefined;
try {
await global.BUNDLER_CTX?.dispose();
global.BUNDLER_CTX = undefined;
await global.SSR_BUNDLER_CTX?.dispose();
global.SSR_BUNDLER_CTX = undefined;
await global.SSR_BUNDLER_CTX?.dispose();
global.SSR_BUNDLER_CTX = undefined;
} catch (error) {}
await pagesSSRBundler();
// await pagesSSRBundler();
allPagesESBuildContextBundler({
post_build_fn: () => {
@@ -37,6 +38,6 @@ export default async function fullRebuild(params?: { msg?: string }) {
if (global.PAGES_SRC_WATCHER) {
global.PAGES_SRC_WATCHER.close();
watcherEsbuildCTX();
chokadirWatcherEsbuildCTX();
}
}
+6 -11
View File
@@ -29,7 +29,6 @@ export default async function watcherEsbuildCTX() {
if (global.BUNDLER_CTX_DISPOSED) {
await fullRebuild({ msg: `Restarting Bundler ...` });
global.BUNDLER_CTX_DISPOSED = false;
}
if (global.SSR_BUNDLER_CTX_DISPOSED) {
@@ -80,11 +79,7 @@ export default async function watcherEsbuildCTX() {
}
if (global.BUNDLER_CTX) {
try {
await global.BUNDLER_CTX.rebuild();
} catch (error) {
console.log(`ESBUILD Rebuild Error =>`, error);
}
await global.BUNDLER_CTX.rebuild();
}
if (filename.match(/(404|500)\.tsx?/)) {
@@ -133,12 +128,12 @@ export default async function watcherEsbuildCTX() {
},
);
global.PAGES_SRC_WATCHER = pages_src_watcher;
// global.PAGES_SRC_WATCHER = pages_src_watcher;
}
function reloadWatcher() {
if (global.PAGES_SRC_WATCHER) {
global.PAGES_SRC_WATCHER.close();
watcherEsbuildCTX();
}
// if (global.PAGES_SRC_WATCHER) {
// global.PAGES_SRC_WATCHER.close();
// watcherEsbuildCTX();
// }
}