Skip to content
Open
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
31 changes: 21 additions & 10 deletions packages/storefront/src/images/use-ssr-picture.ts
Original file line number Diff line number Diff line change
Expand Up @@ -209,16 +209,27 @@ const useSSRPicture = async (params: UsePictureParams) => {
sizes = `${(widths[0] / 2)}px`;
}

const { image, sources } = await getPicture({
src,
widths,
formats,
aspectRatio,
fit,
background,
position,
alt,
});
let image: GetPictureResult['image'];
let sources: GetPictureResult['sources'];
if (aspectRatio) {
({ image, sources } = await getPicture({
src,
widths,
formats,
aspectRatio,
fit,
background,
position,
alt,
}));
} else {
// Unknown image size, e.g. a new CMS upload not deployed yet (CMS preview).
// `getPicture` would throw in the middle of the SSR stream and hang the response,
// so render the original file instead
console.warn(`Could not get size of image ${src}, rendering original file`);

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Nit, sem alteração nesta PR: este else também pega quem passa width e height sem aspectRatio (linha 113 pula o tryImageSize nesse caso) e um aspectRatio inválido como "16/9" (vira NaN). Nos dois o aviso diz que não conseguiu ler o tamanho da imagem, o que não é a causa.

Antes esses casos lançavam erro, então ninguém depende deles e o fallback continua sendo melhor que o erro. Um follow-up razoável seria derivar o aspectRatio de width / height quando os dois são informados.

image = { src };
sources = [];
}
delete image.width;
delete image.height;
if (import.meta.env.PROD && assetsPrefix && image.src?.charAt(0) === '/') {
Expand Down
7 changes: 5 additions & 2 deletions packages/storefront/src/lib/components/Picture.astro
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,11 @@ const {
}),
assetsPrefix: Astro.locals.assetsPrefix,
});
if (imgAttrs?.src) {
imgAttrs.src += versionSuffix;
if (imgAttrs?.src && versionSuffix) {
// Original file fallback (unknown image size) has no query string yet
imgAttrs.src += imgAttrs.src.includes('?')
? versionSuffix
: versionSuffix.replace('&', '?');
}
---

Expand Down
59 changes: 59 additions & 0 deletions packages/storefront/tests/ssr-picture.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import {
describe,
test,
expect,
vi,
afterEach,
} from 'vitest';
import { useSSRPicture } from '~/images/use-ssr-picture';

const builtPicture = {
image: { src: '/_astro/banner_abc.jpg', width: 1024, height: 576 },
sources: [
{ type: 'image/avif', srcset: '/_astro/banner_abc.avif 640w,/_astro/banner_def.avif 1024w' },
{ type: 'image/webp', srcset: '/_astro/banner_abc.webp 640w,/_astro/banner_def.webp 1024w' },
],
};

describe('SSR <picture> attributes', () => {
afterEach(() => {
vi.restoreAllMocks();
});

test('renders the original file when the image size is unknown', async () => {
// e.g. a new upload on CMS preview, not listed on the deployed images manifest
const warn = vi.spyOn(console, 'warn').mockImplementation(() => {});
const getPicture = vi.fn(async () => {
throw new Error('`aspectRatio` must be provided for remote images');
});
const { sources, imgAttrs } = await useSSRPicture({
src: '/img/uploads/new-banner.jpg',
alt: 'New banner',
widths: [640, 1024],
tryImageSize: async () => ({}),
getPicture,
});
expect(getPicture).not.toHaveBeenCalled();
expect(warn).toHaveBeenCalledWith(expect.stringContaining('/img/uploads/new-banner.jpg'));
expect(sources).toEqual([]);
expect(imgAttrs?.src).toBe('/img/uploads/new-banner.jpg');
expect(imgAttrs?.alt).toBe('New banner');
});

test('keeps optimized sources when the image size is known', async () => {
const getPicture = vi.fn(async () => structuredClone(builtPicture));
const { sources, imgAttrs } = await useSSRPicture({
src: '/img/uploads/banner.jpg',
alt: 'Banner',
widths: [640, 1024],
tryImageSize: async () => ({ width: 1600, height: 900 }),
getPicture,
});
expect(getPicture).toHaveBeenCalledWith(expect.objectContaining({
src: '/img/uploads/banner.jpg',
aspectRatio: 1600 / 900,
}));
expect(sources).toEqual(builtPicture.sources);
expect(imgAttrs?.src).toBeNull();
});
});