From 94210c69a0a5ffa86227c9500bc14cf0324aa067 Mon Sep 17 00:00:00 2001 From: niuyi1017 <1096196462@qq.com> Date: Mon, 6 Nov 2023 17:29:59 +0800 Subject: [PATCH] feat: add aliyun provider --- docs/content/3.providers/aliyun.md | 60 ++++++++++++++++++++++ playground/nuxt.config.ts | 3 ++ playground/providers.ts | 30 +++++++++++ src/provider.ts | 1 + src/runtime/providers/aliyun.ts | 48 +++++++++++++++++ test/e2e/__snapshots__/no-ssr.test.ts.snap | 41 +++++++++++++++ test/e2e/__snapshots__/ssr.test.ts.snap | 42 +++++++++++++++ test/providers.ts | 6 +++ test/unit/providers.test.ts | 12 ++++- 9 files changed, 242 insertions(+), 1 deletion(-) create mode 100644 docs/content/3.providers/aliyun.md create mode 100644 src/runtime/providers/aliyun.ts diff --git a/docs/content/3.providers/aliyun.md b/docs/content/3.providers/aliyun.md new file mode 100644 index 000000000..78489b43e --- /dev/null +++ b/docs/content/3.providers/aliyun.md @@ -0,0 +1,60 @@ +--- +title: Aliyun +description: Nuxt Image has first class integration with Aliyun. +links: + - label: Source + icon: i-simple-icons-github + to: https://github.com/nuxt/image/blob/main/src/runtime/providers/aliyun.ts + size: xs +--- + +Integration between [Aliyun CDN](https://cdn.console.aliyun.com/) and the image module. + +To use this provider you just need to specify the base url (zone) of your service: + +```ts [nuxt.config.ts] +export default defineNuxtConfig({ + image: { + aliyun: { + baseURL: "https://that-test.site", + }, + }, +}); +``` + +**Example:** + +```vue + +``` + +## Options + +### `baseURL` + +Default: `/` + +Your deployment's domain (zone). + +## modifiers + +**Example:** + +```js +{ + resize: { + fw: 900, + fh: 200 + }, + rotate:180, + bright:50 + ... +} +``` + +For more modifiers configuration items, see [aliyun cdn docs](https://help.aliyun.com/zh/cdn/user-guide/image-editing/) diff --git a/playground/nuxt.config.ts b/playground/nuxt.config.ts index b7c18fb68..32b62b615 100644 --- a/playground/nuxt.config.ts +++ b/playground/nuxt.config.ts @@ -25,6 +25,9 @@ export default defineNuxtConfig({ unsplash: 'https://images.unsplash.com', // ipx blog: '/remote/nuxt-org/blog' // cloudinary }, + aliyun: { + baseURL: 'https://assets.yanbot.tech' + }, twicpics: { baseURL: 'https://demo.twic.pics/' }, diff --git a/playground/providers.ts b/playground/providers.ts index 4231c0c20..683a523f4 100644 --- a/playground/providers.ts +++ b/playground/providers.ts @@ -79,6 +79,36 @@ export const providers: Provider[] = [ } ] }, + // Aliyun + { + name: 'aliyun', + samples: [ + { + src: '/nuxt.png', + height: 200 + }, + { + src: '/nuxt.png', + height: 300, + width: 300 + }, + { + src: '/nuxt.png', + quality: 50 + }, + { + src: '/nuxt.png', + format: 'webp', + quality: 80, + modifiers: { + resize: { + fw: 900, + fh: 200 + } + } + } + ] + }, // Cloudflare { name: 'cloudflare', diff --git a/src/provider.ts b/src/provider.ts index 810040081..97b880dc1 100644 --- a/src/provider.ts +++ b/src/provider.ts @@ -11,6 +11,7 @@ import { ipxSetup } from './ipx' // Please add new providers alphabetically to the list below const BuiltInProviders = [ + 'aliyun', 'cloudflare', 'cloudimage', 'cloudinary', diff --git a/src/runtime/providers/aliyun.ts b/src/runtime/providers/aliyun.ts new file mode 100644 index 000000000..875af0d77 --- /dev/null +++ b/src/runtime/providers/aliyun.ts @@ -0,0 +1,48 @@ +import { joinURL } from 'ufo' +import type { ProviderGetImage } from '@nuxt/image' +import { createOperationsGenerator } from '#image' + +const operationsGenerator = createOperationsGenerator({ + joinWith: '/', + formatter: (key: any, value: any) => { + if (typeof value === 'object') { + return `${key},${Object.entries(value).map(([k, v]) => `${k}_${v}`).join(',')}` + } + return `${key},${value}` + } +}) + +export const getImage: ProviderGetImage = ( + src, + { modifiers = {}, baseURL } = {} +) => { + if (!baseURL) { + // also support runtime config + baseURL = useRuntimeConfig().public.siteUrl + } + const _modifiers = { ...modifiers } + const { resize, width, height, quality } = _modifiers + + let resizeValue = {} + if (width && height) { + resizeValue = { fw: width, fh: height } + } else if (width) { + resizeValue = { w: width } + } else if (height) { + resizeValue = { h: height } + } + if (!resize && Object.keys(resizeValue).length) { + _modifiers.resize = resizeValue + } + delete _modifiers.width + delete _modifiers.height + + if (quality) { + _modifiers.quality = `Q_${quality}` + } + + const operations = operationsGenerator(_modifiers) + return { + url: joinURL(baseURL, src + (operations ? '?image_process=' + operations : '')) + } +} diff --git a/test/e2e/__snapshots__/no-ssr.test.ts.snap b/test/e2e/__snapshots__/no-ssr.test.ts.snap index 9e120d858..8656ba778 100644 --- a/test/e2e/__snapshots__/no-ssr.test.ts.snap +++ b/test/e2e/__snapshots__/no-ssr.test.ts.snap @@ -1,5 +1,23 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html +exports[`browser (ssr: false) > aliyun should render images 1`] = ` +[ + "https://assets.yanbot.tech/nuxt.png?image_process=resize,h_200", + "https://assets.yanbot.tech/nuxt.png?image_process=resize,fw_300,fh_300", + "https://assets.yanbot.tech/nuxt.png?image_process=quality,Q_50", + "https://assets.yanbot.tech/nuxt.png?image_process=resize,fw_900,fh_200/format,webp/quality,Q_80", +] +`; + +exports[`browser (ssr: false) > aliyun should render images 2`] = ` +[ + "https://assets.yanbot.tech/nuxt.png?image_process=resize,h_200", + "https://assets.yanbot.tech/nuxt.png?image_process=resize,fw_300,fh_300", + "https://assets.yanbot.tech/nuxt.png?image_process=quality,Q_50", + "https://assets.yanbot.tech/nuxt.png?image_process=resize,fw_900,fh_200/format,webp/quality,Q_80", +] +`; + exports[`browser (ssr: false) > cloudflare should render images 1`] = ` [ "https://that-test.site/cdn-cgi/image/h=300,fit=contain/https://s3.that-test.site/burger.jpeg", @@ -334,6 +352,19 @@ exports[`browser (ssr: false) > sirv should render images 2`] = ` ] `; +exports[`browser (ssr: false) > sirv should render images 3`] = ` +[ + "https://demo.sirv.com/test.png?w=750&q=75", + "https://demo.sirv.com/harris-large.jpg?w=500&format=png", + "https://demo.sirv.com/lacoste.jpg?crop.type=trim&w=500&q=95", + "https://demo.sirv.com/look-big.jpg?h=500&scale.option=fit", + "https://demo.sirv.com/look-big.jpg?crop.type=face&h=500", + "https://demo.sirv.com/QW.pdf?page=1&w=500", + "https://demo.sirv.com/look-big.jpg?text=Hello&text.align=center&text.position.gravity=south&text.background.color=ffff&text.size=60&text.font.family=Arial&text.color=white&h=500", + "https://demo.sirv.com/t-shirt-man.jpg?watermark=/watermark-v1.png&watermark.position=center&watermark.scale.width=30%&h=500", +] +`; + exports[`browser (ssr: false) > storyblok should render images 1`] = ` [ "https://a.storyblok.com/f/39898/3310x2192/e4ec08624e/demo-image.jpeg", @@ -381,6 +412,16 @@ exports[`browser (ssr: false) > strapi should render images 2`] = ` ] `; +exports[`browser (ssr: false) > strapi should render images 3`] = ` +[ + "http://localhost:1337/uploads/4d9z1eiyo2gmf6gd7xhp_823ae510e8.png", + "http://localhost:1337/uploads/thumbnail_4d9z1eiyo2gmf6gd7xhp_823ae510e8.png", + "http://localhost:1337/uploads/small_4d9z1eiyo2gmf6gd7xhp_823ae510e8.png", + "http://localhost:1337/uploads/medium_4d9z1eiyo2gmf6gd7xhp_823ae510e8.png", + "http://localhost:1337/uploads/large_4d9z1eiyo2gmf6gd7xhp_823ae510e8.png", +] +`; + exports[`browser (ssr: false) > twicpics should render images 1`] = ` [ "https://demo.twic.pics/football.jpg", diff --git a/test/e2e/__snapshots__/ssr.test.ts.snap b/test/e2e/__snapshots__/ssr.test.ts.snap index 64d85a90d..1dd87c094 100644 --- a/test/e2e/__snapshots__/ssr.test.ts.snap +++ b/test/e2e/__snapshots__/ssr.test.ts.snap @@ -1,5 +1,23 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html +exports[`browser (ssr: true) > aliyun should render images 1`] = ` +[ + "https://assets.yanbot.tech/nuxt.png?image_process=resize,h_200", + "https://assets.yanbot.tech/nuxt.png?image_process=resize,fw_300,fh_300", + "https://assets.yanbot.tech/nuxt.png?image_process=quality,Q_50", + "https://assets.yanbot.tech/nuxt.png?image_process=resize,fw_900,fh_200/format,webp/quality,Q_80", +] +`; + +exports[`browser (ssr: true) > aliyun should render images 2`] = ` +[ + "https://assets.yanbot.tech/nuxt.png?image_process=resize,h_200", + "https://assets.yanbot.tech/nuxt.png?image_process=resize,fw_300,fh_300", + "https://assets.yanbot.tech/nuxt.png?image_process=quality,Q_50", + "https://assets.yanbot.tech/nuxt.png?image_process=resize,fw_900,fh_200/format,webp/quality,Q_80", +] +`; + exports[`browser (ssr: true) > cloudflare should render images 1`] = ` [ "https://that-test.site/cdn-cgi/image/h=300,fit=contain/https://s3.that-test.site/burger.jpeg", @@ -308,6 +326,24 @@ exports[`browser (ssr: true) > sanity should render images 2`] = ` ] `; +exports[`browser (ssr: true) > should emit load and error events 1`] = ` +[ + "https://images.prismic.io/200629-sms-hoy/f596a543-d593-4296-9abd-3d3ac15f1e39_ray-hennessy-mpw37yXc_WQ-unsplash.jpg?auto=compress,format&w=600&h=900", + "https://images.prismic.io/200629-sms-hoy/f596a543-d593-4296-9abd-3d3ac15f1e39_ray-hennessy-mpw37yXc_WQ-unsplash.jpg?auto=compress,format&w=200&h=900", + "https://images.prismic.io/200629-sms-hoy/f596a543-d593-4296-9abd-3d3ac15f1e39_ray-hennessy-mpw37yXc_WQ-unsplash.jpg?auto=compress,format&w=600&h=200", + "https://images.prismic.io/200629-sms-hoy/f596a543-d593-4296-9abd-3d3ac15f1e39_ray-hennessy-mpw37yXc_WQ-unsplash.jpg?auto=compress,format&w=200&h=200&fit=crop", +] +`; + +exports[`browser (ssr: true) > should emit load and error events 2`] = ` +[ + "https://images.prismic.io/200629-sms-hoy/f596a543-d593-4296-9abd-3d3ac15f1e39_ray-hennessy-mpw37yXc_WQ-unsplash.jpg?auto=compress,format&w=600&h=900", + "https://images.prismic.io/200629-sms-hoy/f596a543-d593-4296-9abd-3d3ac15f1e39_ray-hennessy-mpw37yXc_WQ-unsplash.jpg?auto=compress,format&w=200&h=900", + "https://images.prismic.io/200629-sms-hoy/f596a543-d593-4296-9abd-3d3ac15f1e39_ray-hennessy-mpw37yXc_WQ-unsplash.jpg?auto=compress,format&w=600&h=200", + "https://images.prismic.io/200629-sms-hoy/f596a543-d593-4296-9abd-3d3ac15f1e39_ray-hennessy-mpw37yXc_WQ-unsplash.jpg?auto=compress,format&w=200&h=200&fit=crop", +] +`; + exports[`browser (ssr: true) > sirv should render images 1`] = ` [ "https://demo.sirv.com/test.png?w=750&q=75", @@ -509,6 +545,12 @@ exports[`browser (ssr: true) > vercel should render images 2`] = ` ] `; +exports[`browser (ssr: true) > vercel should render images 3`] = ` +[ + "https://image-component.nextjs.gallery/_next/image?url=/colors.jpg&w=750&q=75", +] +`; + exports[`browser (ssr: true) > wagtail should render images 1`] = ` [ "https://cms.demo.nypr.digital/images/329944/original|format-webp|webpquality-70", diff --git a/test/providers.ts b/test/providers.ts index 4bc373571..2be396a6a 100644 --- a/test/providers.ts +++ b/test/providers.ts @@ -3,6 +3,7 @@ export const images = [ args: ['/test.png', {}], none: { url: '/test.png' }, ipx: { url: '/_ipx/_/test.png' }, + aliyun: { url: '/test.png' }, cloudflare: { url: '/test.png' }, cloudinary: { url: '/f_auto,q_auto/test' }, twicpics: { url: '/test.png' }, @@ -31,6 +32,7 @@ export const images = [ args: ['/test.png', { width: 200 }], none: { url: '/test.png' }, ipx: { url: '/_ipx/w_200/test.png' }, + aliyun: { url: '/test.png?image_process=resize,w_200' }, cloudflare: { url: '/cdn-cgi/image/w=200/test.png' }, cloudinary: { url: '/f_auto,q_auto,w_200/test' }, twicpics: { url: '/test.png?twic=v1/cover=200x-' }, @@ -59,6 +61,7 @@ export const images = [ args: ['/test.png', { height: 200 }], none: { url: '/test.png' }, ipx: { url: '/_ipx/h_200/test.png' }, + aliyun: { url: '/test.png?image_process=resize,h_200' }, cloudflare: { url: '/cdn-cgi/image/h=200/test.png' }, cloudinary: { url: '/f_auto,q_auto,h_200/test' }, twicpics: { url: '/test.png?twic=v1/cover=-x200' }, @@ -87,6 +90,7 @@ export const images = [ args: ['/test.png', { width: 200, height: 200 }], none: { url: '/test.png' }, ipx: { url: '/_ipx/s_200x200/test.png' }, + aliyun: { url: '/test.png?image_process=resize,fw_200,fh_200' }, cloudflare: { url: '/cdn-cgi/image/w=200,h=200/test.png' }, cloudinary: { url: '/f_auto,q_auto,w_200,h_200/test' }, twicpics: { url: '/test.png?twic=v1/cover=200x200' }, @@ -115,6 +119,7 @@ export const images = [ args: ['/test.png', { width: 200, height: 200, fit: 'contain' }], none: { url: '/test.png' }, ipx: { url: '/_ipx/fit_contain&s_200x200/test.png' }, + aliyun: { url: '/test.png?image_process=fit,contain/resize,fw_200,fh_200' }, cloudflare: { url: '/cdn-cgi/image/w=200,h=200,fit=contain/test.png' }, cloudinary: { url: '/f_auto,q_auto,w_200,h_200,c_scale/test' }, twicpics: { url: '/test.png?twic=v1/inside=200x200' }, @@ -143,6 +148,7 @@ export const images = [ args: ['/test.png', { width: 200, height: 200, fit: 'contain', format: 'jpeg' }], none: { url: '/test.png' }, ipx: { url: '/_ipx/fit_contain&f_jpeg&s_200x200/test.png' }, + aliyun: { url: '/test.png?image_process=fit,contain/format,jpeg/resize,fw_200,fh_200' }, cloudflare: { url: '/cdn-cgi/image/w=200,h=200,fit=contain,f=jpeg/test.png' }, cloudinary: { url: '/f_jpg,q_auto,w_200,h_200,c_scale/test' }, twicpics: { url: '/test.png?twic=v1/output=jpeg/inside=200x200' }, diff --git a/test/unit/providers.test.ts b/test/unit/providers.test.ts index f0700ab37..4e807ea55 100644 --- a/test/unit/providers.test.ts +++ b/test/unit/providers.test.ts @@ -7,6 +7,7 @@ import { images } from '../providers' import { cleanDoubleSlashes } from '#image/utils' import * as ipx from '#image/providers/ipx' import * as none from '~/src/runtime/providers/none' +import * as aliyun from '#image/providers/aliyun' import * as cloudflare from '#image/providers/cloudflare' import * as cloudinary from '#image/providers/cloudinary' import * as twicpics from '#image/providers/twicpics' @@ -69,7 +70,16 @@ describe('Providers', () => { url: '/_ipx/_/images/test.png' }) }) - + it('aliyun', () => { + const providerOptions = { + baseURL: '/' + } + for (const image of images) { + const [src, modifiers] = image.args + const generated = aliyun.getImage(src, { modifiers, ...providerOptions }, emptyContext) + expect(generated).toMatchObject(image.aliyun) + } + }) it('cloudflare', () => { const providerOptions = { baseURL: '/'