Problem
The publish.html filter receives { siteId, pageId, slug }. For entry / collection routes, slug is the composed template's slug — the same value for every entry of a collection — not the entry's actual route. A filter therefore cannot build a per-page canonical or og:url: every /work/<slug> page would receive the same (wrong) URL, which is worse than emitting none.
Plain pages are fine (their slug ≈ path); only templated entry routes are affected.
The data already exists at that point
In server/publish/bakeDataRows.ts, each entry's real path is computed just before the pipeline runs:
const urlPath = publicRowPath(route.tableRouteBase, route.rowSlug) // e.g. /work/octane-render
const syntheticUrl = new URL(`http://localhost${urlPath}`)
const rendered = await renderPublishedDataRowTemplate(siteSnapshot, row, { db, url: syntheticUrl, ... })
const html = await applyPublishedHtmlPipeline(rendered, db)
applyPublishedHtmlPipeline then forwards rendered.slug (the composed template slug) to the filter, not urlPath.
Proposed solution
Add the page's public path to the publish.html (and publish.headers) filter context, sourced from the value already used to write the artefact:
hookBus.applyFilter('publish.html', html, { siteId, pageId, slug, path /* or url */ })
Plain pages pass their existing path; entry routes pass urlPath. Small change, and it unblocks correct canonical/OG URLs for any publish.html consumer.
Consumer / motivation
Built an SEO plugin that uses publish.html to add <title>, description, Open Graph and Twitter tags: https://github.com/seltzdesign/instatic-seo — it currently has to keep canonical/og:url off by default purely because of this gap. Happy to send a PR.
Problem
The
publish.htmlfilter receives{ siteId, pageId, slug }. For entry / collection routes,slugis the composed template's slug — the same value for every entry of a collection — not the entry's actual route. A filter therefore cannot build a per-pagecanonicalorog:url: every/work/<slug>page would receive the same (wrong) URL, which is worse than emitting none.Plain pages are fine (their
slug≈ path); only templated entry routes are affected.The data already exists at that point
In
server/publish/bakeDataRows.ts, each entry's real path is computed just before the pipeline runs:applyPublishedHtmlPipelinethen forwardsrendered.slug(the composed template slug) to the filter, noturlPath.Proposed solution
Add the page's public path to the
publish.html(andpublish.headers) filter context, sourced from the value already used to write the artefact:Plain pages pass their existing path; entry routes pass
urlPath. Small change, and it unblocks correct canonical/OG URLs for anypublish.htmlconsumer.Consumer / motivation
Built an SEO plugin that uses
publish.htmlto add<title>, description, Open Graph and Twitter tags: https://github.com/seltzdesign/instatic-seo — it currently has to keep canonical/og:urloff by default purely because of this gap. Happy to send a PR.