Skip to content

fix: infer configured providers and their modifiers in useImage#2141

Open
DamianGlowala wants to merge 3 commits intomainfrom
fix/use-image-generics
Open

fix: infer configured providers and their modifiers in useImage#2141
DamianGlowala wants to merge 3 commits intomainfrom
fix/use-image-generics

Conversation

@DamianGlowala
Copy link
Member

@DamianGlowala DamianGlowala commented Feb 26, 2026

🔗 Linked issue

resolves #2036
resolves #2107

📚 Description

Now, list of available providers is correctly autocompleted and there are no type errors when choosing a provider other than ipx. Also, the second argument (modifiers) now inferes available modifiers for a selected provider, so that they are now connected and type-safe.

const img = useImage()

img('<img_src>', { modifiers: <provider_specific> }, { provider: <provider> })

@cloudflare-workers-and-pages
Copy link

cloudflare-workers-and-pages bot commented Feb 26, 2026

Deploying nuxt-image with  Cloudflare Pages  Cloudflare Pages

Latest commit: 92cf582
Status: ✅  Deploy successful!
Preview URL: https://b56fd4c8.nuxt-image.pages.dev
Branch Preview URL: https://fix-use-image-generics.nuxt-image.pages.dev

View logs

@pkg-pr-new
Copy link

pkg-pr-new bot commented Feb 26, 2026

Open in StackBlitz

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

commit: 92cf582

@coderabbitai
Copy link

coderabbitai bot commented Feb 26, 2026

📝 Walkthrough

Walkthrough

Image typings were generalized to be provider-generic and sizes were unified. In src/types/image.ts ImageOptions and Img signatures became generic over a provider type (TProvider), a Sizes alias was added, and modifiers/sizes typings were adjusted to be provider-aware. In src/runtime/image.ts multiple function signatures and call sites were updated to accept or cast to ImageOptions (including getImage, getMeta, getSizes, getVariantSrc, and resolveImage), and optional chaining was applied when spreading presets/modifiers.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'fix: infer configured providers and their modifiers in useImage' directly and clearly describes the main change - improving type inference for configured providers and their modifiers in the useImage API.
Linked Issues check ✅ Passed The PR addresses the core requirement from issue #2036 by making ImageOptions generic with TProvider, updating function signatures to support provider-aware generics, and enabling type-safe provider and modifier inference in useImage.
Out of Scope Changes check ✅ Passed All changes are directly scoped to fixing provider and modifier type inference: generifying ImageOptions, updating public API signatures, and adjusting internal implementations to support provider-aware typing.
Description check ✅ Passed The pull request description clearly relates to the changeset, describing improvements to type inference for providers and modifiers in the useImage API.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/use-image-generics

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 and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/types/image.ts`:
- Line 94: The declared getSizes signature is inconsistent with the runtime
which expects an options object containing a required sizes field
(ImageSizesOptions); update the type signature for getSizes to accept options:
ImageSizesOptions<TProvider> (remove the separate sizes?: string[] parameter) so
it matches the runtime implementation, referencing the existing types
ImageOptions<TProvider>, ImageSizesOptions and the return type ImageSizes.
- Around line 87-95: The getSizes signature is wrong: it declares a separate
optional sizes parameter but the implementation reads sizes from the options
object; update getSizes to accept options?: ImageSizesOptions (i.e. change the
third param into a single options parameter) so its signature becomes getSizes:
<TProvider extends keyof ConfiguredImageProviders = keyof
ConfiguredImageProviders>(source: string, options?: ImageSizesOptions) =>
ImageSizes; while you're here, align the default type parameters between the Img
methods and ImageOptions by using the same default (either keyof
ConfiguredImageProviders or DefaultProvider) so ImageOptions<TProvider> resolves
consistently for methods like getSizes, getImage, getMeta and the
options/modifiers types match at call sites.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 510f2e1 and f40c5a8.

📒 Files selected for processing (2)
  • src/runtime/image.ts
  • src/types/image.ts

Comment on lines +87 to +95
<TProvider extends keyof ConfiguredImageProviders = keyof ConfiguredImageProviders>(
source: string,
modifiers?: ImageOptions<TProvider>['modifiers'],
options?: ImageOptions<TProvider>
): ResolvedImage['url']
options: CreateImageOptions
getImage: (source: string, options?: ImageOptions) => ResolvedImage
getSizes: (source: string, options?: ImageOptions, sizes?: string[]) => ImageSizes
getMeta: (source: string, options?: ImageOptions) => Promise<ImageInfo>
getImage: <TProvider extends keyof ConfiguredImageProviders = keyof ConfiguredImageProviders>(source: string, options?: ImageOptions<TProvider>) => ResolvedImage
getSizes: <TProvider extends keyof ConfiguredImageProviders = keyof ConfiguredImageProviders>(source: string, options?: ImageOptions<TProvider>, sizes?: string[]) => ImageSizes
getMeta: <TProvider extends keyof ConfiguredImageProviders = keyof ConfiguredImageProviders>(source: string, options?: ImageOptions<TProvider>) => Promise<ImageInfo>
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

rg -n "DefaultProvider" --type=ts -C2

Repository: nuxt/image

Length of output: 465


🏁 Script executed:

cat -n src/types/image.ts | head -100

Repository: nuxt/image

Length of output: 3952


🏁 Script executed:

# Search for getSizes implementation and ImageSizesOptions definition
rg -n "getSizes|ImageSizesOptions" --type=ts -C3

Repository: nuxt/image

Length of output: 5711


Fix getSizes type signature and consider aligning default type parameters.

The getSizes method signature is incorrect: it declares sizes as a separate optional parameter, but the runtime implementation expects it as part of the options object (via ImageSizesOptions). The type should be:

getSizes: <TProvider extends keyof ConfiguredImageProviders = keyof ConfiguredImageProviders>(source: string, options?: ImageSizesOptions) => ImageSizes

Additionally, there's an inconsistency in default type parameters: Img interface methods default to keyof ConfiguredImageProviders (all providers), while ImageOptions defaults to DefaultProvider (configured default). This causes ImageOptions<TProvider> to accept different modifiers depending on whether TProvider is inferred or explicitly matched against the method's default. Consider aligning these defaults.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/types/image.ts` around lines 87 - 95, The getSizes signature is wrong: it
declares a separate optional sizes parameter but the implementation reads sizes
from the options object; update getSizes to accept options?: ImageSizesOptions
(i.e. change the third param into a single options parameter) so its signature
becomes getSizes: <TProvider extends keyof ConfiguredImageProviders = keyof
ConfiguredImageProviders>(source: string, options?: ImageSizesOptions) =>
ImageSizes; while you're here, align the default type parameters between the Img
methods and ImageOptions by using the same default (either keyof
ConfiguredImageProviders or DefaultProvider) so ImageOptions<TProvider> resolves
consistently for methods like getSizes, getImage, getMeta and the
options/modifiers types match at call sites.

@codecov-commenter
Copy link

codecov-commenter commented Feb 26, 2026

Codecov Report

❌ Patch coverage is 0% with 9 lines in your changes missing coverage. Please review.
✅ Project coverage is 32.52%. Comparing base (510f2e1) to head (92cf582).

Files with missing lines Patch % Lines
src/runtime/image.ts 0.00% 8 Missing and 1 partial ⚠️
Additional details and impacted files
@@           Coverage Diff           @@
##             main    #2141   +/-   ##
=======================================
  Coverage   32.52%   32.52%           
=======================================
  Files           7        7           
  Lines         372      372           
  Branches      131      131           
=======================================
  Hits          121      121           
  Misses        194      194           
  Partials       57       57           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@DamianGlowala DamianGlowala marked this pull request as draft February 26, 2026 07:53
@DamianGlowala DamianGlowala marked this pull request as ready for review February 26, 2026 07:59
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/runtime/image.ts (1)

235-243: ⚠️ Potential issue | 🟡 Minor

Inconsistent ImageOptions usage—missing type parameter.

All other functions in this file use ImageOptions<any>, but getVariantSrc uses bare ImageOptions. With ImageOptions defaulting to DefaultProvider (which may be never if no default provider is configured), this could cause type mismatches.

Suggested fix for consistency
-function getVariantSrc(ctx: ImageCTX, input: string, variant: ImageSizesVariant, density: number, opts?: ImageOptions) {
+function getVariantSrc(ctx: ImageCTX, input: string, variant: ImageSizesVariant, density: number, opts?: ImageOptions<any>) {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/runtime/image.ts` around lines 235 - 243, The function getVariantSrc uses
the bare ImageOptions type which is inconsistent with the rest of the file and
can default to an unwanted provider type; update its signature and any usage to
use ImageOptions<any> (i.e., change the parameter opts?: ImageOptions to opts?:
ImageOptions<any>) so it matches other functions (refer to getVariantSrc,
ImageCTX, ImageSizesVariant, ctx.$img) and avoids type mismatches with
DefaultProvider.
🧹 Nitpick comments (1)
src/types/image.ts (1)

84-94: Default type parameter inconsistency between Img methods and ImageOptions.

The Img interface methods default TProvider to keyof ConfiguredImageProviders, while ImageOptions defaults to DefaultProvider. When ImageOptions<TProvider> is used without explicit type argument in a method body, it inherits the method's default, but standalone uses of ImageOptions get DefaultProvider. This can cause modifiers to resolve differently depending on inference context.

Consider aligning the defaults—either both use keyof ConfiguredImageProviders or both use DefaultProvider:

Option A: Align ImageOptions default with Img
-export interface ImageOptions<TProvider extends keyof ConfiguredImageProviders = DefaultProvider> {
+export interface ImageOptions<TProvider extends keyof ConfiguredImageProviders = keyof ConfiguredImageProviders> {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/types/image.ts` around lines 84 - 94, The issue is inconsistent default
type parameters between Img methods (which default TProvider to keyof
ConfiguredImageProviders) and ImageOptions (which defaults to DefaultProvider),
causing modifiers and options to infer differently; fix by aligning
defaults—update the generic default on the Img call signature and the
getImage/getSizes/getMeta method signatures to use DefaultProvider instead of
keyof ConfiguredImageProviders so ImageOptions<TProvider> resolves the same
whether inferred or used directly; ensure the modifiers parameter types in the
call signature reflect ImageOptions<DefaultProvider> and run type checks to
confirm no other places rely on the old default.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/runtime/image.ts`:
- Around line 180-185: The code is unsafely casting
opts?.modifiers?.width/height to number which can let string sizes slip through;
replace the direct "as number" casts with calls to the existing parseSize
utility (same one used at line 132) to normalize width/height into numeric
values (fallback to 0 when parseSize returns NaN/undefined), i.e. compute
_cWidth = parseSize(opts?.modifiers?.width) and _cHeight =
parseSize(opts?.modifiers?.height) so that downstream arithmetic on
variant._cWidth and variant._cHeight is safe and consistent with other parsing
logic.

---

Outside diff comments:
In `@src/runtime/image.ts`:
- Around line 235-243: The function getVariantSrc uses the bare ImageOptions
type which is inconsistent with the rest of the file and can default to an
unwanted provider type; update its signature and any usage to use
ImageOptions<any> (i.e., change the parameter opts?: ImageOptions to opts?:
ImageOptions<any>) so it matches other functions (refer to getVariantSrc,
ImageCTX, ImageSizesVariant, ctx.$img) and avoids type mismatches with
DefaultProvider.

---

Nitpick comments:
In `@src/types/image.ts`:
- Around line 84-94: The issue is inconsistent default type parameters between
Img methods (which default TProvider to keyof ConfiguredImageProviders) and
ImageOptions (which defaults to DefaultProvider), causing modifiers and options
to infer differently; fix by aligning defaults—update the generic default on the
Img call signature and the getImage/getSizes/getMeta method signatures to use
DefaultProvider instead of keyof ConfiguredImageProviders so
ImageOptions<TProvider> resolves the same whether inferred or used directly;
ensure the modifiers parameter types in the call signature reflect
ImageOptions<DefaultProvider> and run type checks to confirm no other places
rely on the old default.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f40c5a8 and 80613db.

📒 Files selected for processing (2)
  • src/runtime/image.ts
  • src/types/image.ts

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
src/types/image.ts (1)

28-29: Provider-specific modifiers aren't wrapped in Partial, unlike base modifiers.

The base modifiers use Partial<Omit<...>>, but provider-specific modifiers from ConfiguredImageProviders[TProvider]['modifiers'] are used directly. If a provider declares required modifier properties, they would become required in ImageOptions as well.

If this strictness is intentional, consider documenting it. Otherwise, wrap in Partial for consistency:

♻️ Suggested change for consistency
   modifiers?: Partial<Omit<ImageModifiers, 'format' | 'quality' | 'background' | 'fit'>>
-    & ('modifiers' extends keyof ConfiguredImageProviders[TProvider] ? ConfiguredImageProviders[TProvider]['modifiers'] : Record<string, unknown>)
+    & ('modifiers' extends keyof ConfiguredImageProviders[TProvider] ? Partial<ConfiguredImageProviders[TProvider]['modifiers']> : Record<string, unknown>)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/types/image.ts` around lines 28 - 29, The provider-specific modifiers in
the ImageOptions type are not wrapped in Partial, so required fields declared on
ConfiguredImageProviders[TProvider]['modifiers'] will become required; update
the modifiers union to wrap the provider-specific branch in Partial(...) (i.e.,
Partial<ConfiguredImageProviders[TProvider]['modifiers']>) so provider modifiers
are optional like the base Partial<Omit<ImageModifiers, 'format' | 'quality' |
'background' | 'fit'>>; adjust the type expression referencing modifiers,
ImageModifiers, ConfiguredImageProviders and TProvider accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/types/image.ts`:
- Around line 28-29: The provider-specific modifiers in the ImageOptions type
are not wrapped in Partial, so required fields declared on
ConfiguredImageProviders[TProvider]['modifiers'] will become required; update
the modifiers union to wrap the provider-specific branch in Partial(...) (i.e.,
Partial<ConfiguredImageProviders[TProvider]['modifiers']>) so provider modifiers
are optional like the base Partial<Omit<ImageModifiers, 'format' | 'quality' |
'background' | 'fit'>>; adjust the type expression referencing modifiers,
ImageModifiers, ConfiguredImageProviders and TProvider accordingly.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 80613db and 92cf582.

📒 Files selected for processing (1)
  • src/types/image.ts

export interface ImageSizesOptions extends ImageOptions {
sizes: Record<string, string | number> | string
& ('modifiers' extends keyof ConfiguredImageProviders[TProvider] ? ConfiguredImageProviders[TProvider]['modifiers'] : Record<string, unknown>)
sizes?: Sizes
Copy link
Member

Choose a reason for hiding this comment

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

does this work in other contexts? e.g. if not passed to getSizes?

if it isn't respected then ImageSizesOptions should still be a separate type

Copy link
Member Author

Choose a reason for hiding this comment

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

from what I can see, ImageSizesOptions was used solely for the purpose of getSizes

Copy link
Member

@danielroe danielroe left a comment

Choose a reason for hiding this comment

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

this is great!! 🙌

would you add some type tests in test/nuxt/use-image.test.ts, like the existing ones? 🙏

also, we need to check that this works correctly to respect the default provider, e.g. if the configured default provider is 'directus', then the options of useImage should be directus options unless a different provider is specified.

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.

Unknown provider: supabase TypeScript error on useImage provider option with v2.0.0

3 participants