From 003e14404789ca17688939104537fab7eee15f0e Mon Sep 17 00:00:00 2001 From: Daniel Roe Date: Sat, 29 May 2021 11:24:32 +0100 Subject: [PATCH] feat: add netlify provider (#299) LFS only --- docs/pages/en/4.providers/netlify.md | 21 ++++++++++++ playground/nuxt.config.ts | 3 ++ playground/providers.ts | 17 ++++++++++ src/provider.ts | 1 + src/runtime/providers/netlify.ts | 50 ++++++++++++++++++++++++++++ test/providers.ts | 6 ++++ test/unit/providers.test.ts | 13 ++++++++ 7 files changed, 111 insertions(+) create mode 100644 docs/pages/en/4.providers/netlify.md create mode 100644 src/runtime/providers/netlify.ts diff --git a/docs/pages/en/4.providers/netlify.md b/docs/pages/en/4.providers/netlify.md new file mode 100644 index 000000000..9c7b8513b --- /dev/null +++ b/docs/pages/en/4.providers/netlify.md @@ -0,0 +1,21 @@ +--- +title: Netlify Provider +description: Optimize images with Netlify's dynamic image transformation service. +navigation: + title: Netlify +--- + +Netlify offers dynamic image transformation for all JPEG, PNG, and GIF files you have set to be tracked with [Netlify Large Media](https://docs.netlify.com/large-media/overview/). + +:::alert{type="warning"} +Before setting `provider: 'netlify'`, make sure you have followed the steps to enable [Netlify Large Media](https://docs.netlify.com/large-media/overview/). +::: + +## Modifiers + +In addition to `height` and `width`, the Netlify provider supports the following modifiers: + +### `fit` + +* **Default**: `contain` +* **Valid options**: `contain` (equivalent to `nf_resize=fit`) and `fill` (equivalent to `nf_resize=smartcrop`) diff --git a/playground/nuxt.config.ts b/playground/nuxt.config.ts index 2b5901e29..dc4fd3c30 100644 --- a/playground/nuxt.config.ts +++ b/playground/nuxt.config.ts @@ -40,6 +40,9 @@ export default { imagekit: { baseURL: 'https://ik.imagekit.io/demo' }, + netlify: { + baseURL: 'https://netlify-photo-gallery.netlify.app' + }, prismic: {}, sanity: { projectId: 'j1o4tmjp' diff --git a/playground/providers.ts b/playground/providers.ts index 7ad4e6815..e78c11b6f 100644 --- a/playground/providers.ts +++ b/playground/providers.ts @@ -67,6 +67,23 @@ export const providers: Provider[] = [ { src: '/plant.jpeg' } ] }, + // Netlify + { + name: 'netlify', + samples: [ + { + src: '/images/apple.jpg', + width: 101, + fit: 'contain' + }, + { + src: '/images/apple.jpg', + width: 200, + height: 200, + fit: 'fill' + } + ] + }, // Prismic { name: 'prismic', diff --git a/src/provider.ts b/src/provider.ts index 7fbeb0617..acebc9aa3 100644 --- a/src/provider.ts +++ b/src/provider.ts @@ -11,6 +11,7 @@ const BuiltInProviders = [ 'imagekit', 'imgix', 'ipx', + 'netlify', 'prismic', 'sanity', 'static', diff --git a/src/runtime/providers/netlify.ts b/src/runtime/providers/netlify.ts new file mode 100644 index 000000000..5ee035787 --- /dev/null +++ b/src/runtime/providers/netlify.ts @@ -0,0 +1,50 @@ +import { joinURL } from 'ufo' +import type { ProviderGetImage } from 'src' +import { createOperationsGenerator } from '~image' + +export const operationsGenerator = createOperationsGenerator({ + keyMap: { + height: 'h', + fit: 'nf_resize', + width: 'w' + }, + valueMap: { + fit: { + fill: 'smartcrop', + contain: 'fit' + } + }, + joinWith: '&', + formatter: (key, value) => `${key}=${value}` +}) + +const isDev = process.env.NODE_ENV === 'development' + +// https://docs.netlify.com/large-media/transform-images/ + +export const getImage: ProviderGetImage = (src, { modifiers = {}, baseURL = '/' } = {}) => { + if (modifiers.format) { + // Not currently supported + delete modifiers.format + } + const hasTransformation = modifiers.height || modifiers.width + if (!modifiers.fit && hasTransformation) { + // fit is required for resizing images + modifiers.fit = 'contain' + } + if (hasTransformation && modifiers.fit !== 'contain' && !(modifiers.height && modifiers.width)) { + // smartcrop is only supported with both height and width + if (isDev) { + // eslint-disable-next-line + console.warn(`Defaulting to fit=contain as smart cropping is only supported when providing both height and width. Warning originated from \`${src}\`.`) + } + modifiers.fit = 'contain' + } + if (isDev) { + return { url: src } + } + const operations = operationsGenerator(modifiers) + return { + url: joinURL(baseURL, src + (operations ? ('?' + operations) : '')) + } +} diff --git a/test/providers.ts b/test/providers.ts index cdafab101..683a07348 100644 --- a/test/providers.ts +++ b/test/providers.ts @@ -7,6 +7,7 @@ export const images = [ fastly: { url: '/test.png' }, 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' } }, { @@ -17,6 +18,7 @@ export const images = [ fastly: { url: '/test.png?width=200' }, 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' } }, { @@ -27,6 +29,7 @@ export const images = [ fastly: { url: '/test.png?height=200' }, 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' } }, { @@ -37,6 +40,7 @@ export const images = [ fastly: { url: '/test.png?width=200&height=200' }, 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' } }, { @@ -47,6 +51,7 @@ export const images = [ fastly: { url: '/test.png?width=200&height=200&fit=bounds' }, 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' } }, { @@ -57,6 +62,7 @@ export const images = [ fastly: { url: '/test.png?width=200&height=200&fit=bounds&format=jpeg' }, 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' } } ] as const diff --git a/test/unit/providers.test.ts b/test/unit/providers.test.ts index 9f003bd26..68050ad8a 100644 --- a/test/unit/providers.test.ts +++ b/test/unit/providers.test.ts @@ -7,6 +7,7 @@ import * as twicpics from '~/runtime/providers/twicpics' import * as fastly from '~/runtime/providers/fastly' 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' describe('Providers', () => { @@ -120,6 +121,18 @@ describe('Providers', () => { } }) + test('netlify', () => { + const providerOptions = { + baseURL: '' + } + + for (const image of images) { + const [src, modifiers] = image.args + const generated = netlify.getImage(src, { modifiers: { ...modifiers }, ...providerOptions }, {} as any) + expect(generated).toMatchObject(image.netlify) + } + }) + test('prismic', () => { const providerOptions = { baseURL: '' // Use empty base URL for the sake of simplicity