Skip to content
Open
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
4 changes: 2 additions & 2 deletions src/runtime/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ export function findPageHeadline(navigation?: ContentNavigationItem[], path?: st
}
for (const child of link.children) {
if (child.path === path) {
return link.title
return typeof link.title === 'string' ? link.title : String(link.title ?? '')
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Object titles still stringify to "[object Object]" — coercion satisfies the type but not the user-visible bug.

The motivating case from issue #3754 is YAML parsing a title like Getting Started: Basics into an object such as { "Getting Started": "Basics" }. For that input:

  • String({ "Getting Started": "Basics" })"[object Object]"

So while this change does fix the TypeScript return-type violation (and correctly maps undefined/null to ""), the rendered headline in Docus will still display "[object Object]" for the exact scenario this PR cites. The defensive coercion only helps for primitives like number/boolean.

If the goal is to keep this purely as a type-safety guard until the upstream YAML pipeline is fixed, that's reasonable — but consider either:

  1. Reconstructing the original string from the parsed object (best-effort), or
  2. Returning undefined for non-string titles so downstream consumers can fall back to another source instead of rendering garbage.
♻️ Option 1: Best-effort reconstruction of `key: value` titles from YAML-parsed objects

Extract a small helper and use it at both return points:

+function coerceTitle(title: unknown): string {
+  if (typeof title === 'string') return title
+  if (title == null) return ''
+  // YAML may parse "Foo: Bar" as { Foo: "Bar" }; reconstruct best-effort.
+  if (typeof title === 'object') {
+    const entries = Object.entries(title as Record<string, unknown>)
+    if (entries.length === 1) {
+      const [k, v] = entries[0]
+      return `${k}: ${typeof v === 'string' ? v : ''}`.trim()
+    }
+  }
+  return String(title)
+}

Then:

-            return typeof link.title === 'string' ? link.title : String(link.title ?? '')
+            return coerceTitle(link.title)

(applied at both line 68 and line 78)

♻️ Option 2: Return `undefined` so callers can fall back gracefully
-            return typeof link.title === 'string' ? link.title : String(link.title ?? '')
+            return typeof link.title === 'string' ? link.title : undefined

This keeps the function honest about its string | undefined contract and lets consumers decide how to handle missing/malformed titles, rather than rendering "[object Object]".

Also applies to: 78-78

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/runtime/utils/index.ts` at line 68, The current coercion returns "[object
Object]" for YAML-parsed object titles; replace the String(...) fallback with a
best-effort reconstruction helper: add a helper function
reconstructTitleFromObject(value: unknown): string | undefined that returns the
original string for primitives, returns "key: value" for single-key objects
(recursively stringifying nested primitives), joins array elements with ", " for
arrays, and returns undefined for other objects; then update both return sites
that currently use String(link.title ?? '') to instead call
reconstructTitleFromObject(link.title) so callers get a sensible title or
undefined to allow graceful fallback.

}
}
}
Expand All @@ -75,7 +75,7 @@ export function findPageHeadline(navigation?: ContentNavigationItem[], path?: st
for (const child of link.children) {
const isIndex = child.stem?.endsWith('/index')
if (child.path === path && !isIndex) {
return link.title
return typeof link.title === 'string' ? link.title : String(link.title ?? '')
}
}
const headline = findPageHeadline(link.children, path, options)
Expand Down
Loading