diff --git a/docs/pages/en/3.api/1.options.md b/docs/pages/en/3.api/1.options.md
index f5d4dd4c2..34fed4985 100644
--- a/docs/pages/en/3.api/1.options.md
+++ b/docs/pages/en/3.api/1.options.md
@@ -175,3 +175,54 @@ export default {
- For `static` provider, if images weren't crawled during generation (unreachable modals, pages or dynamic runtime size), changing `dir` from `static` causes 404 errors.
- For `ipx` provider, make sure to deploy customized `dir` as well.
- For some providers (like vercel), using a directory other than `static/` for assets is not supported since resizing happens at runtime (instead of build/generate time) and source fetched from the `static/` directory (deployment URL)
+
+## `alias`
+
+This option allows you to specify aliases for `src`.
+
+When using the default ipx provider, URL aliases are shortened on the server-side.
+This is especially useful for optimizing external URLs and not including them in HTML.
+
+When using other providers, aliases are resolved in runtime and included in HTML. (only the usage is simplified)
+
+**Example:**
+
+```ts [nuxt.config.js]
+export default {
+ image: {
+ domains: [
+ 'images.unsplash.com'
+ ],
+ alias: {
+ unsplash: 'https://images.unsplash.com'
+ }
+ }
+}
+```
+
+**Before** using alias:
+
+```html
+
+```
+
+Generates:
+
+```html
+
+```
+
+**After** using alias:
+
+
+```html
+
+```
+
+Generates:
+
+```html
+
+```
+
+Both usage and output are simplified!
diff --git a/playground/nuxt.config.ts b/playground/nuxt.config.ts
index 17e4107ef..b3c66d9c2 100644
--- a/playground/nuxt.config.ts
+++ b/playground/nuxt.config.ts
@@ -16,12 +16,16 @@ export default {
image: {
domains: [
'https://nuxtjs.org',
- 'https://unsplash.com',
+ 'https://images.unsplash.com',
'https://upload.wikimedia.org'
],
screens: {
750: 750
},
+ alias: {
+ unsplash: 'https://images.unsplash.com', // ipx
+ blog: '/remote/nuxt-org/blog' // cloudinary
+ },
twicpics: {
baseURL: 'https://demo.twic.pics/'
},
diff --git a/playground/providers.ts b/playground/providers.ts
index dc407be81..1bcd9d8be 100644
--- a/playground/providers.ts
+++ b/playground/providers.ts
@@ -18,21 +18,29 @@ export const providers: Provider[] = [
{
src: '/images/colors.jpg',
from: 'Jeremy Thomas',
+ width: 300,
+ height: 300,
link: 'https://unsplash.com/@jeremythomasphoto?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText'
},
{
src: '/images/everest.jpg',
- from: 'Mount Everest Wikipedia page',
+ from: 'Mount Everest Wikipedia page (alias)',
+ width: 300,
+ height: 300,
link: 'https://en.wikipedia.org/wiki/Mount_Everest'
},
{
src: '/images/tacos.svg',
from: 'Illustration from Icons8',
+ width: 300,
+ height: 300,
link: 'https://icons8.com/illustrations/illustration/abstract-1419'
},
{
- src: 'https://images.unsplash.com/photo-1606112219348-204d7d8b94ee?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1940&q=80',
+ src: '/unsplash/photo-1606112219348-204d7d8b94ee',
from: 'Photo by Omid Armin',
+ width: 300,
+ height: 300,
link: 'https://unsplash.com/@omidarmin?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText'
}
]
@@ -45,7 +53,7 @@ export const providers: Provider[] = [
src: '/remote/nuxt-org/blog/going-full-static/main'
},
{
- src: '/remote/nuxt-org/blog/going-full-static/main',
+ src: '/blog/going-full-static/main',
width: 200,
height: 200,
fit: 'cropping'
diff --git a/src/ipx.ts b/src/ipx.ts
index 0e62851fb..f0123e01c 100644
--- a/src/ipx.ts
+++ b/src/ipx.ts
@@ -3,15 +3,16 @@ import { update as updaterc } from 'rc9'
import { mkdirp, readFile, writeFile } from 'fs-extra'
import { lt } from 'semver'
-import type { ProviderSetup } from './types'
+import type { ProviderSetup, ImageProviders } from './types'
export const ipxSetup: ProviderSetup = async (_providerOptions, moduleOptions, nuxt) => {
const isStatic = nuxt.options.target === 'static'
const runtimeDir = resolve(__dirname, 'runtime')
- const ipxOptions = {
+ const ipxOptions: ImageProviders['ipx'] = {
dir: resolve(nuxt.options.rootDir, moduleOptions.dir),
domains: moduleOptions.domains,
- sharp: moduleOptions.sharp
+ sharp: moduleOptions.sharp,
+ alias: moduleOptions.alias
}
// Add IPX middleware unless nuxtrc or user added a custom middleware
diff --git a/src/module.ts b/src/module.ts
index 910a4849e..6c5b458ab 100644
--- a/src/module.ts
+++ b/src/module.ts
@@ -1,6 +1,6 @@
import { resolve } from 'upath'
import defu from 'defu'
-import { parseURL } from 'ufo'
+import { parseURL, withLeadingSlash } from 'ufo'
import type { Module } from '@nuxt/types'
import { setupStaticGeneration } from './generate'
import { resolveProviders, detectProvider } from './provider'
@@ -29,7 +29,8 @@ const imageModule: Module = async function imageModule (moduleOpt
},
internalUrl: '',
providers: {},
- static: {}
+ static: {},
+ alias: {}
}
const options: ModuleOptions = defu(moduleOptions, nuxt.options.image, defaults)
@@ -39,6 +40,9 @@ const imageModule: Module = async function imageModule (moduleOpt
.map(domain => parseURL(domain, 'https://').host)
.filter(Boolean) as string[]
+ // Normalize alias to start with leading slash
+ options.alias = Object.fromEntries(Object.entries(options.alias).map(e => [withLeadingSlash(e[0]), e[1]]))
+
options.provider = detectProvider(options.provider, nuxt.options.target === 'static')
options[options.provider] = options[options.provider] || {}
@@ -46,7 +50,8 @@ const imageModule: Module = async function imageModule (moduleOpt
'screens',
'presets',
'provider',
- 'domains'
+ 'domains',
+ 'alias'
])
const providers = resolveProviders(nuxt, options)
diff --git a/src/runtime/image.ts b/src/runtime/image.ts
index e18c104c1..e820ad7d5 100644
--- a/src/runtime/image.ts
+++ b/src/runtime/image.ts
@@ -1,5 +1,5 @@
import defu from 'defu'
-import { hasProtocol, parseURL } from 'ufo'
+import { hasProtocol, parseURL, joinURL, withLeadingSlash } from 'ufo'
import type { ImageOptions, ImageSizesOptions, CreateImageOptions, ResolvedImage, MapToStatic, ImageCTX, $Img } from '../types/image'
import { imageMeta } from './utils/meta'
import { parseSize } from './utils'
@@ -86,7 +86,7 @@ async function getMeta (ctx: ImageCTX, input: string, options?: ImageOptions) {
}
function resolveImage (ctx: ImageCTX, input: string, options: ImageOptions): ResolvedImage {
- if (typeof input !== 'string') {
+ if (typeof input !== 'string' || input === '') {
throw new TypeError(`input must be a string (received ${typeof input}: ${JSON.stringify(input)})`)
}
@@ -99,6 +99,18 @@ function resolveImage (ctx: ImageCTX, input: string, options: ImageOptions): Res
const { provider, defaults } = getProvider(ctx, options.provider || ctx.options.provider)
const preset = getPreset(ctx, options.preset)
+ // Normalize input with leading slash
+ input = hasProtocol(input) ? input : withLeadingSlash(input)
+
+ // Resolve alias if provider is not ipx
+ if (!provider.supportsAlias) {
+ for (const base in ctx.options.alias) {
+ if (input.startsWith(base)) {
+ input = joinURL(ctx.options.alias[base], input.substr(base.length))
+ }
+ }
+ }
+
// Externalize remote images if domain does not match with `domains`
if (provider.validateDomains && hasProtocol(input)) {
const inputHost = parseURL(input).host
diff --git a/src/runtime/providers/ipx.ts b/src/runtime/providers/ipx.ts
index c2cba7811..fd86adad2 100644
--- a/src/runtime/providers/ipx.ts
+++ b/src/runtime/providers/ipx.ts
@@ -31,3 +31,4 @@ export const getImage: ProviderGetImage = (src, { modifiers = {}, baseURL = '/_i
}
export const validateDomains = true
+export const supportsAlias = true
diff --git a/src/runtime/providers/static.ts b/src/runtime/providers/static.ts
index af4ef1219..921467779 100644
--- a/src/runtime/providers/static.ts
+++ b/src/runtime/providers/static.ts
@@ -4,3 +4,5 @@ export const getImage: typeof _getImage = (src, options, ctx) => ({
..._getImage(src, options, ctx),
isStatic: true
})
+
+export const supportsAlias = true
diff --git a/src/types/image.ts b/src/types/image.ts
index 93f77d143..e006c054a 100644
--- a/src/types/image.ts
+++ b/src/types/image.ts
@@ -24,6 +24,7 @@ export interface ImageProvider {
defaults?: any
getImage: ProviderGetImage
validateDomains?: Boolean
+ supportsAlias?: Boolean
}
export interface CreateImageOptions {
@@ -35,7 +36,8 @@ export interface CreateImageOptions {
}
presets: { [name: string]: ImageOptions }
provider: string
- screens?: Record,
+ screens: Record,
+ alias: Record,
domains: string[]
}
diff --git a/src/types/module.ts b/src/types/module.ts
index 79a0fbc6f..73ff573f8 100644
--- a/src/types/module.ts
+++ b/src/types/module.ts
@@ -32,7 +32,8 @@ export interface ModuleOptions extends ImageProviders {
presets: { [name: string]: ImageOptions }
dir: string
domains: string[]
- sharp: {}
+ sharp: any
+ alias: Record
screens: CreateImageOptions['screens'],
internalUrl: string
providers: { [name: string]: InputProvider | any } & ImageProviders