Skip to content

Commit

Permalink
feat: add the inferDimensions option (#18)
Browse files Browse the repository at this point in the history
* feat: add the dimensions option

The dimensions option adds `width` and `height` attributes to the <img>
element based on the dimensions of the original image.

* fix: use dimensions of the last src instead of the input

* fix: fix dimensions option in production build

* fix: use inferDimensions instead of dimensions as the option name
  • Loading branch information
ouuan authored Jan 23, 2023
1 parent b6d162c commit a3269f9
Show file tree
Hide file tree
Showing 6 changed files with 107 additions and 3 deletions.
8 changes: 8 additions & 0 deletions src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,14 @@ export function createImageApi (config: Config) {
Object.assign(lastImage, preset.attrs)
lastImage.src ||= lastSrc

if (preset.inferDimensions) {
const { args, generate } = last(last(preset.images).srcset)
const lastSharp = await generate(loadImage(resolve(config.root, filename)), args)
const { info: { width, height } } = await lastSharp.toBuffer({ resolveWithObject: true })
lastImage.width ||= width
lastImage.height ||= height
}

return imagesAttrs
},
}
Expand Down
10 changes: 7 additions & 3 deletions src/presets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ interface WidthPresetOptions extends ImageAttrs {
resizeOptions?: ResizeOptions
withImage?: ImageGenerator
media?: string
inferDimensions?: boolean
}

export { mimeTypeFor }
Expand All @@ -23,13 +24,14 @@ export function hdPreset (options: WidthPresetOptions) {
const highDensity = widthPreset({ density: 2, media: '(-webkit-min-device-pixel-ratio: 1.5)', ...options })
const desktopWidth = Math.max(...options.widths as any) || 'original'
const desktop = widthPreset({ ...options, widths: [desktopWidth] })
return { attrs: desktop.attrs, images: highDensity.images.concat(desktop.images) }
return { attrs: desktop.attrs, images: highDensity.images.concat(desktop.images), inferDimensions: options.inferDimensions }
}

export function widthPreset ({ density, widths, formats, resizeOptions, withImage, ...options }: WidthPresetOptions): ImagePreset {
export function widthPreset ({ density, widths, formats, resizeOptions, withImage, inferDimensions, ...options }: WidthPresetOptions): ImagePreset {
const [attrs, sourceAttrs] = extractSourceAttrs(options)
return {
attrs,
inferDimensions,
images: Object.entries(formats)
.map(([format, formatOptions]) => ({
...sourceAttrs,
Expand Down Expand Up @@ -61,16 +63,18 @@ interface DensityPresetOptions extends ImageAttrs {
resizeOptions?: ResizeOptions
withImage?: ImageGenerator
media?: string
inferDimensions?: boolean
}

function multiply (quantity: number, n?: number | undefined) {
return n ? quantity * n : undefined
}

export function densityPreset ({ baseWidth, baseHeight, density, formats, resizeOptions, withImage, ...options }: DensityPresetOptions): ImagePreset {
export function densityPreset ({ baseWidth, baseHeight, density, formats, resizeOptions, withImage, inferDimensions, ...options }: DensityPresetOptions): ImagePreset {
const [attrs, sourceAttrs] = extractSourceAttrs(options)
return {
attrs,
inferDimensions,
images: Object.entries(formats)
.map(([format, formatOptions]) => ({
...sourceAttrs,
Expand Down
1 change: 1 addition & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ export interface ImageSource {

export interface ImagePreset {
attrs?: ImageAttrs
inferDimensions?: boolean
images: ImageSource[]
}
export type ImagePresets = Record<string, ImagePreset>
Expand Down
86 changes: 86 additions & 0 deletions tests/api.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import { describe, expect, test } from 'vitest'
import type { ImagePreset } from 'vite-plugin-image-presets'
import ImagePresetsPlugin, { formatPreset, widthPreset } from 'vite-plugin-image-presets'

async function getResolvedImage (preset: ImagePreset, isBuild: boolean) {
const plugin = ImagePresetsPlugin({ default: preset })
await plugin.configResolved({
base: '',
command: isBuild ? 'build' : 'serve',
root: __dirname,
build: { assetsDir: '' } as any,
} as any)

return plugin.api.resolveImage('assets/white.png', { preset: 'default' })
}

describe('inferDimensions', () => {
test('original width and height', async () => {
const resolved = await getResolvedImage(formatPreset({
inferDimensions: true,
formats: {
webp: { quality: 70 },
original: {},
},
}), false)
expect(resolved[1]).toHaveProperty('width', 5)
expect(resolved[1]).toHaveProperty('height', 3)
})

test('original width and height in production build', async () => {
const resolved = await getResolvedImage(formatPreset({
inferDimensions: true,
formats: {
webp: { quality: 70 },
original: {},
},
}), true)
expect(resolved[1]).toHaveProperty('width', 5)
expect(resolved[1]).toHaveProperty('height', 3)
})

test('scaled width and height', async () => {
const resolved = await getResolvedImage(widthPreset({
inferDimensions: true,
widths: [4, 2],
formats: {
webp: { quality: 70 },
},
}), false)
expect(resolved[0]).toHaveProperty('width', 2)
expect(resolved[0]).toHaveProperty('height', 1)
})

test('overriden by attrs', async () => {
const resolved = await getResolvedImage(formatPreset({
inferDimensions: true,
height: 20,
formats: {
original: {},
},
}), false)
expect(resolved[0]).toHaveProperty('width', 5)
expect(resolved[0]).toHaveProperty('height', 20)
})

test('inferDimensions = false', async () => {
const resolved = await getResolvedImage(formatPreset({
inferDimensions: false,
formats: {
original: {},
},
}), false)
expect(resolved[0]).not.toHaveProperty('width')
expect(resolved[0]).not.toHaveProperty('height')
})

test('inferDimensions undefined', async () => {
const resolved = await getResolvedImage(formatPreset({
formats: {
original: {},
},
}), false)
expect(resolved[0]).not.toHaveProperty('width')
expect(resolved[0]).not.toHaveProperty('height')
})
})
Binary file added tests/assets/white.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions tests/presets.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ describe('formatPreset', () => {

expect(preset.attrs).toEqual({})
expect(preset.images.length).toEqual(2)
expect(preset.inferDimensions).toBeUndefined()

const [avifImage, originalImage] = preset.images

Expand All @@ -42,10 +43,12 @@ describe('widthPreset', () => {
webp: { quality: 70 },
jpg: { quality: 80 },
},
inferDimensions: true,
})

expect(preset.attrs).toEqual({ class: 'img', loading: 'lazy' })
expect(preset.images.length).toEqual(2)
expect(preset.inferDimensions).toEqual(true)

const [webpImage, jpegImage] = preset.images

Expand Down Expand Up @@ -75,10 +78,12 @@ describe('densityPreset', () => {
webp: { quality: 70 },
avif: { quality: 80 },
},
inferDimensions: false,
})

expect(preset.attrs).toEqual({ loading: 'lazy' })
expect(preset.images.length).toEqual(2)
expect(preset.inferDimensions).toEqual(false)

const [webpImage, avifImage] = preset.images

Expand Down

0 comments on commit a3269f9

Please sign in to comment.