e8593e7ead
* initial commit * WIP: starting to define interfaces for images and transformers * WIP: basic sharp service to test out the API setup * adding a few tests for sharp.toImageSrc * Adding tests for sharp.parseImageSrc * hooking up basic SSR support * updating image services to return width/height * simplifying config setup for v1 * hooking up basic SSR + SSG support (dev & build) * refactor: a bit of code cleanup and commenting * WIP: migrating local files to ESM + vite plugin * WIP: starting to hook up user-provided loaderEntryPoints * chore: update lock file * chore: update merged lockfile * refactor: code cleanup and type docs * pulling over the README template for first-party integrations * moving metadata out to the loader * updating the test for the refactored import * revert: remove unrelated webapi formatting * revert: remove unrelated change * fixing up the existing sharp tests * fix: vite plugin wasn't dynamically loading the image service properly * refactor: minor API renaming, removing last hard-coded use of sharp loader * don't manipulate src for hosted image services * Adding support for automatically calculating dimensions by aspect ratio, if needed * a few bug fixes + renaming the aspect ratio search param to "ar" * Adding ETag support, removing need for loaders to parse file metadata * using the battle tested `etag` package * Adding support for dynamically calculating partial sizes * refactor: moving to the packages/integrations dir, Astro Labs TBD later * refactor: renaming parse/serialize functions * Adding tests for SSG image optimizations * refactor: clean up outdated names related to ImageProps * nit: reusing cached SSG filename * chore: update pnpm lock file * handling file URLs when resolving local image imports * updating image file resolution to use file URLs * increasing test timeout for image build tests * fixing eslint error in sharp test * adding slash for windows compat in src URLs * chore: update lockfile after merge * Adding README content * adding a readme call to action for configuration options * review: A few of the quick updates from the PR review * hack: adds a one-off check to allow query params for the _image route * Adds support for src={import("...")}, and named component exports * adding SSR tests * nit: adding a bit more comments * limiting the query params in SSG dev to the images integration
125 lines
3.4 KiB
Text
125 lines
3.4 KiB
Text
---
|
|
// @ts-ignore
|
|
import loader from 'virtual:image-loader';
|
|
import { getImage } from '../src';
|
|
import type { ImageAttributes, ImageMetadata, TransformOptions, OutputFormat } from '../src/types';
|
|
|
|
export interface LocalImageProps extends Omit<TransformOptions, 'src'>, Omit<ImageAttributes, 'src'> {
|
|
src: ImageMetadata | Promise<{ default: ImageMetadata }>;
|
|
}
|
|
|
|
export interface RemoteImageProps extends TransformOptions, ImageAttributes {
|
|
src: string;
|
|
format: OutputFormat;
|
|
width: number;
|
|
height: number;
|
|
}
|
|
|
|
export type Props = LocalImageProps | RemoteImageProps;
|
|
|
|
function isLocalImage(props: Props): props is LocalImageProps {
|
|
// vite-plugin-astro-image resolves ESM imported images
|
|
// to a metadata object
|
|
return typeof props.src !== 'string';
|
|
}
|
|
|
|
function parseAspectRatio(aspectRatio: TransformOptions['aspectRatio']) {
|
|
if (!aspectRatio) {
|
|
return undefined;
|
|
}
|
|
|
|
// parse aspect ratio strings, if required (ex: "16:9")
|
|
if (typeof aspectRatio === 'number') {
|
|
aspectRatio = aspectRatio;
|
|
} else {
|
|
const [width, height] = aspectRatio.split(':');
|
|
aspectRatio = parseInt(width) / parseInt(height);
|
|
}
|
|
}
|
|
|
|
async function resolveProps(props: Props): Promise<TransformOptions> {
|
|
// For remote images, just check the width/height provided
|
|
if (!isLocalImage(props)) {
|
|
return calculateSize(props);
|
|
}
|
|
|
|
let { width, height, aspectRatio, format, ...rest } = props;
|
|
|
|
// if a Promise<ImageMetadata> was provided, unwrap it first
|
|
const { src, ...metadata } = 'then' in props.src ? (await props.src).default : props.src;
|
|
|
|
if (!width && !height) {
|
|
// neither dimension was provided, use the file metadata
|
|
width = metadata.width;
|
|
height = metadata.height;
|
|
} else if (width) {
|
|
// one dimension was provided, calculate the other
|
|
let ratio = parseAspectRatio(aspectRatio) || metadata.width / metadata.height;
|
|
height = height || width / ratio;
|
|
} else if (height) {
|
|
// one dimension was provided, calculate the other
|
|
let ratio = parseAspectRatio(aspectRatio) || metadata.width / metadata.height;
|
|
width = width || height * ratio;
|
|
}
|
|
|
|
return {
|
|
...rest,
|
|
width,
|
|
height,
|
|
aspectRatio,
|
|
src,
|
|
format: format || metadata.format as OutputFormat,
|
|
}
|
|
}
|
|
|
|
function calculateSize(transform: TransformOptions): TransformOptions {
|
|
// keep width & height as provided
|
|
if (transform.width && transform.height) {
|
|
return transform;
|
|
}
|
|
|
|
if (!transform.width && !transform.height) {
|
|
throw new Error(`"width" and "height" cannot both be undefined`);
|
|
}
|
|
|
|
if (!transform.aspectRatio) {
|
|
throw new Error(`"aspectRatio" must be included if only "${transform.width ? "width": "height"}" is provided`)
|
|
}
|
|
|
|
let aspectRatio: number;
|
|
|
|
// parse aspect ratio strings, if required (ex: "16:9")
|
|
if (typeof transform.aspectRatio === 'number') {
|
|
aspectRatio = transform.aspectRatio;
|
|
} else {
|
|
const [width, height] = transform.aspectRatio.split(':');
|
|
aspectRatio = parseInt(width) / parseInt(height);
|
|
}
|
|
|
|
if (transform.width) {
|
|
// only width was provided, calculate height
|
|
return {
|
|
...transform,
|
|
width: transform.width,
|
|
height: transform.width / aspectRatio
|
|
};
|
|
} else if (transform.height) {
|
|
// only height was provided, calculate width
|
|
return {
|
|
...transform,
|
|
width: transform.height * aspectRatio,
|
|
height: transform.height
|
|
}
|
|
}
|
|
|
|
return transform;
|
|
}
|
|
|
|
const props = Astro.props as Props;
|
|
|
|
const imageProps = await resolveProps(props);
|
|
|
|
const attrs = await getImage(loader, imageProps);
|
|
---
|
|
|
|
<img {...attrs} />
|