From 9d84730cea1bb9d6b940afed1a3bd7003146765b Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 4 Sep 2026 12:06:23 +0000 Subject: [PATCH] fix(docs): load MDX bodies on demand so the Worker fits Cloudflare's 64 MiB limit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `Deploy Docs` has been rejected by the Cloudflare API on every run since #106 (2026-08-25T21:05Z) with `code: 10027` — the Worker exceeds the 64 MiB uncompressed limit. Version creation fails, so no new Worker version exists and the previously accepted one keeps being served: nothing 500s, the site just stops changing. The bytes were not the corpus. All 397 `.mdx` files are 2.50 MiB of source; `handler.mjs` measured 100.93 MiB locally on 94a4126. The multiplier was the bundling. `fumadocs-mdx:collections/server` imports every page eagerly, so each server entrypoint that touches `source` — the docs page, and also `/llms.txt`, `/llms-full.txt`, `/llms.mdx/*`, `/og/*`, `/api/search` and `/sitemap.xml` — pulled the whole corpus into its own chunk, and the bundler inlined the set five times over. Measured on one probe sentence that occurs once, in one English page: 15 copies in `handler.mjs` before, 6 after. `async: true` on the docs collection makes each page's compiled body a dynamic import, so Turbopack emits per-page chunks (27 chunk files before, 971 after) instead of one corpus-sized chunk per entrypoint. handler.mjs 100.93 MiB -> 48.47 MiB (-52.0%) The only consumer this changes is the docs page, which now awaits `page.data.load()` for `body` and `toc`. Frontmatter stays eager, so `title`, `description`, `seoTitle` and `full` are untouched, and `getText('processed')` — what the llms.txt routes call — remains a method on the entry, so the generated `llms` bodies are byte-identical. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01GkauAsZBEemRbco2rEX9Lx --- .../docs/app/[lang]/docs/[[...slug]]/page.tsx | 8 +++++-- apps/docs/source.config.ts | 23 +++++++++++++++++++ 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/apps/docs/app/[lang]/docs/[[...slug]]/page.tsx b/apps/docs/app/[lang]/docs/[[...slug]]/page.tsx index 3246679..40c7cee 100644 --- a/apps/docs/app/[lang]/docs/[[...slug]]/page.tsx +++ b/apps/docs/app/[lang]/docs/[[...slug]]/page.tsx @@ -240,7 +240,11 @@ export default async function Page(props: { const page = source.getPage(params.slug ?? [], params.lang); if (!page) notFound(); - const MDX = page.data.body; + // `async: true` on the docs collection makes the compiled body and toc + // load on demand instead of being statically imported, so they are awaited + // here. Frontmatter (`title`, `description`, `full`) stays eager. + const loaded = await page.data.load(); + const MDX = loaded.body; // Resolved once and handed to both controls, so they cannot drift apart and // so a third control added below inherits the locale-independent URL instead @@ -283,7 +287,7 @@ export default async function Page(props: { dangerouslySetInnerHTML={{ __html: jsonLdHtml(item) }} /> ))} - + {page.data.title} {page.data.description}
diff --git a/apps/docs/source.config.ts b/apps/docs/source.config.ts index a46822e..a20e5bb 100644 --- a/apps/docs/source.config.ts +++ b/apps/docs/source.config.ts @@ -5,6 +5,29 @@ import path from 'node:path'; export const docs = defineDocs({ dir: path.resolve(process.cwd(), '../../content/docs'), docs: { + /** + * Load each page's compiled body on demand instead of statically importing + * all of them into every server entrypoint. + * + * Without this, `fumadocs-mdx:collections/server` eagerly imports all 397 + * `.mdx` files, so every route that touches `source` — the docs page, but + * also `/llms.txt`, `/llms-full.txt`, `/llms.mdx/*`, `/og/*`, `/api/search` + * and `/sitemap.xml` — pulls the entire corpus into its own chunk. The + * bundler then inlined the whole set five times over into one worker: + * 2.50 MiB of authored MDX became a 100.93 MiB `handler.mjs`, and + * Cloudflare rejects any Worker over 64 MiB uncompressed (`code: 10027`). + * + * The multiplier, not the corpus, was the problem: measured on the same + * tree, one probe sentence from a single English page appeared 15 times in + * the bundle before this flag and 6 times after, taking `handler.mjs` from + * 100.93 MiB to 48.47 MiB. + * + * The cost is that `page.data.body` and `page.data.toc` become + * `page.data.load()`. Frontmatter stays eager, so `title`, `description`, + * `seoTitle` and `full` are unaffected, and `getText('processed')` — what + * the llms.txt routes call — is still a method on the entry. + */ + async: true, schema: pageSchema.extend({ /** * Optional SEO title: what the `` tag should say, when that is not