perf(build): minify server build environments by default - #1777
Conversation
Vite only minifies the client environment; non-client (rsc/ssr and the Cloudflare worker) environments default build.minify to false, so the deployed worker and SSR renderer ship unminified. Add a build-only configEnvironment default that sets minify: true for server environments, yielding to any explicit user/plugin minify setting (including false).
commit: |
|
|
/bigbonk review for issues |
There was a problem hiding this comment.
Review: perf(build): minify server build environments by default
I verified the core mechanism against the actual @voidzero-dev/vite-plus-core (Vite 8) and @cloudflare/vite-plugin@1.31.0 sources in node_modules, and exercised the plugin's configEnvironment hook directly. The implementation is correct and the PR's central claims hold. No blocking issues found.
Verified claims
-
"Yields to user config" guarantee holds. Confirmed in
vite-plus-core:getDefaultEnvironmentOptions()seeds each environment'sbuildfrom top-levelconfig.build(build: config.build).- Each env is
mergeConfig(defaultNonClientEnvironmentOptions, config.environments[name])beforerunConfigEnvironmentHookruns, so theconfigarg passed toconfigEnvironment(name, config, ...)already reflects top-level and per-env (environments.<name>.build.minify) user settings. - The
if (config.build?.minify !== undefined) return nullguard therefore correctly defers tofalse,true, and string minifier choices ('esbuild'/'oxc').
-
Covers the Cloudflare worker env. Confirmed
@cloudflare/vite-plugindefines its worker environment(s) via theconfighook (getEnvironmentsConfig→createCloudflareEnvironmentOptions) and never setsbuild.minify. So vinext's hook seesminify === undefinedfor the worker env and fillstrue. UsingconfigEnvironment(not theconfig-hook per-env blocks) is the right call here, since vinext'sconfighook doesn't own that env. -
Prod-only.
apply: "build"is honored by Vite's plugin filtering beforeconfigEnvironmentruns, matching the siblingvinext:client-css-url-assets-defaultsplugin. Dev/preview are excluded.
I confirmed all of the above by invoking the hook across cases (client→null; rsc/ssr/worker default→{build:{minify:true}}; minify:false/true/'esbuild'→null) — all pass. tests/build-optimization.test.ts (82 tests) also still passes.
Non-blocking observations
-
No test coverage for the new plugin. This is the one real gap. There's an established, directly-applicable pattern right next to this code:
vinext:client-css-url-assets-defaultsis unit-tested intests/build-optimization.test.tsboth at the hook level and through a fullcreateBuilder()lifecycle (asserting the default applies to rsc/ssr and that a user value propagates to all envs). Given that the headline correctness property of this PR is "true default that yields to user config," a small test asserting:configEnvironment("rsc", {})→{ build: { minify: true } }configEnvironment("client", {})→nullconfigEnvironment("rsc", { build: { minify: false } })→null
would lock in the behavior cheaply and guard against regressions. Recommend adding it before merge.
-
Minify without sourcemaps hurts prod worker debuggability. Server envs default
sourcemap: false, so minified worker stack traces will reference mangled identifiers. The PR explicitly scopes sourcemaps out as a possible follow-up — that's a reasonable, deliberate decision, just flagging the tradeoff. Cloudflare Workers supports uploading sourcemaps, so a follow-up enablingsourcemap: "hidden"(ortrue) for server envs would restore readable traces without bloating the bundle. -
minify: truevs pinning a minifier — correct choice for this Vite 8 / rolldown+oxc repo; lets the native toolchain pick, consistent with the repo's "prefer oxc" guidance.
Verification summary
- Inspected
getDefaultEnvironmentOptions/runConfigEnvironmentHook(vite-plus-core) andcreateCloudflareEnvironmentOptions(@cloudflare/vite-plugin) — mechanism confirmed. - Exercised the hook directly across 8 cases — all behave as documented.
tests/build-optimization.test.ts— 82 passed, no regression.
LGTM aside from the missing test coverage, which I'd recommend adding given the behavior is non-obvious and easy to break.
|
Review posted to PR #1777. SummaryI reviewed PR #1777 ( Verdict: Correct, no blocking issues. The implementation's central guarantee — a "true default that yields to user config" — holds. What I verified
Findings (all non-blocking)
|
|
/bigbonk re-review for issues. There's nothing to test in the pr - it's a self-explanatory plugin. Sourcemaps should probs be opt-in by a user rather than on-by-default. |
There was a problem hiding this comment.
Re-review: perf(build): minify server build environments by default
Re-verified the mechanism against the actual @voidzero-dev/vite-plus-core@0.1.24 (Vite 8) source in node_modules and re-confirmed the change is correct, scoped, and safe to merge.
Mechanism re-confirmed (line refs in vite-plus-core's node.js)
getDefaultEnvironmentOptions(config)returns{ build: config.build, ... }— each environment is seeded from the top-levelconfig.build.- Resolution order: every env is
mergeConfig(defaultNonClientEnvironmentOptions, config.environments[name])beforerunConfigEnvironmentHookruns, and the hook's return value is merged back in (environments[name] = mergeConfig(environments[name], res)). - Therefore the guard
if (config.build?.minify !== undefined) return nullcorrectly sees and defers to any prior choice: top-levelbuild.minify,environments.<name>.build.minify, or a value set by an earlier plugin (e.g.@cloudflare/vite-plugin, which I confirmed never setsminifyon its worker env). It only fillstruewhen stillundefined.
apply: "build" keeps this prod-only (no dev/preview impact), matching the sibling vinext:client-css-url-assets-defaults plugin. The "true default that yields to user config" property holds.
On the two pieces of feedback
Tests — Agreed, withdrawing my earlier suggestion to add a unit test. The hook is a three-line, self-explanatory default and the behavior is already covered indirectly by the existing tests/build-optimization.test.ts lifecycle. Not a blocker.
Sourcemaps — Confirmed this PR does not touch sourcemaps at all (diff is minify-only, 1 file / +33). Server envs keep sourcemap: false, so nothing is turned on-by-default here. My earlier note only flagged a possible follow-up to add sourcemaps; per the steer, that follow-up should make sourcemaps opt-in by the user, not on-by-default. Nothing to change in this PR.
Verdict
LGTM. Correct, minimal, prod-scoped, and yields to explicit user config. No blocking issues.
|
Re-review posted to PR #1777. SummaryI re-reviewed the minify-only change and verified the mechanism against the actual Verdict: LGTM, no blocking issues.
On the two feedback items:
|
…unminified for output introspection Minifying server environments by default exposed two problems: 1. Real bug: build-time route classification ran in `generateBundle`, but rolldown's minifier renames the top-level `__VINEXT_CLASS` function and mangles its `routeIdx` parameter before that hook runs, so the stub regex never matched and the patch silently no-op'd — every route fell back to the Layer 3 runtime probe in production. Move the injection to a `renderChunk` hook with `order: "pre"` so it runs before minification; the patched body is then minified along with the rest of the chunk (the runtime calls it by reference, not by name). 2. Test artifacts: three build tests introspect emitted server chunks for readable identifiers (registerConfiguredCacheAdapters, __VINEXT_CLASS) or depend on unminified chunking (MDX frontmatter leak check). These are test-only concerns — the runtime behavior is unchanged. Build their fixtures with `build.minify: false` (a user-overridable default) so the assertions stay deterministic.
The renderChunk refactor removed the external consumer of this type; keep it as a local type so knip passes.
|
Pushed two follow-ups since the last review — please re-review the full change:
/bigbonk review for issues |
There was a problem hiding this comment.
Re-review: minify server bundles + route-classification renderChunk fix
Re-reviewed the two follow-ups (906f25d, 2c379c0) on top of the original minify default. I verified the central claims against the actual @voidzero-dev/vite-plus-core@0.1.24 source and by building fixtures both minified and unminified.
Verdict: the production-bug fix is correct and necessary. No blocking issues. Two non-blocking items below (stale generateBundle references, and a test-coverage gap for the exact regression being fixed).
Verified: the bug was real and the fix works
I confirmed the failure mode and the fix against vite-plus-core:
resolveBuildEnvironmentOptionsdefaultsminifytofalseforconsumer === "server"(node.js:38634) and normalizesbuild.minify: true→"oxc"(node.js:38649), which maps to real output minification (node.js:38764). So oncevinext:server-minify-defaultsfillsminify: trueon the rsc env, the rolldown/oxc minifier does mangle the top-level__VINEXT_CLASSname.output.minifyis a rolldown output-level option applied after allrenderChunkplugin hooks, so arenderChunkwithorder: "pre"legitimately sees the unminified stub. The oldgenerateBundleran post-minify, whereCLASS_STUB_RE(/function __VINEXT_CLASS\(routeIdx\).../) could never match.
I reproduced both states by building a minimal force-static fixture through createBuilder().buildApp():
- Minified (default) build: the emitted RSC chunk has
__VINEXT_CLASSrenamed away (no readable name survives) and carries a populated dispatch — the runtime consumer readsn?.buildTimeClassifications?.get(r)and branches oni===`static`?`s`:`d`. So classification is injected and wired. (Note the minifier rewrites string quotes to backticks — worth keeping in mind for any future output-introspection assertion.) minify: falsebuild: readable__VINEXT_CLASSdispatch present, as the integration test asserts.
This confirms the headline claim: build-time classification now survives the default minified prod build, and previously silently no-op'd.
Non-blocking: the regression test does not exercise the bug it fixes
The three updated build-introspection tests now build with minify: false (+ per-env overrides), which is a sound way to keep the readable-identifier introspection deterministic. But it means tests/build-time-classification-integration.test.ts — the test that exists specifically to prove the dispatch table is populated — runs in the one configuration where the old buggy generateBundle code would also have passed. The actual production default (minify on) is no longer covered by any automated test, even though "classification silently no-op's under minify" is exactly the regression this PR fixes.
Recommend adding one small assertion that builds the same fixture with the default (minified) settings and verifies a populated dispatch was injected (e.g. that the chunk contains the classification branch / buildTimeClassifications consumer with non-null values, tolerating backtick-quoted string literals). That locks in the fix; without it, a future revert to generateBundle would stay green. I verified such a check passes today, so it's cheap to add.
Non-blocking: stale generateBundle references after the hook move
Several comments and user-facing error strings still say generateBundle for the route-classification path:
packages/vinext/src/build/route-classification-injector.ts:84and:150— the drift-diagnosticErrormessages ("The generator and generateBundle have drifted"). These are the ones I'd fix, since they'd mislead anyone debugging a real drift failure; the hook is nowrenderChunk.packages/vinext/src/index.ts:777("consumed in generateBundle") and:784("Shared with the Layer 2 generateBundle hook below").packages/vinext/src/entries/app-rsc-entry.ts:385,:397("Replaced in generateBundle").packages/vinext/src/build/route-classification-manifest.ts:203,packages/vinext/src/build/layout-classification.ts:130, andtests/build-time-classification-integration.test.ts:7,38,191.
(The remaining generateBundle hook at index.ts:2647 is the unrelated CSS-url-assets-restore hook — correct, leave it.)
Minor robustness observation (not a regression)
Moving from whole-bundle generateBundle to per-chunk renderChunk changes the drift invariant slightly: findClassificationChunk now receives a single chunk per call. If a chunk ever references __VINEXT_CLASS (a call site) without containing the stub body, it would throw "no chunk contains the stub body." Today codegen keeps the stub definition and all __VINEXT_CLASS(N) call sites in the same RSC entry module, so this can't split — but it's marginally less robust than the old whole-bundle scan. Fine as-is; just noting it.
Other items confirmed
knipchange (2c379c0):RouteClassificationChunkis now internal to the injector module and only referenced within it (thedist/export is stale build output). Correct.vp checkon the changed source files: format + lint + types pass.tests/build-time-classification-integration.test.ts(8),tests/cache-adapters-build.test.ts(1),tests/tsconfig-path-alias-build.test.ts(2),tests/build-optimization.test.ts(82) all pass.
LGTM to merge. I'd strongly suggest the minified-default regression test before merge given the whole point of this PR; the stale generateBundle strings are a nice-to-have cleanup.
|
Review posted to PR #1777. SummaryI re-reviewed both follow-ups ( Verdict: the production-bug fix is correct and necessary. No blocking issues. What I verified
Findings (all non-blocking)
The knip change is correct, |
…ndle refs Re-add real regression coverage that the route-classification injector runs before minification (the production default). The prior fix made three tests pass by building with minify:false, which is exactly the config where the old post-minify generateBundle code also passed — so the tests no longer guarded the bug. - classification integration: add a production-default (minify on) suite that asserts minify-robust signals (injected switch dispatch shape + the module-graph reason literal, which is build-time-only). Verified it FAILS when the injector is reverted to a post-minify generateBundle hook. - cache adapters: assert the minify-safe LOCAL_ADAPTER_MARKER under the default minified build instead of grepping the renamed registerConfiguredCacheAdapters symbol. - tsconfig path alias (#659): run minified; the old not.toContain('title: "Second Post"') was a quote-escaping artifact (frontmatter renders as MDX content identically in both builds, not a minify leak) — replaced with a real parse-error/raw-YAML guard. - update stale generateBundle references to renderChunk in injector error strings, app-rsc-entry stubs, and index.ts comments; note the per-chunk patch assumption near the renderChunk hook.
|
Addressed both findings from the last review ( 1. Regression test now exercises the bug (un-masked). Added a "production default — minify on" suite to 2. Stale
/bigbonk review for issues |
|
@james-elicx Bonk workflow was cancelled. View workflow run · To retry, trigger Bonk again. |
|
/bigbonk re-review for issues |
There was a problem hiding this comment.
Re-review: minify server bundles + renderChunk classification fix + regression coverage
Re-reviewed the latest follow-up (79f7df4) addressing the two findings from my prior review. I verified the central claims empirically — by reproducing the original bug and confirming the new regression suite catches it — rather than taking the description at face value.
Verdict: LGTM, no blocking issues. The earlier finding #1 (the regression test didn't exercise the bug it fixes) is now genuinely resolved.
Verified: the new minify-on suite actually guards the bug
I reproduced the original failure faithfully by temporarily moving the injector back to a post-minify generateBundle hook (which is the configuration the prior minify: false-only tests would have stayed green under). With that revert in place, the new "production default — minify on" suite FAILED — specifically the module-graph reason-literal assertion, which received the fully-minified RSC chunk with no module-graph literal present, confirming the dispatch table was never injected. Restoring the renderChunk + order: "pre" fix makes it pass. So the suite is a real guard, not a tautology.
I also confirmed the uniqueness claim the test relies on: module-graph as a string literal appears only in build-time modules (route-classification-manifest.ts:144, layout-classification.ts, layout-classification-types.ts, report.ts) and is absent from every server/* runtime module that gets bundled into the RSC chunk (app-page-dispatch.ts / app-page-execution.ts carry only segment-config / no-classifier). So a passing module-graph match is conclusive proof the build-time reasons patch ran. Good choice of signal.
The structural case \d+: return new Map([[\d+, assertion is likewise minify-robust (string contents and switch structure survive mangling) and absent from the untouched return null stub.
Verified: minify-defaults plugin and renderChunk move
- The
vinext:server-minify-defaultsplugin is unchanged from the version I confirmed earlier against@voidzero-dev/vite-plus-core@0.1.24:getDefaultEnvironmentOptionsseeds each env'sbuildfrom top-levelconfig.build, envs are merged beforerunConfigEnvironmentHook, so theconfig.build?.minify !== undefinedguard genuinely defers to any user/plugin choice (incl.false).apply: "build"keeps it prod-only. The "true default that yields to user config" property holds. - The
renderChunk+order: "pre"move is correct: rolldown'soutput.minifyruns after allrenderChunkhooks, so the stub is still readable when patched, and the runtime calls the function by reference (not by name) so post-patch minification is safe. The per-chunk assumption is now documented with a clear "revisit if codegen splits call sites" note.
Findings (all non-blocking)
-
Two stale
generateBundlereferences remain — my prior review listed these and they were not included in the cleanup commit (the commit message enumeratedroute-classification-injector.ts,app-rsc-entry.ts, andindex.ts, but missed these two):packages/vinext/src/build/route-classification-manifest.ts:203— "suitable for embedding into the generated RSC entry viagenerateBundle" (nowrenderChunk). The exported function is also still namedbuildGenerateBundleReplacement(line 209) — comment fix is enough; renaming the symbol is optional churn.packages/vinext/src/build/layout-classification.ts:130—@internaldoc says "ThegenerateBundlehook inindex.tscallsclassifyLayoutByModuleGraphdirectly" (nowrenderChunk).
Pure documentation drift; would mislead the next person tracing the classification path. Cheap to fix in this PR or a follow-up.
Verification summary
- Reproduced the bug (post-minify
generateBundle) → new minify-on suite FAILS on themodule-graphassertion; restored fix → passes. The regression is genuinely guarded. - Confirmed
module-graphis a unique build-time-only signal (absent from bundledserver/*runtime). tests/build-time-classification-integration.test.ts(10),tests/cache-adapters-build.test.ts(1),tests/tsconfig-path-alias-build.test.ts(2),tests/build-optimization.test.ts(82) — all pass.vp checkon the three changed source files — format + lint + types pass.
LGTM to merge. The two stale generateBundle doc references are the only nit; not a blocker.
|
Review posted to PR #1777. SummaryI re-reviewed the latest follow-up ( Verdict: LGTM, no blocking issues. The key prior finding — that the regression test didn't exercise the bug it fixes — is now genuinely resolved. What I verified
Findings (non-blocking)
The bug-reproduction edits I made during review were reverted; the working tree is clean. |
…ify:false) Previously the dispatch-logic suites built the fixture with minify:false so they could regex-extract and eval the readable __VINEXT_CLASS function — but that is the one config where the original post-minify-hook bug also passes, so it masked the regression. Rewrite the extractors to be name-agnostic: derive the (mangled) dispatch function name from its property-keyed call site (__buildTimeClassifications/ __buildTimeReasons — property keys survive minification) and eval the body (string-literal values like "static" also survive). All suites now build with vinext's production defaults (minify on). Verified by reproducing the bug (disabling the patch): the minified tests fail with "was not patched".
|
Removed the test-only minify disabling entirely ( Instead of disabling minify to keep Proved it actually guards the bug: I reproduced the original failure by disabling the renderChunk patch, rebuilt minified, and the suites FAIL with /bigbonk review for issues |
There was a problem hiding this comment.
Re-review: classification tests now run against minified output (no minify: false)
I verified the latest commit (71e86d2) empirically — extracting and evaluating the dispatch functions from the actual minified RSC chunks the test produces, and reproducing the regression-guard behavior — rather than trusting the description.
Verdict: the change is correct and is a genuine improvement over the prior minify: false approach. No blocking issues. This is the right fix for the masking concern: the suites now exercise the real shipping path.
Verified empirically
- Name-agnostic recovery works on real minified output. Built the fixture with production defaults; the dispatch function is emitted as
function $S(e){return(e=>{switch(e){case 0:return new Map([[0,\static`]]);...default:return null}})(e)}.classDispatchNamecorrectly recovers$Sfrom the unmangled__buildTimeClassifications:$S(0)property-keyed call site, andevalDispatchFn's regex (function\s+${esc}\s*(\s*(\w+)\s*)\s*{\sreturn\s([\s\S]?)(\s\1\s*)\s*}) captures the body cleanly. The non-greedy[\s\S]*?correctly skips the innerswitch(e)(it's(e){, not(e)}) and terminates at the real})(e)}self-call. Evals to a working dispatch returning the correctMap` per route. - The reasons (debug) path works too. Recovered the mangled reasons fn (
eC) and confirmed it evals to[[0,{layer:"module-graph",result:"static"}],[1,{layer:"segment-config",key:"dynamic",value:"force-dynamic"}]]for/force-dyn— string literals ("static","module-graph") survive minification verbatim, even nested in object literals with backtick-quoted values. - The regression guard genuinely fires. Simulated an unpatched minified stub (
function $S(e){return null}with a__buildTimeClassifications:$S(0)call site):classDispatchNamederives$S, thennullStubRematches andevalDispatchFnthrows$S was not patched — still returns null unconditionally. So a revert to a post-minify hook fails every suite, exactly as claimed. This is the property the priorminify: falsetests had lost. - All tests pass.
tests/build-time-classification-integration.test.ts(8),tests/cache-adapters-build.test.ts(1),tests/tsconfig-path-alias-build.test.ts(2) = 11 passed.vp checkon the three changed test files is clean;vp run knipis clean. - The two adapted tests are well-reasoned.
cache-adapters-buildkeying off theLOCAL_ADAPTER_MARKERstring literal instead of the renamedregisterConfiguredCacheAdapterssymbol is the correct minify-safe signal. Thetsconfig-path-alias(#659) change is a nice catch — the oldnot.toContain('title: "Second Post"')only ever passed because unminified output escaped the inner quotes (title: \"Second Post\"), so it was a quote-style artifact, not a real leak guard; the replacement raw-YAML-frontmatter check (/^---\s*$[\s\S]*?title:/m) is a genuine #659 guard.
Findings (non-blocking)
-
Two stale
generateBundledoc references from my prior review are still present — the cleanup commit (79f7df4) missed them and this commit only touched the test file:packages/vinext/src/build/route-classification-manifest.ts:203— "suitable for embedding into the generated RSC entry viagenerateBundle" (nowrenderChunk).packages/vinext/src/build/layout-classification.ts:130—@internaldoc: "ThegenerateBundlehook inindex.tscallsclassifyLayoutByModuleGraphdirectly" (nowrenderChunk).
Compounding this, the exported symbol
buildGenerateBundleReplacement(manifest.ts:209, referenced at :219 and :226) still carries the old hook name in its identifier. Pure naming/doc drift — no functional impact — but it will mislead the next person tracing the classification path. Cheapest fix is the two doc comments; the symbol rename is optional churn. -
extractRouteIndexByPatterncoupling (informational, pre-existing). The__buildTimeClassifications: <name>(N) [\s\S]*? pattern: "..."pairing relies onpattern:appearing after the call site within each route object and no intervening__buildTimeClassifications:. Holds for current codegen (verified the four routes map correctly) and the non-greedy match grabs the nearestpattern:. If codegen ever reorderspattern:before the classification field, indices would silently shift by one route. Not a regression in this PR (same coupling existed before), just noting the fragility now that minify reshapes output.
The first item is the only thing I'd suggest fixing, and it's a doc nit, not a blocker. Net: this commit removes the masking and makes the suite a real guard against the original post-minify bug. LGTM.
|
Review posted to PR #1777. SummaryI re-reviewed the latest commit ( Verdict: correct and a genuine improvement. No blocking issues. This properly addresses the masking concern from the previous review. What I verified (empirically, against real minified output)
Findings (non-blocking)
Only the doc nit is worth fixing, and it's not a blocker. |
…erateBundle doc refs Address review nits: the classification dispatch builder still carried the old hook name (it now runs in renderChunk, not generateBundle). Rename it to buildClassificationReplacement (parallel to buildReasonsReplacement) and fix two stale 'generateBundle' doc comments (route-classification-manifest.ts:203, layout-classification.ts:130) to say renderChunk.
|
Addressed the remaining nits ( Finding #1 (doc/naming drift):
Finding #2 (extractRouteIndexByPattern non-greedy pairing): left as-is — it's pre-existing and, importantly, self-checking: if the call-site→
/bigbonk review for issues |
There was a problem hiding this comment.
Re-review: rename buildGenerateBundleReplacement + fix stale generateBundle doc refs (97c29de)
Verified the latest commit against the working tree rather than the description.
Verdict: LGTM, no blocking issues. Both findings from my prior review are now resolved.
What I verified
Finding #1 (doc/naming drift) — resolved.
-
The rename
buildGenerateBundleReplacement→buildClassificationReplacementis complete and consistent: the declaration, both self-referential doc comments (the "Sibling of…" and "Layer 1 priority…" comments onbuildReasonsReplacement), the import + call site inroute-classification-injector.ts, and all 8 references intests/route-classification-manifest.test.ts(including thedescribeblock, theevalDispatcherror string, and the threeParameters<typeof …>type annotations) are updated.grepfinds zerobuildGenerateBundleReplacementreferences inpackages/vinext/src/ortests/— the only remaining hits are inpackages/vinext/dist/(stale build output, regenerated on next build). The new name parallels its siblingbuildReasonsReplacementcleanly. -
The two stale doc comments are fixed:
route-classification-manifest.ts:203now says "via therenderChunkhook" andlayout-classification.ts:130now says "TherenderChunkhook inindex.ts". -
I audited every remaining
generateBundlemention inpackages/vinext/src/. All are legitimate:route-classification-injector.ts:55— the intentional "Patching ingenerateBundle(post-minify) would silently…" rationale comment the author flagged. Correct to keep.index.ts:2512–2536— therenderChunkhandler's own comments, which explicitly explain why notgenerateBundle. Correct.index.ts:1598,2603,2655+css-url-assets.ts:23— the unrelated CSS-url-assets restore hook (genuinely agenerateBundlehook). Correct.
So no misleading drift remains.
Finding #2 (extractRouteIndexByPattern non-greedy pairing) — agreed, leave as-is. This is pre-existing (not introduced by this PR) and, as the author notes, self-checking: the dispatch-correctness assertions key off the recovered Map contents, so a desynced call-site→pattern: pairing would surface as a wrong-route assertion failure rather than silently passing. A codegen reorder can't mask a wrong mapping without breaking a test. Not worth churn here.
Verification summary
tests/route-classification-manifest.test.ts(16) +tests/build-time-classification-integration.test.ts(8) — 24 passed.vp checkon the four changed source/test files — format + lint + types clean.- No source/test references to the old symbol name remain (dist/ excluded — stale build artifact).
Pure rename + doc cleanup, no functional change. Ready to merge.
|
Review posted to PR #1777. SummaryI reviewed the latest commit ( Verdict: LGTM, no blocking issues. Both findings are resolved. What I confirmedFinding #1 (doc/naming drift) — fully resolved.
Finding #2 ( Verification
Pure rename + doc cleanup, no functional change. |
…it (#1810) * fix(build): write BUILD_ID via writeBundle so App Router builds emit it The vinext:build-id plugin used closeBundle, which does not fire during the multi-environment createBuilder().buildApp() pipeline used for App Router production builds. As a result dist/server/BUILD_ID was silently never written for pure App Router apps — only hybrid apps (which run a second vite.build() pass) and Pages Router apps got the file. This was masked until server bundles started being minified by default (#1777): the e2e deploy harness (scripts/e2e-deploy.sh) falls back to regex-parsing the buildId out of dist/server/index.js when BUILD_ID is absent, and minification mangles the `get buildId()` / `buildId = "..."` patterns the regex relies on. The result was ~700 App Router deploy-suite tests failing at setup with "Failed to extract build ID from dist/server/index.js". Switch the plugin to writeBundle (mirroring the working vinext:image-config plugin), which fires for every emitted bundle; the existing one-time write guard ensures the file is written exactly once. Add a regression assertion to the App Router production build test. * fix(build): share one build ID across all plugin instances in a build A single `vinext build` can instantiate vinext() more than once: the App Router multi-environment build (createBuilder().buildApp()) and the separate Pages Router SSR build for hybrid app+pages apps are distinct plugin instances. With no user generateBuildId, each instance resolved its own random UUID, so the App Router runtime, the Pages Router runtime, the prerender manifest, and dist/server/BUILD_ID could each get a different build ID — risking ISR/seed-cache key mismatches for hybrid apps. The CLI now resolves the build ID once (honoring the user's generateBuildId) and publishes it via __VINEXT_SHARED_BUILD_ID. The plugin adopts it unless the user supplied their own generateBuildId (which is authoritative and already shared because every instance calls it). resolveBuildId()'s standalone semantics are unchanged, so the build-only coordination never leaks into dev or tests. Verified on the hybrid app-router-cloudflare example: the App Router RSC/SSR runtime, Pages entry, prerender manifest, and BUILD_ID file now all carry the same build ID. Adds a coordination test alongside the BUILD_ID emission guard. * fix(build): always adopt shared build ID, even with generateBuildId Addresses review on #1810. The previous `!rawConfig?.generateBuildId` guard reintroduced the divergence it was meant to fix: when generateBuildId returns null (documented Next.js behavior) or is non-deterministic, resolveBuildId() mints a fresh random UUID per plugin instance, so a hybrid app+pages build still got divergent IDs across the buildApp() and Pages vite.build() instances. The CLI's resolvedNextConfig.buildId is already the fully-resolved authoritative value (it ran resolveBuildId honoring generateBuildId, including the null→UUID fallback), so the plugin now always adopts the shared ID when set. Add a regression test using generateBuildId: () => null + a shared ID, and document that the CLI intentionally does not clear the env var (the build process exits).
Problem
vinext's server-side build environments ship unminified. Vite only applies
build.minifyto theclientenvironment by default — for every non-client environment (rsc,ssr, and the Cloudflare worker env)build.minifydefaults tofalse, and vinext never sets it. So the deployed worker (dist/server/index.js), the SSR renderer (dist/server/ssr/index.js), and the Pages-Router-on-Cloudflare worker all ship full of readable identifiers, comments, and whitespace.Raw size drives workerd cold-start parse CPU; gzip size is counted against the Cloudflare Workers size limit. Minifying is a large, cheap win.
Approach
Add a build-only
configEnvironmentplugin (vinext:server-minify-defaults) that setsminify: truefor non-client environments. This is a true default that yields to user configuration, not a hard override:apply: "build"scopes it to production builds (never dev/preview).buildfrom the top-levelconfig.buildbefore runningconfigEnvironment(seegetDefaultEnvironmentOptions), so the incomingconfig.build?.minifyreflects any explicit setting — top-level,environments.<name>.build.minify, or set by an earlier plugin (e.g.@cloudflare/vite-plugin). If anyone already chose a value (includingfalse), we leave it untouched; we only fill the default when it's stillundefined.minify: truelets the rolldown/oxc toolchain pick its native minifier rather than pinning'esbuild'.Using
configEnvironment(rather than the per-environment blocks in theconfighook) means it also covers the Cloudflare worker environment that@cloudflare/vite-pluginowns — which vinext'sconfighook doesn't define — so Pages-Router-on-Cloudflare apps benefit too.Scope kept to minify only; sourcemaps are intentionally not touched (possible focused follow-up).
Measured before/after (real builds this session)
dist/server/index.jsdist/server/ssr/index.jsdist/server/index.jsdist/server/ssr/index.jsdist/pages_router_cloudflare/index.js(worker)"before" numbers were produced by temporarily setting
build: { minify: false }(reproducing the old default) and rebuilding; "after" are clean builds with this change.User-override verification
Temporarily set
build: { minify: false }inexamples/hackernews,examples/app-router-cloudflare, andexamples/pages-router-cloudflare'svite.config.ts, rebuilt, and confirmed the output reverted to unminified (readable identifiers/whitespace, e.g. hackernewsdist/server/index.jsback to 1342164 raw; pages worker back to 826635 raw withimport fs from "node:fs";). This proves the default yields to explicit user config. All temporary config edits were reverted.Runtime check
Ran
vp previewon the minifiedapp-router-cloudflareworker:GET /returned HTTP 200 with fully rendered HTML (<title>vinext on Cloudflare Workers</title>), including thecounterclient component and its hydration chunk (_next/static/counter-*.js) — confirming the RSC/SSR/client split is intact under minification.Tests run (targeted only)
tests/build-optimization.test.ts+tests/shims.test.ts— 1103 passedtests/use-client-export-all-build.test.ts+tests/clean-build-output.test.ts— 6 passed (RSC"use client"directive handling intact)vp checkonpackages/vinext— formatting + lint + typecheck pass, no new errors(Did not run the full suite per CPU constraints.)