Skip to content

fix(storefront): Keep CMS preview working when a new image isn't deployed yet - #880

Open
vitorrgg wants to merge 2 commits into
mainfrom
fix/storefront-picture-missing-image-size
Open

vitorrgg wants to merge 2 commits into
mainfrom
fix/storefront-picture-missing-image-size

Conversation

@vitorrgg

Copy link
Copy Markdown
Member

Problema

No painel de prévia do CMS, trocar a imagem de um banner por um upload novo derruba a prévia inteira com 504 (Gateway Timeout).

A prévia é renderizada pela função SSR que já está em produção (/~preview/?content:...). A imagem nova ainda não consta do images.src.csv do deploy atual. Isso vale para upload não salvo, para rascunho salvo e até para os ~5 min de deploy depois de publicar. Sem as dimensões, o useSSRPicture não resolve o aspectRatio, e o getPicture lança `aspectRatio` must be provided for remote images. O erro estoura no meio do stream do Astro (There was an uncaught error in the middle of the stream), e a resposta fica pendurada até o timeout do Cloud Run (10 s na loja onde reproduzi).

Reproduzido numa loja em produção trocando o 1º slide do hero:

  • Imagem já publicada: prévia 200.
  • Imagem publicada, mas sem versão otimizada: prévia 200, cai no JPG original (Could not match built … image).
  • Imagem que ainda não está no deploy: 504 em 10 s, com o erro acima no log.

O código envolvido não muda desde 2024–2025, então não é regressão. O problema ficou visível porque o preview do PR do CMS estava falhando por outro motivo: sem ele, o lojista não tinha outro jeito de ver a imagem nova antes de publicar.

Solução

Quando não há como saber o tamanho da imagem, o useSSRPicture não chama mais o getPicture. Ele renderiza o arquivo original (<img src> sem os <source> otimizados) e loga um aviso.

  • Imagens com tamanho conhecido: o getPicture é chamado exatamente como antes. Sem aspectRatio ele sempre lançava erro, então a mudança só troca esse erro pelo fallback.
  • Os dois componentes que usam o hook mudam: Picture.runtime.astro (SSR) e Picture.astro (build estático). No build estático, uma imagem referenciada que não existe em public/ deixa de derrubar a renderização da página e passa a sair como <img> quebrado, com o aviso no log.

O que isso não resolve

  • A imagem nova continua sem aparecer na prévia, porque o arquivo ainda não existe no servidor. O que muda é que o resto da página renderiza e as outras edições continuam visíveis. Para mostrar a imagem de fato, a prévia precisaria usar o blob que o Decap guarda (getAsset). Isso fica para outra PR.
  • Um erro no meio do stream continua segurando a resposta até o timeout em vez de falhar na hora (serve-storefront). Qualquer outro componente que lance erro durante o SSR tem o mesmo efeito.

Testes

  • packages/storefront/tests/ssr-picture.test.ts: com tamanho desconhecido, renderiza o original sem chamar getPicture; com tamanho conhecido, mantém as fontes otimizadas. O primeiro caso falha sem a correção, com o mesmo erro do log de produção.
  • Suíte do storefront: vitest run passa 8/8. O ESLint com a config do storefront está limpo.
  • Ponta a ponta: não testei numa loja com o pacote alterado.

🤖 Generated with Claude Code

…oyed yet

Editing a banner with a newly uploaded image made the whole CMS preview fail
with a gateway timeout: the image is not on the deployed images manifest yet,
so `useSSRPicture` could not resolve its size and `getPicture` threw
"`aspectRatio` must be provided for remote images" in the middle of the SSR
stream, hanging the response until the Cloud Run request timeout.

When the image size is unknown, `useSSRPicture` now renders the original file
(plain `<img src>`, no optimized sources) and logs a warning instead of
throwing. Images with known size keep the same optimized output.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@vitorrgg vitorrgg left a comment

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.

Revisão

A correção está certa e resolve o que se propõe: nada bloqueante. Dois ajustes pequenos (itens 1 e 2) entram em um commit a seguir nesta mesma branch; os itens 3 e 4 ficam como registro.

O que foi conferido

  • Premissa da PR: sem aspectRatio o getPicture sempre lançava erro. Confere: tanto getBuiltPicture (runtime) quanto o getter do Picture.astro passam por createPictureGetter, que lança antes de qualquer outra coisa. Então o caminho com tamanho conhecido não muda.
  • Testes: vitest run passa 8/8. Com o use-ssr-picture.ts da main, o primeiro caso falha com `aspectRatio` must be provided for remote images, o mesmo erro do log de produção.
  • Lint: ESLint com a config do storefront sem avisos nos dois arquivos.
  • Não conferido: ponta a ponta numa loja com o pacote alterado (igual ao que a PR já declara).

Achados

1. Picture.astro monta uma URL inválida no novo fallback (baixo)

src/lib/components/Picture.astro:86 faz imgAttrs.src += versionSuffix, e o sufixo é &V=<rand>. Isso pressupõe um src que já tem query string (/_image?href=...). Com o fallback, o src passa a ser o caminho original, e o resultado é /img/uploads/banner.jpg&V=123.

Só afeta build não estático sem DEPLOY_RUNTIME=serverless e com DEPLOY_RAND definido (no build estático o sufixo é vazio; no serverless o componente é trocado pelo Picture.runtime.astro). Mesmo assim, é justamente o caso em que o arquivo existe mas o tamanho não pôde ser lido, ou a imagem é remota, e a URL original funcionaria. Ajuste: usar ?V= quando o src não tem query string.

2. Teste deixa o console.warn mockado e não verifica o aviso (baixo)

Comentário na linha.

3. Aviso impreciso quando width e height são passados sem aspectRatio (nit, sem alteração)

Comentário na linha.

4. O build estático deixa de falhar com imagem inexistente (decisão a confirmar)

O build-prod.sh renderiza a home no passo estático com o Picture.astro. Antes, um banner apontando para um arquivo que não existe em public/ quebrava o build e o deploy não saía. Agora o deploy passa com um <img> quebrado e só um console.warn no log do build. A PR já descreve isso, e acho o comportamento novo melhor (uma referência errada não trava a loja inteira), mas vale a confirmação de quem mantém, porque o erro deixa de ser visível para o lojista.

describe('SSR <picture> attributes', () => {
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
vi.spyOn(console, 'warn').mockImplementation(() => {});

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.

O spy do console.warn nunca é restaurado, então continua mockado no teste seguinte (e em qualquer outro que entrar neste arquivo), escondendo avisos reais. O teste também não verifica que o aviso foi emitido, que é a única pista que sobra no log quando o fallback acontece.

Ajuste: afterEach(() => vi.restoreAllMocks()) e um expect(warn).toHaveBeenCalledWith(expect.stringContaining('/img/uploads/new-banner.jpg')).

// 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.

…thout optimization

When the image size is unknown, `Picture.astro` now renders the original file,
but still appended the `&V=` version suffix that assumes an `/_image?...` URL,
producing `/img/banner.jpg&V=123`. Use `?V=` when the URL has no query string.

Also restore the `console.warn` spy between tests and assert the fallback
warning is logged.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
@vitorrgg

Copy link
Copy Markdown
Member Author

Ajustes da revisão aplicados em 750a46d: itens 1 (sufixo ?V= no Picture.astro quando o src não tem query string) e 2 (spy do console.warn restaurado e aviso verificado no teste). vitest run segue 8/8 e o ESLint limpo. Os itens 3 e 4 ficam sem alteração, como descrito na revisão.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant