From 6b2d7a25905dc550da4d924c11071de7019f93e0 Mon Sep 17 00:00:00 2001
From: Mostafa Sadeghi <205455727+mostafasadeghidev@users.noreply.github.com>
Date: Mon, 3 Aug 2026 05:50:53 +0200
Subject: [PATCH] fix(publish): interpolate entry tokens in page meta title and
description
{currentEntry.*} / {page.*} / {site.*} tokens in site metaTitle,
metaDescription, and the page-title fallback were published verbatim
(and pre-escape, effectively static), so every CMS entry route rendered
the template page's static
instead of a per-entry SEO title.
buildDocumentMetaTags now receives the composed TemplateRenderDataContext
(the same frames dynamic text bindings resolve against) and runs the
title + description through interpolateTokens before escapeHtml. Entry
routes resolve {currentEntry.name} from the entryStack seeded by
renderPublishedDataRowTemplate; plain pages resolve entry tokens to ''
(or the token's own |fallback) with no leaked placeholder syntax, and
token-free strings are untouched via the containsTokens fast path.
Co-Authored-By: Claude Fable 5
---
docs/features/publisher.md | 4 +-
docs/features/templates.md | 2 +
src/__tests__/publisher/render.test.ts | 70 ++++++++++++++++++++++++++
src/core/publisher/render.ts | 30 +++++++++--
4 files changed, 99 insertions(+), 7 deletions(-)
diff --git a/docs/features/publisher.md b/docs/features/publisher.md
index c87e8eb12..a8b32f0dd 100644
--- a/docs/features/publisher.md
+++ b/docs/features/publisher.md
@@ -327,8 +327,8 @@ The publisher emits `` in this order:
1. ``
2. ``
-3. `` from `page.title`
-4. `` if present in page settings
+3. `` — `site.settings.metaTitle` → `page.title` → `site.name`, token-interpolated against the render context before escaping, so `{currentEntry.*}` resolves per-entry on entry routes (e.g. `{currentEntry.name} | Acme`) and `{page.*}` / `{site.*}` / `{route.*}` work everywhere
+4. `` if `site.settings.metaDescription` is set — same token interpolation
5. `` if a favicon is configured
6. `"' } }],
+ },
+ })
+ expect(html).not.toContain('