Link Checker
Post-build broken link checker that validates internal links in built HTML and MDX source files.
Overview
zudo-doc includes a built-in link checker (scripts/) that runs two types of validation after the site is built.
What It Checks
Mode 1: Built HTML scan
Scans all .html files in dist/ for internal <a href="..."> links and verifies each target exists on disk. It handles:
Base path stripping (e.g.,
/→my- docs/ docs/ foo docs/foo)Trailing slash resolution (
docs/foo/→docs/)foo/ index. html Extension resolution (
docs/foo→docs/orfoo/ index. html docs/)foo. html Query string and fragment stripping before resolution
Relative link resolution against the file's directory
Caching resolved paths for performance
Links that are skipped (not checked):
External URLs (
https:,/ / http:)/ / Anchor-only links (
#section)mailto:,javascript:,data:,tel:URIsVersioned docs links (
/) — version content may be incompletev/ */
Mode 2: MDX source scan
Scans all .mdx and .md files in the configured content directories for absolute links that bypass the base path. This catches links like:
<!-- These are flagged — they bypass the base path -->
[guide](/docs/guides/foo)
<a href="/ja/docs/guides/foo">link</a>
<!-- These are fine — they use relative paths -->
[guide](./foo.mdx)
[guide](../other/bar.mdx)When a base path is configured (e.g., /), absolute links starting with / or / will break because they're missing the base path prefix. The MDX source scan catches these before they become broken links in production.
Links inside fenced code blocks are ignored.
Image Checker
zudo-doc also includes a strict post-build image and asset checker (zudo-doc check images). It scans every rendered .html file under the build output and checks local references supplied by these elements and attributes:
HTML
img[src]and each validimg[srcset]candidateHTML
source[src]and each validsource[srcset]candidate (including<picture>sources)HTML
video[src]andvideo[poster]HTML
audio[src],track[src], andinput[type="image"][src]SVG
image[href]andimage[xlink:href]
Both site-absolute URLs (/) and relative URLs (., ., or images/) are resolved with URL semantics from the page that contains them. The configured base path and the page's <base href> are honored. Query strings and fragments do not affect the file lookup. A reference is valid only when it resolves to a file inside the build output; directories and symlinks that point outside the output are reported.
The checker skips external URLs (including URLs with a scheme such as http: or https:), data: URLs, protocol-relative URLs (/), and fragment-only references (#diagram).
Build-time plugin and strict CLI
The package's build-time img-src-check plugin runs after the build and uses the existing onBrokenMarkdownLinks setting. Its default is warn, which reports broken local references without failing the build; error fails the build, and ignore disables this scan. This setting also controls broken Markdown links, so use the CLI below when the image check itself must be a strict, independent gate.
The strict CLI exits nonzero when any broken local reference remains after allowlisting:
pnpm build && pnpm check:imagesIts options are:
--dist <dir>— build output directory (default:dist)--base <path>— public URL base (default:/)--allowlist <file>— exact broken-reference exceptions--help— show the current command help
For example, a site deployed below / can pass its base explicitly:
pnpm check:images -- --dist dist --base /docs/ --allowlist=.check-images-allowlistThe allowlist contains one exact <page>:<url> key per line, where <page> is the path relative to the build output and <url> is the original URL value. Blank lines and lines beginning with # are ignored:
# A generated page still points at an intentionally absent asset
docs/generated/index.html:/images/legacy.pngKeep exceptions narrow and remove each line when its underlying asset is fixed. A generated project should add the strict check to CI after its build (and after any link check), so broken local assets cannot be published:
- name: Build site
run: pnpm build
- name: Check images
run: pnpm run check:imagesRunning the Link Checker
Standalone
Build the site first, then run the checker:
pnpm build && pnpm check:linksFailure category flags
By default, the checker exits with code 0 even when issues are found. Select the categories that should fail the command with the explicit --strict-broken, --strict-absolute, and --strict-trailing flags. The b4push and CI workflows fail on broken links and absolute MDX links while leaving trailing-slash findings informational:
pnpm check:links -- --strict-broken --strict-absolute --allowlist=.check-links-allowlistRun pnpm check:links -- --help to see the current CLI options.
Allowlist
--allowlist=PATH excludes exact <file>:<line>:<href> entries from the strict-mode failure counts — for issues that cannot be fixed at the source, such as a JA page that intentionally links to an EN-only sibling, or an absolute href that resolves to a runtime-generated route with no MDX file. The repo ships a real allowlist at .check-links-allowlist: one <file>:<line>:<href> entry per line, with # comments and blank lines ignored. Each entry must match the printed report verbatim — file path relative to the repo root, 1-based line number, and href as it appeared in the source or built HTML. Delete an entry once its underlying issue is fixed, so the strict gate catches future regressions of the same shape.
As part of b4push
The link checker runs as step 28 of 32 in the pre-push validation (pnpm b4push). The strict image checker follows it as step 29, before HTML validation:
26. Build
27. Content-fallback check
28. **Link check**
29. **Image check**
30. HTML validation
31. Preview smoke
32. Manual smokeThe canonical, authoritative b4push step list lives in the header comment of scripts/ — treat that script as the single source of truth for the full 32-step sequence.
In CI
The link checker runs in the build-site job of the PR checks workflow (pr-checks.yml), immediately after pnpm build; the strict image checker runs immediately after the link checker. The production and preview deployment workflows run the same image gate before archiving their build output.
Output
When no issues are found:
Checking links (base: /my-docs/)...
✓ No broken links or absolute path issues foundWhen issues are found:
Checking links (base: /my-docs/)...
=== Broken Links in Built HTML ===
dist/docs/page/index.html:42 /my-docs/docs/missing-page
=== Absolute Links Bypassing Base Path (MDX Source) ===
src/content/docs/guides/test.mdx:15 /docs/guides/foo
✗ Found 1 broken link and 1 absolute path warning Each entry shows the file path, line number, and the problematic href.
Configuration
The link checker reads its configuration from the resolved config (the fields you pass to zudoDoc({...}) in zfb.config.ts):
base— The site's base path, used to strip prefixes when resolving linksdocsDir— Primary content directory for MDX source scanninglocales— Locale configurations withdirfields pointing to additional content directories for MDX source scanning (e.g.,locales:){ ja: { dir: "src/ content/ docs- ja" } }