@@ -22,18 +22,49 @@ jobs:
2222 runs-on : ubuntu-latest
2323 steps :
2424 - uses : actions/checkout@v4
25- - name : Check links
26- uses : lycheeverse/lychee-action@v2
27- with :
28- args : >-
29- --verbose
30- --no-progress
31- --exclude-mail
32- --offline
33- --base "."
34- --exclude "pages/projects/project-template.html"
35- --accept 200,429
36- "**/*.html"
37- env :
38- GITHUB_TOKEN : ${{secrets.GITHUB_TOKEN}}
25+ - name : Check local links and fragments
26+ shell : pwsh
27+ run : |
28+ $ErrorActionPreference = 'Stop'
29+ $files = Get-ChildItem -Recurse -File -Filter *.html
30+ $issues = New-Object System.Collections.Generic.List[string]
31+
32+ foreach ($f in $files) {
33+ $content = Get-Content $f.FullName -Raw
34+ $matches = [regex]::Matches($content, '(?:href|src)="([^"]+)"')
35+
36+ foreach ($m in $matches) {
37+ $u = $m.Groups[1].Value
38+ if ($u -match '^(https?:|mailto:|tel:|javascript:|data:)') { continue }
39+
40+ $parts = $u.Split('#', 2)
41+ $path = $parts[0]
42+ $frag = if ($parts.Count -gt 1) { $parts[1] } else { $null }
43+
44+ if ([string]::IsNullOrWhiteSpace($path)) {
45+ if ($frag -and $content -notmatch ('id="' + [regex]::Escape($frag) + '"')) {
46+ $issues.Add("$($f.FullName) -> #$frag (missing fragment)")
47+ }
48+ continue
49+ }
50+
51+ $target = Join-Path $f.DirectoryName $path
52+ if (-not (Test-Path $target)) {
53+ $issues.Add("$($f.FullName) -> $u (missing file)")
54+ continue
55+ }
56+
57+ if ($frag) {
58+ $tcontent = Get-Content $target -Raw
59+ if ($tcontent -notmatch ('id="' + [regex]::Escape($frag) + '"')) {
60+ $issues.Add("$($f.FullName) -> $u (missing fragment)")
61+ }
62+ }
63+ }
64+ }
65+
66+ if ($issues.Count -gt 0) {
67+ $issues | Sort-Object -Unique | ForEach-Object { Write-Error $_ }
68+ throw "Link check failed: $($issues.Count) issue(s)."
69+ }
3970
0 commit comments