fix(netlify): enable code-splitting for the netlify-edge preset - #4525
fix(netlify): enable code-splitting for the netlify-edge preset#4525JakeChampion wants to merge 1 commit into
Conversation
netlify-edge extends base-worker, which sets inlineDynamicImports: true with the rationale that iife output cannot code-split. This preset overrides the output format to esm, where that rationale no longer applies, but kept inheriting the inlining. As a result intended-lazy dynamic imports (lazy route handlers, deferred WASM init in libraries) are hoisted into the entry chunk and their top-level code runs on every isolate cold start. Netlify bundles the full module graph of the generated function, including emitted chunks, and dynamic import of those chunks works at runtime. Set inlineDynamicImports: false (matching the cloudflare presets) so lazily-imported work stays off the cold start path. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Sibh1BhDY97CPafumYmJyU
|
@JakeChampion 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)
📝 WalkthroughWalkthroughThe Netlify edge preset now sets Rollup’s ChangesNetlify dynamic import bundling
Estimated code review effort: 2 (Simple) | ~5 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 |
|
Please make a minimal deployment of a Nitro v3 app (current branch) with same setup to proof it works. |
|
Live deployment - https://nitro-pr-4525-code-splitting.netlify.app Setup
import { defineConfig } from "nitro";
export default defineConfig({
preset: "netlify-edge",
serverDir: "server",
compatibilityDate: "latest",
});
Build output (
|
What
Set
inlineDynamicImports: falsein thenetlify-edgepreset's rollup output config, so dynamic imports stay dynamic and the build emits chunks, matching what thecloudflare-pages/cloudflare-modulepresets already do.Why
netlify-edgeextendsbase-worker, which setsinlineDynamicImports: truewith the comment "iffe does not support code-splitting". This preset overrides the output format toesm, where that rationale no longer applies, but it kept inheriting the inlining.The consequence: every intended-lazy dynamic import gets hoisted into the single entry chunk, and because ESM top-level code runs at module-link time, it executes on every isolate cold start. Nitro's own lazy route handlers all load eagerly, and libraries that deliberately defer expensive init behind
import()lose that laziness entirely. The concrete case that surfaced this:nuxt-og-imagelazy-loads its resvg WASM binding, but with the inlined build theWebAssembly.instantiatecall runs unconditionally at boot, so every cold start pays WASM compilation even for requests that never render an OG image (fix on their side: nuxt-modules/og-image#673 - both changes are independently useful).Does the platform support this?
Yes - verified in production:
import()targets; the template-literal specifiers Rollup emits are followed too).import()works. Built Nitro's owntest/fixturewith this change (.netlify/edge-functions/server/getsserver.jsplus_chunks/,_routes/,_libs/etc.), deployed it to a production Netlify site, and exercised it:/wasm/dynamic-importand/wasm/static-import: return2+3=5await WebAssembly.instantiate(...)works, is not evaluated at isolate boot, and behaves correctly on the very first request served by a freshly created isolatepnpm lintandpnpm typecheckpass.Effect on cold starts
With this change the fixture's entry drops from one monolithic file to a small entry + chunks, and deferred work (route handlers, WASM compilation) executes on first use instead of at boot. For deployments with heavy lazily-imported dependencies this removes the dominant cold-start cost.