From 57b9d425374e4f7b317a0dda3d293c97170fba33 Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Fri, 8 May 2026 16:38:14 +0900 Subject: [PATCH 1/2] fix(web): bundle docs sources at build time so the worker has no fs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The worker deploy from #3859 returned HTTP 500 on /docs with `Error: [unenv] fs.readFile is not implemented yet!` (captured via `wrangler tail`). `loadDocSource` was calling `node:fs/promises` `readFile` inside an RSC; even though the page is generated as SSG (`●`), Cloudflare Workers' unenv shim does not implement filesystem reads, so any code path that reaches the worker (cache miss, prerender fallback) fails. Move the read to a prebuild step that emits a TypeScript module: - `web/scripts/generate-docs-content.mjs` reads each section's source from `/docs/` and writes `web/lib/docs-content.generated.ts` containing `export const DOC_SOURCES: Record`. - `web/scripts/prepare-build.mjs` invokes the generator after the cache prune, so every `bun run build` and `bunx opennextjs-cloudflare build` regenerates the constant module from the live `docs/`. - `web/lib/docs-source.ts` now reads `DOC_SOURCES[file]` synchronously with no Node I/O. - `web/app/[locale]/docs/page.tsx` drops the `Promise.all` since reads are synchronous. - `web/.gitignore` excludes the generated file (kept generated, not source-of-truth). Effect: the bundle ships every doc as a string literal. The worker has no `fs.readFile` call to fail. `docs/` remains the only place an editor needs to touch. --- web/.gitignore | 1 + web/app/[locale]/docs/page.tsx | 10 ++++----- web/lib/docs-source.ts | 14 ++++++------ web/scripts/generate-docs-content.mjs | 31 +++++++++++++++++++++++++++ web/scripts/prepare-build.mjs | 3 +++ 5 files changed, 46 insertions(+), 13 deletions(-) create mode 100644 web/scripts/generate-docs-content.mjs diff --git a/web/.gitignore b/web/.gitignore index 769e3b67e..e542d48b5 100644 --- a/web/.gitignore +++ b/web/.gitignore @@ -53,3 +53,4 @@ next-env.d.ts /playwright-report/ /blob-report/ /playwright/.cache/ +lib/docs-content.generated.ts diff --git a/web/app/[locale]/docs/page.tsx b/web/app/[locale]/docs/page.tsx index 0cf27cba0..cda0c62b7 100644 --- a/web/app/[locale]/docs/page.tsx +++ b/web/app/[locale]/docs/page.tsx @@ -8,12 +8,10 @@ import { loadDocSource } from "@/lib/docs-source" export default async function DocsPage() { const t = await getTranslations("docs") - const sectionsWithSource = await Promise.all( - DOC_SECTIONS.map(async (section) => ({ - ...section, - source: await loadDocSource(section.file), - })), - ) + const sectionsWithSource = DOC_SECTIONS.map((section) => ({ + ...section, + source: loadDocSource(section.file), + })) return ( { - const fullPath = path.join(DOCS_ROOT, file) - return readFile(fullPath, "utf8") +export function loadDocSource(file: string): string { + const source = DOC_SOURCES[file] + if (source === undefined) { + throw new Error(`Unknown doc file: ${file}`) + } + return source } diff --git a/web/scripts/generate-docs-content.mjs b/web/scripts/generate-docs-content.mjs new file mode 100644 index 000000000..65dc884d6 --- /dev/null +++ b/web/scripts/generate-docs-content.mjs @@ -0,0 +1,31 @@ +import { readFile, writeFile } from "node:fs/promises" +import path from "node:path" + +const SECTIONS = [ + { file: "guide/overview.md" }, + { file: "guide/installation.md" }, + { file: "guide/orchestration.md" }, + { file: "guide/agent-model-matching.md" }, + { file: "guide/team-mode.md" }, + { file: "reference/cli.md" }, + { file: "reference/configuration.md" }, + { file: "reference/features.md" }, + { file: "manifesto.md" }, +] + +const DOCS_ROOT = path.resolve(process.cwd(), "..", "docs") +const OUTPUT = path.resolve(process.cwd(), "lib", "docs-content.generated.ts") + +const sources = {} +for (const s of SECTIONS) { + sources[s.file] = await readFile(path.join(DOCS_ROOT, s.file), "utf8") +} + +const out = + "// Generated by scripts/generate-docs-content.mjs - DO NOT EDIT\n" + + "export const DOC_SOURCES: Record = " + + JSON.stringify(sources, null, 2) + + "\n" + +await writeFile(OUTPUT, out) +console.log("Generated " + OUTPUT + " with " + SECTIONS.length + " docs sources") diff --git a/web/scripts/prepare-build.mjs b/web/scripts/prepare-build.mjs index d6980f982..49d5e8330 100644 --- a/web/scripts/prepare-build.mjs +++ b/web/scripts/prepare-build.mjs @@ -1,7 +1,10 @@ import { rmSync } from "node:fs" +import { execSync } from "node:child_process" const buildCachePaths = [".next/cache/fetch-cache"] for (const filePath of buildCachePaths) { rmSync(filePath, { force: true, recursive: true }) } + +execSync("node ./scripts/generate-docs-content.mjs", { stdio: "inherit" }) From 65fc0d943416a3e0757102291d54401f09027b4c Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Fri, 8 May 2026 16:46:05 +0900 Subject: [PATCH 2/2] ci: generate docs content before web checks; bun prepare hook for local dev The first build of #3860 failed at type-check because the generated `lib/docs-content.generated.ts` is gitignored (regenerated on every build) and CI's `type-check` step runs before `build`. Two fixes: - web-ci.yml: explicit `Generate docs content from repo-root docs/` step right after `bun install` so format-check, lint, and type-check all see the file. - web/package.json: add `prepare` lifecycle script. `bun install` invokes it automatically, so a fresh local checkout boots into a working state too. Build still re-runs the generator via prebuild, so docs/ edits land in the bundle without an explicit dev action. --- .github/workflows/web-ci.yml | 3 +++ web/package.json | 1 + 2 files changed, 4 insertions(+) diff --git a/.github/workflows/web-ci.yml b/.github/workflows/web-ci.yml index f3c0c9d3e..9007b399d 100644 --- a/.github/workflows/web-ci.yml +++ b/.github/workflows/web-ci.yml @@ -34,6 +34,9 @@ jobs: - name: Install dependencies run: bun install --frozen-lockfile + - name: Generate docs content from repo-root docs/ + run: node ./scripts/generate-docs-content.mjs + - name: Format check run: bun run format:check diff --git a/web/package.json b/web/package.json index 716d184c9..56d1c9583 100644 --- a/web/package.json +++ b/web/package.json @@ -3,6 +3,7 @@ "version": "0.1.0", "private": true, "scripts": { + "prepare": "node ./scripts/generate-docs-content.mjs", "prebuild": "node ./scripts/prepare-build.mjs", "dev": "next dev", "build": "next build",