From 7c71818027f2f9555aa8802e83c90eef90b82cb8 Mon Sep 17 00:00:00 2001 From: Tobias Gesellchen Date: Tue, 26 May 2026 00:01:45 +0200 Subject: [PATCH] fix(docs): resolve .md links to page RelPermalink in render hook Stripping the .md extension alone is not enough under Hugo pretty URLs. A page rendered at /guides/DEPLOYMENT-OVERVIEW/ treats a bare relative href like 'CLOUD-DEPLOY-WALKTHROUGH' as relative to that directory, producing /guides/DEPLOYMENT-OVERVIEW/CLOUD-DEPLOY-WALKTHROUGH (404). Switch to site.GetPage to look up the target page by its content path (resolved relative to the current file's directory) and write its RelPermalink into the href. This gives an absolute path that is correct in both the dev server and the GitHub Pages build (where --baseURL injects the /Bose-SoundTouch/ prefix via RelPermalink automatically). Also handles anchored links (OTHER.md#section) and falls back to bare-stripped path when GetPage finds no match. Co-Authored-By: Claude Sonnet 4.6 --- .../layouts/_default/_markup/render-link.html | 40 +++++++++++++++---- 1 file changed, 33 insertions(+), 7 deletions(-) diff --git a/docs/layouts/_default/_markup/render-link.html b/docs/layouts/_default/_markup/render-link.html index 643e356..2d614b8 100644 --- a/docs/layouts/_default/_markup/render-link.html +++ b/docs/layouts/_default/_markup/render-link.html @@ -1,15 +1,41 @@ {{- /* - Render hook: strip .md extension from internal links so that links - written as [foo](OTHER-PAGE.md) in Markdown source resolve to the - Hugo page URL instead of a raw .md path (which returns 404). + Render hook: resolve internal .md links to their Hugo RelPermalink. - This replaces the jekyll-relative-links behaviour from the old Jekyll - site without requiring changes to any content files. + Plain "strip .md" is not enough under pretty URLs: a page served at + /guides/DEPLOYMENT-OVERVIEW/ would resolve a bare relative href + "CLOUD-DEPLOY-WALKTHROUGH" to /guides/DEPLOYMENT-OVERVIEW/CLOUD-DEPLOY-WALKTHROUGH + (404). Using site.GetPage gives the canonical /guides/CLOUD-DEPLOY-WALKTHROUGH/. + + Handles: + - same-directory links: CLOUD-DEPLOY-WALKTHROUGH.md + - cross-directory links: ../architecture/DEVICE-LOCAL-INSTALL.md + - anchored links: OTHER-PAGE.md#section + - external links (http/https): passed through unchanged, opened in new tab */ -}} {{- $dest := .Destination -}} {{- $isAbs := or (strings.HasPrefix $dest "http://") (strings.HasPrefix $dest "https://") (strings.HasPrefix $dest "//") -}} -{{- if and (not $isAbs) (strings.HasSuffix $dest ".md") -}} - {{- $dest = strings.TrimSuffix ".md" $dest -}} +{{- if not $isAbs -}} + {{- /* Split off any fragment (#anchor) */ -}} + {{- $fragment := "" -}} + {{- $base := $dest -}} + {{- if strings.Contains $dest "#" -}} + {{- $parts := split $dest "#" -}} + {{- $base = index $parts 0 -}} + {{- $fragment = printf "#%s" (index $parts 1) -}} + {{- end -}} + {{- if strings.HasSuffix $base ".md" -}} + {{- $pathNoExt := strings.TrimSuffix ".md" $base -}} + {{- /* Resolve relative to the current content file's directory */ -}} + {{- $dir := "" -}} + {{- with .Page.File }}{{ $dir = .Dir }}{{ end -}} + {{- $resolved := site.GetPage (path.Join $dir $pathNoExt) -}} + {{- if $resolved -}} + {{- $dest = printf "%s%s" $resolved.RelPermalink $fragment -}} + {{- else -}} + {{- /* Fallback: strip .md (better than leaving the extension) */ -}} + {{- $dest = printf "%s%s" $pathNoExt $fragment -}} + {{- end -}} + {{- end -}} {{- end -}}