By Shehab Waleed, Founder, Cairo Studio 2 min read
Our site renders a share image for each page: the page title set in our typeface on a 1200 by 630 card. satori turns the layout into an SVG and sharp turns that into a JPEG. It worked in development, and the first request in production returned a 500 with one line in the log.
Error: Could not load the "sharp" module
How adapter-node builds your server
SvelteKit’s Node adapter does not ship node_modules as they are. It bundles the server code with Rollup so that a deployment only needs its production dependencies. The adapter’s source says so in a comment.
// we bundle the Vite output so that deployments only need
// their production dependencies. Anything in devDependencies
// will get included in the bundled code
The packages listed under dependencies are marked external and loaded from node_modules at runtime. Everything else is copied into the bundle. sharp had ended up under devDependencies.
Why sharp cannot be bundled
sharp is a wrapper around libvips, a native image library. At runtime it loads a compiled binary for the current operating system and processor, from a separate package that npm picks during install. Rollup can copy JavaScript into a bundle. It cannot copy that binary, and the bundled sharp had no way to find one.
The fix
"dependencies": {
"sharp": "^0.33.5"
}With sharp in dependencies, the adapter leaves it external. Our deploy runs npm install on the server, which installs the binary for Linux on that machine, and the route has served images since.
The rule we use now
With adapter-node, the split between dependencies and devDependencies is not bookkeeping. It decides what gets bundled. Anything the server imports at runtime goes in dependencies when it has native parts, or when it reads files from its own package folder. Pure JavaScript usually survives bundling either way, but we no longer rely on that.
We have not tested every native package, so treat the list below as our experience rather than a full survey. Deploys taught us another lesson in the same month, when a deploy put noindex on eight pages.
