feat: on-demand image optimization with sharp#95
Conversation
Add /api/image endpoint that resizes and converts content images to WebP on first request, caching results to disk. Images go from ~1.8MB originals to ~6KB optimized WebP at 640w. - New /api/image endpoint: accepts url, w (width), q (quality) params - Remark plugin rewrites image URLs to point through optimization endpoint - Image component and SSR preload use optimized URLs - SVGs pass through unchanged, sharp externalized for Nitro bundling - Allowlisted widths prevent cache flooding Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Warning Rate limit exceeded
You’ve run out of usage credits. Purchase more in the billing tab. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (11)
✨ Finishing Touches🧪 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 |
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Serve AVIF, WebP, or original format based on browser Accept header. AVIF preferred > WebP > original (resized only). Adds Vary: Accept for proper CDN caching. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Portable across deployment targets (filesystem, KV, Redis, etc.). Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Persists optimized images to .cache/images/ on disk. Survives server restarts. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Move useStorage before early returns to satisfy Biome hook ordering rule. Replace magic status numbers with StatusCodes constants. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Replace magic numbers 1024 and 75 with named constants from image-utils.ts, used across all image optimization call sites. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
24 tests covering negotiateFormat, cacheKey, MIME mapping, isLocalImage, isSvg, buildOptimizedUrl, and constants. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
rohanchkrabrty
left a comment
There was a problem hiding this comment.
- .cache/images/ has no size cap or eviction — disk usage grows forever as content changes.
- q accepts any 1–100; restrict to a few steps like [60, 75, 90], otherwise each image can produce 2,100 cache entries (100 × 3 formats × 7
widths). - Cache stampede in image.ts:86-99 — two concurrent requests for the same uncached variant both run sharp. Coalesce in-flight work with a
Map<key, Promise>. - cacheKey in image.ts:29-32 ignores mtime, so replacing an image at the same path keeps serving the old bytes. Mix fs.stat(filePath).mtimeMs
into the hash. - The remark plugin rewrites every
to /api/image?…, so builds deployed to S3, Cloudflare Pages static, or GitHub Pages 404 on images.
Either pre-generate variants at build time, or skip the rewrite when the preset has no Node runtime.
- Restrict quality to allowed steps [60, 75, 90, 100] with snap-to-nearest - Add cache stampede protection via in-flight promise map - Add LRU cache eviction with 500 entry cap - Skip image optimization rewrite for static build presets - Accept optimize option in remark-resolve-images plugin Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Review fixes applied
|
Summary
/api/imageendpoint that resizes + converts content images to WebP via sharp on first requestChanges
src/server/api/image.ts— new optimization endpoint with disk caching, allowlisted widths, SVG passthroughsrc/lib/image-utils.ts— shared helpers (buildOptimizedUrl,isLocalImage,isSvg)src/lib/remark-resolve-images.ts— rewrite resolved image URLs through/api/imagesrc/components/mdx/image.tsx— route local images through optimization endpointsrc/server/vite-config.ts— externalize sharp for Nitro bundlingsrc/server/entry-server.tsx— preload optimized URLs, deduplicatesrc/lib/page-context.tsx— client preload uses optimized URLsTest plan
/api/image?url=/_content/...&w=640&q=75returns 200 with WebP🤖 Generated with Claude Code