From 046093b9cbbcda68a7f41d09f48abd4bc4b2c992 Mon Sep 17 00:00:00 2001 From: Diego Rodrigues de Sa e Souza <8016841+diegosouzapw@users.noreply.github.com> Date: Sun, 5 Jul 2026 11:14:25 -0300 Subject: [PATCH] feat(build): make Turbopack the default bundler for dev and build (#6283) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Turbopack (stable in Next 16) becomes the code default in the three entry points that previously required an explicit OMNIROUTE_USE_TURBOPACK=1: - scripts/build/build-next-isolated.mjs (production build) - scripts/dev/run-next.mjs (dev server) - scripts/dev/run-next-playwright.mjs (playwright dev runner) OMNIROUTE_USE_TURBOPACK=0 remains the webpack escape hatch (Windows / native-binding / bundler-compat issues), and only the documented '0' opts out — junk values keep the default. Benchmarked on this codebase (same tree, Next 16.2.9): webpack 1035s vs Turbopack 539s on a 32-core box; ~20min vs 6min59s on ubuntu-latest. Artifact validated end-to-end (standalone smoke + e2e/package-artifact/ electron-package-smoke CI jobs, Docker amd64+arm64 builds clean with the v3.8.27 ImportTracer panic gone on 16.2.9). TDD: tests/unit/build-bundler-default-turbopack.test.ts (new) + run-next-playwright.test.ts extended with the unset-env default case; both red before the flip, green after. ENVIRONMENT.md updated. --- docs/reference/ENVIRONMENT.md | 2 +- scripts/build/build-next-isolated.mjs | 7 +++- scripts/dev/run-next-playwright.mjs | 3 +- scripts/dev/run-next.mjs | 5 ++- .../build-bundler-default-turbopack.test.ts | 34 +++++++++++++++++++ tests/unit/run-next-playwright.test.ts | 9 +++++ 6 files changed, 56 insertions(+), 4 deletions(-) create mode 100644 tests/unit/build-bundler-default-turbopack.test.ts diff --git a/docs/reference/ENVIRONMENT.md b/docs/reference/ENVIRONMENT.md index e288136d00..3a3b1ed3bd 100644 --- a/docs/reference/ENVIRONMENT.md +++ b/docs/reference/ENVIRONMENT.md @@ -133,7 +133,7 @@ OmniRoute uses **SQLite** (via `better-sqlite3`) for all persistence. These vari | `OMNIROUTE_DISABLE_LIVE_WS` | `false` | `scripts/start-ws-server.mjs` | CI/harness toggle that disables the standalone live WebSocket helper script. | | `RELAY_IP_PER_MINUTE` | `30` | `src/app/api/v1/relay/chat/completions/route.ts` | Per-(token, IP) relay rate limit, requests/minute. In-memory, per instance. `0` or negative disables the IP-dimension gate (per-token DB limit still applies). | | `NODE_ENV` | `production` | Next.js core | Controls logging verbosity, caching, error detail exposure, and Next.js optimizations. | -| `OMNIROUTE_USE_TURBOPACK` | `1` (default in `.env.example`) | `package.json` / Next.js 16 | Toggles the Next.js 16 Turbopack bundler in `npm run dev` and `npm run build`. Set to `0` on Windows or when running into native binding incompatibilities. | +| `OMNIROUTE_USE_TURBOPACK` | `1` (Turbopack — code default) | `package.json` / Next.js 16 | Turbopack is the default bundler for `npm run dev` and `npm run build` (2-3× faster builds, benchmarked). Set to `0` to fall back to webpack on Windows or when running into native binding / bundler-compat incompatibilities. | | `OMNIROUTE_SKIP_DB_HEALTHCHECK` | _(unset)_ | `src/lib/db/core.ts` / `src/lib/db/healthCheck.ts` | Set to `1` to skip the SQLite integrity health check on startup. Useful for faster boot on large databases. | | `CREDENTIAL_HEALTH_CHECK_INTERVAL` | `300000` | `open-sse/config/constants.ts` / `src/lib/credentialHealth/scheduler.ts` | Interval (ms) for the background credential health check scheduler. Minimum: 10000 (10s). | | `CREDENTIAL_HEALTH_CACHE_TTL` | `300000` | `open-sse/config/constants.ts` / `src/lib/credentialHealth/cache.ts` | TTL (ms) for cached credential health status. | diff --git a/scripts/build/build-next-isolated.mjs b/scripts/build/build-next-isolated.mjs index 28681bb299..09dc26bb89 100644 --- a/scripts/build/build-next-isolated.mjs +++ b/scripts/build/build-next-isolated.mjs @@ -108,7 +108,12 @@ function runNextBuild() { } export function resolveNextBuildBundlerFlag(baseEnv = process.env) { - return baseEnv.OMNIROUTE_USE_TURBOPACK === "1" ? "--turbopack" : "--webpack"; + // Turbopack is the default production bundler (Next 16 stable). Benchmarked on + // this codebase: 2-3x faster than the single-threaded webpack pass (17min -> 9min + // on a 32-core box; ~20min -> 7min on ubuntu-latest), artifact validated + // end-to-end (standalone smoke + e2e/package/electron CI jobs). Webpack stays as + // the explicit escape hatch (=0) for bundler-compat regressions. + return baseEnv.OMNIROUTE_USE_TURBOPACK === "0" ? "--webpack" : "--turbopack"; } export function resolveNextBuildEnv(baseEnv = process.env) { diff --git a/scripts/dev/run-next-playwright.mjs b/scripts/dev/run-next-playwright.mjs index e4e800f26f..37a7055e8c 100644 --- a/scripts/dev/run-next-playwright.mjs +++ b/scripts/dev/run-next-playwright.mjs @@ -187,7 +187,8 @@ const testServerEnv = { }; export function shouldUseWebpackForPlaywrightDev({ mode, env }) { - return mode === "dev" && env.OMNIROUTE_USE_TURBOPACK !== "1"; + // Webpack only on the explicit escape hatch (=0) — turbopack is the default. + return mode === "dev" && env.OMNIROUTE_USE_TURBOPACK === "0"; } function runChild(command, args, env) { diff --git a/scripts/dev/run-next.mjs b/scripts/dev/run-next.mjs index 5b8a9af0ce..38f914d2e3 100644 --- a/scripts/dev/run-next.mjs +++ b/scripts/dev/run-next.mjs @@ -68,7 +68,10 @@ process.env.NODE_ENV = dev ? "development" : "production"; const { dashboardPort } = runtimePorts; const hostname = process.env.HOST || "0.0.0.0"; -const useTurbopack = dev && mergedEnv.OMNIROUTE_USE_TURBOPACK === "1"; +// Turbopack by default in dev (matches the Next 16 CLI default and the production +// build default in build-next-isolated.mjs); OMNIROUTE_USE_TURBOPACK=0 is the +// webpack escape hatch. +const useTurbopack = dev && mergedEnv.OMNIROUTE_USE_TURBOPACK !== "0"; process.env.OMNIROUTE_WS_BRIDGE_SECRET ||= randomUUID(); // Per-process secret used to prove the trusted peer-IP stamp came from this // server (read by the authz middleware in the same process). See peer-stamp.mjs. diff --git a/tests/unit/build-bundler-default-turbopack.test.ts b/tests/unit/build-bundler-default-turbopack.test.ts new file mode 100644 index 0000000000..65a31e44fa --- /dev/null +++ b/tests/unit/build-bundler-default-turbopack.test.ts @@ -0,0 +1,34 @@ +import test from "node:test"; +import assert from "node:assert/strict"; + +// Turbopack is the default production bundler (Next 16 stable, benchmarked ~2-3x +// faster than the webpack pass); webpack stays available as the explicit opt-out +// escape hatch (OMNIROUTE_USE_TURBOPACK=0) for environments that hit native +// binding or bundler-compat issues. +const buildIsolated = await import("../../scripts/build/build-next-isolated.mjs"); + +test("resolveNextBuildBundlerFlag defaults to --turbopack when the env var is unset", () => { + assert.equal(buildIsolated.resolveNextBuildBundlerFlag({}), "--turbopack"); +}); + +test("resolveNextBuildBundlerFlag keeps --turbopack for explicit opt-in", () => { + assert.equal( + buildIsolated.resolveNextBuildBundlerFlag({ OMNIROUTE_USE_TURBOPACK: "1" }), + "--turbopack" + ); +}); + +test("resolveNextBuildBundlerFlag honors the webpack escape hatch (=0)", () => { + assert.equal( + buildIsolated.resolveNextBuildBundlerFlag({ OMNIROUTE_USE_TURBOPACK: "0" }), + "--webpack" + ); +}); + +test("resolveNextBuildBundlerFlag treats other values as the turbopack default", () => { + // Only the documented "0" opts out — junk values must not silently flip the bundler. + assert.equal( + buildIsolated.resolveNextBuildBundlerFlag({ OMNIROUTE_USE_TURBOPACK: "yes" }), + "--turbopack" + ); +}); diff --git a/tests/unit/run-next-playwright.test.ts b/tests/unit/run-next-playwright.test.ts index 9b66bca43d..61d166d380 100644 --- a/tests/unit/run-next-playwright.test.ts +++ b/tests/unit/run-next-playwright.test.ts @@ -56,6 +56,15 @@ test("shouldUseWebpackForPlaywrightDev only opts into webpack when turbopack is }), false ); + + // Turbopack is the default: an unset env var must NOT fall back to webpack. + assert.equal( + playwrightRunner.shouldUseWebpackForPlaywrightDev({ + mode: "dev", + env: {}, + }), + false + ); }); test("standalone asset helpers detect and rehydrate missing standalone static assets", () => {