From 8e4e9dd87c42961201e3bdc107fdb1598900202c Mon Sep 17 00:00:00 2001 From: gorkem-bwl Date: Fri, 22 May 2026 14:20:10 -0400 Subject: [PATCH] fix(docs): make the build self-contained for Vercel MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Vercel's "Root Directory" setting limits the upload to docs/api/, so the build cannot read sibling paths like ../../server/openapi.json or ../api-content/. A small prebuild script copies both into a vendored folder inside the project tree (gitignored). ## Changes - Add scripts/sync-sources.mjs to copy /docs/api-content/ and /server/openapi.json into docs/api/src/vendored/ before dev/build - Wire predev/prebuild npm scripts so the sync runs automatically - Update astro.config.mjs and src/content.config.ts to read from the vendored paths - Gitignore src/vendored/ — contributors still edit the originals --- docs/api/.gitignore | 5 +++ docs/api/astro.config.mjs | 9 +++-- docs/api/package.json | 2 ++ docs/api/scripts/sync-sources.mjs | 55 +++++++++++++++++++++++++++++++ docs/api/src/content.config.ts | 5 ++- 5 files changed, 72 insertions(+), 4 deletions(-) create mode 100644 docs/api/scripts/sync-sources.mjs diff --git a/docs/api/.gitignore b/docs/api/.gitignore index 101d4adf2..7d1e295ac 100644 --- a/docs/api/.gitignore +++ b/docs/api/.gitignore @@ -2,6 +2,10 @@ dist/ .astro/ +# Sources copied in at build time by scripts/sync-sources.mjs. +# Edit /docs/api-content/ and /server/openapi.json instead. +src/vendored/ + # Dependencies node_modules/ @@ -14,3 +18,4 @@ pnpm-debug.log* # Env .env .env.production +.vercel diff --git a/docs/api/astro.config.mjs b/docs/api/astro.config.mjs index 62052312e..f978dc1c9 100644 --- a/docs/api/astro.config.mjs +++ b/docs/api/astro.config.mjs @@ -2,9 +2,12 @@ import { defineConfig } from "astro/config"; import starlight from "@astrojs/starlight"; import starlightOpenAPI, { openAPISidebarGroups } from "starlight-openapi"; -// The OpenAPI spec lives in the server package. The Starlight integration -// reads it at build time and generates the full /api/reference/* tree. -const OPENAPI_PATH = "../../server/openapi.json"; +// The OpenAPI spec is vendored from server/openapi.json by +// scripts/sync-sources.mjs (runs as predev/prebuild). We point at the +// vendored copy so the build is self-contained — Vercel only uploads +// the docs/api/ subtree, so paths outside this directory are not +// available at build time. +const OPENAPI_PATH = "./src/vendored/openapi.json"; export default defineConfig({ // Served at https://checkmate.so/docs — the leading slash is included so diff --git a/docs/api/package.json b/docs/api/package.json index c04a11211..16d6f2a84 100644 --- a/docs/api/package.json +++ b/docs/api/package.json @@ -5,6 +5,8 @@ "private": true, "description": "Public API documentation for Checkmate. Built with Astro Starlight.", "scripts": { + "prebuild": "node ./scripts/sync-sources.mjs", + "predev": "node ./scripts/sync-sources.mjs", "dev": "astro dev", "start": "astro dev", "build": "astro build", diff --git a/docs/api/scripts/sync-sources.mjs b/docs/api/scripts/sync-sources.mjs new file mode 100644 index 000000000..13e504ac3 --- /dev/null +++ b/docs/api/scripts/sync-sources.mjs @@ -0,0 +1,55 @@ +// Copies external sources into the Astro project tree so the build is +// self-contained. Required because Vercel's "Root Directory" setting +// only uploads the docs/api/ subtree, leaving sibling/parent paths +// (server/openapi.json, docs/api-content/) outside the build context. +// +// Run automatically by predev / prebuild via package.json. If the +// external paths exist, they win; otherwise we assume the files were +// already vendored (e.g. by a previous run) and continue. + +import { existsSync, mkdirSync, cpSync, copyFileSync, statSync } from "node:fs"; +import { dirname, join } from "node:path"; +import { fileURLToPath } from "node:url"; + +const __dirname = dirname(fileURLToPath(import.meta.url)); +const PROJECT_ROOT = join(__dirname, ".."); +const REPO_ROOT = join(PROJECT_ROOT, "..", ".."); + +const SOURCES = [ + { + // Markdown prose (the public docs content). + from: join(REPO_ROOT, "docs", "api-content"), + to: join(PROJECT_ROOT, "src", "vendored", "api-content"), + kind: "dir", + }, + { + // OpenAPI spec — read at build time by starlight-openapi. + from: join(REPO_ROOT, "server", "openapi.json"), + to: join(PROJECT_ROOT, "src", "vendored", "openapi.json"), + kind: "file", + }, +]; + +let synced = 0; +for (const src of SOURCES) { + if (!existsSync(src.from)) { + if (existsSync(src.to)) { + console.log(`[sync-sources] ${src.from} not present; using existing vendored copy.`); + continue; + } + console.error(`[sync-sources] ERROR: source missing and no vendored copy: ${src.from}`); + process.exit(1); + } + + mkdirSync(dirname(src.to), { recursive: true }); + if (src.kind === "dir") { + cpSync(src.from, src.to, { recursive: true }); + } else { + copyFileSync(src.from, src.to); + } + const stat = statSync(src.from); + console.log(`[sync-sources] ${src.from} → ${src.to} (${stat.isDirectory() ? "dir" : `${stat.size} bytes`})`); + synced++; +} + +console.log(`[sync-sources] OK (${synced} sources copied)`); diff --git a/docs/api/src/content.config.ts b/docs/api/src/content.config.ts index 93338dd10..bfc65ef89 100644 --- a/docs/api/src/content.config.ts +++ b/docs/api/src/content.config.ts @@ -13,7 +13,10 @@ import { docsSchema } from "@astrojs/starlight/schema"; export const collections = { docs: defineCollection({ loader: glob({ - base: "../api-content", + // Vendored from /docs/api-content/ by scripts/sync-sources.mjs. + // This indirection keeps the build self-contained for Vercel + // (whose Root Directory setting limits the upload to docs/api/). + base: "./src/vendored/api-content", // README is contributor-facing docs (how to build the site), // not part of the published content. pattern: ["**/[^_]*.{md,mdx}", "!README.md"],