mirror of
https://github.com/withastro/astro.git
synced 2025-03-24 23:21:57 -05:00
fix(assets): Fallback format not being taken into account properly (#8842)
Co-authored-by: Florian Lefebvre <contact@florian-lefebvre.dev> Co-authored-by: Sarah Rainsberger <sarah@rainsberger.ca> Co-authored-by: Emanuele Stoppa <my.burning@gmail.com>
This commit is contained in:
parent
84d7c14646
commit
b405b039a6
6 changed files with 59 additions and 9 deletions
5
.changeset/dull-bikes-decide.md
Normal file
5
.changeset/dull-bikes-decide.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
'astro': patch
|
||||
---
|
||||
|
||||
Fixes Picture component not taking into account the fallback format specified
|
|
@ -11,7 +11,16 @@ type Props = (LocalImageProps | RemoteImageProps) & {
|
|||
pictureAttributes?: HTMLAttributes<'picture'>;
|
||||
};
|
||||
|
||||
const { formats = ['webp'], pictureAttributes = {}, ...props } = Astro.props;
|
||||
const defaultFormats = ['webp'] as const;
|
||||
const defaultFallbackFormat = 'png' as const;
|
||||
|
||||
// Certain formats don't want PNG fallbacks:
|
||||
// - GIF will typically want to stay as a gif, either for animation or for the lower amount of colors
|
||||
// - SVGs can't be converted to raster formats in most cases
|
||||
// For those, we'll use the original format as the fallback instead.
|
||||
const specialFormatsFallback = ['gif', 'svg'] as const;
|
||||
|
||||
const { formats = defaultFormats, pictureAttributes = {}, fallbackFormat, ...props } = Astro.props;
|
||||
|
||||
if (props.alt === undefined || props.alt === null) {
|
||||
throw new AstroError(AstroErrorData.ImageMissingAlt);
|
||||
|
@ -24,16 +33,18 @@ const optimizedImages: GetImageResult[] = await Promise.all(
|
|||
)
|
||||
);
|
||||
|
||||
const fallbackFormat =
|
||||
props.fallbackFormat ?? isESMImportedImage(props.src)
|
||||
? ['svg', 'gif'].includes(props.src.format)
|
||||
? props.src.format
|
||||
: 'png'
|
||||
: 'png';
|
||||
let resultFallbackFormat = fallbackFormat ?? defaultFallbackFormat;
|
||||
if (
|
||||
!fallbackFormat &&
|
||||
isESMImportedImage(props.src) &&
|
||||
specialFormatsFallback.includes(props.src.format)
|
||||
) {
|
||||
resultFallbackFormat = props.src.format;
|
||||
}
|
||||
|
||||
const fallbackImage = await getImage({
|
||||
...props,
|
||||
format: fallbackFormat,
|
||||
format: resultFallbackFormat,
|
||||
widths: props.widths,
|
||||
densities: props.densities,
|
||||
});
|
||||
|
|
|
@ -198,6 +198,13 @@ export const baseService: Omit<LocalImageService, 'transform'> = {
|
|||
if (options.src.format === 'svg') {
|
||||
options.format = 'svg';
|
||||
}
|
||||
|
||||
if (
|
||||
(options.src.format === 'svg' && options.format !== 'svg') ||
|
||||
(options.src.format !== 'svg' && options.format === 'svg')
|
||||
) {
|
||||
throw new AstroError(AstroErrorData.UnsupportedImageConversion);
|
||||
}
|
||||
}
|
||||
|
||||
// If the user didn't specify a format, we'll default to `webp`. It offers the best ratio of compatibility / quality
|
||||
|
|
|
@ -554,6 +554,21 @@ export const UnsupportedImageFormat = {
|
|||
)} are supported by our image services.`,
|
||||
hint: "Using an `img` tag directly instead of the `Image` component might be what you're looking for.",
|
||||
} satisfies ErrorData;
|
||||
|
||||
/**
|
||||
* @docs
|
||||
* @see
|
||||
* - [Images](https://docs.astro.build/en/guides/images/)
|
||||
* @description
|
||||
* Astro does not currently supporting converting between vector (such as SVGs) and raster (such as PNGs and JPEGs) images.
|
||||
*/
|
||||
export const UnsupportedImageConversion = {
|
||||
name: 'UnsupportedImageConversion',
|
||||
title: 'Unsupported image conversion',
|
||||
message:
|
||||
'Converting between vector (such as SVGs) and raster (such as PNGs and JPEGs) images is not currently supported.',
|
||||
} satisfies ErrorData;
|
||||
|
||||
/**
|
||||
* @docs
|
||||
* @see
|
||||
|
|
|
@ -195,8 +195,16 @@ describe('astro:image', () => {
|
|||
let html = await res.text();
|
||||
$ = cheerio.load(html);
|
||||
|
||||
// Fallback format
|
||||
let $img = $('#picture-fallback img');
|
||||
expect($img).to.have.a.lengthOf(1);
|
||||
|
||||
const imageURL = new URL($img.attr('src'), 'http://localhost');
|
||||
expect(imageURL.searchParams.get('f')).to.equal('jpeg');
|
||||
expect($img.attr('fallbackformat')).to.be.undefined;
|
||||
|
||||
// Densities
|
||||
let $img = $('#picture-density-2-format img');
|
||||
$img = $('#picture-density-2-format img');
|
||||
let $picture = $('#picture-density-2-format picture');
|
||||
let $source = $('#picture-density-2-format source');
|
||||
expect($img).to.have.a.lengthOf(1);
|
||||
|
|
|
@ -10,3 +10,7 @@ import myImage from "../assets/penguin1.jpg";
|
|||
<div id="picture-widths">
|
||||
<Picture src={myImage} width={Math.round(myImage.width / 2)} alt="A penguin" widths={[myImage.width]} />
|
||||
</div>
|
||||
|
||||
<div id="picture-fallback">
|
||||
<Picture src={myImage} fallbackFormat="jpeg" alt="A penguin" />
|
||||
</div>
|
||||
|
|
Loading…
Add table
Reference in a new issue