From deb47a03c5ad8424e9a0fdd8c450e9f74c70a2bc Mon Sep 17 00:00:00 2001 From: Will Kahn-Greene Date: Sun, 30 Aug 2026 19:08:28 -0400 Subject: [PATCH] fix(linkindex): stop trimming leading/trailing hyphens from GithubSlug Traced the real github-slugger (the package GitHub extracted from its own rendering pipeline) and it trims nothing at all -- not a leading/trailing hyphen from stripped punctuation, and not even literal leading/trailing whitespace. GithubSlug's strings.Trim(s, "-") diverged from that, so a heading like "`--json` output" slugged to "json-output" instead of the "--json-output" GitHub actually assigns, breaking check's anchor matching for README.md's own "#--json-output" links (#120). --- internal/linkindex/linkindex.go | 14 +++++++++----- internal/linkindex/slug_test.go | 9 +++++---- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/internal/linkindex/linkindex.go b/internal/linkindex/linkindex.go index 93d94b6..c48acba 100644 --- a/internal/linkindex/linkindex.go +++ b/internal/linkindex/linkindex.go @@ -136,14 +136,18 @@ var ( whitespaceRunRE = regexp.MustCompile(`\s+`) ) -// GithubSlug replicates GitHub's heading-anchor slugger: lowercase; strip all -// but letters/digits/underscore/whitespace/hyphen; each whitespace char -// becomes one hyphen; trim leading/trailing hyphens. +// GithubSlug replicates GitHub's heading-anchor slugger (github-slugger, the +// package GitHub itself extracted from its rendering pipeline): lowercase; +// strip all but letters/digits/underscore/whitespace/hyphen; each whitespace +// char becomes one hyphen. Nothing is trimmed -- a leading/trailing hyphen +// survives whether it came from stripped punctuation ("`--json` output" +// becomes "--json-output", not "json-output") or from literal leading/ +// trailing whitespace in the heading text, matching the reference +// implementation, which calls no trim at all. func GithubSlug(heading string) string { s := strings.ToLower(heading) s = nonSlugRE.ReplaceAllString(s, "") - s = whitespaceRE.ReplaceAllString(s, "-") - return strings.Trim(s, "-") + return whitespaceRE.ReplaceAllString(s, "-") } // ConfluenceSlug replicates Confluence's scheme: preserve case and diff --git a/internal/linkindex/slug_test.go b/internal/linkindex/slug_test.go index b47fe77..fc5e78f 100644 --- a/internal/linkindex/slug_test.go +++ b/internal/linkindex/slug_test.go @@ -6,12 +6,13 @@ func TestGithubSlug(t *testing.T) { cases := map[string]string{ "Hello World": "hello-world", "Hello, World!": "hello-world", - " Leading/trailing": "leadingtrailing", - " Hello ": "hello", - "Café Menu": "café-menu", // Unicode letters are preserved, not stripped + " Leading/trailing": "--leadingtrailing", // leading whitespace is not trimmed + " Hello ": "-hello-", // nor is trailing + "Café Menu": "café-menu", // Unicode letters are preserved, not stripped "under_score": "under_score", "": "", - "---": "", // trims to nothing + "---": "---", // hyphens already in the heading are not trimmed either + "`--json` output": "--json-output", // punctuation-derived hyphens survive too (#120) } for in, want := range cases { if got := GithubSlug(in); got != want {