Skip to content

Commit

Permalink
feat(sanity): sanity provider enhancements (#301)
Browse files Browse the repository at this point in the history
  • Loading branch information
danielroe authored Jun 2, 2021
1 parent 03ea6fe commit 288997b
Show file tree
Hide file tree
Showing 6 changed files with 174 additions and 16 deletions.
42 changes: 41 additions & 1 deletion docs/pages/en/4.providers/sanity.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ navigation:
title: Sanity
---

Integration between [Sanity](https://www.sanity.io/docs/image-urls) and the image module.
Integration between [Sanity](https://www.sanity.io/docs/image-urls) and Nuxt Image.

To use this provider you just need to specify the `projectId` of your project in Sanity.

Expand All @@ -20,3 +20,43 @@ export default {
}
}
```

## Modifiers

The Sanity provider supports a number of additional modifiers. For a full list, check out [the Sanity documentation](https://www.sanity.io/docs/image-urls). All of the modifiers mentioned in the Sanity docs are supported, with the following notes.

### Extra convenience modifiers

The following more readable modifiers are also supported:

- `background` - equivalent to `bg`
- `download` - equivalent to `dl`
- `sharpen` - equivalent to `sharp`
- `orientation` - equivalent to `or`
- `minHeight` or `min-height` - equivalent to `min-h`
- `maxHeight` or `max-height` - equivalent to `max-h`
- `minWidth` or `min-width` - equivalent to `min-w`
- `maxWidth` or `max-width` - equivalent to `max-w`
- `saturation` - equivalent to `sat`

### `fit`

In addition to the values specified in the Sanity docs, which are respected, the following options from the [default fit behavior](/components/nuxt-img#fit) are supported:

- `cover` - this will behave like the Sanity modifier `crop`
- `contain` - this will behave like the Sanity modifier `fill`, and defaults to filling with a white background. (You can specify your own background color with the `background` modifier.)
- `inside` - this will behave like the Sanity modifier `min`
- `outside` - this will behave like the Sanity modifier `max`
- `fill` - this will behave like the Sanity modifier `scale`

:::alert{type="warning"}
For compatibility with other providers, `fit: fill` is equivalent to the Sanity parameter `?fit=scale`. If you need the Sanity `?fit=fill` behavior, use `fit: contain` instead.
:::

### `format`

You can specify any of the formats suppored by Sanity. If this is omitted, the Sanity provider will default to `auto=format`.

### `crop` and `hotspot`

You can pass your Sanity crop and hotspot image data as modifiers and Nuxt Image will correctly generate the `rect`, `fp-x` and `fp-y` parameters for you.
2 changes: 1 addition & 1 deletion playground/nuxt.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export default <NuxtConfig> {
},
prismic: {},
sanity: {
projectId: 'j1o4tmjp'
projectId: 'zp7mbokg'
},
vercel: {
baseURL: 'https://image-component.nextjs.gallery/_next/image'
Expand Down
36 changes: 31 additions & 5 deletions playground/providers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,19 +120,45 @@ export const providers: Provider[] = [
name: 'sanity',
samples: [
{
src: 'image-7aa06723bb01a7a79055b6d6f5be80329a0e5b58-780x1170-jpg'
src: 'image-G3i4emG6B8JnTmGoN0UjgAp8-300x450-jpg'
},
{
src: 'image-7aa06723bb01a7a79055b6d6f5be80329a0e5b58-780x1170-jpg',
src: 'image-G3i4emG6B8JnTmGoN0UjgAp8-300x450-jpg',
width: 200,
height: 200,
fit: 'crop'
fit: 'fill'
},
{
src: 'image-G3i4emG6B8JnTmGoN0UjgAp8-300x450-jpg',
width: 200,
height: 200,
fit: 'cover'
},
{
src: 'image-7aa06723bb01a7a79055b6d6f5be80329a0e5b58-780x1170-jpg',
src: 'image-G3i4emG6B8JnTmGoN0UjgAp8-300x450-jpg',
width: 200,
height: 200,
fit: 'min'
fit: 'contain'
},
{
src: 'image-G3i4emG6B8JnTmGoN0UjgAp8-300x450-jpg',
width: 200,
modifiers: {
crop: {
_type: 'sanity.imageCrop',
bottom: 0.36903637222484653,
left: 0.23801369863013686,
right: 0,
top: 0.009840969925995906
},
hotspot: {
_type: 'sanity.imageHotspot',
height: 0.6063612029601636,
width: 0.21575342465753433,
x: 0.4469178082191783,
y: 0.32778302629507167
}
}
}
]
},
Expand Down
78 changes: 75 additions & 3 deletions src/runtime/providers/sanity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,88 @@ const sanityCDN = 'https://cdn.sanity.io/images'

const operationsGenerator = createOperationsGenerator({
keyMap: {
background: 'b',
format: 'fm',
height: 'h',
quality: 'q',
width: 'w'
width: 'w',
// Convenience modifiers
background: 'bg',
download: 'dl',
sharpen: 'sharp',
orientation: 'or',
'min-height': 'min-h',
'max-height': 'max-h',
'min-width': 'min-w',
'max-width': 'max-w',
minHeight: 'min-h',
maxHeight: 'max-h',
minWidth: 'min-w',
maxWidth: 'max-w',
saturation: 'sat'
},
valueMap: {
format: {
jpeg: 'jpg'
},
fit: {
cover: 'crop',
contain: 'fill',
fill: 'scale',
inside: 'min',
outside: 'max'
}
},
joinWith: '&',
formatter: (key, value) => `${key}=${value}`
formatter: (key, value) => String(value) === 'true' ? key : `${key}=${value}`
})

const isDev = process.env.NODE_ENV === 'development'

const getMetadata = (id: string) => {
const result = id.match(/-(?<width>\d*)x(?<height>\d*)-(?<format>.*)$/)
if (!result || !result.groups) {
// Invalid Sanity image asset ID
if (isDev) {
// eslint-disable-next-line
console.warn(`An invalid image asset ID was passed in: ${id}`)
}
return { width: undefined, height: undefined, format: undefined }
}

const width = Number(result.groups.width)
const height = Number(result.groups.height)

return {
width,
height,
format: result.groups.format
}
}

export const getImage: ProviderGetImage = (src, { modifiers = {}, projectId, dataset = 'production' } = {}) => {
const { height: sourceHeight, width: sourceWidth } = getMetadata(src)
if (modifiers.crop && typeof modifiers.crop !== 'string' && sourceWidth && sourceHeight) {
const left = modifiers.crop.left * sourceWidth
const top = modifiers.crop.top * sourceHeight
const right = sourceWidth - modifiers.crop.right * sourceWidth
const bottom = sourceHeight - modifiers.crop.bottom * sourceHeight
modifiers.rect = [left, top, right - left, bottom - top].map(i => i.toFixed(0)).join(',')
delete modifiers.crop
}
if (modifiers.hotspot && typeof modifiers.hotspot !== 'string') {
modifiers['fp-x'] = modifiers.hotspot.x
modifiers['fp-y'] = modifiers.hotspot.y
delete modifiers.hotspot
}
if (!modifiers.format || modifiers.format === 'auto') {
if (modifiers.format === 'auto') {
delete modifiers.format
}
modifiers.auto = 'format'
}
if (modifiers.fit === 'contain' && !modifiers.bg) {
modifiers.bg = 'ffffff'
}
const operations = operationsGenerator(modifiers)

const parts = src.split('-').slice(1)
Expand Down
18 changes: 12 additions & 6 deletions test/providers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ export const images = [
imgix: { url: '/test.png' },
imagekit: { url: '/test.png' },
netlify: { url: '/test.png' },
prismic: { url: '/test.png?auto=compress,format&rect=0,0,200,200&w=100&h=100' }
prismic: { url: '/test.png?auto=compress,format&rect=0,0,200,200&w=100&h=100' },
sanity: { url: 'https://cdn.sanity.io/images/projectid/production/test-300x450.png?auto=format' }
},
{
args: ['/test.png', { width: 200 }],
Expand All @@ -19,7 +20,8 @@ export const images = [
imgix: { url: '/test.png?w=200' },
imagekit: { url: '/test.png?tr=w-200' },
netlify: { url: '/test.png?w=200&nf_resize=fit' },
prismic: { url: '/test.png?auto=compress,format&rect=0,0,200,200&w=200&h=100' }
prismic: { url: '/test.png?auto=compress,format&rect=0,0,200,200&w=200&h=100' },
sanity: { url: 'https://cdn.sanity.io/images/projectid/production/test-300x450.png?w=200&auto=format' }
},
{
args: ['/test.png', { height: 200 }],
Expand All @@ -30,7 +32,8 @@ export const images = [
imgix: { url: '/test.png?h=200' },
imagekit: { url: '/test.png?tr=h-200' },
netlify: { url: '/test.png?h=200&nf_resize=fit' },
prismic: { url: '/test.png?auto=compress,format&rect=0,0,200,200&w=100&h=200' }
prismic: { url: '/test.png?auto=compress,format&rect=0,0,200,200&w=100&h=200' },
sanity: { url: 'https://cdn.sanity.io/images/projectid/production/test-300x450.png?h=200&auto=format' }
},
{
args: ['/test.png', { width: 200, height: 200 }],
Expand All @@ -41,7 +44,8 @@ export const images = [
imgix: { url: '/test.png?w=200&h=200' },
imagekit: { url: '/test.png?tr=w-200,h-200' },
netlify: { url: '/test.png?w=200&h=200&nf_resize=fit' },
prismic: { url: '/test.png?auto=compress,format&rect=0,0,200,200&w=200&h=200' }
prismic: { url: '/test.png?auto=compress,format&rect=0,0,200,200&w=200&h=200' },
sanity: { url: 'https://cdn.sanity.io/images/projectid/production/test-300x450.png?w=200&h=200&auto=format' }
},
{
args: ['/test.png', { width: 200, height: 200, fit: 'contain' }],
Expand All @@ -52,7 +56,8 @@ export const images = [
imgix: { url: '/test.png?w=200&h=200&fit=fill' },
imagekit: { url: '/test.png?tr=w-200,h-200,cm-pad_resize' },
netlify: { url: '/test.png?w=200&h=200&nf_resize=fit' },
prismic: { url: '/test.png?auto=compress,format&rect=0,0,200,200&w=200&h=200&fit=fill' }
prismic: { url: '/test.png?auto=compress,format&rect=0,0,200,200&w=200&h=200&fit=fill' },
sanity: { url: 'https://cdn.sanity.io/images/projectid/production/test-300x450.png?w=200&h=200&fit=fill&auto=format&bg=ffffff' }
},
{
args: ['/test.png', { width: 200, height: 200, fit: 'contain', format: 'jpeg' }],
Expand All @@ -63,7 +68,8 @@ export const images = [
imgix: { url: '/test.png?w=200&h=200&fit=fill&fm=jpeg' },
imagekit: { url: '/test.png?tr=w-200,h-200,cm-pad_resize,f-jpeg' },
netlify: { url: '/test.png?w=200&h=200&nf_resize=fit' },
prismic: { url: '/test.png?auto=compress,format&rect=0,0,200,200&w=200&h=200&fit=fill&fm=jpeg' }
prismic: { url: '/test.png?auto=compress,format&rect=0,0,200,200&w=200&h=200&fit=fill&fm=jpeg' },
sanity: { url: 'https://cdn.sanity.io/images/projectid/production/test-300x450.png?w=200&h=200&fit=fill&fm=jpg&bg=ffffff' }
}
] as const

Expand Down
14 changes: 14 additions & 0 deletions test/unit/providers.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import * as imgix from '~/runtime/providers/imgix'
import * as imagekit from '~/runtime/providers/imagekit'
import * as netlify from '~/runtime/providers/netlify'
import * as prismic from '~/runtime/providers/prismic'
import * as sanity from '~/runtime/providers/sanity'

describe('Providers', () => {
test('ipx', () => {
Expand Down Expand Up @@ -147,4 +148,17 @@ describe('Providers', () => {
expect(generated).toMatchObject(image.prismic)
}
})

test('sanity', () => {
const providerOptions = {
baseURL: '',
projectId: 'projectid'
}

for (const image of images) {
const [, modifiers] = image.args
const generated = sanity.getImage('image-test-300x450-png', { modifiers: { ...modifiers }, ...providerOptions }, {} as any)
expect(generated).toMatchObject(image.sanity)
}
})
})

0 comments on commit 288997b

Please sign in to comment.