From ddd9aec5588c2e2b019b01dff6075fdcc7b3e95c Mon Sep 17 00:00:00 2001 From: Zheng Feng Date: Tue, 1 Sep 2026 22:56:23 +0800 Subject: [PATCH] fix(frontend): default to Webpack over Turbopack in dev to avoid PostCSS worker leak on macOS (#5133) * fix(frontend): default to Webpack over Turbopack in dev to avoid PostCSS worker leak on macOS On macOS arm64, Turbopack + Next.js 16.2.11 + Tailwind CSS v4 causes an unbounded spawn of PostCSS evaluator processes that consume high CPU and memory and never return a response. Webpack is unaffected. Change the no-override default in getDevBundler() from platform-dependent Turbopack (all non-Windows) to Webpack. DEER_FLOW_DEV_BUNDLER=turbo continues to work as an explicit opt-in for local diagnosis. Fixes #5132 * docs(frontend): address webpack default review feedback * docs(frontend): clarify webpack default rationale --- AGENTS.md | 2 +- frontend/AGENTS.md | 26 +++++++++++------------ frontend/README.md | 28 ++++++++++++------------- frontend/scripts/dev.mjs | 10 ++++++--- frontend/tests/unit/scripts/dev.test.ts | 15 ++++++------- 5 files changed, 41 insertions(+), 40 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 201497276..448805af3 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -160,7 +160,7 @@ cd backend && make lint # ruff check cd backend && make format # ruff format # Frontend (see frontend/AGENTS.md for the full set) -cd frontend && pnpm dev # Dev server: Webpack on Windows, Turbopack elsewhere (override with DEER_FLOW_DEV_BUNDLER) +cd frontend && pnpm dev # Dev server: Webpack by default (override with DEER_FLOW_DEV_BUNDLER=turbo) cd frontend && pnpm check # Lint + type check (run before committing) cd frontend && pnpm test # Unit tests ``` diff --git a/frontend/AGENTS.md b/frontend/AGENTS.md index 4436135f9..ccef652fa 100644 --- a/frontend/AGENTS.md +++ b/frontend/AGENTS.md @@ -17,22 +17,22 @@ DeerFlow Frontend is a Next.js 16 web interface for an AI agent system. It commu ## Commands -| Command | Purpose | -| ---------------- | ------------------------------------------------------------------- | -| `pnpm dev` | Platform-aware dev server (Webpack on Windows, Turbopack elsewhere) | -| `pnpm build` | Production build | -| `pnpm check` | Lint + type check (run before committing) | -| `pnpm lint` | ESLint only | -| `pnpm lint:fix` | ESLint with auto-fix | -| `pnpm format` | Prettier check (`pnpm format:write` to apply) | -| `pnpm test` | Run unit tests with Rstest | -| `pnpm test:e2e` | Run E2E tests with Playwright (Chromium) | -| `pnpm typecheck` | TypeScript type check (`tsc --noEmit`) | -| `pnpm start` | Start production server | +| Command | Purpose | +| ---------------- | --------------------------------------------- | +| `pnpm dev` | Start the development server with Webpack | +| `pnpm build` | Production build | +| `pnpm check` | Lint + type check (run before committing) | +| `pnpm lint` | ESLint only | +| `pnpm lint:fix` | ESLint with auto-fix | +| `pnpm format` | Prettier check (`pnpm format:write` to apply) | +| `pnpm test` | Run unit tests with Rstest | +| `pnpm test:e2e` | Run E2E tests with Playwright (Chromium) | +| `pnpm typecheck` | TypeScript type check (`tsc --noEmit`) | +| `pnpm start` | Start production server | Unit tests live under `tests/unit/` and mirror the `src/` layout (e.g., `tests/unit/core/api/stream-mode.test.ts` tests `src/core/api/stream-mode.ts`). Powered by Rstest; import source modules via the `@/` path alias. -Use `DEER_FLOW_DEV_BUNDLER=turbo` or `DEER_FLOW_DEV_BUNDLER=webpack` with `pnpm dev` to override the platform default when diagnosing a local Next.js bundler issue. +Webpack is the default development bundler. Use `DEER_FLOW_DEV_BUNDLER=turbo` with `pnpm dev` to opt in to Turbopack when diagnosing a local Next.js bundler issue. Rstest runs them as two projects (`rstest.config.ts`). `*.test.ts` / `*.test.tsx` run in a plain **node** environment — that is nearly the whole suite, and it is the default for anything that is pure logic. `*.dom.test.ts` / `*.dom.test.tsx` run in **happy-dom**, for tests that need a document: hooks driven through `renderHook` from `@testing-library/react`, and components. Keep the split — a DOM environment costs roughly 3x the runtime of the node suite, so tests that do not render should not opt into it. A hook whose behavior only exists under real React (effect ordering, cleanup on unmount, re-render on store change) belongs in a `.dom.test.*` file rather than a node test that mocks `react` itself. diff --git a/frontend/README.md b/frontend/README.md index e234cf873..6957577f4 100644 --- a/frontend/README.md +++ b/frontend/README.md @@ -127,24 +127,24 @@ src/ ## Scripts -| Command | Description | -| ------------------- | ------------------------------------------------------------------ | -| `pnpm dev` | Start development server (Webpack on Windows, Turbopack elsewhere) | -| `pnpm build` | Build for production | -| `pnpm start` | Start production server | -| `pnpm test` | Run unit tests with Rstest | -| `pnpm test:e2e` | Run E2E tests with Playwright | -| `pnpm format` | Check formatting with Prettier | -| `pnpm format:write` | Apply formatting with Prettier | -| `pnpm lint` | Run ESLint | -| `pnpm lint:fix` | Fix ESLint issues | -| `pnpm typecheck` | Run TypeScript type checking | -| `pnpm check` | Run both lint and typecheck | +| Command | Description | +| ------------------- | ------------------------------------- | +| `pnpm dev` | Start development server with Webpack | +| `pnpm build` | Build for production | +| `pnpm start` | Start production server | +| `pnpm test` | Run unit tests with Rstest | +| `pnpm test:e2e` | Run E2E tests with Playwright | +| `pnpm format` | Check formatting with Prettier | +| `pnpm format:write` | Apply formatting with Prettier | +| `pnpm lint` | Run ESLint | +| `pnpm lint:fix` | Fix ESLint issues | +| `pnpm typecheck` | Run TypeScript type checking | +| `pnpm check` | Run both lint and typecheck | ## Development Notes - Uses pnpm workspaces (see `packageManager` in package.json) -- Turbopack is used by default in development except on Windows, where Webpack avoids known Turbopack runtime instability. Set `DEER_FLOW_DEV_BUNDLER=turbo` or `DEER_FLOW_DEV_BUNDLER=webpack` to override the platform default for local diagnosis. +- Webpack is the default development bundler until the upstream Turbopack PostCSS worker leak is fixed in a stable Next.js release (#5132). Set `DEER_FLOW_DEV_BUNDLER=turbo` to opt in to Turbopack for local diagnosis, or `DEER_FLOW_DEV_BUNDLER=webpack` to select Webpack explicitly. Reconsider the default after the stable fix is verified on macOS arm64 and Linux. - Environment validation can be skipped with `SKIP_ENV_VALIDATION=1` (useful for Docker) - Backend API URLs are optional; nginx proxy is used by default in development diff --git a/frontend/scripts/dev.mjs b/frontend/scripts/dev.mjs index 2299a4b73..381a8535b 100644 --- a/frontend/scripts/dev.mjs +++ b/frontend/scripts/dev.mjs @@ -5,10 +5,10 @@ import path from "node:path"; import { fileURLToPath } from "node:url"; /** - * @param {string} platform + * @param {string} _platform * @param {Record} env */ -export function getDevBundler(platform = process.platform, env = process.env) { +export function getDevBundler(_platform = process.platform, env = process.env) { const override = env.DEER_FLOW_DEV_BUNDLER?.trim(); if (override) { if (override !== "turbo" && override !== "webpack") { @@ -18,7 +18,11 @@ export function getDevBundler(platform = process.platform, env = process.env) { } return override; } - return platform === "win32" ? "webpack" : "turbo"; + // Keep Webpack as the cross-platform default while #5132's Turbopack + // PostCSS worker leak remains unfixed in a stable Next.js release. Retain + // the platform parameter so restoring the platform-aware default stays a + // small change once the upstream fix is stable and verified on macOS/Linux. + return "webpack"; } /** diff --git a/frontend/tests/unit/scripts/dev.test.ts b/frontend/tests/unit/scripts/dev.test.ts index 403a255ef..1dcf3f26b 100644 --- a/frontend/tests/unit/scripts/dev.test.ts +++ b/frontend/tests/unit/scripts/dev.test.ts @@ -3,11 +3,6 @@ import { describe, expect, test } from "@rstest/core"; import { getDevBundler, getNextDevArgs } from "../../../scripts/dev.mjs"; describe("frontend dev launcher", () => { - test("uses webpack on Windows to avoid Turbopack runtime instability", () => { - expect(getDevBundler("win32")).toBe("webpack"); - expect(getNextDevArgs("win32")).toEqual(["dev", "--webpack"]); - }); - test("allows an explicit bundler override on every platform", () => { expect(getDevBundler("win32", { DEER_FLOW_DEV_BUNDLER: "turbo" })).toBe( "turbo", @@ -32,9 +27,11 @@ describe("frontend dev launcher", () => { ]); }); - test("keeps Turbopack for non-Windows development", () => { - expect(getDevBundler("linux")).toBe("turbo"); - expect(getDevBundler("darwin")).toBe("turbo"); - expect(getNextDevArgs("linux")).toEqual(["dev", "--turbo"]); + test("uses webpack by default on every platform", () => { + expect(getDevBundler("win32")).toBe("webpack"); + expect(getDevBundler("linux")).toBe("webpack"); + expect(getDevBundler("darwin")).toBe("webpack"); + expect(getNextDevArgs("win32")).toEqual(["dev", "--webpack"]); + expect(getNextDevArgs("linux")).toEqual(["dev", "--webpack"]); }); });