fix(navigation): coerce findPageHeadline return value to string#3786
fix(navigation): coerce findPageHeadline return value to string#3786
Conversation
When a .navigation.yml title contains a colon (e.g. "Getting Started: Basics"), YAML parsers may interpret it as an object instead of a string. findPageHeadline returns link.title directly, which surfaces as [object Object] in consuming components like Docus page headers. Coerce the title to a string before returning to guarantee the function's return type contract is met regardless of YAML parsing quirks. Fixes nuxt#3754
|
Someone is attempting to deploy a commit to the Nuxt Team on Vercel. A member of the Team first needs to authorize it. |
commit: |
📝 WalkthroughWalkthroughThe change modifies the Estimated code review effort🎯 2 (Simple) | ⏱️ ~5 minutes 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
src/runtime/utils/index.ts (1)
68-68: 💤 Low valueMinor: extract the coercion to avoid duplication.
The expression
typeof link.title === 'string' ? link.title : String(link.title ?? '')is duplicated verbatim across two branches. If you keep the current coercion strategy (rather than adopting one of the options above), consider extracting it into a small local helper so future tweaks happen in one place.+const toTitleString = (t: unknown): string => + typeof t === 'string' ? t : String(t ?? '')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, Extract the duplicated coercion logic for link.title into a small local helper (e.g., coerceLinkTitle or formatLinkTitle) and replace both occurrences of the expression typeof link.title === 'string' ? link.title : String(link.title ?? '') with a call to that helper; ensure the helper accepts the link (or title) and returns the same string result so future changes are centralized and both branches use the single helper implementation.
🤖 Prompt for all review comments with 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.
Inline comments:
In `@src/runtime/utils/index.ts`:
- 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.
---
Nitpick comments:
In `@src/runtime/utils/index.ts`:
- Line 68: Extract the duplicated coercion logic for link.title into a small
local helper (e.g., coerceLinkTitle or formatLinkTitle) and replace both
occurrences of the expression typeof link.title === 'string' ? link.title :
String(link.title ?? '') with a call to that helper; ensure the helper accepts
the link (or title) and returns the same string result so future changes are
centralized and both branches use the single helper implementation.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 4914b299-8973-4651-aecd-5563e58ea398
📒 Files selected for processing (1)
src/runtime/utils/index.ts
| for (const child of link.children) { | ||
| if (child.path === path) { | ||
| return link.title | ||
| return typeof link.title === 'string' ? link.title : String(link.title ?? '') |
There was a problem hiding this comment.
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:
- Reconstructing the original string from the parsed object (best-effort), or
- Returning
undefinedfor 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 : undefinedThis 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.
Description
When a
.navigation.ymltitle contains a colon (e.g.Getting Started: Basics), YAML parsers may interpret it as an object ({ "Getting Started": "Basics" }) instead of a string.findPageHeadlinereturnslink.titledirectly, which surfaces as[object Object]in consuming components like Docus page headers.Reproduction
content/1.getting-started/.navigation.yml:nuxt generate[object Object]as the headlineRoot Cause
link.titleis typed asstringbut YAML parsing can produce an object at runtime.findPageHeadlinereturns it without validation.Fix
Coerce
link.titleto a string before returning, ensuring the function's return type contract is always met:This is a defensive measure — the ideal fix would be upstream in the YAML-to-navigation pipeline, but this ensures consumers never receive non-string values regardless of input.
Changes
src/runtime/utils/index.ts: Coercelink.titleat both return points infindPageHeadlineFixes #3754