From c74fad179379260dcf46edeae22c1f04ee842508 Mon Sep 17 00:00:00 2001 From: Ashelyn Rose Date: Sat, 2 Dec 2023 16:29:03 -0700 Subject: Manual static image optimization --- components/Image/index.tsx | 108 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 components/Image/index.tsx (limited to 'components/Image/index.tsx') diff --git a/components/Image/index.tsx b/components/Image/index.tsx new file mode 100644 index 0000000..e7a393c --- /dev/null +++ b/components/Image/index.tsx @@ -0,0 +1,108 @@ +import { promises as fs } from 'fs' +import path from 'path' +import { ImgHTMLAttributes } from 'react' +import sharp from 'sharp' + +const outputDir = path.join(process.cwd(), '.next/static/images') +const serverPath = '/_next/static/images/' + +interface ReqProps { + src: string, + alt: string, +} + +type FixedSingleDimension = {width: number} | {height: number} +type FixedBothDimension = {width: number, height: number} +type MultiSize = {width: number[]} | {height: number[]} +type SizeProps = FixedSingleDimension | FixedBothDimension | MultiSize +type ImageAttrs = Omit, "width" | "height" | "src"> + +type ImageProps = ImageAttrs + & SizeProps + & ReqProps + +interface ProcessedImage { + hostPath: string, + randPrefix: string, + widths: {[width: number] : string} +} + +async function ImageServer({src,...imgAttrs}: ImageProps) { + const srcImg = sharp(src) + const extension = path.extname(src) + const filename = path.basename(src, extension) + const randPrefix = (Math.random() + 1).toString(36).substring(7) + const width = 'width' in imgAttrs ? imgAttrs.width : null + const height = 'height' in imgAttrs ? imgAttrs.height : null + + console.log(`processing ${src} for width ${width} and height ${height}`) + + // Make sure we can write to the dir + await fs.mkdir(outputDir, {recursive: true}) + + // Handle where we have an array of dimensions + if (Array.isArray(width) || Array.isArray(height)) { + const dimension = Array.isArray(width) ? 'width' : 'height' + const sizes = Array.isArray(width) ? width : height as number[] + const suffix = dimension.charAt(0) + + // Copy in original + fs.copyFile(src, path.join(outputDir, `${randPrefix}-${filename}${extension}`)) + + // Create resized copies + const srcSetLines = await Promise.all(sizes + .map(async size => { + const outputName = `${randPrefix}-${filename}-${size}${suffix}${extension}` + const outputPath = path.join(outputDir, outputName) + return srcImg + .resize({[dimension]: size}) + .toFile(outputPath) + .then(result => `${serverPath}${outputName} ${result.width}w`) + })) + + return ( + + ) + } else { + const dimensions = { + width: typeof width === 'number' ? width : undefined, + height: typeof height === 'number' ? height : undefined + } + + const dimensionString = (() => { + if (dimensions.height && dimensions.height) + return `${dimensions.width}x${dimensions.height}` + if (dimensions.width) + return `${dimensions.width}w` + return `${dimensions.height}h` + })() + + const outputName = `${randPrefix}-${filename}-${dimensionString}${extension}` + const outputPath = path.join(outputDir, outputName) + const outputSrc = serverPath + outputName + await srcImg.resize(dimensions).toFile(outputPath) + + return ( + + ) + } +} + +export default function ImageServerWrap(props: ImageProps) { + return ( + <> + {/* @ts-expect-error Async Server Component */} + + + ) +} -- cgit 1.4.1