// srcset strings of a featured image from the Journal API's // featured_images[].variants (D-85, D-71): one for the WebP and // one for the in the original format, the original itself added // when it is wider than every variant, plus the size the 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 renders from the variants. */ export interface JournalImageSrcsets { /** srcset of the type="image/webp" . */ webp: string /** srcset of the , in the original format. */ original: string /** The size the 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 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 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() 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, } }