Skip to content

feat: support Markdown anchor links in rendered READMEs - #5

Open
soramikan wants to merge 1 commit into
zig-toolbelt:mainfrom
soramikan:feat/markdown-anchor-links
Open

soramikan wants to merge 1 commit into
zig-toolbelt:mainfrom
soramikan:feat/markdown-anchor-links

Conversation

@soramikan

Copy link
Copy Markdown

Summary

Fixes #3 — rendered READMEs now emit GitHub-compatible heading id slugs (via marked-gfm-heading-id), so in-document Markdown links like [Install](#install) and README tables of contents update the URL hash and scroll to the target section. SvelteKit's router already scrolls to elements matching a #hash on internal navigation, and SSR output makes deep links work on initial load — no client-side JS was needed.

  • marked-gfm-heading-id@^4.1.4 (peer range marked >=13 <19, we use marked@18) produces identical slugs to GitHub, including foo/foo-1 dedup, and resets its slugger per document via a preprocess hook.
  • prose-headings:scroll-mt-20 keeps scrolled-to headings clear of the sticky h-14 header.
  • Existing pieces already supported this: rewriteRelativeUrls exempts # links and DOMPurify config allows id.

Refactoring along the way

  • Replaced the global marked.use(markedHighlight(...)) side effect with a createMarked() factory (src/lib/server/marked.ts). READMEs use createMarked(gfmHeadingId()); docs use createMarked(). Docs highlighting previously depended on module evaluation order — now deterministic. Global marked is no longer mutated anywhere (guarded by a regression test).
  • Extracted rewriteRelativeUrls + README_SANITIZE_OPTIONS into DB-free readme-html.ts so the pipeline is unit-testable without the DB import graph.
  • Renamed the install widget wrapper id="install"id="install-command" so a ## Install README heading can't be shadowed by it (no references to #install existed).

Known limitations (documented in tests)

  • DOMPurify's SANITIZE_DOM strips DOM-clobbering ids — a ## Constructor heading loses its id. GitHub solves this with a user-content- prefix, which would require rewriting # hrefs too; out of scope here.
  • Anchors only resolve while the Readme tab is mounted (tab state isn't URL-driven).
  • Cached package_content.readme HTML keeps old markup until refetch (TTL, default 24h). To roll out immediately: UPDATE package_content SET last_sync = NULL; — next view serves stale content once, then refreshes in the background.
  • package-lock.json also normalized stale peer: true flags and synced the lockfile version to 1.1.0 (npm side effect, harmless).

Test plan

  • npm run check — 0 errors / 0 warnings
  • npx vitest run --project server — 47/47 pass (10 new tests: slug generation, dedup, punctuation/non-ASCII, per-doc reset, # href survival through sanitize+rewrite, code-highlight coexistence, global-instance purity)
  • npm run build — succeeds
  • Manual: open a package whose README has a TOC → click anchor link (URL gains #…, page scrolls below header); load /packages/{owner}/{repo}#… directly → auto-scrolls

Rendered Markdown headings had no id attributes, so in-document links
like [Install](#install) and README tables of contents went nowhere.
Register marked-gfm-heading-id on the README pipeline to emit
GitHub-compatible slugs, and add scroll-mt so scrolled-to headings
clear the sticky header.

Markdown pipelines now build their Marked instance via a
createMarked() factory instead of mutating the global singleton:
READMEs get heading ids while docs keep highlight-only rendering,
deterministically rather than depending on module load order.
Also rename the install widget's id to install-command so a
"## Install" README heading cannot shadow it.

Closes zig-toolbelt#3
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support Markdown Anchor Links

1 participant