feat(build): make Turbopack the default bundler for dev and build (#6283)

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.
This commit is contained in:
Diego Rodrigues de Sa e Souza
2026-07-05 11:14:25 -03:00
committed by GitHub
parent f35839fe01
commit 046093b9cb
6 changed files with 56 additions and 4 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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", () => {