Files
marktvogt.de/web/scripts/bundle.mjs
vikingowl df5b0563c9 refactor(web): bundle server with esbuild, slim runtime to alpine+node
Post-process adapter-node output into a single self-contained
build/bundle.mjs (614 KB) via esbuild, then ship it on a fresh
alpine:3.21 base with just the node binary copied in. Drops the
node_modules + package manager baggage that comes with node:25-alpine.

- Add esbuild devDep + `bundle` script (scripts/bundle.mjs)
- Dockerfile: drop `deps` stage; final stage is alpine + node binary +
  bundle + static client assets
- Uncompressed image: 177 MB -> 149 MB (-16%)
- Verified: /, /healthz, static assets all respond identically;
  outbound TLS to api.marktvogt.de works via node's built-in CA bundle
2026-04-18 05:05:07 +02:00

29 lines
1.0 KiB
JavaScript

// Bundles the SvelteKit adapter-node output into a single self-contained
// file so the runtime image can ship without node_modules.
//
// Input: build/index.js (+ its static + dynamic imports from build/**)
// Output: build/bundle.mjs
//
// Dropping node_modules shrinks the image from ~130 MB to ~60 MB.
import { build } from 'esbuild';
await build({
entryPoints: ['build/index.js'],
bundle: true,
platform: 'node',
format: 'esm',
outfile: 'build/bundle.mjs',
// Keep node built-ins external; bundle everything else (including deps
// pulled in transitively by @sveltejs/kit, valibot, etc.).
external: ['node:*'],
// ESM output lacks the CommonJS `require` global; some transitive deps
// call it via interop helpers. This shim makes it available.
banner: {
js: "import { createRequire as __cr } from 'node:module'; const require = __cr(import.meta.url);"
},
// Dynamic imports in manifest.js reference hashed chunks by string
// literal — esbuild statically traces these, no splitting needed.
logLevel: 'info'
});