Commit Graph

15 Commits

Author SHA1 Message Date
James Anderson 4bc2dd3235 fix(image): match App Router deploy parity (#2310)
* fix(image): restore app router deploy parity

* test(image): expect configured optimizer widths

* fix(image): match responsive width selection
2026-06-26 20:36:27 +01:00
James Anderson eeac5db0a3 fix(image): preserve unoptimized remote URLs (#2139)
* fix(image): preserve unoptimized remote URLs

* fix(image): honor overrideSrc when unoptimized
2026-06-18 10:49:42 +01:00
James Anderson 76de660c20 fix(image): emit static imports as managed assets (#2040)
* fix(image): emit static imports as managed assets

* fix(image): handle static asset rebuilds and caching

* fix(image): scope managed media hash etags

* test(image): escape all URL ampersands
2026-06-15 23:04:40 +01:00
James Anderson 61cf2e78f5 fix(image): emit /_next/image URLs (#1562)
* fix(image): emit /_next/image URLs to match Next.js

Closes #1513

The default image loader and optimization endpoint switched from the
vinext-specific /_vinext/image path to Next.js's canonical /_next/image.
This unblocks the deploy suite tests that import Next.js's expected
URL shape (/_next/image?url=...&w=...&q=...).

* refactor(image): use IMAGE_OPTIMIZATION_PATH constant at remaining call sites

Replace hardcoded "/_next/image" strings in index.ts, app-rsc-handler.ts,
and the generated worker entry templates in deploy.ts with the
IMAGE_OPTIMIZATION_PATH constant from server/image-optimization, matching
the pattern already used in prod-server.ts. Prevents future drift if the
path ever changes again.

* feat(image): accept both /_next/image and /_vinext/image at the optimizer

Add a VINEXT_IMAGE_OPTIMIZATION_PATH constant and an
isImageOptimizationPath() helper, then route through every match site
(prod-server, dev server passthrough, app RSC handler, generated worker
templates, and shipped example workers). Apps that wire image URLs to
either prefix now hit the same handler; new URLs are still emitted via
IMAGE_OPTIMIZATION_PATH.
2026-05-26 10:28:22 +01:00
Nathan Nguyen 8dfd6c8ede fix(image): preserve inline styles on remote images (#1287)
* fix(image): preserve inline styles on remote images

Remote next/image renders with explicit width and height delegate through UnpicImage, but that branch dropped the user style prop. This made remote images ignore inline styling such as borderRadius, objectPosition, and transform while local images and fill images preserved it.

The shim now passes style through the remote constrained Unpic boundary and isolates the Unpic type gap where the runtime supports style but the public type omits it. A focused SSR regression test covers the rendered remote image style contract.

* refactor(image): avoid remote style type assertion

The previous fix isolated an assertion around @unpic/react because the runtime forwards style but the public component type omits it.

Keep the existing dependency shape and pass the runtime-supported style prop through a typed spread object instead. The remote image style regression coverage remains unchanged.
2026-05-18 10:44:31 +01:00
Nathan Nguyen 1cd7255881 fix(image): emit preload hints for priority images (#1266)
Priority images currently only render eager loading and high fetch priority. That misses the resource hint Next.js emits, so the browser cannot discover the optimized image until it reaches the img element.

The image shim now calls ReactDOM.preload with the final optimized image URL, srcset, sizes, and fetch priority metadata for priority and preload images. Focused image component coverage asserts the SSR preload hint and the modern preload prop behaviour.

Co-authored-by: James <james@eli.cx>
2026-05-16 19:12:28 +00:00
Nathan Nguyen aec23e3b68 fix(image): preserve fill positioning for remote images (#1265)
Remote Image with fill=true currently routes through Unpic fullWidth, which renders a flowing image without data-nimg="fill" or absolute positioning. That violates Next.js's fill invariant for parent-filling layouts.

Centralize fill styles and render remote fill with the same DOM contract as local fill and getImageProps. Add SSR regression coverage for remote fill.
2026-05-16 19:29:15 +01:00
James Anderson d9906b94d6 chore: add basic vinext site (#1236)
* chore: scaffold new next app with vinext

* basic app

* add to deploy workflow

* fmt
2026-05-15 13:41:10 +00:00
Divanshu Chauhan (divkix) 3a9046dce2 fix(image): support images.dangerouslyAllowLocalIP for private-IP rejections (#1153)
* fix(image): support images.dangerouslyAllowLocalIP for private-IP rejections

Ports Next.js commit 5452439f3db2a78967178ca4180b27fb48393a19 (PR #91686)
to vinext's next/image shim and server-side image config.

Changes:
- Add isPrivateIp() helper in shims/image-config.ts (IPv4 + IPv6, no deps)
- Update validateRemoteUrl in shims/image.tsx to reject literal private-IP
  hostnames unless dangerouslyAllowLocalIP is true
- Wire dangerouslyAllowLocalIP through next-config type, Vite define,
  global.d.ts, and server-side ImageConfig
- Add unit tests for isPrivateIp and component-level private-IP guard

Closes #1065

* fix(image): use ipaddr.js for correct private-IP classification

Addresses review feedback on the SSRF guard:
- Replaces the hand-rolled isPrivateIp with ipaddr.js (same library Next.js
  uses), eliminating bugs in IPv4/IPv6 range checks
- Fixes incorrect 2001:2f::/32 check (not a real range) and adds all missing
  non-unicast ranges: CGNAT, multicast, reserved, benchmarking, teredo,
  documentation, discard, NAT64
- Correctly handles ::ffff: IPv4-mapped addresses via isIPv4MappedAddress()
- Adds explanatory comments for best-effort client-side guard and
  dangerouslyAllowLocalIP server-side config
- Adds comments in tests explaining the vi.resetModules() pattern

* fix(image): externalize ipaddr.js to prevent SSR crash

ipaddr.js is a CJS UMD module that assigns to `this.ipaddr` at the
bottom of its wrapper. When Vite's SSR module runner loads it with
`noExternal: true`, the UMD wrapper executes with `this = undefined`
(strict mode in ESM context), producing:

  TypeError: Cannot set properties of undefined (setting 'ipaddr')

This crashed every page render that imports `next/image` (via the
image-config.ts shim). The image-optimization-parity tests failed
with 500 errors.

Fix: add `ipaddr.js` to `ssr.external` in all 3 Vite environment
configs (top-level, RSC, SSR) so Node's native loader handles the
CJS module correctly.
2026-05-11 13:21:02 +01:00
Divanshu Chauhan (divkix) 8926edbbad fix: next/image only fire onError once per src per mount (#990) (#1004)
* fix: next/image only fire onError and onLoad once per src per mount (#990)

Use ref-based dedup to prevent onLoad and onError from firing multiple
times for the same image src across React re-renders. Ported from the
upstream Next.js fix in vercel/next.js#93209.

- Destructure onError from props (previously fell through rest spread)
- Add lastLoadedSrcRef/lastErrorSrcRef to guard against duplicate fires
- Pass onError/onLoad to all 4 render paths (was missing from UnpicImage)
- Add SSR tests covering all render paths and verifying no DOM attribute leaks

The dedup prevents infinite re-render loops when user code calls setState
inside onError/onLoad, matching Next.js behavior.

* fix: add "use client" directive to next/image shim

useRef is a hook requiring client rendering context. Without "use client", RSC executes useRef() and throws "useRef is not a function", breaking create-next-app CI.

* fix: move resolveImageSource above handler closures for readability
2026-05-02 17:50:31 +01:00
James Anderson 364e36bcd8 fix(image): strip priority prop before forwarding to UnpicImage to prevent DOM leak (#662)
* fix(image): strip priority prop before forwarding to UnpicImage to prevent DOM leak

The `priority` prop is a Next.js-specific concept that must never reach the
DOM as an attribute. On the two UnpicImage render paths (remote URL with fill
and remote URL with width+height), `priority={true}` was forwarded directly
to `@unpic/react`'s Image component which did not reliably strip it before
rendering the DOM `<img>`, triggering:

  Received `true` for a non-boolean attribute `priority`.

Fix: replace `priority={priority}` with the equivalent HTML semantics —
`loading={priority ? 'eager' : loading ?? 'lazy'}` and
`fetchPriority={priority ? 'high' : undefined}` — matching what the local
image and custom-loader paths already do correctly.

Adds 10 reproduction tests covering both affected render paths.

* test: add 50ms slack to compressed streaming timing assertion to fix CI flakiness
2026-03-23 07:54:38 +00:00
Stephen Zhou c17d6941be chore: migrate to vite plus (#535)
* chore: migrate to vite plus

* Disable typeAware and typeCheck

* Update CI

* Fix CI

* Fix test

* Clean

* Run test with vp

* Try revert

* react: false In test

* Fix test

* Revert "Try revert"

This reverts commit 009da10473.

* Update

* Update

* Try revert ci changes

* revert

* Run vp migrate

* Disable typeAware and typeCheck for now

* Better resolve for test

* Use vp dev instead of vite

* Update expect

* Fix NormalizeManifestModuleId

* Try increase timeout

* Update to use vp

* Try new check

* Bring back npx vp

* Migrate CI

* Make next-intl resolvable

* Update

* Update

* Update
2026-03-15 10:50:13 +00:00
Nathan Nguyen eaade0ff71 fix: support onLoadingComplete in modern next/image shim (#496)
* fix: support onLoadingComplete in modern next/image shim

Add the deprecated-but-still-supported onLoadingComplete prop to the
modern next/image component, matching Next.js behavior where the
callback receives the underlying HTMLImageElement on load.

- Add onLoadingComplete to ImageProps interface and type declarations
- Wire handleLoad into all render paths (local, custom loader, remote)
- Destructure onLoad/onLoadingComplete in getImageProps to prevent leak

* fix: correct callback order and add remote URL test

- Swap onLoad/onLoadingComplete invocation order to match Next.js
  (onLoad fires first, then onLoadingComplete)
- Fix stale comment that described legacy { naturalWidth, naturalHeight }
- Add remote URL (UnpicImage) SSR test for full path coverage
2026-03-12 12:44:56 +00:00
James Anderson 764a496ce7 add oxfmt formatter (#380)
* add oxfmt formatter: config, scripts, CI, editor setup, docs

* rebuild lockfile

* fix: add Format to required checks list, remove dead ignore pattern

* run fmt

* add format to agents.md again
2026-03-09 14:56:14 +00:00
Steve Faulkner 12fea722b6 Initial public release of vinext 2026-02-24 09:29:39 -06:00