diff --git a/dist/commands/index.js b/dist/commands/index.js old mode 100755 new mode 100644 diff --git a/dist/functions/server/web-pages/grab-page-component.js b/dist/functions/server/web-pages/grab-page-component.js index 4d4c476..92c42cb 100644 --- a/dist/functions/server/web-pages/grab-page-component.js +++ b/dist/functions/server/web-pages/grab-page-component.js @@ -63,14 +63,12 @@ export default async function grabPageComponent(params) { // Only retry transient failures: if the last rebuild failed // (BUNEXT_BUILD_FAILED), the file is broken and more rebuilds // will just repeat the same error. - while (retries < MAX_RETRIES && - !global.BUNEXT_BUILD_FAILED) { + while (retries < MAX_RETRIES && !global.BUNEXT_BUILD_FAILED) { await fullRebuild({ msg: `Retrying Bundle map for file \`${relative_file_path}\``, }); await Bun.sleep(1000); - bundledMap = - global.BUNEXT_BUNDLER_CTX_MAP[relative_file_path]; + bundledMap = global.BUNEXT_BUNDLER_CTX_MAP[relative_file_path]; if (bundledMap?.path) break; retries++; @@ -122,9 +120,7 @@ export default async function grabPageComponent(params) { const is404 = error instanceof NotFoundError || error?.name === "NotFoundError" || error?.status === 404; - if (!params.retry && !params.no_retry && is_dev) { - // Skip the retry cascade when the last rebuild failed: the error - // is persistent and more rebuilds would repeat it endlessly. + if (!params.retry && !params.no_retry && is_dev && !is404) { while (!global.BUNEXT_BUILD_FAILED && global.BUNEXT_REBUILD_RETRIES < 2) { global.BUNEXT_REBUILD_RETRIES = diff --git a/package.json b/package.json index 4b7fbda..e106459 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@moduletrace/bunext", - "version": "1.1.15", + "version": "1.1.16", "main": "dist/index.js", "module": "index.ts", "dependencies": { diff --git a/src/__tests__/e2e/e2e.test.ts b/src/__tests__/e2e/e2e.test.ts index 09f6ff2..146d967 100644 --- a/src/__tests__/e2e/e2e.test.ts +++ b/src/__tests__/e2e/e2e.test.ts @@ -131,7 +131,6 @@ describe("E2E Integration", () => { if (server) { server.stop(true); } - global.BUNEXT_SERVER = undefined; process.chdir(originalCwd); @@ -142,6 +141,18 @@ describe("E2E Integration", () => { delete process.env.PORT; } + // Reset globals this file mutated so later test files (which share + // the process when bun runs un-isolated) never see fixture state. + global.BUNEXT_DIR_NAMES = undefined as any; + global.BUNEXT_CONFIG = undefined as any; + global.BUNEXT_CONSTANTS = undefined as any; + global.BUNEXT_SERVER = undefined; + global.BUNEXT_ROUTER = undefined as any; + global.BUNEXT_BUNDLER_CTX_MAP = undefined; + global.BUNEXT_SSR_BUNDLER_CTX_MAP = undefined as any; + global.BUNEXT_HMR_CONTROLLERS = []; + global.BUNEXT_REACT_DOM_MODULE_CACHE = new Map(); + // Remove generated artifacts from the fixture const dotBunext = path.join(fixtureDir, ".bunext"); if (fs.existsSync(dotBunext)) { diff --git a/src/__tests__/functions/server/grab-page-component.test.tsx b/src/__tests__/functions/server/grab-page-component.test.tsx index 3ffaad3..11a30d6 100644 --- a/src/__tests__/functions/server/grab-page-component.test.tsx +++ b/src/__tests__/functions/server/grab-page-component.test.tsx @@ -41,6 +41,7 @@ describe("grabPageComponent", () => { req: new Request("http://localhost:3000/unknown-foo-bar123"), }); + console.log("DEBUG-404:", res instanceof Response ? "Response" : JSON.stringify(res)); expect(res.serverRes?.response_options?.status).toBe(404); expect(log.error).not.toHaveBeenCalled(); }); diff --git a/src/__tests__/functions/server/server-post-build-fn.test.ts b/src/__tests__/functions/server/server-post-build-fn.test.ts index be0b0a8..5ede0e3 100644 --- a/src/__tests__/functions/server/server-post-build-fn.test.ts +++ b/src/__tests__/functions/server/server-post-build-fn.test.ts @@ -1,19 +1,9 @@ -import { describe, expect, test, beforeEach, afterEach, mock } from "bun:test"; +import { describe, expect, test, beforeEach, afterEach } from "bun:test"; -const grabPageComponent = mock(async () => ({ - serverRes: { props: { ok: true } }, -})); - -mock.module( - "../../../functions/server/web-pages/grab-page-component", - () => ({ - default: grabPageComponent, - }), -); - -const { default: serverPostBuildFn } = await import( - "../../../functions/server/server-post-build-fn" -); +// NOTE: this file runs the REAL grabPageComponent (no module mock). The +// callers use `return_server_res_only` + `no_retry`, so no bundling or +// rebuilds happen as long as the fake router + artifact map are set up. +import serverPostBuildFn from "../../../functions/server/server-post-build-fn"; function makeController(page_url: string, target_map?: { local_path: string }) { const enqueued: string[] = []; @@ -31,30 +21,41 @@ function makeController(page_url: string, target_map?: { local_path: string }) { describe("server-post-build-fn", () => { beforeEach(() => { - grabPageComponent.mockClear(); + global.BUNEXT_ROUTER = { + match: (path: string) => { + if (path === "/about") + return { filePath: "/pages/about.tsx" }; + if (path === "/new-page") + return { filePath: "/pages/new-page.tsx" }; + return null; + }, + } as any; global.BUNEXT_BUNDLER_CTX_MAP = { - "/pages/about.tsx": { - local_path: "/pages/about.tsx", + "pages/about.tsx": { + local_path: "pages/about.tsx", path: "pages/about.js", } as any, - "/pages/404.tsx": { - local_path: "/pages/404.tsx", + "pages/404.tsx": { + local_path: "pages/404.tsx", path: "pages/404.js", } as any, }; global.BUNEXT_HMR_CONTROLLERS = []; global.BUNEXT_ROOT_FILE_UPDATED = false; + global.BUNEXT_IS_404_PAGE = false; }); afterEach(() => { + global.BUNEXT_ROUTER = undefined as any; global.BUNEXT_BUNDLER_CTX_MAP = undefined as any; global.BUNEXT_HMR_CONTROLLERS = []; global.BUNEXT_ROOT_FILE_UPDATED = false; + global.BUNEXT_IS_404_PAGE = false; }); test("soft-updates a normal page controller", async () => { const c = makeController("http://localhost/about", { - local_path: "/pages/about.tsx", + local_path: "pages/about.tsx", }); global.BUNEXT_HMR_CONTROLLERS = [c as any]; @@ -63,13 +64,12 @@ describe("server-post-build-fn", () => { expect(c.enqueued.length).toBe(1); expect(c.enqueued[0]).toContain("event: update"); expect(c.enqueued[0]).not.toContain('"reload":true'); - expect(c.enqueued[0]).toContain("/pages/about.tsx"); - expect(grabPageComponent).toHaveBeenCalled(); + expect(c.enqueued[0]).toContain("pages/about.tsx"); }); test("soft-updates unmatched URL via custom 404 artifact", async () => { const c = makeController("http://localhost/missing", { - local_path: "/pages/404.tsx", + local_path: "pages/404.tsx", }); global.BUNEXT_HMR_CONTROLLERS = [c as any]; @@ -77,13 +77,12 @@ describe("server-post-build-fn", () => { expect(c.enqueued.length).toBe(1); expect(c.enqueued[0]).not.toContain('"reload":true'); - expect(c.enqueued[0]).toContain("/pages/404.tsx"); - expect(grabPageComponent).toHaveBeenCalled(); + expect(c.enqueued[0]).toContain("pages/404.tsx"); }); test("full-reloads when the controller's target artifact is missing from the map", async () => { const c = makeController("http://localhost/gone", { - local_path: "/pages/removed.tsx", + local_path: "pages/removed.tsx", }); global.BUNEXT_HMR_CONTROLLERS = [c as any]; @@ -91,15 +90,14 @@ describe("server-post-build-fn", () => { expect(c.enqueued.length).toBe(1); expect(c.enqueued[0]).toContain('"reload":true'); - expect(grabPageComponent).not.toHaveBeenCalled(); }); test("full-reloads every controller when reload_all_controllers is set", async () => { const c1 = makeController("http://localhost/about", { - local_path: "/pages/about.tsx", + local_path: "pages/about.tsx", }); const c2 = makeController("http://localhost/missing", { - local_path: "/pages/404.tsx", + local_path: "pages/404.tsx", }); global.BUNEXT_HMR_CONTROLLERS = [c1 as any, c2 as any]; @@ -109,12 +107,11 @@ describe("server-post-build-fn", () => { expect(c1.enqueued[0]).toContain('"reload":true'); expect(c2.enqueued.length).toBe(1); expect(c2.enqueued[0]).toContain('"reload":true'); - expect(grabPageComponent).not.toHaveBeenCalled(); }); test("enqueues reload when the root file changed and resets the flag", async () => { const c = makeController("http://localhost/about", { - local_path: "/pages/about.tsx", + local_path: "pages/about.tsx", }); global.BUNEXT_HMR_CONTROLLERS = [c as any]; global.BUNEXT_ROOT_FILE_UPDATED = true; @@ -133,19 +130,18 @@ describe("server-post-build-fn", () => { await serverPostBuildFn(); expect(c.enqueued.length).toBe(0); - expect(grabPageComponent).not.toHaveBeenCalled(); }); test("only touches controllers whose page is in changed_pages", async () => { const changed = makeController("http://localhost/about", { - local_path: "/pages/about.tsx", + local_path: "pages/about.tsx", }); const untouched = makeController("http://localhost/missing", { - local_path: "/pages/404.tsx", + local_path: "pages/404.tsx", }); global.BUNEXT_HMR_CONTROLLERS = [changed as any, untouched as any]; - await serverPostBuildFn({ changed_pages: ["/pages/about.tsx"] }); + await serverPostBuildFn({ changed_pages: ["pages/about.tsx"] }); expect(changed.enqueued.length).toBe(1); expect(untouched.enqueued.length).toBe(0); diff --git a/src/__tests__/functions/server/start-server.test.ts b/src/__tests__/functions/server/start-server.test.ts index 8b4b5ce..f44a220 100644 --- a/src/__tests__/functions/server/start-server.test.ts +++ b/src/__tests__/functions/server/start-server.test.ts @@ -1,4 +1,4 @@ -import { describe, expect, test, mock, afterEach } from "bun:test"; +import { describe, expect, test, mock, afterEach, afterAll } from "bun:test"; import startServer from "../../../../src/functions/server/start-server"; import { log } from "../../../../src/utils/log"; @@ -29,6 +29,12 @@ describe("startServer", () => { } }); + afterAll(() => { + // Prevent the module mocks (log, grab-config, grab-app-port) from + // leaking into other test files sharing this process. + mock.restore(); + }); + test("starts the server and assigns to global.BUNEXT_SERVER", async () => { global.BUNEXT_CONFIG = { development: true }; diff --git a/src/functions/server/web-pages/grab-page-component.tsx b/src/functions/server/web-pages/grab-page-component.tsx index 538ec05..bc86c14 100644 --- a/src/functions/server/web-pages/grab-page-component.tsx +++ b/src/functions/server/web-pages/grab-page-component.tsx @@ -115,16 +115,12 @@ export default async function grabPageComponent( // Only retry transient failures: if the last rebuild failed // (BUNEXT_BUILD_FAILED), the file is broken and more rebuilds // will just repeat the same error. - while ( - retries < MAX_RETRIES && - !global.BUNEXT_BUILD_FAILED - ) { + while (retries < MAX_RETRIES && !global.BUNEXT_BUILD_FAILED) { await fullRebuild({ msg: `Retrying Bundle map for file \`${relative_file_path}\``, }); await Bun.sleep(1000); - bundledMap = - global.BUNEXT_BUNDLER_CTX_MAP[relative_file_path]; + bundledMap = global.BUNEXT_BUNDLER_CTX_MAP[relative_file_path]; if (bundledMap?.path) break; retries++; } @@ -185,9 +181,7 @@ export default async function grabPageComponent( error?.name === "NotFoundError" || error?.status === 404; - if (!params.retry && !params.no_retry && is_dev) { - // Skip the retry cascade when the last rebuild failed: the error - // is persistent and more rebuilds would repeat it endlessly. + if (!params.retry && !params.no_retry && is_dev && !is404) { while ( !global.BUNEXT_BUILD_FAILED && global.BUNEXT_REBUILD_RETRIES < 2