From 7b6efbcc50bb1a0293bf847c223e8157a257997f Mon Sep 17 00:00:00 2001 From: Maximilian Kaske Date: Mon, 20 Apr 2026 10:41:06 +0200 Subject: [PATCH] fix: restore registry json on vercel cache hit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The registry JSON files were missing from production deployments (fixes #73) because turbo's registry:build cache only restores files inside packages/registry/public/r/, while copy-to-web.mjs also wrote to apps/web/public/r/ — an output path outside the task's package scope. When Vercel hit the remote cache, the copy script didn't re-run and apps/web/public/r/ was left empty, so /r/*.json returned 404. Move the cross-package copy into web:build and declare public/r/** as a web:build output so the files are cached and restored alongside .next/. Co-Authored-By: Claude Opus 4.7 (1M context) --- apps/web/package.json | 2 +- apps/web/scripts/copy-registry.mjs | 40 +++++++++++++++++++++++ packages/registry/scripts/copy-to-web.mjs | 17 +++++----- turbo.json | 2 +- 4 files changed, 50 insertions(+), 11 deletions(-) create mode 100644 apps/web/scripts/copy-registry.mjs diff --git a/apps/web/package.json b/apps/web/package.json index fc35203..c469017 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -4,7 +4,7 @@ "private": true, "scripts": { "dev": "next dev", - "build": "next build", + "build": "node scripts/copy-registry.mjs && next build", "start": "next start", "lint": "eslint .", "typecheck": "tsc --noEmit", diff --git a/apps/web/scripts/copy-registry.mjs b/apps/web/scripts/copy-registry.mjs new file mode 100644 index 0000000..3546570 --- /dev/null +++ b/apps/web/scripts/copy-registry.mjs @@ -0,0 +1,40 @@ +#!/usr/bin/env node + +/** + * Copies the built registry JSON from packages/registry/public/r/ into + * apps/web/public/r/ so Next.js can serve them at /r/*.json. + * + * Running this as part of web:build (instead of registry:build) means the + * files land in web:build's turbo cache outputs and get restored on cache + * hit — otherwise apps/web/public/r/ is empty on Vercel when registry:build + * hits the remote cache and its script doesn't re-execute. + */ +import { cpSync, existsSync, mkdirSync, rmSync } from "node:fs"; +import { dirname, join } from "node:path"; +import { fileURLToPath } from "node:url"; + +const __dirname = dirname(fileURLToPath(import.meta.url)); +const WEB_DIR = join(__dirname, ".."); +const REGISTRY_R = join( + WEB_DIR, + "..", + "..", + "packages", + "registry", + "public", + "r", +); +const WEB_R = join(WEB_DIR, "public", "r"); + +if (!existsSync(REGISTRY_R)) { + console.error( + "No packages/registry/public/r/ found. Did registry:build run?", + ); + process.exit(1); +} + +rmSync(WEB_R, { recursive: true, force: true }); +mkdirSync(WEB_R, { recursive: true }); +cpSync(REGISTRY_R, WEB_R, { recursive: true }); + +console.log("Registry output copied to apps/web/public/r/"); diff --git a/packages/registry/scripts/copy-to-web.mjs b/packages/registry/scripts/copy-to-web.mjs index 5bf823e..dbbe0f7 100644 --- a/packages/registry/scripts/copy-to-web.mjs +++ b/packages/registry/scripts/copy-to-web.mjs @@ -1,9 +1,11 @@ #!/usr/bin/env node /** - * Copies the shadcn build output from dist/public/r/ to: - * - packages/registry/public/r/ (canonical location) - * - apps/web/public/r/ (served by Next.js) + * Copies the shadcn build output from dist/public/r/ to + * packages/registry/public/r/ (the canonical registry output location). + * + * apps/web copies from here during its own build so the JSON files become + * part of web:build's turbo cache — see apps/web/scripts/copy-registry.mjs. */ import { cpSync, existsSync, mkdirSync } from "node:fs"; import { dirname, join } from "node:path"; @@ -13,16 +15,13 @@ const __dirname = dirname(fileURLToPath(import.meta.url)); const ROOT_DIR = join(__dirname, ".."); const DIST_R = join(ROOT_DIR, "dist", "public", "r"); const LOCAL_R = join(ROOT_DIR, "public", "r"); -const WEB_R = join(ROOT_DIR, "..", "..", "apps", "web", "public", "r"); if (!existsSync(DIST_R)) { console.error("No dist/public/r/ found. Did shadcn build run?"); process.exit(1); } -for (const dest of [LOCAL_R, WEB_R]) { - mkdirSync(dest, { recursive: true }); - cpSync(DIST_R, dest, { recursive: true }); -} +mkdirSync(LOCAL_R, { recursive: true }); +cpSync(DIST_R, LOCAL_R, { recursive: true }); -console.log("Registry output copied to public/r/ and apps/web/public/r/"); +console.log("Registry output copied to public/r/"); diff --git a/turbo.json b/turbo.json index 548c39c..667c2e6 100644 --- a/turbo.json +++ b/turbo.json @@ -3,7 +3,7 @@ "tasks": { "build": { "dependsOn": ["^build", "^registry:build"], - "outputs": [".next/**", "!.next/cache/**"] + "outputs": [".next/**", "!.next/cache/**", "public/r/**"] }, "registry:build": { "outputs": ["public/r/**"]