mirror of
https://github.com/withastro/astro.git
synced 2025-03-10 23:01:26 -05:00
Expose inferRemoteSize
function (#11098)
* feat: expose and rename `inferSize` * feat: separate `ISize` type * feat: reformat function to use `ImageMetadata` * nit(assets): re-use image-metadata code for remote images * chore: changeset * chore: changeset * feat(assets): Export from `astro:assets` * fix: proper errors * fix: dont export from astro/assets * fix: ests * Update .changeset/large-geese-play.md Co-authored-by: Sarah Rainsberger <sarah@rainsberger.ca> * fix: ests * Update .changeset/large-geese-play.md Co-authored-by: Sarah Rainsberger <sarah@rainsberger.ca> --------- Co-authored-by: Erika <3019731+Princesseuh@users.noreply.github.com> Co-authored-by: Sarah Rainsberger <sarah@rainsberger.ca>
This commit is contained in:
parent
d54671fcf0
commit
9bbfad3fc2
11 changed files with 64 additions and 26 deletions
20
.changeset/large-geese-play.md
Normal file
20
.changeset/large-geese-play.md
Normal file
|
@ -0,0 +1,20 @@
|
|||
---
|
||||
"astro": minor
|
||||
---
|
||||
|
||||
Adds a new `inferRemoteSize()` function that can be used to infer the dimensions of a remote image.
|
||||
|
||||
Previously, the ability to infer these values was only available by adding the [`inferSize`] attribute to the `<Image>` and `<Picture>` components or `getImage()`. Now, you can also access this data outside of these components.
|
||||
|
||||
This is useful for when you need to know the dimensions of an image for styling purposes or to calculate different densities for responsive images.
|
||||
|
||||
```astro
|
||||
---
|
||||
import { inferRemoteSize, Image } from 'astro:assets';
|
||||
|
||||
const imageUrl = 'https://...';
|
||||
const { width, height } = await inferRemoteSize(imageUrl);
|
||||
---
|
||||
|
||||
<Image src={imageUrl} width={width / 2} height={height} densities={[1.5, 2]} />
|
||||
```
|
1
packages/astro/client.d.ts
vendored
1
packages/astro/client.d.ts
vendored
|
@ -54,6 +54,7 @@ declare module 'astro:assets' {
|
|||
) => Promise<import('./dist/assets/types.js').GetImageResult>;
|
||||
imageConfig: import('./dist/@types/astro.js').AstroConfig['image'];
|
||||
getConfiguredImageService: typeof import('./dist/assets/index.js').getConfiguredImageService;
|
||||
inferRemoteSize: typeof import('./dist/assets/utils/index.js').inferRemoteSize;
|
||||
Image: typeof import('./components/Image.astro').default;
|
||||
Picture: typeof import('./components/Picture.astro').default;
|
||||
};
|
||||
|
|
|
@ -63,6 +63,7 @@
|
|||
"./actions/runtime/*": "./dist/actions/runtime/*",
|
||||
"./assets": "./dist/assets/index.js",
|
||||
"./assets/utils": "./dist/assets/utils/index.js",
|
||||
"./assets/utils/inferRemoteSize.js": "./dist/assets/utils/remoteProbe.js",
|
||||
"./assets/endpoint/*": "./dist/assets/endpoint/*.js",
|
||||
"./assets/services/sharp": "./dist/assets/services/sharp.js",
|
||||
"./assets/services/squoosh": "./dist/assets/services/squoosh.js",
|
||||
|
|
|
@ -10,7 +10,7 @@ import {
|
|||
isImageMetadata,
|
||||
} from './types.js';
|
||||
import { isESMImportedImage, isRemoteImage, resolveSrc } from './utils/imageKind.js';
|
||||
import { probe } from './utils/remoteProbe.js';
|
||||
import { inferRemoteSize } from './utils/remoteProbe.js';
|
||||
|
||||
export async function getConfiguredImageService(): Promise<ImageService> {
|
||||
if (!globalThis?.astroAsset?.imageService) {
|
||||
|
@ -66,17 +66,10 @@ export async function getImage(
|
|||
|
||||
// Infer size for remote images if inferSize is true
|
||||
if (options.inferSize && isRemoteImage(resolvedOptions.src)) {
|
||||
try {
|
||||
const result = await probe(resolvedOptions.src); // Directly probe the image URL
|
||||
resolvedOptions.width ??= result.width;
|
||||
resolvedOptions.height ??= result.height;
|
||||
delete resolvedOptions.inferSize; // Delete so it doesn't end up in the attributes
|
||||
} catch {
|
||||
throw new AstroError({
|
||||
...AstroErrorData.FailedToFetchRemoteImageDimensions,
|
||||
message: AstroErrorData.FailedToFetchRemoteImageDimensions.message(resolvedOptions.src),
|
||||
});
|
||||
}
|
||||
const result = await inferRemoteSize(resolvedOptions.src); // Directly probe the image URL
|
||||
resolvedOptions.width ??= result.width;
|
||||
resolvedOptions.height ??= result.height;
|
||||
delete resolvedOptions.inferSize; // Delete so it doesn't end up in the attributes
|
||||
}
|
||||
|
||||
const originalFilePath = isESMImportedImage(resolvedOptions.src)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
export { emitESMImage } from './emitAsset.js';
|
||||
export { emitESMImage } from './node/emitAsset.js';
|
||||
export { isESMImportedImage, isRemoteImage } from './imageKind.js';
|
||||
export { imageMetadata } from './metadata.js';
|
||||
export { getOrigQueryParams } from './queryParams.js';
|
||||
|
@ -12,3 +12,4 @@ export {
|
|||
type RemotePattern,
|
||||
} from './remotePattern.js';
|
||||
export { hashTransform, propsToFilename } from './transformToPath.js';
|
||||
export { inferRemoteSize } from './remoteProbe.js';
|
||||
|
|
|
@ -2,9 +2,9 @@ import fs from 'node:fs/promises';
|
|||
import path from 'node:path';
|
||||
import { fileURLToPath, pathToFileURL } from 'node:url';
|
||||
import type * as vite from 'vite';
|
||||
import { prependForwardSlash, slash } from '../../core/path.js';
|
||||
import type { ImageMetadata } from '../types.js';
|
||||
import { imageMetadata } from './metadata.js';
|
||||
import { prependForwardSlash, slash } from '../../../core/path.js';
|
||||
import type { ImageMetadata } from '../../types.js';
|
||||
import { imageMetadata } from '../metadata.js';
|
||||
|
||||
type FileEmitter = vite.Rollup.EmitFile;
|
||||
|
|
@ -1,11 +1,15 @@
|
|||
import { lookup } from './vendor/image-size/lookup.js';
|
||||
import type { ISize } from './vendor/image-size/types/interface.ts';
|
||||
import { AstroError, AstroErrorData } from '../../core/errors/index.js';
|
||||
import type { ImageMetadata } from '../types.js';
|
||||
import { imageMetadata } from './metadata.js';
|
||||
|
||||
export async function probe(url: string): Promise<ISize> {
|
||||
export async function inferRemoteSize(url: string): Promise<Omit<ImageMetadata, 'src' | 'fsPath'>> {
|
||||
// Start fetching the image
|
||||
const response = await fetch(url);
|
||||
if (!response.body || !response.ok) {
|
||||
throw new Error('Failed to fetch image');
|
||||
throw new AstroError({
|
||||
...AstroErrorData.FailedToFetchRemoteImageDimensions,
|
||||
message: AstroErrorData.FailedToFetchRemoteImageDimensions.message(url),
|
||||
});
|
||||
}
|
||||
|
||||
const reader = response.body.getReader();
|
||||
|
@ -31,17 +35,22 @@ export async function probe(url: string): Promise<ISize> {
|
|||
|
||||
try {
|
||||
// Attempt to determine the size with each new chunk
|
||||
const dimensions = lookup(accumulatedChunks);
|
||||
const dimensions = await imageMetadata(accumulatedChunks, url);
|
||||
|
||||
if (dimensions) {
|
||||
await reader.cancel(); // stop stream as we have size now
|
||||
|
||||
return dimensions;
|
||||
}
|
||||
} catch (error) {
|
||||
// This catch block is specifically for `sizeOf` failures,
|
||||
// This catch block is specifically for `imageMetadata` errors
|
||||
// which might occur if the accumulated data isn't yet sufficient.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error('Failed to parse the size');
|
||||
throw new AstroError({
|
||||
...AstroErrorData.NoImageMetadata,
|
||||
message: AstroErrorData.NoImageMetadata.message(url),
|
||||
});
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ import {
|
|||
} from '../core/path.js';
|
||||
import { isServerLikeOutput } from '../core/util.js';
|
||||
import { VALID_INPUT_FORMATS, VIRTUAL_MODULE_ID, VIRTUAL_SERVICE_ID } from './consts.js';
|
||||
import { emitESMImage } from './utils/emitAsset.js';
|
||||
import { emitESMImage } from './utils/node/emitAsset.js';
|
||||
import { getAssetsPrefix } from './utils/getAssetsPrefix.js';
|
||||
import { isESMImportedImage } from './utils/imageKind.js';
|
||||
import { getProxyCode } from './utils/proxy.js';
|
||||
|
@ -133,6 +133,7 @@ export default function assets({
|
|||
import { getImage as getImageInternal } from "astro/assets";
|
||||
export { default as Image } from "astro/components/Image.astro";
|
||||
export { default as Picture } from "astro/components/Picture.astro";
|
||||
export { inferRemoteSize } from "astro/assets/utils/inferRemoteSize.js";
|
||||
|
||||
export const imageConfig = ${JSON.stringify(settings.config.image)};
|
||||
// This is used by the @astrojs/node integration to locate images.
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import type { PluginContext } from 'rollup';
|
||||
import { z } from 'zod';
|
||||
import type { ImageMetadata, OmitBrand } from '../assets/types.js';
|
||||
import { emitESMImage } from '../assets/utils/emitAsset.js';
|
||||
import { emitESMImage } from '../assets/utils/node/emitAsset.js';
|
||||
|
||||
export function createImage(
|
||||
pluginContext: PluginContext,
|
||||
|
|
|
@ -70,6 +70,11 @@ describe('astro:image:infersize', () => {
|
|||
true
|
||||
);
|
||||
});
|
||||
|
||||
it('direct function call work', async () => {
|
||||
let $dimensions = $('#direct');
|
||||
assert.equal($dimensions.text().trim(), '64x64');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
---
|
||||
// https://avatars.githubusercontent.com/u/622227?s=64 is a .jpeg
|
||||
import { Image, Picture, getImage } from 'astro:assets';
|
||||
import { Image, Picture, getImage, inferRemoteSize } from 'astro:assets';
|
||||
|
||||
const { width, height } = await inferRemoteSize('https://avatars.githubusercontent.com/u/622227?s=64');
|
||||
|
||||
const remoteImg = await getImage({
|
||||
src: 'https://avatars.githubusercontent.com/u/622227?s=64',
|
||||
inferSize: true,
|
||||
|
@ -10,3 +13,7 @@ const remoteImg = await getImage({
|
|||
<Image src="https://avatars.githubusercontent.com/u/622227?s=64," inferSize={true} , alt="" />
|
||||
<Picture src="https://avatars.githubusercontent.com/u/622227?s=64," inferSize={true} , alt="" />
|
||||
<img src={remoteImg.src} {...remoteImg.attributes} id="getImage" />
|
||||
|
||||
<div id="direct">
|
||||
{width}x{height}
|
||||
</div>
|
||||
|
|
Loading…
Add table
Reference in a new issue