fix(vite): skip server bundle for static presets - #4509
Conversation
`buildEnvironments` built the `nitro` environment unconditionally. Static
presets define no entry, so the environment has no input and Vite falls back
to resolving `index.html`, which it then rejects for a server consumer:
rolldownOptions.input should not be an html file when building for SSR.
Please specify a dedicated SSR entry.
This affects every static preset (`static`, `github-pages`, `gitlab-pages`,
`vercel-static`, `cloudflare-pages-static`, ...) when using the Vite builder,
and it fires after prerendering, so the output in `.output/public` is already
complete when the build fails.
The rolldown and rollup builders already guard the server build with
`if (!nitro.options.static)`, so this brings the Vite builder in line.
The environment is also marked as built, because `buildApp()` in Vite builds
every environment when none of them were built, which would otherwise
re-trigger the same error for a static build with no other environments.
Closes nitrojs#3843
|
@birkskyum is attempting to deploy a commit to the Nitro Team on Vercel. A member of the Team first needs to authorize it. |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughNitro’s Vite production build skips server bundling for static presets. A fixture and parameterized tests verify prerendered output for four static presets. ChangesStatic Vite build
Estimated code review effort: 2 (Simple) | ~10 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches 💡 1🛠️ Fix failing CI checks 💡
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
commit: |
There was a problem hiding this comment.
🧹 Nitpick comments (3)
src/build/vite/prod.ts (1)
109-110: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winRemove the explanatory comment from the branch.
The comment explains the following assignment. Keep the code self-describing, or move the Vite behavior to documentation.
As per coding guidelines, do not add comments explaining what the line does unless prompted.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/build/vite/prod.ts` around lines 109 - 110, Remove the explanatory comment immediately preceding the static preset handling branch, leaving the assignment and surrounding behavior unchanged.Source: Coding guidelines
test/vite/static-preset.test.ts (1)
13-23: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick winMake the build setup independent of test order.
The tests at Lines 25-32 read
outDir, but only the test at Lines 13-23 creates it. If an assertion test runs alone, it fails before checking the behavior. Move cleanup and build tobeforeAll, or combine setup and assertions in one test.Also applies to: 25-32
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@test/vite/static-preset.test.ts` around lines 13 - 23, Make the shared build setup in the test suite independent of test order by moving the outDir cleanup, directory creation, Nitro creation, prepare, and build steps from the "build" test into a beforeAll hook. Keep the existing assertions in the tests covering outDir unchanged so they can run independently after the one-time setup.test/vite/static-preset-fixture/nitro.config.ts (1)
3-5: 🎯 Functional Correctness | 🔵 Trivial | 🏗️ Heavy liftCover all affected static presets.
The PR objectives list
static,vercel-static, andcloudflare-pages-static. This fixture hardcodes onlystatic, so the test cannot detect a regression in either alias. Parameterize the fixture or add equivalent cases for the other two presets.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@test/vite/static-preset-fixture/nitro.config.ts` around lines 3 - 5, Update the defineConfig fixture to cover all affected presets: static, vercel-static, and cloudflare-pages-static. Parameterize the preset input or add equivalent fixture cases while preserving the existing prerender route configuration.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@src/build/vite/prod.ts`:
- Around line 109-110: Remove the explanatory comment immediately preceding the
static preset handling branch, leaving the assignment and surrounding behavior
unchanged.
In `@test/vite/static-preset-fixture/nitro.config.ts`:
- Around line 3-5: Update the defineConfig fixture to cover all affected
presets: static, vercel-static, and cloudflare-pages-static. Parameterize the
preset input or add equivalent fixture cases while preserving the existing
prerender route configuration.
In `@test/vite/static-preset.test.ts`:
- Around line 13-23: Make the shared build setup in the test suite independent
of test order by moving the outDir cleanup, directory creation, Nitro creation,
prepare, and build steps from the "build" test into a beforeAll hook. Keep the
existing assertions in the tests covering outDir unchanged so they can run
independently after the one-time setup.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 3516be55-43f1-4e10-8e8c-6ab1edd81b06
📒 Files selected for processing (5)
src/build/vite/prod.tstest/vite/static-preset-fixture/nitro.config.tstest/vite/static-preset-fixture/server.tstest/vite/static-preset-fixture/vite.config.tstest/vite/static-preset.test.ts
Address review feedback: - Drop the comment above the `isBuilt` assignment, per AGENTS.md. The reasoning stays in the PR description. - Move the build into `beforeAll` so each case is independent of test order. - Parameterize over `static`, `github-pages`, `vercel-static` and `cloudflare-pages-static`, each with its own output layout. `netlify-static` is left out because it writes outside the configured output dir, which would make the test depend on the fixture directory state.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@test/vite/static-preset.test.ts`:
- Around line 24-31: Wrap the setup and build flow after createNitro in a
try/finally block, and invoke nitro.close() in the finally block so every preset
build releases its hooks and resources even when prepare or build fails.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 09ce518d-9ec5-4fd4-b1be-0e74c4ab7e6b
📒 Files selected for processing (2)
src/build/vite/prod.tstest/vite/static-preset.test.ts
💤 Files with no reviewable changes (1)
- src/build/vite/prod.ts
`build()` closes the instance on the vite path, but a throw from `prepare` or `build` left the env runner open and the instance registered in `nitroInstances`.
Replace web/ (raw HTML/JS: emitjs.html, index.html, app.js) with a SolidStart 2.0 (SolidJS) app, ported 1:1 from the old UI and verified against it with a scripted browser harness: identical DOM behavior and pixel output (only the footer's load-time figure differs), clean hydration, and a working /<repo>/ subpath deploy. - Yarn berry via corepack with nodeLinker: pnpm (no PnP); Node >= 24 (SolidStart 2.0 requirement; .node-version + engines pin it). - The emitted Lean module stays out of the bundle: public/emitjs/ is loaded at runtime, so re-emitting Lean needs no frontend rebuild. New package scripts wrap the pipeline: yarn emit (scripts/emit-web.sh), yarn fetch-words. - Two upstream 2.0 gaps are worked around: nitro v3's static presets crash the Vite builder (nitrojs/nitro#4509), so build with no preset and deploy .output/public only; and the SSR manifest hardcodes root-absolute asset URLs, so scripts/postbuild.mjs rewrites them relative, which also removes the need for BASE_PATH injection. - Remove the Std.Http server demo entirely (WebMain.lean, kwordle-web target, README section); the pure logic in KoreanWordle/Api.lean stays and keeps serving WebJS.lean. - Deploy workflow now builds the app (Node 24 + corepack yarn) and uploads its static output. Co-Authored-By: Claude <noreply@anthropic.com>
🔗 Linked issue
❓ Type of change
📚 Description
buildEnvironmentsbuilds thenitroenvironment unconditionally. Static presets define no entry, so the environment has no input, Vite falls back to resolvingindex.html, and rejects it for a server consumer:This affects every static preset (
static,github-pages,gitlab-pages,vercel-static,cloudflare-pages-static) on the Vite builder. It fires after prerendering, so.output/publicis already complete and correct when the build dies.Reproduced on current
mainwith the reporter's repro from #3843 (thevite-ssr-vue-routerexample plusnitro: { preset: "static", prerender: { routes: ["/", "/about"] } }): fails before this change, builds clean afterwards with both routes prerendered and no server bundle.Why this fix
The rolldown and rollup builders already guard the server build with
if (!nitro.options.static)(src/build/rolldown/prod.ts,src/build/rollup/prod.ts); this brings the Vite builder in line.writeBuildInfoalready acceptsoutput: ... | undefined, so no signature change was needed.The environment is also marked as built. Vite's default
builder.buildAppis a no-op, andbuildApp()treats "no environment was built" as "no plugin handled the build", falling back to building all of them. Without that line, a static build with no other environments hits the same error again, from Vite's own frame.Two alternatives were tried and rejected:
nitroenvironment when static. Redirects the fallback atclient, which also has no input, givingUNRESOLVED_ENTRY: Cannot resolve entry module index.html. It would also need a!options.devguard, sincedev.tsreadsserver.environments.nitro.buildApphook was registered rather than off mutated state. But every static preset is broken today, and this guard is correct regardless of what Vite does.Tests
Adds
test/vite/static-preset.test.tswith a minimal fixture, asserting the build succeeds, no server bundle is emitted, and routes are prerendered to the public dir. It fails before this change with the error above.Reported downstream by SolidStart users building SPAs (
solidStart({ ssr: false })+preset: "static"), where this is currently the only blocker to a server-free static deploy.📝 Checklist