/**
 * An on the fly image resizer
 *
 * Since most of our images are served via a CDN, we don't have to save the resized images.
 * Instead we set cache headers for them and let the cdn cache them for us.
 *
 * sharp uses a highly performant native package called libvips.
 * it's written in C and is extremely fast.
 *
 * The implementation of the demo uses a stream based approach where the image is never stored in memory.
 * This means it's very good at handling images of any size, and is extremely performant.
 * Further improvements could be done by implementing ETags, but that is out of scope for this demo.
 */ import type { LoaderFunction } from "remix" ; import type { Params } from "react-router" ; import path from "path" ; import type { ReadStream } from "fs" ; import { createReadStream , statSync } from "fs" ; import { PassThrough } from "stream" ; import type { FitEnum } from "sharp" ; import sharp from "sharp" ; const ASSETS_ROOT = "assets" ; interface ResizeParams { src : string ; width : number | undefined ; height : number | undefined ; fit : keyof FitEnum ; } export const loader = async ( { params , request } ) => { const { src , width , height , fit } = extractParams ( params , request ) ; try { const readStream = readFileAsStream ( src ) ; return streamingResize ( readStream , width , height , fit ) ; } catch ( error ) { return handleError ( error ) ; } } ; function extractParams ( params , request ) { const src = params [ "*" ] as string ; const searchParams = new URL ( request . url ) . searchParams ; const width = searchParams . has ( "w" ) ? Number . parseInt ( searchParams . get ( "w" ) ?? "0" ) : undefined ; const height = searchParams . has ( "h" ) ? Number . parseInt ( searchParams . get ( "h" ) ?? "0" ) : undefined ; const fitEnum = [ "contain" , "cover" , "fill" , "inside" , "outside" ] ; let fit = sharp . fit . contain ; if ( searchParams . has ( "fit" ) ) { const fitParam = searchParams . get ( "fit" ) ?? "" ; if ( fitEnum . includes ( fitParam ) ) { fit = fitParam as keyof FitEnum ; } } return { src , width , height , fit } ; } function streamingResize ( imageStream , width , height , fit ) { const sharpTransforms = sharp ( ) . resize ( { width , height , fit , position : sharp . strategy . attention } ) . jpeg ( { mozjpeg : true , quality : 80 } ) ; const passthroughStream = new PassThrough ( ) ; imageStream . pipe ( sharpTransforms ) . pipe ( passthroughStream ) ; return new Response ( passthroughStream as any , { headers : { "Content-Type" : "image/jpeg" , "Cache-Control" : "public, max-age=31536000, immutable" } } ) ; } function readFileAsStream ( src ) { const srcPath = path . join ( ASSETS_ROOT , src ) ; const fileStat = statSync ( srcPath ) ; if ( ! fileStat . isFile ( ) ) { throw new Error ( `${ srcPath } is not a file` ) ; } return createReadStream ( path . join ( ASSETS_ROOT , src ) ) ; } function handleError ( error ) { const errorT = error as Error & { code : string ; } ; if ( errorT . code === "ENOENT" ) { return new Response ( "image not found" , { status : 404 , headers : { "Content-Type" : "text/plain" , "Cache-Control" : "no-cache, no-store, must-revalidate" } } ) ; } return new Response ( errorT . message , { status : 500 , statusText : errorT . message , headers : { "Content-Type" : "text/plain" , "Cache-Control" : "no-cache, no-store, must-revalidate" } } ) ; }