- JournalFeaturedImage renders <picture> from featured_images[].variants: a WebP source and the original format as srcset fallback, the original's width and height, alt; lazy in cards, eager with fetchpriority high on the post page; unchanged without variants - app/utils/srcset.ts (imageSrcsets) with node tests - acme fixtures and SSR expectations for both cases
82 lines
3.1 KiB
TypeScript
82 lines
3.1 KiB
TypeScript
// srcset strings of a featured image from the Journal API's
|
|
// featured_images[].variants (D-85, D-71): one for the WebP <source> and
|
|
// one for the <img> in the original format, the original itself added
|
|
// when it is wider than every variant, plus the size the <img> reserves.
|
|
// Pure, no Vue or Nuxt imports, so `node --test` drives it.
|
|
|
|
/** One size of a featured image (featured_images[].variants). */
|
|
export interface JournalImageVariant {
|
|
width: number
|
|
height: number
|
|
/** The thumbnail in the original's format. */
|
|
url: string
|
|
/** Its WebP copy. */
|
|
webp_url: string
|
|
/** The original's size. */
|
|
source_width?: number
|
|
source_height?: number
|
|
}
|
|
|
|
/** What <picture> renders from the variants. */
|
|
export interface JournalImageSrcsets {
|
|
/** srcset of the type="image/webp" <source>. */
|
|
webp: string
|
|
/** srcset of the <img>, in the original format. */
|
|
original: string
|
|
/** The size the <img> reserves: the original's, else the largest variant's. */
|
|
width: number
|
|
height: number
|
|
}
|
|
|
|
/** A URL safe inside a srcset candidate (no spaces or commas). */
|
|
function candidateURL(url: string): string {
|
|
return url.replace(/ /g, '%20').replace(/,/g, '%2C')
|
|
}
|
|
|
|
function validVariant(v: unknown): v is JournalImageVariant {
|
|
if (!v || typeof v !== 'object') return false
|
|
const o = v as Record<string, unknown>
|
|
return (
|
|
Number.isInteger(o.width) && (o.width as number) > 0 &&
|
|
Number.isInteger(o.height) && (o.height as number) > 0 &&
|
|
typeof o.url === 'string' && o.url !== '' &&
|
|
typeof o.webp_url === 'string' && o.webp_url !== ''
|
|
)
|
|
}
|
|
|
|
/**
|
|
* The srcsets of an image from its variants, narrowest first and each
|
|
* width once. originalUrl and originalWidth (the variants' source_width
|
|
* when not given) add the original to the <img> srcset when it is wider
|
|
* than every variant. No usable variant answers null: render the original
|
|
* as before.
|
|
*/
|
|
export function imageSrcsets(
|
|
variants: readonly unknown[] | null | undefined,
|
|
originalUrl?: string | null,
|
|
originalWidth?: number | null,
|
|
): JournalImageSrcsets | null {
|
|
if (!Array.isArray(variants)) return null
|
|
const byWidth = new Map<number, JournalImageVariant>()
|
|
for (const v of variants) {
|
|
if (validVariant(v) && !byWidth.has(v.width)) byWidth.set(v.width, v)
|
|
}
|
|
const list = [...byWidth.values()].sort((a, b) => a.width - b.width)
|
|
const largest = list[list.length - 1]
|
|
if (!largest) return null
|
|
const webp = list.map((v) => `${candidateURL(v.webp_url)} ${v.width}w`)
|
|
const original = list.map((v) => `${candidateURL(v.url)} ${v.width}w`)
|
|
const sourceWidth = originalWidth ?? largest.source_width ?? null
|
|
const sourceHeight = largest.source_height ?? null
|
|
if (originalUrl && sourceWidth && Number.isInteger(sourceWidth) && sourceWidth > largest.width) {
|
|
original.push(`${candidateURL(originalUrl)} ${sourceWidth}w`)
|
|
}
|
|
const hasSource = !!sourceWidth && !!sourceHeight && Number.isInteger(sourceWidth) && Number.isInteger(sourceHeight)
|
|
return {
|
|
webp: webp.join(', '),
|
|
original: original.join(', '),
|
|
width: hasSource ? (sourceWidth as number) : largest.width,
|
|
height: hasSource ? (sourceHeight as number) : largest.height,
|
|
}
|
|
}
|