[PR #364] [CLOSED] feat(image): build-time image optimization via Sharp #513

Closed
opened 2026-05-06 13:08:29 +02:00 by BreizhHardware · 0 comments

📋 Pull Request Information

Original PR: https://github.com/cloudflare/vinext/pull/364
Author: @Divkix
Created: 3/9/2026
Status: Closed

Base: mainHead: feat/image-optimization


📝 Commits (10+)

  • 0c0103c feat(image): add build-time image optimization via Sharp
  • 7cc2890 fix: remove static sharp type imports to fix CI typecheck
  • d002b7f refactor(image): use manifest-based optimization matching Next.js API
  • 39ba58c docs: update documentation to reflect build-time image optimization
  • 5cb1682 test(image): add isSafeImageContentType, manifest lookup, and parseImageParams edge case tests
  • 3a18927 test(image): add comprehensive handleImageOptimization and parseImageParams tests
  • 87a9f0d test(image): add 29 tests for security, parity, and robustness gaps
  • 0abe485 feat(image): add detectContentType, bypass types, and validation parity with Next.js
  • 5a0cccc feat(image): add AVIF quality offset and Content-Disposition filename
  • ebeb774 feat(image): add overrideSrc, data:image/* placeholder, deprecated props, blurWidth/blurHeight

📊 Changes

17 files changed (+4408 additions, -257 deletions)

View changed files

📝 .agents/skills/migrate-to-vinext/SKILL.md (+9 -9)
📝 .agents/skills/migrate-to-vinext/references/compatibility.md (+26 -26)
📝 .agents/skills/migrate-to-vinext/references/troubleshooting.md (+9 -9)
📝 AGENTS.md (+10 -8)
📝 README.md (+31 -31)
📝 packages/vinext/package.json (+6 -0)
📝 packages/vinext/src/config/next-config.ts (+2 -0)
📝 packages/vinext/src/entries/app-rsc-entry.ts (+53 -1)
📝 packages/vinext/src/index.ts (+333 -26)
📝 packages/vinext/src/server/image-optimization.ts (+514 -41)
📝 packages/vinext/src/server/prod-server.ts (+205 -3)
📝 packages/vinext/src/shims/image.tsx (+163 -63)
packages/vinext/src/utils/sharp.ts (+15 -0)
📝 tests/__snapshots__/entry-templates.test.ts.snap (+258 -6)
📝 tests/image-component.test.ts (+395 -19)
tests/image-optimization.test.ts (+2362 -0)
📝 tests/shims.test.ts (+17 -15)

📄 Description

Summary

  • Adds optional Sharp-based image optimization for local images across all server modes (dev, build, Node.js prod)
  • Build mode: Pre-generates .webp at multiple responsive widths via ?vinext-opt virtual modules. Static imports get optimizedSrcSet with direct asset URLs instead of /_vinext/image runtime routing
  • Dev mode: Real-time Sharp resize + WebP with in-memory LRU cache (500 entries, 60s Cache-Control) replacing the 302 redirect
  • Node.js prod: On-demand optimization with filesystem cache (.vinext-image-cache/)
  • Sharp is optional — when not installed, behavior is identical to before

Edge cases handled

  • SVG passthrough (vector, no benefit from raster optimization)
  • Animated GIF detection via metadata.pages > 1
  • Upscale prevention (no widths > 2× original)
  • Graceful fallback on Sharp failure or absence

Files changed

File Change
packages/vinext/src/utils/sharp.ts NEW — lazy Sharp detection utility
packages/vinext/src/shims/image.tsx Extended StaticImageData with optimizedSrcSet, updated rendering
packages/vinext/src/index.ts ?vinext-opt resolver/loader, build-time transform, dev server handler
packages/vinext/src/server/app-dev-server.ts App Router dev Sharp optimization
packages/vinext/src/server/prod-server.ts App + Pages Router prod Sharp optimization
packages/vinext/package.json sharp >=0.33.0 as optional peer dep
tests/image-optimization.test.ts NEW — Sharp detection + build-time transform tests
tests/image-component.test.ts Extended with optimizedSrcSet rendering tests

Test plan

  • pnpm test tests/image-component.test.ts tests/image-imports.test.ts tests/image-optimization.test.ts — 60 tests pass
  • Dev mode: pnpm dev in fixture, verify /_vinext/image returns image/webp Content-Type
  • Build: verify .webp files in dist/client/assets/
  • Built HTML: srcSet points to static .webp assets, not /_vinext/image URLs
  • Without sharp installed: build works, original images served unchanged
  • pnpm run typecheck and pnpm run lint pass in CI

Closes #363


🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/cloudflare/vinext/pull/364 **Author:** [@Divkix](https://github.com/Divkix) **Created:** 3/9/2026 **Status:** ❌ Closed **Base:** `main` ← **Head:** `feat/image-optimization` --- ### 📝 Commits (10+) - [`0c0103c`](https://github.com/cloudflare/vinext/commit/0c0103cc912b1d464763550d604417b5960fcc58) feat(image): add build-time image optimization via Sharp - [`7cc2890`](https://github.com/cloudflare/vinext/commit/7cc2890cd39ff91aaef9d4306b9f67639483db2b) fix: remove static sharp type imports to fix CI typecheck - [`d002b7f`](https://github.com/cloudflare/vinext/commit/d002b7fb63513571e38c144f226bb45457c56414) refactor(image): use manifest-based optimization matching Next.js API - [`39ba58c`](https://github.com/cloudflare/vinext/commit/39ba58c9866875a517c71b8423c1ba379266b927) docs: update documentation to reflect build-time image optimization - [`5cb1682`](https://github.com/cloudflare/vinext/commit/5cb168224d439d951e1ef9289d1b2777a083278a) test(image): add isSafeImageContentType, manifest lookup, and parseImageParams edge case tests - [`3a18927`](https://github.com/cloudflare/vinext/commit/3a189271b47e4c764715ad04971d9dfe0bd12422) test(image): add comprehensive handleImageOptimization and parseImageParams tests - [`87a9f0d`](https://github.com/cloudflare/vinext/commit/87a9f0d472e33fd92fb5ba9b4cdbd4ddd01c49a5) test(image): add 29 tests for security, parity, and robustness gaps - [`0abe485`](https://github.com/cloudflare/vinext/commit/0abe48565e7fbeb4eeafee44186120f48537e451) feat(image): add detectContentType, bypass types, and validation parity with Next.js - [`5a0cccc`](https://github.com/cloudflare/vinext/commit/5a0cccc8c40facfb7d09177abca0c95e94bbed52) feat(image): add AVIF quality offset and Content-Disposition filename - [`ebeb774`](https://github.com/cloudflare/vinext/commit/ebeb774992ce214581cc12e447232c63da7fb1b6) feat(image): add overrideSrc, data:image/* placeholder, deprecated props, blurWidth/blurHeight ### 📊 Changes **17 files changed** (+4408 additions, -257 deletions) <details> <summary>View changed files</summary> 📝 `.agents/skills/migrate-to-vinext/SKILL.md` (+9 -9) 📝 `.agents/skills/migrate-to-vinext/references/compatibility.md` (+26 -26) 📝 `.agents/skills/migrate-to-vinext/references/troubleshooting.md` (+9 -9) 📝 `AGENTS.md` (+10 -8) 📝 `README.md` (+31 -31) 📝 `packages/vinext/package.json` (+6 -0) 📝 `packages/vinext/src/config/next-config.ts` (+2 -0) 📝 `packages/vinext/src/entries/app-rsc-entry.ts` (+53 -1) 📝 `packages/vinext/src/index.ts` (+333 -26) 📝 `packages/vinext/src/server/image-optimization.ts` (+514 -41) 📝 `packages/vinext/src/server/prod-server.ts` (+205 -3) 📝 `packages/vinext/src/shims/image.tsx` (+163 -63) ➕ `packages/vinext/src/utils/sharp.ts` (+15 -0) 📝 `tests/__snapshots__/entry-templates.test.ts.snap` (+258 -6) 📝 `tests/image-component.test.ts` (+395 -19) ➕ `tests/image-optimization.test.ts` (+2362 -0) 📝 `tests/shims.test.ts` (+17 -15) </details> ### 📄 Description ## Summary - Adds optional Sharp-based image optimization for local images across all server modes (dev, build, Node.js prod) - **Build mode**: Pre-generates `.webp` at multiple responsive widths via `?vinext-opt` virtual modules. Static imports get `optimizedSrcSet` with direct asset URLs instead of `/_vinext/image` runtime routing - **Dev mode**: Real-time Sharp resize + WebP with in-memory LRU cache (500 entries, 60s Cache-Control) replacing the 302 redirect - **Node.js prod**: On-demand optimization with filesystem cache (`.vinext-image-cache/`) - **Sharp is optional** — when not installed, behavior is identical to before ### Edge cases handled - SVG passthrough (vector, no benefit from raster optimization) - Animated GIF detection via `metadata.pages > 1` - Upscale prevention (no widths > 2× original) - Graceful fallback on Sharp failure or absence ### Files changed | File | Change | |------|--------| | `packages/vinext/src/utils/sharp.ts` | **NEW** — lazy Sharp detection utility | | `packages/vinext/src/shims/image.tsx` | Extended `StaticImageData` with `optimizedSrcSet`, updated rendering | | `packages/vinext/src/index.ts` | `?vinext-opt` resolver/loader, build-time transform, dev server handler | | `packages/vinext/src/server/app-dev-server.ts` | App Router dev Sharp optimization | | `packages/vinext/src/server/prod-server.ts` | App + Pages Router prod Sharp optimization | | `packages/vinext/package.json` | `sharp >=0.33.0` as optional peer dep | | `tests/image-optimization.test.ts` | **NEW** — Sharp detection + build-time transform tests | | `tests/image-component.test.ts` | Extended with `optimizedSrcSet` rendering tests | ## Test plan - [x] `pnpm test tests/image-component.test.ts tests/image-imports.test.ts tests/image-optimization.test.ts` — 60 tests pass - [ ] Dev mode: `pnpm dev` in fixture, verify `/_vinext/image` returns `image/webp` Content-Type - [ ] Build: verify `.webp` files in `dist/client/assets/` - [ ] Built HTML: srcSet points to static `.webp` assets, not `/_vinext/image` URLs - [ ] Without sharp installed: build works, original images served unchanged - [ ] `pnpm run typecheck` and `pnpm run lint` pass in CI Closes #363 --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
BreizhHardware 2026-05-06 13:08:29 +02:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/vinext#513
No description provided.