https://github.com/didrod205/linklint
Catch broken links & dead anchors in Markdown/HTML docs β relative links, GitHub-slug #anchors, cross-file anchors, images, references. Deterministic, offline, runs in CI. A fast internal-integrity link checker.
https://github.com/didrod205/linklint
anchor broken-link-checker cli dead-link docs documentation link-checker link-lint linkcheck markdown markdown-link static-analysis typescript
Last synced: about 11 hours ago
JSON representation
Catch broken links & dead anchors in Markdown/HTML docs β relative links, GitHub-slug #anchors, cross-file anchors, images, references. Deterministic, offline, runs in CI. A fast internal-integrity link checker.
- Host: GitHub
- URL: https://github.com/didrod205/linklint
- Owner: didrod205
- License: mit
- Created: 2026-06-01T05:14:09.000Z (about 1 month ago)
- Default Branch: main
- Last Pushed: 2026-06-29T07:24:57.000Z (6 days ago)
- Last Synced: 2026-06-29T09:17:30.708Z (6 days ago)
- Topics: anchor, broken-link-checker, cli, dead-link, docs, documentation, link-checker, link-lint, linkcheck, markdown, markdown-link, static-analysis, typescript
- Language: TypeScript
- Homepage: https://www.npmjs.com/package/@didrod2539/linklint
- Size: 56.6 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
# π linklint
### Catch broken links & dead anchors in your docs β locally, no network.
[](https://www.npmjs.com/package/@didrod2539/linklint)
[](https://github.com/didrod205/linklint/actions/workflows/ci.yml)
[](https://www.npmjs.com/package/@didrod2539/linklint)
[](./LICENSE)
A deterministic CLI that checks the **internal integrity** of your Markdown/HTML
docs: broken relative links, dead `#anchors` (using GitHub's slug rules),
cross-file anchors (`other.md#section`), missing images, and undefined
references β **without making a single network request**. Score, AβF grade, and
JSON/Markdown reports.
---
## One-line summary
`linklint` builds a link/anchor graph of your docs and reports every internal
link that points at a file, heading, or image that doesn't exist β fast,
deterministic, and offline.
## Why this project exists
When you rename a file, restructure `docs/`, or edit a heading, links break
**silently**:
- A relative link points at a file you moved β readers hit a 404.
- A table-of-contents anchor (`#installation`) survives, but you renamed the
heading β the link now scrolls nowhere.
- A cross-file link `../api.md#auth` references a section that was deleted.
- An image `` lost its file in a refactor.
These never fail your build, so **users find them before you do** β and broken
docs erode trust, hurt SEO, and slow onboarding. Existing checkers like
`markdown-link-check` and `lychee` focus on **external URLs**, which is slow and
rate-limited. `linklint` does the part that's fully knowable and deterministic:
**internal** link & anchor integrity. It's the perfect pre-commit / CI gate.
## Key features
- π **Relative file links** β resolves and verifies every `[x](./path)` link
(with directory-`index`/`README` fallback and extension-less resolution).
- β **Anchor checks** β validates `#section` against headings using **GitHub's
slug algorithm** (including duplicate `-1`/`-2` suffixes) and explicit `id`s.
- π **Cross-file anchors** β checks `other.md#heading` against the *target*
document's headings, project-wide.
- πΌοΈ **Images** β flags missing local image sources.
- π§© **Reference links** β resolves `[text][id]` to its definition and verifies
it; flags undefined references.
- π§Ή **Hygiene** β empty links, malformed `mailto:`, absolute filesystem paths,
ambiguous duplicate-heading anchors, and optional orphan-document detection.
- π **Score + AβF grade**, JSON/Markdown export, **CI gate** exit codes.
- π **No network.** Internal integrity only β deterministic and instant.
## Install
```bash
# run without installing
npx @didrod2539/linklint scan
# or install
npm install -g @didrod2539/linklint # global CLI (provides `linklint`)
npm install -D @didrod2539/linklint # project dev-dependency (for CI)
```
Node β₯ 18. ESM + CJS + TypeScript types.
## Quick start
```bash
# scan the current directory (Markdown + HTML)
linklint scan
```
```
docs/README.md 31/100 (F) 13 links
β Link target not found: "missing-page.md":13
β Anchor "#does-not-exist" has no matching heading or id in this document:9
β Anchor "#no-such-heading" not found in docs/guide.md:12
β Image not found: "images/missing.png":16
docs/guide.md 74/100 (C) 3 links
β Reference "[nope]" is never defined:17
β Multiple headings slug to "#setup" β anchors get -1/-2 suffixes:7
Overall 68/100 (D) 3 doc(s), 17 link(s), 6 error(s), 1 warning(s), 0 info
```
## CLI usage
```bash
linklint scan [...targets] # check docs (files or directories; default: .)
linklint report # re-render a saved JSON report as Markdown
linklint init # scaffold linklint.config.json
linklint --help
linklint --version
```
`scan` options:
| Option | Description |
| --- | --- |
| `--config ` | Path to a config file (otherwise auto-detected) |
| `--external` | Also report external (http) links as info |
| `--orphans` | Flag documents nothing links to |
| `--json ` | Write a JSON report |
| `--md ` | Write a Markdown report |
| `--min-score ` | Exit non-zero if the overall score < n |
| `--quiet` | Hide info-level issues in the console |
Without `--min-score`, `scan` exits non-zero if there are **any errors** β so
it's a CI gate out of the box. Pointed at a directory it recurses, skipping
`node_modules`, `.git`, build folders, etc.
## Example result
Full reports for the bundled sample docs are in
[`examples/sample-report.md`](./examples/sample-report.md) and
[`examples/sample-report.json`](./examples/sample-report.json).
> πΈ _Screenshot / demo GIF placeholder:_ `./docs/screenshot.png` β record the
> terminal running `npx @didrod2539/linklint scan examples/docs`.
## Configuration
Create `linklint.config.json` (or run `linklint init`):
```json
{
"extensions": [".md", ".markdown", ".html", ".htm"],
"ignoreDirs": ["node_modules", ".git", "dist", "build"],
"reportExternal": false,
"checkOrphans": true,
"ignoreTargets": ["CHANGELOG"],
"minScore": 90,
"ruleSeverity": { "ambiguous-anchor": "warning" }
}
```
| Field | Meaning |
| --- | --- |
| `extensions` | File extensions to scan |
| `ignoreDirs` | Directory names to skip while walking |
| `reportExternal` | Emit external `http(s)` links as info (not network-checked) |
| `checkOrphans` | Flag documents that nothing links to |
| `ignoreTargets` | Substrings; matching link targets are skipped |
| `minScore` | CI gate threshold (overridable with `--min-score`) |
| `ruleSeverity` | Override severity per rule id |
Rule ids: `broken-file-link`, `broken-anchor`, `broken-cross-anchor`,
`broken-image`, `undefined-reference`, `ambiguous-anchor`, `empty-link`,
`mailto-format`, `absolute-path-link`, `external-link`, `orphan-document`.
## Real-world use cases
1. **Gate docs in CI.** Add `linklint scan` to your workflow. A PR that renames
a file or a heading and leaves a stale link fails the build before the broken
doc ships.
2. **Pre-commit safety net.** Run `linklint scan docs/ README.md` in a
pre-commit hook so internal links are verified at authoring time β no browser,
no network, instant.
3. **Audit a large docs site or wiki.** `linklint scan . --orphans --md
link-audit.md` produces a graded report of every broken link, dead anchor,
and orphaned page across hundreds of files.
## Programmatic API
```ts
import { buildProjectFromInputs, analyze, toMarkdown } from "@didrod2539/linklint";
const project = buildProjectFromInputs(root, inputs); // inputs: { path, content }[]
const report = analyze(project, config);
console.log(report.summary.errors, report.documents);
await fs.writeFile("links.md", toMarkdown(report));
```
## Roadmap
- Optional external link checking (opt-in, cached, rate-limited).
- AsciiDoc and reStructuredText support.
- `--fix` to update links when files are renamed (with a moves map).
- Auto-suggest the closest heading slug for a typo'd anchor (basic version ships
in the "Did you mean�" detail).
- Monorepo / multi-root awareness and a base-path option for site builders.
- A GitHub Action with PR annotations on changed docs.
## FAQ
**Does it check external URLs (http/https)?**
No β by design. External checking is slow, flaky, and rate-limited. linklint
focuses on **internal** integrity, which is deterministic and instant. Use
`--external` to at least *list* external links, and pair with a dedicated
external checker if you need it.
**How does anchor checking work?**
It computes each heading's slug with **GitHub's algorithm** (lowercase, strip
punctuation, spacesβhyphens, duplicate `-1`/`-2` suffixes) and matches your
`#anchor` against those slugs plus any explicit HTML `id`/``.
**Does it handle `other.md#section`?**
Yes. linklint builds a project-wide index, so cross-file anchors are validated
against the *target* document's actual headings.
**Markdown and HTML?**
Both. Markdown is parsed with a dependency-free, code-fence-aware parser; HTML
with a fast static parser. Links inside code spans/fences are correctly ignored.
**Will it have false positives on templated links?**
Use `ignoreTargets` (substring match) for generated or templated paths, or
`ruleSeverity`/`ignoreDirs` to tune. linklint errs toward correctness on real
relative links.
## Contributing
Contributions welcome! Each check is a small, self-contained rule in
`src/rules/`. See [CONTRIBUTING.md](./CONTRIBUTING.md) and the
[Code of Conduct](./CODE_OF_CONDUCT.md).
```bash
git clone https://github.com/didrod205/linklint.git
cd linklint
npm install
npm test
npm run build
node dist/cli.js scan examples/docs
```
## License
[MIT](./LICENSE) Β© linklint contributors
## π Sponsor
linklint is free, MIT-licensed, and built in spare time. If it caught a broken
link before your readers did, please consider supporting it:
- β **Star this repo** β free, and it helps others find it.
- π **[Sponsor via Lemon Squeezy](https://elab-studio.lemonsqueezy.com/checkout/buy/5d059b89-51d0-456b-b33a-ed56994f7010)** β one-time or recurring.
**Where your support goes:** opt-in external checking, AsciiDoc/reST support, a
`--fix` mode for renames, a PR-annotating GitHub Action, and fast issue
responses.