Migrate documentation from VuePress 2 to Fumadocs - #144
Conversation
VuePress 2 → Fumadocs: before / afterAll figures measured on the same machine (Apple silicon, 8 build workers), cold caches, Build
The old build spent 96 % of its time rendering pages one at a time. The new one prerenders 1253 routes
What a reader actually downloadsNeither the old nor the new nginx config had gzip enabled. This PR turns it on.
Raw page size did grow (137 KB → 294 KB) because Fumadocs inlines the navigation tree and the RSC Disk / image footprint — this got worse
The output growth is Next's per-route RSC prefetch payloads (4374 Open decision: cutting this needs the Fumadocs root folder feature so the sidebar renders only Tooling
Behaviour changes worth knowing
Fixed in passing
|
Content preservation: verificationEvery Each side is reduced to a canonical form that strips only the syntax this migration was supposed to Inventory
Two paths changed deliberately: Automated comparison
Code blocks are the strongest signal for this project — every Every flagged difference, classifiedProse — 69 pages
Headings — 6 pages
The Reproducing thisgit worktree add /tmp/old-site main
cd /tmp/old-site && npm ci --legacy-peer-deps
node verify-content.mjs /tmp/old-site/guide ./content/docsConclusionNo page was lost, added, or silently rewritten. Every difference the checker found is either a
Not addressed hereThree duplicate pages predating this migration were left untouched:
|
VuePress 2 never shipped a stable release; the site was pinned to 2.0.0-rc.31. This moves the docs to Fumadocs 16 on Next.js 16, exported as a static build so the existing nginx container still serves it. Every one of the 624 page URLs is preserved. Function pages live under a `content/docs/(functions)/` folder group, which nests them under a "Functions" heading in the sidebar without appearing in the URL, so `/Member/kick` stays `/Member/kick`. Content: - 624 .md -> .mdx, with an H1 -> frontmatter `title` pass - `::: tip|warning|danger|info` -> <Callout>, `::: details` -> <Accordion> - Vue attribute bindings, unclosed void tags, bare braces and stray `<` fixed for MDX's stricter parser - @discord-message-components/vue (unmaintained since 2022) replaced with @skyra/discord-components-react; these are web components and render client-side only Build pipeline: - the three VuePress markdown replacers are now remark plugins (cooldown sections, $function auto-linking, cached remote images) - sidebar.js weight ordering translated into 38 meta.json files - search moves to Fumadocs' static Orama index, still fed the Badge-derived tags the old parseTags.js supplied - docs-pages.json is regenerated post-build, keeping its published shape Also fixed along the way: - $modal/$channel/$poll/$button auto-linked to Trigger pages instead of the functions, because the old name->path scan was last-wins - three pages with unclosed fences or duplicated container openers that were already rendering incorrectly - nginx served everything uncompressed; gzip cuts a page from 294 KB to 28 KB and the search index from 8.6 MB to 1.4 MB - "/" 301-redirected to "/index" Tooling moves from npm to pnpm. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
next/image requires intrinsic dimensions. remarkImage supplies them for local files by rewriting the path into a static import, but external URLs are only sized by fetching them at build time, which source.config.ts disables because many linked Discord CDN attachments are dead. Those nodes keep a plain string src with no width or height. next/image validates this only outside production, so `next build` rendered the images while `next dev` returned a 500 on all 70 pages holding one -- and production was shipping <img> tags with no dimensions, causing layout shift on all 260 remote images. Render those as a plain <img>, which is what the VuePress site served for them anyway. Separately, `collapsible: false` is unusable in fumadocs 16.14.0: the docs layout passes className as a render-prop function, which the shared SidebarFolderTrigger forwards to a bare <div> when a folder isn't collapsible. React drops non-string className values, so the Other and Legal folder headers rendered with no styling at all. defaultOpen alone keeps them expanded and correctly styled. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
9257ae5 to
ea61d44
Compare
Add shiki numbers
Some changes
and made the main icon setting (shared.ts) update everywhere dynamically
Small changes
moving edit page to bottom of the page.
Replace all `/Other/syntax` to `/Guide/syntax`
VuePress 2 never shipped a stable release; the site was pinned to 2.0.0-rc.31. This moves the docs to Fumadocs 16 on Next.js 16, exported as a static build so the existing nginx container still serves it.
Every one of the 624 page URLs is preserved. Function pages live under a
content/docs/(functions)/folder group, which nests them under a "Functions" heading in the sidebar without appearing in the URL, so/Member/kickstays/Member/kick.Content:
titlepass::: tip|warning|danger|info-> ,::: details-><fixed for MDX's stricter parserBuild pipeline:
Also fixed along the way:
Tooling moves from npm to pnpm.