asset-doctor
v0.1.0
Published
Health checks for your public/ assets: find broken references (runtime 404s), orphaned files, and oversized assets — and generate a typed asset manifest. Zero dependencies.
Maintainers
Readme
asset-doctor 🩺
Health checks for your public/ folder — catch runtime 404s before your users do.
Your bundler verifies every import. Nobody verifies your string asset paths.
A typo in "/img/hero.png" builds fine, deploys fine, and 404s in production.
Meanwhile dead files pile up in public/ and a 20 MB SVG ships unnoticed.
npx asset-doctor check --max-size 500public/ 101 assets · scanned 237 files · 97 referenced
✖ missing 4 reference(s) point to files that don't exist
/img/[email protected] (src/components/Hero.tsx:12)
/lottie/loading.json (src/hooks/useAnimations.ts:8)
✖ oversize 2 file(s) exceed 500 KB
/img/background.svg (15.0 MB)
⚠ orphans 4 file(s) (15.6 MB) never referenced — review before deleting
ℹ dynamic 14 template-string reference(s) couldn't be verifiedExits 1 on missing or oversize files — drop it straight into CI.
Why
Real result from the first project this ran on: 10 broken asset references
(code fetching .json files that had been replaced by .lottie versions —
silent runtime 404s), a 20 MB SVG shipping to every visitor, and 15 MB of
orphaned files. All invisible to the bundler, the type checker, and code review.
What it checks
| Check | What it catches |
| --- | --- |
| ✖ missing | Root-absolute references ("/img/x.png") to files that don't exist — runtime 404s. Found in JS/TS/JSX, CSS, HTML, Markdown, and even site.webmanifest icons. |
| ✖ oversize | Public files over --max-size KB — your asset weight budget, enforced in CI. |
| ⚠ orphans | Files in public/ that no scanned file references. Conventional files (favicon*, robots.txt, sitemap*.xml, *.webmanifest, .well-known/**, …) are exempt by default. |
Typed asset manifest
Stop hand-maintaining an assets.ts full of string paths. Generate one:
npx asset-doctor manifest -o src/assets.gen.ts// AUTO-GENERATED by asset-doctor — do not edit.
export const assets = {
img: {
logo: "/img/logo.png",
heroImage2x: "/img/hero-image_2x.png",
},
} as const;
export type AssetPath = (typeof assetList)[number];Now assets.img.logo autocompletes, and a renamed file becomes a compile
error instead of a production 404. (Generated files are automatically
excluded from reference scanning, so the manifest never masks real orphans.)
Versus existing tools
| | asset-doctor | knip / deadfile | assetdrain | | --- | :---: | :---: | :---: | | Broken references (404s) | ✅ | ❌ | ❌ | | Orphaned public assets | ✅ | ❌ module graph only — string paths are invisible to it | ✅ | | Size budget for CI | ✅ | ❌ | ❌ | | Typed manifest generator | ✅ | ❌ | ❌ | | Runtime dependencies | 0 | — | — |
Module-graph tools (knip, deadfile) are great at unused code, but public/
assets referenced by URL string never enter the module graph — they can't see
them. assetdrain cleans unused assets but doesn't catch broken references,
which is where the actual bugs are.
Usage
# Full health check (CI-friendly: exits 1 on missing/oversize)
asset-doctor check --max-size 500
# Treat orphans as errors too
asset-doctor check --fail-orphans
# Custom public dir, extra orphan exemptions, JSON output
asset-doctor check --public static --exempt "og/**" --json
# Generate the typed manifest
asset-doctor manifest -o src/assets.gen.tsGitHub Action
- uses: STkangyh/asset-doctor@v1
with:
max-size: 500Library
import { scan, generateManifest, collectAssets } from "asset-doctor";
const report = scan({ root: process.cwd(), maxSizeKb: 500 });
if (report.missing.length > 0) process.exit(1);Honest limitations
- Matching is exact string matching (after stripping
?query/#hash). Paths built with template interpolation can't be verified — they're counted and reported asdynamic, never guessed at. - Because of dynamic paths, orphans are warnings by default. Review before
deleting; use
--fail-orphansonce you trust the result. - A root-absolute string with an asset extension (
"/foo/bar.json") is assumed to targetpublic/. If it's actually an API route, add it to--exempt.
Sister project
🩺 lottie-doctor — diagnose and repair broken Lottie files (garbled text, size bloat).
License
MIT
