Skip to content

Commit

Permalink
feat: support alias (#348)
Browse files Browse the repository at this point in the history
  • Loading branch information
pi0 authored Jul 1, 2021
1 parent 7676b76 commit 3d11b87
Show file tree
Hide file tree
Showing 10 changed files with 101 additions and 14 deletions.
51 changes: 51 additions & 0 deletions docs/pages/en/3.api/1.options.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
<nuxt-img src="https://images.unsplash.com/<id>" />
```

Generates:

```html
<img src="/_ipx/https://images.unsplash.com/<id>">
```

**After** using alias:


```html
<nuxt-img src="/unsplash/<id>" />
```

Generates:

```html
<img src="/_ipx/unsplash/<id>" />
```

Both usage and output are simplified!
6 changes: 5 additions & 1 deletion playground/nuxt.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,16 @@ export default <NuxtConfig> {
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/'
},
Expand Down
14 changes: 11 additions & 3 deletions playground/providers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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&amp;utm_medium=referral&amp;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&amp;utm_medium=referral&amp;utm_content=creditCopyText'
}
]
Expand All @@ -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'
Expand Down
7 changes: 4 additions & 3 deletions src/ipx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
11 changes: 8 additions & 3 deletions src/module.ts
Original file line number Diff line number Diff line change
@@ -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'
Expand Down Expand Up @@ -29,7 +29,8 @@ const imageModule: Module<ModuleOptions> = async function imageModule (moduleOpt
},
internalUrl: '',
providers: {},
static: {}
static: {},
alias: {}
}

const options: ModuleOptions = defu(moduleOptions, nuxt.options.image, defaults)
Expand All @@ -39,14 +40,18 @@ const imageModule: Module<ModuleOptions> = 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] || {}

const imageOptions: Omit<CreateImageOptions, 'providers'> = pick(options, [
'screens',
'presets',
'provider',
'domains'
'domains',
'alias'
])

const providers = resolveProviders(nuxt, options)
Expand Down
16 changes: 14 additions & 2 deletions src/runtime/image.ts
Original file line number Diff line number Diff line change
@@ -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'
Expand Down Expand Up @@ -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)})`)
}

Expand All @@ -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
Expand Down
1 change: 1 addition & 0 deletions src/runtime/providers/ipx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,4 @@ export const getImage: ProviderGetImage = (src, { modifiers = {}, baseURL = '/_i
}

export const validateDomains = true
export const supportsAlias = true
2 changes: 2 additions & 0 deletions src/runtime/providers/static.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,5 @@ export const getImage: typeof _getImage = (src, options, ctx) => ({
..._getImage(src, options, ctx),
isStatic: true
})

export const supportsAlias = true
4 changes: 3 additions & 1 deletion src/types/image.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export interface ImageProvider {
defaults?: any
getImage: ProviderGetImage
validateDomains?: Boolean
supportsAlias?: Boolean
}

export interface CreateImageOptions {
Expand All @@ -35,7 +36,8 @@ export interface CreateImageOptions {
}
presets: { [name: string]: ImageOptions }
provider: string
screens?: Record<string, number>,
screens: Record<string, number>,
alias: Record<string, string>,
domains: string[]
}

Expand Down
3 changes: 2 additions & 1 deletion src/types/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ export interface ModuleOptions extends ImageProviders {
presets: { [name: string]: ImageOptions }
dir: string
domains: string[]
sharp: {}
sharp: any
alias: Record<string, string>
screens: CreateImageOptions['screens'],
internalUrl: string
providers: { [name: string]: InputProvider | any } & ImageProviders
Expand Down

0 comments on commit 3d11b87

Please sign in to comment.