nuxt / image

Plug-and-play image optimization for Nuxt applications.
https://image.nuxt.com
MIT License
1.35k stars 271 forks source link

Prismic provider doesn't play well with Unsplash urls from Prismic API #1172

Open GlennBergmans opened 11 months ago

GlennBergmans commented 11 months ago

With the new Slice Machine and Slice Simulator upgrade, Prismic returns images directly from Unsplash. Particularly in the slice simulator, it is only possible to use Unsplash urls. The prismic provider however always optimises the URL using the Prismic imgix domain, causing invalid urls for the Unsplash source urls from the API. Although in production Unsplash urls are not likely used, in development this gives problems in generating screenshots using the slice simulator. It is therefore not possible to use values returned by the Prismic API directly in the prismic provider.

There are a few solutions possible:

I'm not sure which philosophy best matches the Nuxt/Image approach.

GlennBergmans commented 11 months ago

For those here looking for a solution, this is a custom provider I wrote to solve this (temporarily). It's basically a merge of the prismic and unsplash providers. It's not very clean code, but it works.

// ~/utils/providers/PrismicUnsplash.ts

import { joinURL, parseQuery, parseURL, stringifyQuery, withQuery, withBase, getQuery } from 'ufo'
import type { ProviderGetImage } from '@nuxt/image'
import { createOperationsGenerator } from '#image'

const operationsGenerator = createOperationsGenerator()
const PRISMIC_IMGIX_BUCKET = 'https://images.prismic.io'
const unsplashCDN = 'https://images.unsplash.com/'

// This provider merges the `prismic` and `unsplash` providers, because the Prismic API returns either a
// Prismic Imgix or an Unsplash value. Unsplash values are used primarily in Slice Simulator.

// Prismic image bucket is left configurable in order to test on other environments
export const getImage: ProviderGetImage = (
  src,
  { modifiers = {}, baseURL = PRISMIC_IMGIX_BUCKET } = {}
) => {
  const operations = operationsGenerator(modifiers)
  const parsedURL = parseURL(src)

  // If it's an unsplash image
  if (parsedURL.host == 'images.unsplash.com') {
    return {
      url: withQuery(withBase(src, unsplashCDN), getQuery('?' + operations))
    }
  }

  return {
    url: joinURL(
      baseURL,
      parsedURL.pathname + '?' +
      // Remove duplicated keys, prioritizing override from developers
      stringifyQuery(Object.assign(parseQuery(parsedURL.search), parseQuery(operations)))
    )
  }
}

Then add this to the nuxt/image configuration in nuxt.config.ts:

image: {
  providers: {
    PrismicUnsplash: {
      provider: '~/utils/providers/PrismicUnsplash.ts', // Path to custom provider
    }
  },
  provider: 'PrismicUnsplash',
}
simonmaass commented 1 week ago

I just stumbled upon the same problem - thank you!

simonmaass commented 1 week ago

@GlennBergmans i just encountered the problem that with your code the placeholder url is not being generated correct: Image

simonmaass commented 1 week ago

@GlennBergmans i found the problem... the unplash and prismic providers take the operationsGenerator from the imgix provider

see here: https://github.com/nuxt/image/blob/e7e670d8c93eb123e8418de9e7ad62c6270c98e9/src/runtime/providers/prismic.ts#L3

This workes for me:

import { joinURL, parseQuery, parseURL, stringifyQuery, withQuery, withBase, getQuery } from 'ufo'
import type { ProviderGetImage } from '@nuxt/image'
import { operationsGenerator } from '#image/providers/imgix'

// https://github.com/nuxt/image/issues/1172

const PRISMIC_IMGIX_BUCKET = 'https://images.prismic.io'
const unsplashCDN = 'https://images.unsplash.com/'

// This provider merges the `prismic` and `unsplash` providers, because the Prismic API returns either a
// Prismic Imgix or an Unsplash value. Unsplash values are used primarily in Slice Simulator.

// Prismic image bucket is left configurable in order to test on other environments
export const getImage: ProviderGetImage = (src, { modifiers = {}, baseURL = PRISMIC_IMGIX_BUCKET } = {}) => {
  const operations = operationsGenerator(modifiers)
  const parsedURL = parseURL(src)

  // If it's an unsplash image
  if (parsedURL.host == 'images.unsplash.com') {
    return {
      url: withQuery(withBase(src, unsplashCDN), getQuery('?' + operations))
    }
  }

  return {
    url: joinURL(
      baseURL,
      parsedURL.pathname +
        '?' +
        // Remove duplicated keys, prioritizing override from developers
        stringifyQuery(Object.assign(parseQuery(parsedURL.search), parseQuery(operations)))
    )
  }
}

cheers!