From d71a6ef5b108b24bb2736b69c50bca3b954c77dd Mon Sep 17 00:00:00 2001 From: Julien Danjou Date: Fri, 28 Aug 2026 15:24:47 +0200 Subject: [PATCH] fix(seo): restore the homepage OpenGraph image MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `getOgImageUrl` strips the leading and trailing slashes off the pathname to build the image filename. For the homepage that pathname is `/`, so stripping left an empty string and the lookup missed — every docs page had an OpenGraph image and the homepage shipped `` with no content. The homepage's collection id is `index`, which is what `getStaticPaths` names its image, so fall back to that when the slug comes out empty. Covered by a regression test that fails against the old expression. The generated-image set comes from the content collection and needs the Astro build pipeline, so the test stubs it and exercises the derivation, which is the half that was wrong. Change-Id: Ifb9a23ea2caa20d28489a4f21363d85ed5e3342c --- src/util/getOgImageUrl.test.ts | 30 ++++++++++++++++++++++++++++++ src/util/getOgImageUrl.ts | 6 +++++- 2 files changed, 35 insertions(+), 1 deletion(-) create mode 100644 src/util/getOgImageUrl.test.ts diff --git a/src/util/getOgImageUrl.test.ts b/src/util/getOgImageUrl.test.ts new file mode 100644 index 0000000000..a189aace58 --- /dev/null +++ b/src/util/getOgImageUrl.test.ts @@ -0,0 +1,30 @@ +import { describe, expect, it, vi } from 'vitest'; +import { getOgImageUrl } from './getOgImageUrl'; + +// `getOgImageUrl` looks a derived filename up in the set of images +// astro-og-canvas actually generated, which comes from the content collection +// and so needs the Astro build pipeline. Stub that set and test the derivation +// — the half where the homepage bug was. +vi.mock('../pages/open-graph/[...path]', () => ({ + getStaticPaths: async () => [ + { params: { path: 'index.png' } }, + { params: { path: 'merge-queue.png' } }, + ], +})); + +describe('getOgImageUrl', () => { + it('resolves the homepage to the index image', () => { + // Regression: stripping the slashes off `/` left an empty slug, so the + // homepage was the one page that shipped an empty `og:image`. + expect(getOgImageUrl('/')).toBe('/open-graph/index.png'); + }); + + it('resolves a normal page, with or without a trailing slash', () => { + expect(getOgImageUrl('/merge-queue')).toBe('/open-graph/merge-queue.png'); + expect(getOgImageUrl('/merge-queue/')).toBe('/open-graph/merge-queue.png'); + }); + + it('returns undefined when no image was generated', () => { + expect(getOgImageUrl('/not-a-page')).toBeUndefined(); + }); +}); diff --git a/src/util/getOgImageUrl.ts b/src/util/getOgImageUrl.ts index b4987ea872..ed43b9046a 100644 --- a/src/util/getOgImageUrl.ts +++ b/src/util/getOgImageUrl.ts @@ -20,6 +20,10 @@ const paths = new Set(routes.map(({ params }) => params.path)); * @returns Path to the OpenGraph image if found. Otherwise, `undefined`. */ export function getOgImageUrl(path: string): string | undefined { - const imagePath = path.replace(/^\//, '').replace(/\/$/, '') + '.png'; + // The homepage's collection id is `index`, so stripping its slashes leaves an + // empty string and the lookup misses — which is why the homepage shipped with + // an empty `og:image` while every other page had one. + const slug = path.replace(/^\//, '').replace(/\/$/, '') || 'index'; + const imagePath = slug + '.png'; if (paths.has(imagePath)) return '/open-graph/' + imagePath; }