Repository navigation
Conversation
…(v2.39.2) The e2e dev server ran webpack, which kept every compiled route in the JS heap. A full warm-up took ~30 min and ~9.7 GB, and the CI agent OOM-killed the server mid-run, hence the 5 passes per shard. Measured locally (cold, every route warmed, 8 GB heap cap, heap after a forced GC): webpack had reached 3.5 GB heap / 5.8 GB private memory by the halfway point, still growing; Turbopack holds 1.5 GB heap and, with full memory eviction, 2.7-2.9 GB private memory for the whole sweep, warmed in under 5 min. - `dev` and `build` use Turbopack. The Dockerfile and both CI pipelines call these scripts; the Playwright webServer now starts `next dev --turbopack`. - SKILLY_E2E_DEV_SERVER=1 (Playwright webServer, ci/e2e-shard.sh, the GHA e2e job) sets `experimental.turbopackMemoryEviction: "full"`. It replaces SKILLY_DEV_KEEP_ROUTES, whose onDemandEntries setting Turbopack ignores and whose disabled memory-threshold restart is no longer needed. - The `webpack()` extensionAlias hook is gone: @skilly/shared is consumed from its compiled dist/, whose ".js" specifiers resolve as written. - outputFileTracingIncludes copies sharp's installed @img platform packages into the standalone output. Turbopack's tracer follows only sharp's .node binary, not the libvips library it loads, so without it every route that imports sharp answered 500 in production. - .dockerignore skips the .next-e2e-* build folders (Turbopack's dev cache is ~1 GB per folder). - Comments in the CI scripts, Jenkinsfile and e2e setup now say which numbers were measured under webpack; pass counts and shard sizing are unchanged until a CI run re-measures them. - SKILLY_SPEC.md: §2 names the bundler; roadmap item 18 describes the e2e dev server's eviction. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What & why
The web app now uses Turbopack instead of webpack for
pnpm dev, the e2e dev server and the production build.Under webpack, the e2e dev server kept every compiled route in the JS heap. The comments in the repo record the cost: a full warm-up took about 30 minutes and about 9.7 GB, and the CI agent OOM-killed the server mid-run. That is why Jenkins runs each shard as 5 passes, each on a fresh server.
I measured both bundlers locally. Each run started from an empty build folder, warmed every page and API route (about 250), and had an 8 GB heap cap. Heap is read after a forced GC.
'auto')'full'eviction (the e2e setting)On a second sweep with
'full', routes came back from Turbopack's disk cache without recompiling: 46 s for the whole sweep, median 182 ms per route. Per-route status codes were identical between runs.Changes
devandbuilduse Turbopack (packages/web/package.json). The Dockerfile, the GitHub Actionsbuildjob and Jenkins call these scripts. The Playwright webServer now startsnext dev --turbopackitself.SKILLY_E2E_DEV_SERVER=1replacesSKILLY_DEV_KEEP_ROUTES. The Playwright webServer,ci/e2e-shard.shand the GitHub Actionse2ejob set it. It turns onexperimental.turbopackMemoryEviction: "full"(next.config.mjs), which onlynext devreads. Two webpack-only settings are gone:onDemandEntries, which Turbopack ignores;webpack()hook removed. Its.js→.tsalias was dead weight:@skilly/sharedis consumed from its compileddist/, whose.jsspecifiers resolve as written.outputFileTracingIncludes). Turbopack's file tracing follows only sharp's.nodebinary, not the libvips library that binary loads, so the first Turbopack build returned 500 on every route that imports sharp ("Could not load the sharp module"). Webpack's tracer had a sharp-specific rule for this. The include copies sharp's installed@img/sharp-*packages from the pnpm store..dockerignoreskips the.next-e2e-*build folders. Turbopack's dev cache is about 1 GB per folder.ci/e2e-shard.sh,ci/e2e-shard-plan.sh, the Jenkinsfile,global-setup.tsandplaywright.config.tsnow say which numbers were measured under webpack. Pass counts, warm-up scope and shard sizing are unchanged until a CI run on Turbopack re-measures them.APP_VERSION2.39.2, with a changelog entry.Type of change
Checklist
pnpm --filter @skilly/shared buildandpnpm typecheckpass, plus the tests of the touched packages (shared, web). I didn't re-run worker tests; the worker is untouched.chore(build): … (v2.39.2).Verification
Route tables: I built production with both bundlers. Both list the same 211 routes with the same static/dynamic marker.
Production smoke test: I ran both standalone builds with
NODE_ENV=production, laid out the waypackages/web/Dockerfilelays them out. Results were identical on every functional check:/healthzand/readyzreturn 200;srcscript, and rotates per request;no-storeon/api/*;/_next/staticasset returns 200;/api/meand/api/skillsreturn 401, and an admin API returns 403;The only differences are chunk counts: 12 script tags under Turbopack against 18 under webpack.
Docker image: I built the image with Turbopack (
node:22-alpine) and ran it. sharp resizes and encodes inside the container (musl libvips loads)./healthzand/readyzreturn 200,/api/skillsreturns 401, and every script carries the nonce.Turbopack dev server: it boots, and it served every page and API route, including pages that import
@skilly/shared, with no edge-runtime warnings in the log.Static checks:
ci/e2e-shard*.shpasssh -nandbash --posix -n, andci.ymlparses.For the reviewer
e2elabel for GitHub Actions, or run the Jenkins stage).turbopackMemoryEvictionis an experimental Next setting, used only by the e2e dev server.pnpm devgets a Turbopack cache inpackages/web/.next/dev/cache(about 1 GB, gitignored). A local.env.localthat still setsSKILLY_DEV_KEEP_ROUTESis harmless; nothing reads it any more.MaxListenersExceededWarningtwice per full sweep. It comes from Next's own dev server (DevServer.getStaticPathsWorker), not from app code.E2E_PASSESor move Jenkins toE2E_WARM=allonce a CI run confirms the memory numbers on the 16 GB agent.🤖 Generated with Claude Code