From 39de08abf928403adf1106caf8bbde5b4f454b00 Mon Sep 17 00:00:00 2001 From: hsiegeln <37154749+hsiegeln@users.noreply.github.com> Date: Tue, 21 Apr 2026 12:33:59 +0200 Subject: [PATCH] fix(ai): sharp via dynamic import, nicht top-level MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Der vorige Versuch mit ssr.external in vite.config.ts war ein No-op: adapter-node macht einen eigenen Rollup-Bundle-Schritt nach Vite und ignoriert ssr.external komplett. Ergebnis: sharp's dynamic-require fuer die native .node-Binary landet kaputt im Server-Bundle (332KB Bundle-Chunk, 297 sharp-Referenzen). Dynamic import mit /* @vite-ignore */ verhindert, dass Rollup sharp aufloest — die Require geht stattdessen zur Laufzeit regulaer an Node und findet @img/sharp-linuxmusl-arm64 in node_modules. Ergebnis lokal: Server-Chunk von 332KB auf 14KB geschrumpft, nur noch 2 Referenzen auf den Paketnamen (der Import-String selbst). Co-Authored-By: Claude Opus 4.7 (1M context) --- src/lib/server/ai/image-preprocess.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/lib/server/ai/image-preprocess.ts b/src/lib/server/ai/image-preprocess.ts index 3b51d0f..f69a91f 100644 --- a/src/lib/server/ai/image-preprocess.ts +++ b/src/lib/server/ai/image-preprocess.ts @@ -1,4 +1,4 @@ -import sharp from 'sharp'; +import type SharpType from 'sharp'; const MAX_EDGE = 1600; const JPEG_QUALITY = 85; @@ -8,10 +8,20 @@ export type PreprocessedImage = { mimeType: 'image/jpeg'; }; +// Dynamic import: adapter-node bundelt server-code via Rollup/@rollup/plugin-commonjs, +// und das packt sharp's dynamic-require fuer die native Plattform-.node-Binary +// kaputt ins Bundle. Mit import() via Vite's Hint-Kommentar kann Rollup sharp +// nicht auflösen und laesst es zur Laufzeit regulaer aus node_modules laden. +async function loadSharp(): Promise { + const mod = await import(/* @vite-ignore */ 'sharp'); + return (mod as unknown as { default: typeof SharpType }).default ?? (mod as unknown as typeof SharpType); +} + // Resize auf max 1600px lange Kante, JPEG re-encode, Metadata strippen. // sharp liest HEIC/HEIF transparent, wenn libheif im libvips-Build enthalten ist // (in Alpine's vips-dev + in den offiziellen sharp-Prebuilds). export async function preprocessImage(input: Buffer): Promise { + const sharp = await loadSharp(); const pipeline = sharp(input, { failOn: 'error' }).rotate(); // respect EXIF orientation const meta = await pipeline.metadata(); if (!meta.width || !meta.height) {