https://github.com/didrod205/docsanity
One command for docs-site health: broken links, orphan pages, duplicate titles/descriptions, readability & Markdown structure β unified into one score. Local, deterministic, no API key. Docusaurus/Astro/Nextra/Hugo.
https://github.com/didrod205/docsanity
astro broken-link-checker cli docs docs-linter documentation docusaurus link-checker markdown mdx nextra orphan-pages readability seo static-site-generator typescript
Last synced: about 11 hours ago
JSON representation
One command for docs-site health: broken links, orphan pages, duplicate titles/descriptions, readability & Markdown structure β unified into one score. Local, deterministic, no API key. Docusaurus/Astro/Nextra/Hugo.
- Host: GitHub
- URL: https://github.com/didrod205/docsanity
- Owner: didrod205
- License: mit
- Created: 2026-06-04T00:53:39.000Z (about 1 month ago)
- Default Branch: main
- Last Pushed: 2026-06-29T07:24:10.000Z (6 days ago)
- Last Synced: 2026-06-29T09:17:11.911Z (6 days ago)
- Topics: astro, broken-link-checker, cli, docs, docs-linter, documentation, docusaurus, link-checker, markdown, mdx, nextra, orphan-pages, readability, seo, static-site-generator, typescript
- Language: TypeScript
- Size: 51.8 KB
- Stars: 1
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- Contributing: CONTRIBUTING.md
- Funding: .github/FUNDING.yml
- License: LICENSE
- Code of conduct: CODE_OF_CONDUCT.md
Awesome Lists containing this project
README
# π©Ί docsanity
### One command for docs-site health β broken links, orphan pages, SEO, readability & structure in a single score.
[](https://www.npmjs.com/package/docsanity)
[](https://github.com/didrod205/docsanity/actions/workflows/ci.yml)
[](https://www.npmjs.com/package/docsanity)
[](./LICENSE)
Your docs site looks fine β until a reader hits a 404 from a link you moved, lands
on a page Google shows with a *duplicate* title, or bounces off a wall of
grad-school prose. These problems live **between** files (which page links where,
which titles repeat, what's orphaned), so single-file linters miss them and no one
notices until it ships.
**docsanity audits a whole docs directory in one pass** and unifies four health
dimensions into one score and one report β **100% locally, no API key:**
- π **Links & references** β broken relative links, dead `#anchors`, missing
images, and **orphan pages** nothing links to.
- π **SEO & frontmatter** β missing/oversized `title` & `description`, plus
**site-wide duplicate titles and descriptions** across every page.
- π **Readability** β a per-page reading grade; flags pages that read too hard.
- βΏ **Structure & a11y** β single H1, no skipped heading levels, image alt text,
fenced code blocks that declare a language.
```bash
npx docsanity scan ./docs
```
```
broken.md 50/100 (F)
β L5 Link target not found: "./nope.md" links.broken-file-link
β L5 Anchor "#does-not-exist" not found in index.md links.broken-cross-anchor
β L7 Image has no alt text: ./diagram.png structure.image-alt
orphan.md 88/100 (B)
β Orphan page β nothing links here links.orphan-document
β Duplicate title β also used by 1 other page(s) seo.duplicate-title
Overall 82/100 (B) Β· 5 page(s), 4 error(s), 10 warning(s)
```
---
## Why a suite (and not four separate tools)?
The valuable checks need the **whole-site view**, which only a unified pass can
give you:
- **Orphan pages** and **broken cross-file anchors** require the full page graph.
- **Duplicate titles/descriptions** require comparing every page to every other.
- A single **score + grade** you can gate in CI means one number to watch, not four
tools to wire up and reconcile.
docsanity builds the page graph once and runs every dimension over it, then folds
everything into a per-page and per-dimension report. Under the hood it leverages
the focused engines [`linklint`](https://www.npmjs.com/package/@didrod2539/linklint)
(link graph) and [`readlevel`](https://www.npmjs.com/package/@didrod2539/readlevel)
(readability), and adds the SEO-frontmatter and structure checks that tie them into
a docs-site audit.
Why not just ask an LLM to "check my docs"? It can't hold 300 interlinked files in
context, it can't run in CI deterministically, and "did this link break" is a graph
lookup, not a guess.
## Install
```bash
# run it now, no install
npx docsanity scan ./docs
# or add it
npm install -g docsanity # global CLI
npm install -D docsanity # CI dependency
```
Node β₯ 18. Works with Docusaurus, Astro, Nextra, Hugo, Jekyll, VitePress β any
Markdown/MDX docs tree.
## Quick start
```bash
docsanity scan ./docs # audit a folder
docsanity scan ./docs --min-score 85 # CI gate: fail under 85
docsanity scan ./docs --md docs-report.md # write a Markdown report
docsanity scan ./docs --disable readability # turn a dimension off
docsanity init # write docsanity.config.json
```
See [`examples/sample-report.md`](./examples/sample-report.md) and
[`examples/sample-report.json`](./examples/sample-report.json) for full reports.
## Real scenarios
**1. CI gate for your docs.** A PR that moves a page and forgets to fix the inbound
links β or ships a new page with no description β fails the build:
```yaml
# .github/workflows/docs.yml
- run: npx docsanity scan ./docs --min-score 90 --md docs-report.md
```
**2. Pre-launch audit of an inherited site.** Point it at a docs tree you didn't
write and instantly see the orphans, duplicate titles and dead anchors that
accumulated over time β one report, ranked by page score.
**3. Keep AI answer engines happy.** Clear, well-structured, well-linked pages are
what answer engines quote. Fix the readability and structure findings to make your
docs the source they cite.
## What each dimension checks
| Dimension | Checks |
| --------- | ------ |
| **Links & references** | broken relative links, dead `#anchors`, broken cross-file anchors, missing images, undefined references, **orphan pages** |
| **SEO & frontmatter** | required `title`/`description`, length windows, H1-as-title fallback, **site-wide duplicate titles & descriptions** |
| **Readability** | FleschβKincaid / Gunning Fog / SMOG average grade per page, flagged against a configurable ceiling |
| **Structure & a11y** | single H1, no skipped heading levels, image alt text, code-fence language tags |
Scoring is transparent: each finding is a weighted error / warning / info; pages
roll up to a 0β100 score and an AβF grade, averaged into the overall.
## Configuration
`docsanity init` writes `docsanity.config.json`:
```jsonc
{
"extensions": [".md", ".mdx", ".markdown"],
"requireFrontmatter": ["title", "description"],
"descriptionMin": 50,
"descriptionMax": 160,
"titleMax": 60,
"maxGrade": 14,
"minWordsForReadability": 80,
"disable": [], // e.g. ["readability"]
"ignore": [], // rule ids, e.g. ["structure.code-language"]
"minScore": 0 // CI gate threshold
}
```
## Library API
```ts
import { analyzeDocs } from "docsanity";
const report = analyzeDocs(root, inputs, config, {
version: "1.0.0",
generatedAt: new Date().toISOString(),
});
for (const page of report.pages) {
console.log(page.path, page.score, page.findings.length);
}
```
Also exported: `parsePage`, `parseFrontmatter`, `parseMarkdown`, `seoChecks`,
`structureChecks`, `readabilityCheck`, `linkFindings`, `DEFAULT_CONFIG`, and types.
## Roadmap
- More frontmatter conventions (Hugo/Jekyll specifics, custom required schemas).
- HTML docs input (not just Markdown/MDX).
- Per-rule severity overrides and inline ignore comments.
- `--baseline` to fail only on *new* issues vs a committed snapshot.
- A web playground (drop a zip of docs, get the report β nothing uploaded).
## π Sponsor
docsanity is free and MIT-licensed, built and maintained in spare time. If it
caught a broken link before your readers did, please consider supporting it:
- β **Star this repo** β the simplest free way to help others find it.
- π **[Sponsor via Lemon Squeezy](https://elab-studio.lemonsqueezy.com/checkout/buy/5d059b89-51d0-456b-b33a-ed56994f7010)** β one-time or recurring.
## License
[MIT](./LICENSE) Β© docsanity contributors