Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(image): Export more types and utilities for users to use #6739

Merged
merged 2 commits into from
Apr 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/unlucky-emus-learn.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'astro': patch
---

Added more types and utilities exports related to `astro:assets` to help building custom image components and image services
16 changes: 14 additions & 2 deletions packages/astro/src/@types/astro.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,20 @@ export type {
RemarkPlugins,
ShikiConfig,
} from '@astrojs/markdown-remark';
export type { ExternalImageService, LocalImageService } from '../assets/services/service';
export type { ImageMetadata, ImageTransform } from '../assets/types';
export type {
ExternalImageService,
ImageService,
LocalImageService,
} from '../assets/services/service';
export type {
GetImageResult,
ImageInputFormat,
ImageMetadata,
ImageOutputFormat,
ImageQuality,
ImageQualityPreset,
ImageTransform,
} from '../assets/types';
export type { SSRManifest } from '../core/app/types';
export type { AstroCookies } from '../core/cookies';

Expand Down
2 changes: 1 addition & 1 deletion packages/astro/src/assets/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
export { getConfiguredImageService, getImage } from './internal.js';
export { baseService } from './services/service.js';
export { baseService, isLocalService } from './services/service.js';
export { type LocalImageProps, type RemoteImageProps } from './types.js';
export { emitESMImage } from './utils/emitAsset.js';
export { imageMetadata } from './utils/metadata.js';
11 changes: 2 additions & 9 deletions packages/astro/src/assets/internal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import type { StaticBuildOptions } from '../core/build/types.js';
import { AstroError, AstroErrorData } from '../core/errors/index.js';
import { prependForwardSlash } from '../core/path.js';
import { isLocalService, type ImageService, type LocalImageService } from './services/service.js';
import type { ImageMetadata, ImageTransform } from './types.js';
import type { GetImageResult, ImageMetadata, ImageTransform } from './types.js';

export function isESMImportedImage(src: ImageMetadata | string): src is ImageMetadata {
return typeof src === 'object';
Expand All @@ -29,13 +29,6 @@ export async function getConfiguredImageService(): Promise<ImageService> {
return globalThis.astroAsset.imageService;
}

interface GetImageResult {
rawOptions: ImageTransform;
options: ImageTransform;
src: string;
attributes: Record<string, any>;
}

/**
* Get an optimized image and the necessary attributes to render it.
*
Expand All @@ -45,7 +38,7 @@ interface GetImageResult {
* import { getImage } from 'astro:assets';
* import originalImage from '../assets/image.png';
*
* const optimizedImage = await getImage({src: originalImage, width: 1280 })
* const optimizedImage = await getImage({src: originalImage, width: 1280 });
Copy link
Member Author

@Princesseuh Princesseuh Apr 3, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing semi-colon here broke syntax highlighting in the hover because of withastro/language-tools#475

* ---
* <img src={optimizedImage.src} {...optimizedImage.attributes} />
* ```
Expand Down
7 changes: 3 additions & 4 deletions packages/astro/src/assets/services/service.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { AstroError, AstroErrorData } from '../../core/errors/index.js';
import { VALID_INPUT_FORMATS } from '../consts.js';
import { isESMImportedImage } from '../internal.js';
import type { ImageTransform, OutputFormat } from '../types.js';
import { isESMImportedImage } from '../internal.js';import type { ImageOutputFormat, ImageTransform } from '../types.js';

export type ImageService = LocalImageService | ExternalImageService;

Expand Down Expand Up @@ -71,7 +70,7 @@ export interface LocalImageService extends SharedServiceProps {
transform: (
inputBuffer: Buffer,
transform: LocalImageTransform
) => Promise<{ data: Buffer; format: OutputFormat }>;
) => Promise<{ data: Buffer; format: ImageOutputFormat }>;
}

export type BaseServiceTransform = {
Expand Down Expand Up @@ -204,7 +203,7 @@ export const baseService: Omit<LocalImageService, 'transform'> = {
src: params.get('href')!,
width: params.has('w') ? parseInt(params.get('w')!) : undefined,
height: params.has('h') ? parseInt(params.get('h')!) : undefined,
format: params.get('f') as OutputFormat,
format: params.get('f') as ImageOutputFormat,
quality: params.get('q'),
};

Expand Down
4 changes: 2 additions & 2 deletions packages/astro/src/assets/services/sharp.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { FormatEnum } from 'sharp';
import type { ImageQualityPreset, OutputFormat } from '../types.js';
import type { ImageOutputFormat, ImageQualityPreset } from '../types.js';
import {
baseService,
parseQuality,
Expand Down Expand Up @@ -64,7 +64,7 @@ const sharpService: LocalImageService = {

return {
data: data,
format: info.format as OutputFormat,
format: info.format as ImageOutputFormat,
};
},
};
Expand Down
7 changes: 5 additions & 2 deletions packages/astro/src/assets/services/squoosh.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// TODO: Investigate removing this service once sharp lands WASM support, as libsquoosh is deprecated

import type { ImageQualityPreset, OutputFormat } from '../types.js';
import type { ImageOutputFormat, ImageQualityPreset } from '../types.js';
import {
baseService,
parseQuality,
Expand All @@ -11,7 +11,10 @@ import { processBuffer } from './vendor/squoosh/image-pool.js';
import type { Operation } from './vendor/squoosh/image.js';

const baseQuality = { low: 25, mid: 50, high: 80, max: 100 };
const qualityTable: Record<Exclude<OutputFormat, 'png'>, Record<ImageQualityPreset, number>> = {
const qualityTable: Record<
Exclude<ImageOutputFormat, 'png'>,
Record<ImageQualityPreset, number>
> = {
avif: {
// Squoosh's AVIF encoder has a bit of a weird behavior where `62` is technically the maximum, and anything over is overkill
max: 62,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { isMainThread } from 'node:worker_threads';
import { cpus } from 'os';
import { fileURLToPath } from 'url';
import type { OutputFormat } from '../../../types.js';
import type { ImageOutputFormat } from '../../../types.js';
import { getModuleURL } from './emscripten-utils.js';
import type { Operation } from './image.js';
import * as impl from './impl.js';
Expand Down Expand Up @@ -88,7 +88,7 @@ function handleJob(params: JobMessage) {
export async function processBuffer(
buffer: Buffer,
operations: Operation[],
encoding: OutputFormat,
encoding: ImageOutputFormat,
quality?: number
): Promise<Uint8Array> {
// @ts-ignore
Expand Down
4 changes: 2 additions & 2 deletions packages/astro/src/assets/services/vendor/squoosh/image.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { OutputFormat } from '../../../types.js';
import type { ImageOutputFormat } from '../../../types.js';
import * as impl from './impl.js';

type RotateOperation = {
Expand All @@ -15,7 +15,7 @@ export type Operation = RotateOperation | ResizeOperation
export async function processBuffer(
buffer: Buffer,
operations: Operation[],
encoding: OutputFormat,
encoding: ImageOutputFormat,
quality?: number
): Promise<Uint8Array> {
let imageData = await impl.decodeBuffer(buffer)
Expand Down
25 changes: 16 additions & 9 deletions packages/astro/src/assets/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import type { ImageService } from './services/service.js';

export type ImageQualityPreset = 'low' | 'mid' | 'high' | 'max' | (string & {});
export type ImageQuality = ImageQualityPreset | number;
export type InputFormat = (typeof VALID_INPUT_FORMATS)[number] | 'svg';
export type OutputFormat = (typeof VALID_OUTPUT_FORMATS)[number] | (string & {});
export type ImageInputFormat = (typeof VALID_INPUT_FORMATS)[number] | 'svg';
export type ImageOutputFormat = (typeof VALID_OUTPUT_FORMATS)[number] | (string & {});

declare global {
// eslint-disable-next-line no-var
Expand All @@ -23,21 +23,28 @@ export interface ImageMetadata {
src: string;
width: number;
height: number;
format: InputFormat;
format: ImageInputFormat;
}

/**
* Options accepted by the image transformation service.
*/
export type ImageTransform = {
src: ImageMetadata | string;
width?: number;
height?: number;
quality?: ImageQuality;
format?: OutputFormat;
width?: number | undefined;
height?: number | undefined;
quality?: ImageQuality | undefined;
format?: ImageOutputFormat | undefined;
Comment on lines +34 to +37
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding undefined here makes it compatible with exactOptionalPropertyTypes. Otherwise, building a component is clumsy because you need to filter out every props manually

[key: string]: any;
};

export interface GetImageResult {
rawOptions: ImageTransform;
options: ImageTransform;
src: string;
attributes: Record<string, any>;
}

type WithRequired<T, K extends keyof T> = T & { [P in K]-?: T[P] };
type ImageSharedProps<T> = T & {
/**
Expand Down Expand Up @@ -94,11 +101,11 @@ export type LocalImageProps<T> = ImageSharedProps<T> & {
* <Image src={...} format="avif" alt="..." />
* ```
*/
format?: OutputFormat;
format?: ImageOutputFormat;
/**
* Desired quality for the image. Value can either be a preset such as `low` or `high`, or a numeric value from 0 to 100.
*
* The perceptual quality of the output image is loader-specific.
* The perceptual quality of the output image is service-specific.
* For instance, a certain service might decide that `high` results in a very beautiful image, but another could choose for it to be at best passable.
*
* **Example**:
Expand Down
4 changes: 2 additions & 2 deletions packages/astro/src/assets/utils/metadata.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import fs from 'node:fs/promises';
import { fileURLToPath } from 'node:url';
import type { ImageMetadata, InputFormat } from '../types.js';
import type { ImageInputFormat, ImageMetadata } from '../types.js';
import imageSize from '../vendor/image-size/index.js';

export interface Metadata extends ImageMetadata {
Expand Down Expand Up @@ -31,7 +31,7 @@ export async function imageMetadata(
src: fileURLToPath(src),
width: isPortrait ? height : width,
height: isPortrait ? width : height,
format: type as InputFormat,
format: type as ImageInputFormat,
orientation,
};
}
4 changes: 2 additions & 2 deletions packages/astro/src/assets/utils/queryParams.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { ImageMetadata, InputFormat } from '../types.js';
import type { ImageInputFormat, ImageMetadata } from '../types.js';

export function getOrigQueryParams(
params: URLSearchParams
Expand All @@ -14,6 +14,6 @@ export function getOrigQueryParams(
return {
width: parseInt(width),
height: parseInt(height),
format: format as InputFormat,
format: format as ImageInputFormat,
};
}
2 changes: 1 addition & 1 deletion packages/astro/src/assets/vite-plugin-assets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ export default function assets({
load(id) {
if (id === resolvedVirtualModuleId) {
return `
export { getImage, getConfiguredImageService } from "astro/assets";
export { getImage, getConfiguredImageService, isLocalService } from "astro/assets";
export { default as Image } from "astro/components/Image.astro";
`;
}
Expand Down