I’m trying to include validation for internal links (i.e. within the site) in the link render hook. Basically to make Hugo throw a warning upon building, if there is a mistyped link. It works mostly fine, but I get a false positive case, see below:
- Link to same page: clean (no warning)
- Correct link to other page: clean
- Wrong link: warning
- Correct link to other page heading: clean
- Wrong link to other page heading: warning
- Correct link to same page heading: clean
- Wrong link to same page heading: warning
- Correct link to other page anchor: clean
- Wrong link to other page anchor: warning
- Correct link to same page anchor: warning
- Wrong link to same page anchor: warning
I distinguish between headings and anchors because headings (# xyz, ## xyz and the such) become part of a page’s Fragments, but arbitrary anchors like <a id="xyz">...</a> do not.
Question 1: is there a way to have Hugo make any element with id attribute a valid fragment? This would solve everything.
I tried to find a workaround by detecting any element with the searched id in the .Content of the destination page with findRE. This actually works, except if the link leads to an element within the same page - see case 10 above.
Apparently -again- Hugo does not allow access to the .Content of the same page that contains the link from the render hook. It works fine for the .Content of other pages.
Question 2: is there a reason for not accessing the .Content of the same page through the link render hook, but other things like .Title or .Fragments are accessible? Do you think there can be a workaround for that?
Thanks ![]()
Content structure:
.
└── content/
└── posts/
├── post-1
└── post-2
Render hook:
{{- $u := urls.Parse .Destination -}}
{{- /* Initializing destination page and its title */ -}}
{{- $DestPage := .Page -}}
{{- $InternalLinkTitle := "" -}}
{{- /* Validation only if the link is internal, i.e. not absolute */ -}}
{{- if not $u.IsAbs -}}
{{- /* If there is a path, then get its page and title */ -}}
{{- if $u.Path -}}
{{- $DestPage = .Page.GetPage $u.Path -}}
{{- $InternalLinkTitle = $DestPage.Title -}}
{{- end -}}
{{- /* If a destination page exists (i.e. valid path or same page), validate the fragment */ -}}
{{- if $DestPage -}}
{{- /* If there is a fragment in the link */ -}}
{{- if $u.Fragment -}}
{{- /* If the fragment is not found in the destination page fragments */ -}}
{{- if not ($DestPage.Fragments.Identifiers.Contains $u.Fragment) -}}
{{- /* Look for the fragment in arbitrary element id attributes */ -}}
{{- $FragmentRegex := print "<\\S+?.*? id=\"" $u.Fragment "\".*?>.*?</\\S+?>" -}}
{{- $DetectedFragment := findRE $FragmentRegex $DestPage.Content 1 -}}
{{- /* Finally, if the fragment is not detected at all then give the warning */ -}}
{{- if not $DetectedFragment }}
{{- warnf "Broken link fragment in %s: [%s](%s)" .Position .Text .Destination -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{- /* If a destination page doesn't exist, the link is broken */ -}}
{{- else -}}
{{- warnf "Broken link path in %s: [%s](%s)" .Position .Text .Destination -}}
{{- end -}}
{{- end -}}
{{- /* Render the link */ -}}
<a href="{{ .Destination | safeURL }}"
{{- if $u.IsAbs -}}
{{- with .Title }} title="{{ . }}"{{ end }} target="_blank" rel="external"
{{- else -}}
{{- with $InternalLinkTitle }} title="{{ . | safeHTMLAttr }}"{{- end -}}
{{- end -}}>
{{- with .Text }}{{ . }}{{ end -}}
</a>
{{- /* Chomp trailing newline */ -}}
Page 1:
---
title: "Post 1"
author: ""
date: ""
---
## Section 1
[1. link to same page](/posts/post-1/)
[2. correct link to other page](/posts/post-2/)
[3. wrong link](/posts/post-3/)
[4. correct link to other page heading](/posts/post-2/#section-1)
[5. wrong link to other page heading](/posts/post-2/#section-3)
[6. correct link to same page heading](#section-2)
[7. wrong link to same page heading](#section-3)
[8. correct link to other page anchor](/posts/post-2/#anchor)
[9. wrong link to other page anchor](/posts/post-2/#anchor-2)
[10. correct link to same page anchor](#anchor)
[11. wrong link to same page anchor](#anchor-2)
## Section 2
<a id="anchor"></a>
Page 2:
---
title: "Post 2"
author: ""
date: ""
---
## Section 1
## Section 2
<a id="anchor"></a>
Warnings:
WARN Broken link path in "content\posts\post-1\index.md:14:1": [3. wrong link](/posts/post-3/)
WARN Broken link fragment in "content\posts\post-1\index.md:18:1": [5. wrong link to other page heading](/posts/post-2/#section-3)
WARN Broken link fragment in "content\posts\post-1\index.md:22:1": [7. wrong link to same page heading](#section-3)
WARN Broken link fragment in "content\posts\post-1\index.md:26:1": [9. wrong link to other page anchor](/posts/post-2/#anchor-2)
WARN Broken link fragment in "content\posts\post-1\index.md:28:1": [10. correct link to same page anchor](#anchor)
WARN Broken link fragment in "content\posts\post-1\index.md:30:1": [11. wrong link to same page anchor](#anchor-2)
Using Hugo v0.164.0