Skip to content

feat(sanity): support absolute URLs with project and dataset extraction#2274

Open
hacknug wants to merge 4 commits into
nuxt:mainfrom
hacknug:fix-sanity
Open

feat(sanity): support absolute URLs with project and dataset extraction#2274
hacknug wants to merge 4 commits into
nuxt:mainfrom
hacknug:fix-sanity

Conversation

@hacknug

@hacknug hacknug commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

🔗 Linked issue

Closes #1297

📚 Description

Summary

Absolute URLs from Sanity's CDN can now be passed directly as the image source. The provider will now extract projectId and dataset from the URL but they can still be overridden via options if necessary (not sure why but accounted for).

Changes

  • Extracts projectId, dataset and filename from the URL path when src is an absolute URL
  • Uses a fallback chain: provided options > extracted from URL > provider defaults ensuring explicit config always wins
  • Handles falsy options (e.g. projectId: '') correctly, falling back to the URL value with test to prevent future regresisons
  • All existing non-URL usage remains unchanged (as long as it had tests)

Backwards-compatible change. Existing configurations with projectId and asset IDs will work exactly as before.

Notes

  • I left @ts-expect-error comments on the tests in order to not change the type signature of SanityOptions
  • I commited an updated revision of .nuxtrc but I can revert or delete it from the repo (precedent 8341b16)

I'm really sorry for the extra bytes 🙏 .

Hope someone from e18n can shave at least twice as many from any of our deps 🤞

Seems like there was a missmatch between my machine and CI so it wasn't necessary to make any changes to the snapshot and I technically didn't make things worse 😈

Absolute URLs from Sanity's CDN can now be passed directly as the image source; `projectId` and `dataset` are extracted from the URL and may still be overridden via options.
@hacknug hacknug requested a review from danielroe as a code owner July 1, 2026 22:29
Copilot AI review requested due to automatic review settings July 1, 2026 22:29
@pkg-pr-new

pkg-pr-new Bot commented Jul 1, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/@nuxt/image@2274

commit: 55de670

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds support in the Sanity provider for using absolute Sanity CDN URLs as src, extracting projectId/dataset from the URL path while preserving existing behavior for non-URL inputs.

Changes:

  • Sanity provider now detects absolute URLs and extracts projectId, dataset, and filename from the URL path.
  • Adds provider tests for absolute Sanity CDN URL handling and option override/fallback behavior (including falsy options).
  • Updates bundle-size snapshot and bumps @nuxt/test-utils patch version in .nuxtrc.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

File Description
src/runtime/providers/sanity.ts Adds absolute-URL parsing and fallback chain for project/dataset when building Sanity image URLs.
test/nuxt/providers.test.ts Adds test coverage for absolute Sanity CDN URLs and override/fallback cases.
test/unit/bundle.test.ts Updates bundle-size inline snapshot due to runtime code changes.
.nuxtrc Bumps @nuxt/test-utils setup version.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/runtime/providers/sanity.ts Outdated
Comment thread test/nuxt/providers.test.ts Outdated
@coderabbitai

coderabbitai Bot commented Jul 1, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This PR updates the Sanity image provider to detect absolute CDN URLs, extract project and dataset segments, and rebuild the final image URL with merged query parameters. It adds tests for absolute URL override behavior and existing query strings. Separately, it bumps the Nuxt test-utils version and updates one bundle-size snapshot.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Changes

Cohort / File(s) Summary
Sanity absolute URL handling src/runtime/providers/sanity.ts, test/nuxt/providers.test.ts Parses absolute Sanity URLs, rebuilds the final image URL with resolved project/dataset values, and adds test coverage for overrides and query merging.
Maintenance updates .nuxtrc, test/unit/bundle.test.ts Bumps the Nuxt test-utils version and updates the bundle size snapshot.

Sequence Diagram(s)

Not applicable.

Related issues: Fixes broken image path generation when passing an absolute Sanity CDN URL to useImage() with the Sanity provider (#1297).

Suggested labels: bug, provider:sanity

Suggested reviewers: danielroe, farnabaz

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning The .nuxtrc dependency version bump is unrelated to the Sanity URL fix and appears out of scope. Remove the .nuxtrc version bump unless it is required for the fix and document why it belongs in this PR.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: absolute Sanity URL support with project and dataset extraction.
Linked Issues check ✅ Passed The PR fixes #1297 by preserving absolute Sanity image paths and appending transformations correctly.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Description check ✅ Passed The description matches the code changes, describing absolute Sanity CDN URL support, option precedence, and related tests.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (2)
src/runtime/providers/sanity.ts (1)

107-114: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low value

Dead computation and potential double ? for protocol inputs.

parts/format (lines 107-108) are computed unconditionally by splitting the entire absolute URL on -, but are unused whenever hasProtocol(src) is true — wasted work with no functional impact.

Separately, _src for the protocol branch is derived via src.split('/').slice(-3), which does not strip any existing query string from the last path segment. If an absolute src already carries a query (e.g. ...file.png?dl), filenameAndQueries would produce a malformed double-? URL (file.png?dl?h=10...). Not exercised by the new tests, but worth guarding against if absolute URLs with pre-existing queries are a realistic input.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/runtime/providers/sanity.ts` around lines 107 - 114, In the URL builder
inside sanity.ts, avoid splitting the full src into parts/format unless it is
actually needed for the non-protocol branch, and make sure the protocol branch
strips any existing query string from _src before appending operations. Update
the filenameAndQueries construction in the hasProtocol(src) path so joinURL
never receives a value that already contains a query, preventing double-? when
src includes an existing query string.
test/nuxt/providers.test.ts (1)

492-511: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Assertions match the reviewed provider logic; consider adding crop/hotspot coverage.

All four assertions (baseline extraction, projectId override, dataset override, empty-string inherit) correctly match the fallback chain in sanity.ts. However, none of the cases exercise modifiers.crop/hotspot, which is where the getMetadata regex mismatch (flagged in src/runtime/providers/sanity.ts lines 80-82) actually breaks for absolute URLs. Adding a case with a percentage crop against an absolute URL would catch that regression.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/nuxt/providers.test.ts` around lines 492 - 511, Add a regression case in
the sanity provider tests that exercises `modifiers.crop` (and optionally
`hotspot`) with an absolute URL, since the current `sanity().getImage`
assertions only cover height/blur/quality and miss the `getMetadata` regex path
in `src/runtime/providers/sanity.ts`. Extend the existing `sanity` test block to
include an absolute-image URL with percentage-based crop data so the provider
behavior is verified when metadata parsing is involved, especially around the
`getMetadata` and `getImage` flow.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/runtime/providers/sanity.ts`:
- Around line 80-82: `getMetadata` in `src/runtime/providers/sanity.ts` only
recognizes legacy hyphen-delimited filenames, so absolute CDN URLs like
`...-1552x982.png` are parsed as invalid and skip crop/hotspot handling. Update
`getMetadata` to support both format suffix styles (hyphen and dot) and ensure
`getImage` still receives valid `sourceWidth`/`sourceHeight` for absolute URLs
so `modifiers.crop` is converted before `operationsGenerator` runs. Add a
regression test around `getImage`/`getMetadata` using an absolute Sanity URL
with crop or hotspot modifiers.

In `@test/unit/bundle.test.ts`:
- Line 24: The inline snapshot in the bundle size assertion is out of sync with
the current measured output. Update the expectation in the bundle size test by
regenerating the snapshot or changing the value in the roundToKilobytes
assertion to match the current bundle delta reported by CI, using the existing
withImage and withoutImage test setup as the reference point.

---

Nitpick comments:
In `@src/runtime/providers/sanity.ts`:
- Around line 107-114: In the URL builder inside sanity.ts, avoid splitting the
full src into parts/format unless it is actually needed for the non-protocol
branch, and make sure the protocol branch strips any existing query string from
_src before appending operations. Update the filenameAndQueries construction in
the hasProtocol(src) path so joinURL never receives a value that already
contains a query, preventing double-? when src includes an existing query
string.

In `@test/nuxt/providers.test.ts`:
- Around line 492-511: Add a regression case in the sanity provider tests that
exercises `modifiers.crop` (and optionally `hotspot`) with an absolute URL,
since the current `sanity().getImage` assertions only cover height/blur/quality
and miss the `getMetadata` regex path in `src/runtime/providers/sanity.ts`.
Extend the existing `sanity` test block to include an absolute-image URL with
percentage-based crop data so the provider behavior is verified when metadata
parsing is involved, especially around the `getMetadata` and `getImage` flow.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 1d57287a-d8d7-46cc-a39f-0b75a37ed756

📥 Commits

Reviewing files that changed from the base of the PR and between 458b9ac and 4043597.

📒 Files selected for processing (4)
  • .nuxtrc
  • src/runtime/providers/sanity.ts
  • test/nuxt/providers.test.ts
  • test/unit/bundle.test.ts

Comment thread src/runtime/providers/sanity.ts Outdated
Comment on lines +80 to +82
getImage: (src, { modifiers, projectId, dataset, baseURL = sanityCDN }) => {
const [_projectId, _dataset, _src] = hasProtocol(src) ? src.split('/').slice(-3) : [projectId, dataset, src]
const { height: sourceHeight, width: sourceWidth } = getMetadata(_src)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

getMetadata regex never matches absolute-URL filenames, silently breaking crop/hotspot for the new feature.

getMetadata (line 45) matches /-(?<width>\d*)x(?<height>\d*)-(?<format>.*)$/, which requires a second hyphen before the format token (matches the legacy asset-ID format like image-<hash>-300x450-jpg). Real Sanity CDN filenames extracted from an absolute URL use a dot before the extension instead (e.g. 228cc6aa...-1552x982.png — the PR's own example). That string has exactly one hyphen, so the regex never matches, getMetadata(_src) always returns { width: undefined, height: undefined, format: undefined }, and a spurious "invalid image asset ID" dev warning fires on every absolute-URL request.

Consequences:

  • The crop-conversion block (lines 83-90) is guarded by sourceWidth && sourceHeight, so it's silently skipped — modifiers.crop survives as an object and gets passed to operationsGenerator, which will stringify it (String(value)) into a broken crop=[object Object] query param.
  • Percentage-based crop/hotspot transforms are effectively non-functional for the primary new use case (absolute CDN URLs), with no error surfaced to the caller besides a misleading dev console warning.
🐛 Proposed fix: accept both hyphen- and dot-delimited format suffixes
 const getMetadata = (id: string) => {
-  const result = id.match(/-(?<width>\d*)x(?<height>\d*)-(?<format>.*)$/)
+  const result = id.match(/-(?<width>\d*)x(?<height>\d*)[-.](?<format>.*)$/)

Consider also adding a test with modifiers.crop/hotspot against an absolute URL to catch this regression.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
getImage: (src, { modifiers, projectId, dataset, baseURL = sanityCDN }) => {
const [_projectId, _dataset, _src] = hasProtocol(src) ? src.split('/').slice(-3) : [projectId, dataset, src]
const { height: sourceHeight, width: sourceWidth } = getMetadata(_src)
const getMetadata = (id: string) => {
const result = id.match(/-(?<width>\d*)x(?<height>\d*)[-.](?<format>.*)$/)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/runtime/providers/sanity.ts` around lines 80 - 82, `getMetadata` in
`src/runtime/providers/sanity.ts` only recognizes legacy hyphen-delimited
filenames, so absolute CDN URLs like `...-1552x982.png` are parsed as invalid
and skip crop/hotspot handling. Update `getMetadata` to support both format
suffix styles (hyphen and dot) and ensure `getImage` still receives valid
`sourceWidth`/`sourceHeight` for absolute URLs so `modifiers.crop` is converted
before `operationsGenerator` runs. Add a regression test around
`getImage`/`getMetadata` using an absolute Sanity URL with crop or hotspot
modifiers.

Comment thread test/unit/bundle.test.ts Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
test/nuxt/providers.test.ts (1)

516-533: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Model absolute Sanity URLs in the provider type instead of suppressing the calls. These cases need @ts-expect-error because SanityOptions.projectId is still required. If absolute URLs are meant to infer projectId/dataset, encode that in the provider options type so callers don’t need a type suppression at each site.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/nuxt/providers.test.ts` around lines 516 - 533, Update the Sanity
provider typing so absolute image URLs can be passed to getImage without
suppressing type errors in the tests. The issue is that SanityOptions still
requires projectId even when the URL already contains the project/dataset, so
callers must use `@ts-expect-error`. Adjust the provider option types and any
related getImage/SanityOptions definitions to model absolute URLs as a valid
case that infers or bypasses projectId/dataset requirements, then remove the
suppressions from the sanity().getImage test cases.
src/runtime/providers/sanity.ts (1)

64-67: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider making projectId optional now that URL-derived values are supported.

The interface still requires projectId: string, but the new fallback logic (projectId || _projectId) is explicitly designed to let a falsy/empty projectId defer to the value extracted from an absolute URL. Marking it projectId?: string would better reflect the new supported usage and avoid forcing consumers to pass an empty-string placeholder.

♻️ Suggested change
 interface SanityOptions {
-  projectId: string
+  projectId?: string
   dataset?: string
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/runtime/providers/sanity.ts` around lines 64 - 67, The Sanity options
type still requires projectId even though the fallback logic in the Sanity
provider is designed to use a URL-derived value when projectId is falsy. Update
the SanityOptions interface to make projectId optional, and make sure the
provider code that reads projectId || _projectId in the Sanity runtime still
handles both direct and URL-derived cases cleanly without forcing callers to
pass an empty placeholder.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/runtime/providers/sanity.ts`:
- Around line 82-84: The destructured `_id` in `src/runtime/providers/sanity.ts`
can be `string | undefined`, but `getMetadata` requires a definite string.
Update the `isAbsolute` parsing around the `_segment`/`_id` assignment and the
`getMetadata(_id)` call so the code narrows or validates the value before use,
handling malformed absolute URLs safely instead of passing a possibly undefined
value.

---

Nitpick comments:
In `@src/runtime/providers/sanity.ts`:
- Around line 64-67: The Sanity options type still requires projectId even
though the fallback logic in the Sanity provider is designed to use a
URL-derived value when projectId is falsy. Update the SanityOptions interface to
make projectId optional, and make sure the provider code that reads projectId ||
_projectId in the Sanity runtime still handles both direct and URL-derived cases
cleanly without forcing callers to pass an empty placeholder.

In `@test/nuxt/providers.test.ts`:
- Around line 516-533: Update the Sanity provider typing so absolute image URLs
can be passed to getImage without suppressing type errors in the tests. The
issue is that SanityOptions still requires projectId even when the URL already
contains the project/dataset, so callers must use `@ts-expect-error`. Adjust the
provider option types and any related getImage/SanityOptions definitions to
model absolute URLs as a valid case that infers or bypasses projectId/dataset
requirements, then remove the suppressions from the sanity().getImage test
cases.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 33e65ce0-e300-4b0c-8f1c-06376b6de86e

📥 Commits

Reviewing files that changed from the base of the PR and between 4043597 and 7923478.

📒 Files selected for processing (2)
  • src/runtime/providers/sanity.ts
  • test/nuxt/providers.test.ts

Comment thread src/runtime/providers/sanity.ts
@hacknug

hacknug commented Jul 2, 2026

Copy link
Copy Markdown
Contributor Author

@danielroe ready for review. I'm having issues running the e2e tests on my machine (can't run them at all) which is why I probably didn't catch the issue with filerobot.

Seems unrelated to these changes since I only touched the sanity provider and tests though 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

useImage() in combination with Sanity provider generates a broken, incorrect image path

2 participants