Repository navigation
Check that nothing past Next's snapshot block changes webpack's snapshot options - #1426
Conversation
…hot options #1425's tripwire pinned the block in next/dist/build/webpack-config.js that sets webpack's snapshot options, but not the rest of that file, either app's webpack hook, or which copy of Next the calculator builds with. Any of those could hand webpack other options and make the oracle's verdicts wrong. The tripwire now also checks that nothing else in that file mentions snapshot and that the calculator resolves the same file, and a new test runs each app's hook on frozen snapshot options and checks it returns them untouched, keeps mode, and sets no experiments.futureDefaults. From the round-5 review of #1425. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…-config.js Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
From the round-1 review of #1426. The hook test now runs each app's hook for the client, server and edge compilations, builds a compiler from the result with Next's bundled webpack, and compares the snapshot settings webpack ends with (resolved options and the compiler's path sets) with those of the config the hook was given. So it sees plugins a hook adds and server-only changes, and no longer fails on harmless hook edits. The build-code scan also flags .managedPaths, .unmanagedPaths, .immutablePaths and .fileSystemInfo, which a plugin could change without naming snapshot. And the tripwire lists every .mode assignment in Next's build code, which pins base.js giving `next build` production mode. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- The hook test calls beforeRun, run, beforeCompile and compile before it creates the compilation, so a plugin acting in any of them counts (the round-2 review's patch). - The mode comment says what the pin checks: every `.mode =` assignment, with base.js's the only one that sets webpack's mode, and not a mode set by a spread, Object.assign or bracket access. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A run reads records between the run and compile hooks, and a plugin can tap readRecords even with no records path, so the hook test now calls compiler.readRecords there too (the round-3 review's patch). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Merged with
Review records are under |
Follow-up to #1425, from its round-5 review. Test-only: no app code changes.
Why
#1425 turned off webpack's persistent cache for the website's production builds, and kept it for the calculator. Its tests decide which app can keep the cache by asking webpack itself whether it can snapshot each directory Tailwind scans. For that, they copy the snapshot options Next gives webpack, and a tripwire fails if Next's block that sets them (
next/dist/build/webpack-config.js:1827-1840in Next 15.5.12) changes.The review found three ways webpack could still get other options without the tripwire noticing:
webpackhook changes them. Next calls the hooks later, atwebpack-config.js:2002-2016.Nothing does any of these in Next 15.5.12 today. But each would make the oracle's verdicts wrong. On a tree like the review's P1 (a link to a package under
node_modulesthat holds a file link), these tests would pass while the calculator's restored cache could serve stale CSS. #1425's after-build check would still catch a missing class.Change
In
website/src/__tests__/config/next-build-cache.test.ts:The tripwire now also checks Next's build code (
next/dist/build, 242.jsfiles) and which copy of Next the calculator uses:snapshot. Today onlywebpack-config.jsdoes, inside the block..managedPaths,.unmanagedPaths,.immutablePathsor.fileSystemInfo. Webpack builds those from the options, and a plugin could change them without naming the options..mode =assignment is listed. The only one that sets webpack's mode is inwebpack/config/blocks/base.js(ctx.isDevelopment ? 'development' : 'production', wherewebpack-config.jspassesisDevelopment: dev), and the mode picks webpack's defaultsnapshot.module. The other assignments are a fetch polyfill's and css-loader's CSS-modules mode. The list doesn't see a mode set by a spread,Object.assignor bracket access, or a change to whatisDevelopmentis. On this tree those wouldn't change the verdicts: with the oracle's compiler in modenone, both verdict tests still pass (the round-2 review's O1).webpack-config.jsas the website.A new test runs each app's hook for each of the three compilations Next builds (client, server and edge server). It builds a compiler from the hook's output with Next's bundled webpack, takes the steps a run takes before it creates a compilation (the
beforeRunandrunhooks, reading records, creating the module factories, thebeforeCompileandcompilehooks), and compares what webpack ends with against a compiler built from the config the hook was given:So it sees a change to the options, to
mode, or made by a plugin the hook adds, up to the compilation's creation. It stops there, so nothing is built or written. A hook edit that leaves those alone, such as a loader rule or anIgnorePlugin, passes.Invariants (tested)
FileSystemInfothat reads those..mode =assignment to webpack's mode in Next's build code isbase.js's.mode, or through a plugin it adds, up to the compilation's creation.Mutants
mutants-r2.py, at 3bf0e66, in an APFS clone: 25 cases, all as expected. It reruns the round-1 review's cases and the author's. At ebb2bd3,mutants-r4.py(the same 25, same results) andmutants-r4-review.py(the round-2 and round-3 reviews' 22) give the expected results.managedPathsafter the block, or setssnapshot.modulethere;compiler.managedPaths;base.js) setsconfig.snapshot, or gives builds modenone;webpack-config.jssetsmodebefore calling the hooks;snapshot, pushes a managed path in place, emptiesmanagedPathsfor server compilations only, or adds a plugin that emptiesoptions.snapshot.managedPaths;snapshot.modulein place, setsmodeto development, or adds a plugin that clearscompiler.managedPaths;IgnorePlugin;snapshotwith a spread copy;experiments.futureDefaults. That is equivalent here: inapplySnapshotDefaultsit changes only themanagedPathsandimmutablePathsdefaults, Next setsmanagedPaths, and outside Yarn PnP both giveimmutablePaths: [](the review's probe, and this run).snapshot, with the TelemetryPlugin change;managedPaths;modepin, withbase.jsgiving modenone;compiler.managedPathsinbeforeRunor inreadRecords, or the calculator's one that setssnapshot.moduleincompileor inreadRecords(thebeforeRunandcompileones passed at 3bf0e66, and thereadRecordsones at 3c0ad97); a Next plugin setscompiler.options.mode; and 7 of the hook cases above;noneby a spread,Object.assignor bracket access, or passingisDevelopment: true, and the oracle's compiler in modenone(the verdicts don't change);Round 1's runs (
mutants.py, logsmutants.logat a704789 andmutants-0a04572.log) are superseded.mutants.pyas saved has a704789's anchors; the 0a04572 run used the same cases with that commit's two check lines.Notes
dist/build/analyze/) sayssnapshotbut doesn't touch webpack's options. Whoever upgrades should read the files it names and allowlist them.Verification
tsc --noEmit;src/__tests__/config/5 files, 93 tests (next-build-cache.test.ts14); ESLint and Prettier on the changed filesnapshot. The hook test ran only the client compilation and never applied plugins a hook adds, and failed on harmless hook edits.futureDefaultschanges nothing here, though the comment said it did. And this body miscounted the mutants (10 caught, not 12), and claimed more than the tests checked, includingmode, which nothing pinned. All four are fixed in 3bf0e66, the hook test and scan being the review's patch, plus themodepin.modecomment and invariant said more than the pin checks. Both are fixed in 3c0ad97, the hook test being the review's patch.runandcompilehooks and which a plugin can tap; and two phrases under Mutants said slightly more than was true. Both are fixed in ebb2bd3, the hook test being the review's patch.axiom: n/a: website build tests only
🤖 Generated with Claude Code