Skip to content

docs: SEO/GEO audit fixes, comparison pages, and broken doc validation repair - #978

Draft
samber wants to merge 15 commits into
masterfrom
docs/seo-geo-improvements
Draft

docs: SEO/GEO audit fixes, comparison pages, and broken doc validation repair#978
samber wants to merge 15 commits into
masterfrom
docs/seo-geo-improvements

Conversation

@samber

@samber samber commented Aug 20, 2026

Copy link
Copy Markdown
Owner

Summary

Follow-up to a deep SEO/GEO audit of the documentation site. Along the way, the
audit surfaced real bugs unrelated to search ranking (broken doc validation, an
entirely disabled CI job, 11 helpers with no rendering page, non-compiling
getting-started examples) — those are fixed here alongside the SEO/GEO work.

  • Every check-*.js doc validation script parsed frontmatter with a hand-rolled
    regex that silently returned empty arrays for multi-line YAML lists, so
    cross-reference checks were a no-op; the CI job that ran them was also
    commented out entirely. Both are fixed and re-enabled, which surfaced and
    let this PR auto-fix 273 broken similarHelpers/variantHelpers
    references, 2 duplicate slugs, and 11 orphaned iter helpers that had no
    page at all (404 in production).
  • 3 of 4 code examples in the getting-started guide didn't compile against
    the real signatures.
  • Added /compare/* and /guides/* pages (stdlib comparison, alternatives,
    go-funk migration, verified benchmarks, lo-vs-ro decision guide, Lodash
    equivalence table) and a /production-ready page — all external facts
    (stars, releases, coverage) checked against the GitHub API, pkg.go.dev and
    Codecov on 2026-08-20.
  • Regenerated llms.txt as a linked index (was 745 lines of prose with a
    fabricated benchmark and one working link) and added llms-full.txt.
  • Deduplicated titles/descriptions across the 27 helper category pages,
    removed a duplicated "still new and evolving" admonition that was the
    first extractable passage on every page, expanded the glossary, and added
    structured data (Person/WebSite/SoftwareSourceCode, FAQPage).
  • Added AGENTS.md and a package doc.go.

Each commit is scoped to one concern — see individual commit messages for the
full detail and reasoning.

Test plan

Verified locally before opening this PR: cd docs && npm run build (passes with
onBrokenLinks: 'throw'), cd docs && npm run check-docs (all cross-reference,
duplicate-slug and orphaned-page checks pass), and go build ./.... The new
/compare/*, /guides/* and /production-ready pages were also spot-checked
by serving the production build locally.

samber added 10 commits August 20, 2026 21:21
…CI validation

The docs/data/*.md frontmatter parser used by every check-*.js validation
script only understood single-line YAML arrays, so it silently returned
an empty list for every similarHelpers/variantHelpers field (all of them
use multi-line lists). Every cross-reference check has therefore been a
no-op since it was written, and the CI job that runs them was itself
commented out in .github/workflows/doc.yml.

- Replace the hand-rolled frontmatter parser with gray-matter, matching
  what the Docusaurus plugin itself already uses
- Fix check-helpers-visible-in-pages.js, which only recognized categories
  core/mutable/parallel and crashed on iter/experimental -- this is why
  11 documented iter/math and iter/condition helpers had no rendering
  page at all (404 in production)
- Fix check-filename-matches-frontmatter.js's category-to-file-prefix
  mapping (iter -> it-, experimental -> simd-), which produced 166
  false-positive mismatches and made the check unusable
- Add check-duplicate-slugs.js and wire everything into a single
  check-docs script, then re-enable the CI job that runs it
- Auto-fix 273 broken similarHelpers/variantHelpers references (wrong
  subcategory, or pointing at a variant collapsed into a parent card)
  and remove 43 references to helpers that don't exist anywhere in the
  documented corpus
- Fix 2 duplicate slugs (core-isnotnil, it-channelseq) that produced
  colliding HTML ids on the same page
- Add docs/docs/iter/math.md and docs/docs/iter/condition.md so the 11
  orphaned helpers are actually reachable
- Add generated-index links to the 5 top-level sidebar categories, which
  gives the "it" package (157 helpers, previously reachable only via
  in-category links) inbound links from every other category
…d guide

iter.Range(0, 1000) does not exist in the standard library (only Seq,
Seq2, Pull, Pull2), lom.Filter was called with a pointer against a
signature that takes the slice by value and returns the result, and
lop.Map was called with a worker-count argument the function does not
take. All three examples now match the real signatures in
it/math.go, mutable/slice.go and parallel/slice.go.
…eadiness page

The site had zero evaluation content: nothing to intercept "do I still
need lo now that Go has slices/maps", "is lo still maintained", or
"how do I migrate off go-funk". These are the queries where third-party
mirrors currently get cited instead of the site itself.

- /compare/samber-lo-vs-go-stdlib: function-by-function overlap with
  slices/maps, including an explicit "when to use the standard library
  instead" section
- /compare/samber-lo-alternatives: honest comparison with lancet,
  go-funk, go-linq and pie, with verified GitHub stats (stars, last
  push, last release) rather than assumptions
- /compare/samber-lo-vs-go-funk: migration guide with a full function
  mapping table, verified against go doc github.com/thoas/go-funk
- /compare/performance: real go test -benchmem results (not estimates)
  showing lo.Map's allocation profile is identical to a hand-written
  loop, and 4x lower than go-funk's reflection-based Map
- /guides/lo-and-ro: a decision guide (not a "vs" page) between lo
  (finite, in-memory collections) and ro (infinite, event-driven
  streams), including a combined usage example
- /guides/from-lodash: a Lodash-to-Go function equivalence table for
  developers coming from JavaScript/TypeScript
- /production-ready: verified adoption (21,408 stars, 12,533 importers
  per pkg.go.dev), test coverage (96.1% per Codecov) and versioning
  policy, for reviewers evaluating the dependency

FAQPage JSON-LD is included on the stdlib and go-funk comparison pages.
All external facts (stars, releases, coverage) were checked against
the GitHub API, pkg.go.dev and Codecov on 2026-08-20 rather than
carried over from assumption.
The previous llms.txt was 745 lines of prose with 7 URLs total, only one
of which pointed back into the site -- an agent reading it got function
names with no way to navigate to their documentation. It also asserted
a fabricated benchmark result (lo.Uniq shown as 5x slower than a manual
loop) and three different, all-incorrect helper counts (300+, 500+,
"hundreds").

llms.txt is now a short (6 KB) linked index generated from
docs/data/*.md: one entry per documented page, with its real helper
count and a same sample of helper names, plus links to the new
comparison/guide pages. The previous prose content moves to the new
llms-full.txt (277 KB), generated from the same source, for agents that
want the complete corpus in one file. Both are referenced from
robots.txt.
…ossary

- Deduplicate titles/descriptions across the 27 helper category pages.
  Four pages shared the literal title "Slice" (core/iter/mutable/
  parallel), with three more duplicated pairs (Map, Find, Channel,
  Intersect, String, Tuple, Type manipulation) -- none of them
  contained the word "Go". Every page now has a unique, keyword-bearing
  title and a 150+ character description naming its actual helpers.
- Remove the ":::warning Help improve this documentation, still new and
  evolving" admonition that was duplicated verbatim at the top of all
  27 pages -- it was the first extractable passage on every indexable
  page, ahead of the actual content. Replaced with each page's own
  description as an intro paragraph, and a short feedback link moved to
  the page footer.
- Fix the homepage <title> (was 155 characters with the site name
  repeated twice, since Docusaurus appends it automatically on top of a
  manually duplicated one) and add a stats banner (helper count, stars,
  importers, license) plus a link to the new /production-ready page.
- Rewrite /community with real numbers (contributors, stars, importers)
  instead of zero indexable content, and fix "Start contributing!"
  which linked out to a README anchor instead of /docs/contributing.
- Expand the glossary from 13 sparse, unlinked entries to 30, each
  cross-linked to the helpers and pages that use the term (iter.Seq,
  type constraints, debounce/throttle, tuples, set operations...).
- Add a global JSON-LD graph (Person, WebSite, SoftwareSourceCode) to
  docusaurus.config.ts headTags -- the homepage previously had no
  structured data at all. Also de-duplicate og:image/twitter:card/
  twitter:image/og:locale (Docusaurus already emits these from
  themeConfig.image and i18n.defaultLocale; a second, hardcoded copy
  produced conflicting tags on every page), fix og:type to use the
  `property` attribute instead of `name` (Open Graph requires it and
  silently ignores the wrong one), and add an ecosystem footer column
  linking to ro/do/mo/oops/hot.
- Correct the helper count (300+/500+/"hundreds", depending on the
  page) to the actual 449 in about.md.
- Add a documentation link near the top of the README, before the
  single existing mention was ever reached.
Two gaps in the existing tracking:

- Algolia search was only captured on Enter, but DocSearch is an
  as-you-type search -- most users never press it. Now also debounced
  on input, reading DocSearch's own result DOM to emit
  search_no_results (a direct, free signal of missing content, in the
  visitor's own words) or search_results with a hit count.
- No signal existed for which of the 449 helpers people actually look
  at, on pages that list dozens of them. helper_viewed fires once per
  helper per page view via IntersectionObserver, after it has been at
  least half visible for 2 seconds, with its position on the page.
…ty hero image

- Add docs/vercel.json to cache hashed assets and images for a year
  (they were previously served with a 4-hour max-age despite the
  filename hash making them immutable by construction).
- Mark the sponsor logo link as rel="sponsored" per Google's guidance
  for paid placements.
- Replace the /community hero image with a resized WebP: 6206x2224
  source down to the 1200x430 it's actually displayed at, 893 KB down
  to 76 KB.
…kg.go.dev

- AGENTS.md at the repo root: which of the four packages (lo, lo/it,
  lo/mutable, lo/parallel) to reach for, when the standard library is
  the better choice, and the naming/suffix conventions, aimed at
  coding agents editing this codebase or a consumer's.
- doc.go: the module had no package-level doc comment, so pkg.go.dev
  and `go doc` had nothing to show beyond the README rendering.
Nothing in check-docs actually compiled the Go embedded in docs/data/*.md
and docs/docs/**/*.md, so signature drift and copy-paste mistakes in
examples went unnoticed indefinitely (getting-started.md shipped three
non-compiling examples for a while). This wraps every fenced go block
into a standalone program and builds it against the local source tree via
a go.mod replace directive.

Wired into check-docs and the validate-doc CI job, which now needs Go and
goimports installed alongside Node.
docs/scripts/validate-go-code-blocks.js surfaced over 200 real
compilation bugs across ~97 files: variable names reused across
independent examples with incompatible types, missing type/helper
declarations, generic constraint violations (Clonable, comparable,
numeric-only), wrong argument types, and a few undefined cross-references
between separate example blocks in the same file.

Each fix keeps the original example's pedagogical intent and the file's
existing style; no frontmatter or signatures were changed.

@samber samber left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Wired validate-go-code-blocks.js into check-docs, which now catches signature drift and copy-paste mistakes in every Go example before merge (getting-started.md shipped 3 non-compiling examples unnoticed until now).
  • Fixed the ~200 real compilation bugs it found, in one grouped commit, keeping each example's original pedagogical intent.

Comment thread .github/workflows/doc.yml
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version-file: '../go.mod'

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

go-version-file is resolved relative to GITHUB_WORKSPACE, not the docs/ working-directory default set below (that default only applies to run: steps, not uses: steps) — hence ../go.mod.

Comment thread .github/workflows/doc.yml

# - name: Install dependencies
# run: npm i
- name: Install goimports

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

validate-go-code-blocks.js shells out to goimports (per-block, so one bad snippet doesn't block import resolution for the rest), so CI needs it installed alongside Go.

samber added 5 commits August 21, 2026 04:09
Drops content unrelated to the original SEO/GEO audit that had crept
into this branch:
- docs/data/*.md: revert the mechanical Go-example fixes (keep only
  validate-go-code-blocks.js itself, unwired from check-docs for now)
- doc.go: drop the package-level doc comment
- docs/scripts/check-*.js: revert to their pre-fix versions
- docs/static/img/*: restore the images removed as "unused" cleanup
- docs/docusaurus.config.ts: drop rationale comments explaining why
  certain meta tags are intentionally absent
Drops every docs/data/*.md and docs/scripts/utils.js change made on this
branch, including the earlier P0 cross-reference/frontmatter fixes --
out of scope for this PR going forward.
The "N helpers for..." sentence on each core/iter category page (and its
meta description) hardcoded a number that silently drifts every time a
helper is added or removed from that category. Added a HelperCount
component that reads the count from the same helpers-pages plugin data
HelperList already renders, and dropped the number from the static
frontmatter description entirely (it can't run a component at build time).
lo-and-ro is a comparison between two libraries, not a how-to guide, so
it belongs in compare/ alongside the other samber/lo comparisons rather
than in its own guides/ section (now removed along with from-lodash,
which had no other home). Updates every internal link that pointed at
the old /guides/* paths, and drops the maturity/versioning table from
the page (star counts and version numbers age too fast to maintain here).
Keeps only validate-go-code-blocks as an addition; every other check-*
script entry and check-docs itself go back to their master definitions,
out of scope for this PR going forward. Also restores the lo. prefix in
about.md's Uniq example.
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.

1 participant