Rewrites image nodes by mutating the mdast tree directly, then serializes
back to MDX with mdast-util-to-markdown + mdast-util-mdx.
Fixes from oracle review:
- Markdown images converted to JSX PixelatedImage with width/height/placeholder
- No more global string replaceAll that could corrupt code blocks/prose
- Each image node handled individually via AST traversal
- Old cache format guard: cached.mdx must be truthy
- Per-image try/catch: one bad image logs warning, doesn't fail build
- Paragraph containing single image → replaced with JSX block element
New deps: mdast-util-to-markdown, mdast-util-mdx, mdast-util-frontmatter
Ported from tiny-jsonc: regex captures strings as $1 and matches
comments as non-captured groups, so replacement keeps only strings
intact. Fast path tries JSON.parse first (zero overhead for valid JSON),
slow path strips comments + trailing commas.
Renamed strip-jsonc.ts → lib/jsonc.ts, exports parseJsonc().
1. JSONC parser: character-by-character string-aware stripper — no longer
corrupts URLs like https://... inside JSON string values
2. pagesDir: thread resolved prefix into virtual:holocron-config as
pagesDirPrefix, used by loadMdxContent instead of hardcoded /pages
3. Stale images: re-resolve image SHAs on cached pages — if image content
changed but MDX didn't, rewrites are refreshed and files recopied
4. configPath option: wired through readConfig + resolveConfigPath + HMR
5. Windows: use url.fileURLToPath(import.meta.url) instead of .pathname
6. Listener leak: process.once('beforeExit') instead of process.on
7. Normalizer guards: Array.isArray checks on tabs, groups, pages, anchors
before casting — prevents crash on malformed configs
Image cache rewritten:
- Single dist/holocron-images.json instead of per-image JSON files
- Git blob SHA for invalidation (deterministic, works across CI)
- In-memory Record during build, flushed once on process exit
- Concurrent requests for same image deduplicated via inflight promises
Relative images in MDX:
- Images next to MDX files (./screenshot.png) are now supported
- During sync: detected, copied to public/_holocron/images/<hash>-<name>
- Content hash in filename for dedup + cache busting
- imageRewrites stored on NavPage, applied at render time via replaceAll
- Works like mintlify: images anywhere in repo, not just public/
Holocron is a documentation website framework that reads MDX files + a
holocron.jsonc (or docs.json) config file and generates a full docs site
via a single Vite plugin.
**Architecture:**
- Vite plugin wraps spiceflow, tailwind, tsconfig-paths
- Virtual modules for lazy MDX loading (import.meta.glob) and config
- SHA-based cache in dist/holocron-cache.json for instant rebuilds
- Editorial components copied from website/ (same design system)
**Config (docs.json compatible):**
- navigation with tabs, groups, pages, global.anchors
- navbar.links for header, anchors rendered as tab bar items
- Schema saved locally from https://mintlify.com/docs.json
**Core modules:**
- config.ts — JSONC reader, type normalization for 3 navigation formats
- navigation.ts — enriched tree types + getTabs/findPage/flattenForSidebar
- lib/sync.ts — MDX→cache with git blob SHA deduplication
- lib/mdx-processor.ts — frontmatter + heading extraction via safe-mdx
- app-factory.tsx — Spiceflow app with layout, catch-all page, search API