fix(ai): sharp via createRequire, nicht ES-Import
All checks were successful
Build & Publish Docker Image / build-and-push (push) Successful in 28s

ES-Dynamic-Import mit @vite-ignore reichte nicht -- adapter-node's
Rollup-Step extrahiert sharp trotzdem in einen shared chunk und
bundelt sharp's interne dynamic-requires kaputt.

createRequire(import.meta.url) plus require('sharp') ist pure Node-
Runtime-Logik, die Rollup komplett ignoriert. sharp wird regulaer aus
node_modules geladen -- inkl. seiner Plattform-.node-Binary aus
@img/sharp-linuxmusl-arm64.

Verifikation: Build-Output enthaelt 0 Vorkommen von "dynamicRequireTargets"
und "sharp.node" (waren vorher in einem 319KB shared chunk).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
hsiegeln
2026-04-21 13:08:53 +02:00
parent 5492d4dc24
commit 173d9d138d

View File

@@ -1,4 +1,5 @@
import type SharpType from 'sharp';
import { createRequire } from 'node:module';
const MAX_EDGE = 1600;
const JPEG_QUALITY = 85;
@@ -8,20 +9,25 @@ 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<typeof SharpType> {
const mod = await import(/* @vite-ignore */ 'sharp');
return (mod as unknown as { default: typeof SharpType }).default ?? (mod as unknown as typeof SharpType);
// sharp per Node-Runtime-require laden, nicht via ES-Import: adapter-node
// bundelt ES-Imports (auch dynamische, auch mit @vite-ignore) ins Server-
// Bundle, was sharp's internes dynamic-require fuer die Plattform-.node-Binary
// zerstoert. createRequire + require() ist pure Node-Runtime-Logik, die
// Rollup nicht anfasst -- sharp wird regulaer aus node_modules geladen.
const nodeRequire = createRequire(import.meta.url);
let sharpModule: typeof SharpType | null = null;
function loadSharp(): typeof SharpType {
if (!sharpModule) {
sharpModule = nodeRequire('sharp') as typeof SharpType;
}
return sharpModule;
}
// 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<PreprocessedImage> {
const sharp = await loadSharp();
const sharp = loadSharp();
const pipeline = sharp(input, { failOn: 'error' }).rotate(); // respect EXIF orientation
const meta = await pipeline.metadata();
if (!meta.width || !meta.height) {