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
This commit is contained in:
Zheng Feng 2026-09-01 22:56:23 +08:00 committed by GitHub
parent c56c7293f8
commit ddd9aec558
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 41 additions and 40 deletions

View File

@ -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
```

View File

@ -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.

View File

@ -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

View File

@ -5,10 +5,10 @@ import path from "node:path";
import { fileURLToPath } from "node:url";
/**
* @param {string} platform
* @param {string} _platform
* @param {Record<string, string | undefined>} 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";
}
/**

View File

@ -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"]);
});
});