fix(website): bake meta at build time, stop regressing to V0.0.0#180
fix(website): bake meta at build time, stop regressing to V0.0.0#180
Conversation
The landing page kept reverting to "V0.0.0" on every release. Root cause: website/lib/meta.ts resolved repo files at runtime via import.meta.url, and after Next.js compiles server components the URL resolves inside .next/server/. `../..` from there lands in the build cache, not at the repo root, so the package.json read fails silently and `pkg?.version ?? "0.0.0"` kicks in. - website/scripts/gen-meta.mjs: new build-time script. Runs from website/scripts/, walks one level up to find the real repo root, reads package.json + src/mcp/tools-registry.ts + src/triggers/api.ts + src/types.ts + test/ and writes website/lib/generated-meta.json. Fails loud if version is missing (no silent 0.0.0 ever again). - website/package.json: `predev` and `prebuild` hooks run gen-meta. - website/lib/meta.ts: collapsed from 117 lines of runtime file resolution to a static JSON import. Next.js bundles the values directly into the server component output. - website/lib/generated-meta.json: seeded with current values so editors don't complain about the missing import target. Verified locally: `.next/server/app/index.html` now contains "ZERO EXTERNAL DATABASES · v0.9.1" instead of "v0.0.0".
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
📝 WalkthroughWalkthroughThe pull request introduces a build-time metadata generation system. A new script computes project metrics (version, tool counts, endpoint counts, test counts) from repository files and writes them to a JSON file. The metadata module now imports this pre-computed JSON instead of performing runtime file I/O and parsing. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (2)
website/scripts/gen-meta.mjs (1)
110-116: Remove the volatile timestamp from the committed generated file.
generatedAtis not consumed byProjectMeta, and it makespredev/prebuilddirtywebsite/lib/generated-meta.jsonon every run even when the metadata is unchanged.Keep the generated artifact deterministic
const meta = { version, mcpTools: mcpTools || 45, hooks: hooks || 12, restEndpoints: restEndpoints || 107, testsPassing: testsPassing || 794, - generatedAt: new Date().toISOString(), };🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@website/scripts/gen-meta.mjs` around lines 110 - 116, The commit currently injects a volatile generatedAt timestamp into the meta object which makes the output non-deterministic; remove the generatedAt property before writing the file so the JSON output is deterministic. Concretely, stop setting meta.generatedAt (or delete meta.generatedAt) prior to the writeFileSync call that serializes meta (look for the meta object and the writeFileSync/outPath usage in gen-meta.mjs) so generated-meta.json no longer changes on every run.website/package.json (1)
6-10: Consider inlininggen-metainto the build script for explicitness.Currently,
gen-metarelies on npm'sprebuildlifecycle hook. While Vercel's default build command for Next.js projects with abuildscript isnpm run build(which does respect lifecycle hooks), inlining the generator into the build command makes the dependency explicit and eliminates reliance on npm lifecycle behavior:Explicit build approach
"scripts": { "gen-meta": "node scripts/gen-meta.mjs", - "predev": "node scripts/gen-meta.mjs", - "prebuild": "node scripts/gen-meta.mjs", - "dev": "next dev", - "build": "next build", + "dev": "npm run gen-meta && next dev", + "build": "npm run gen-meta && next build", "start": "next start", "lint": "next lint" },This ensures
generated-meta.jsonis regenerated in all build scenarios, including directnext buildinvocations and any future deployment changes.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@website/package.json` around lines 6 - 10, The build currently relies on the npm prebuild lifecycle to run the generator script (scripts/gen-meta.mjs via the gen-meta/prebuild scripts); instead, make the generation explicit by invoking the generator directly from the "build" script so generated-meta.json is always produced regardless of lifecycle behavior. Edit package.json scripts to have the "build" script run the generator (node scripts/gen-meta.mjs) before running next build (i.e., run the generator && next build), and remove or keep the redundant "prebuild" entry as desired so "gen-meta" is no longer a hidden lifecycle dependency.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@website/scripts/gen-meta.mjs`:
- Around line 104-109: The meta object currently uses numeric fallbacks for
mcpTools/hooks/restEndpoints/testsPassing which can silently hide upstream
parse/path failures; instead, in the code that constructs meta (the meta
constant and the variables mcpTools, hooks, restEndpoints, testsPassing),
validate that each metric resolved to a non-zero value and throw a descriptive
error (or exit non-zero) if any are null/undefined/0 so generation fails loudly
(similar to how version is handled), rather than using hardcoded defaults like
45/12/107/794.
---
Nitpick comments:
In `@website/package.json`:
- Around line 6-10: The build currently relies on the npm prebuild lifecycle to
run the generator script (scripts/gen-meta.mjs via the gen-meta/prebuild
scripts); instead, make the generation explicit by invoking the generator
directly from the "build" script so generated-meta.json is always produced
regardless of lifecycle behavior. Edit package.json scripts to have the "build"
script run the generator (node scripts/gen-meta.mjs) before running next build
(i.e., run the generator && next build), and remove or keep the redundant
"prebuild" entry as desired so "gen-meta" is no longer a hidden lifecycle
dependency.
In `@website/scripts/gen-meta.mjs`:
- Around line 110-116: The commit currently injects a volatile generatedAt
timestamp into the meta object which makes the output non-deterministic; remove
the generatedAt property before writing the file so the JSON output is
deterministic. Concretely, stop setting meta.generatedAt (or delete
meta.generatedAt) prior to the writeFileSync call that serializes meta (look for
the meta object and the writeFileSync/outPath usage in gen-meta.mjs) so
generated-meta.json no longer changes on every run.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 8716bce4-97a5-4779-aa7e-55868edc23fd
📒 Files selected for processing (4)
website/lib/generated-meta.jsonwebsite/lib/meta.tswebsite/package.jsonwebsite/scripts/gen-meta.mjs
| const meta = { | ||
| version, | ||
| mcpTools: mcpTools || 45, | ||
| hooks: hooks || 12, | ||
| restEndpoints: restEndpoints || 107, | ||
| testsPassing: testsPassing || 794, |
There was a problem hiding this comment.
Avoid silently falling back to stale metric counts.
These fallbacks can mask parser/path failures and bake incorrect marketing stats; line 108 is already stale versus the generated value of 112. Prefer failing loudly when expected counters resolve to 0, matching the version handling above.
Fail fast instead of baking fallback counts
+function requireNonZeroCount(label, value, sourcePath) {
+ if (value === 0) {
+ throw new Error(
+ `gen-meta: ${label} count resolved to 0 from ${sourcePath}. ` +
+ `Update the parser or source path before building the website.`,
+ );
+ }
+ return value;
+}
+
+const apiPath = join(repoRoot, "src", "triggers", "api.ts");
+const toolsPath = join(repoRoot, "src", "mcp", "tools-registry.ts");
+const typesPath = join(repoRoot, "src", "types.ts");
+const testPath = join(repoRoot, "test");
+
-const restEndpoints = safeCountMatches(
- join(repoRoot, "src", "triggers", "api.ts"),
- /config:\s*\{\s*api_path:\s*"/g,
-);
-const mcpTools = safeCountMatches(
- join(repoRoot, "src", "mcp", "tools-registry.ts"),
- /name:\s*"memory_/g,
-);
-const hooks = countHookTypes(join(repoRoot, "src", "types.ts"));
-const testsPassing = countTestCases(join(repoRoot, "test"));
+const restEndpoints = requireNonZeroCount(
+ "REST endpoint",
+ safeCountMatches(apiPath, /config:\s*\{\s*api_path:\s*"/g),
+ apiPath,
+);
+const mcpTools = requireNonZeroCount(
+ "MCP tool",
+ safeCountMatches(toolsPath, /name:\s*"memory_/g),
+ toolsPath,
+);
+const hooks = requireNonZeroCount("hook", countHookTypes(typesPath), typesPath);
+const testsPassing = requireNonZeroCount("test", countTestCases(testPath), testPath);
+
const meta = {
version,
- mcpTools: mcpTools || 45,
- hooks: hooks || 12,
- restEndpoints: restEndpoints || 107,
- testsPassing: testsPassing || 794,
+ mcpTools,
+ hooks,
+ restEndpoints,
+ testsPassing,🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@website/scripts/gen-meta.mjs` around lines 104 - 109, The meta object
currently uses numeric fallbacks for mcpTools/hooks/restEndpoints/testsPassing
which can silently hide upstream parse/path failures; instead, in the code that
constructs meta (the meta constant and the variables mcpTools, hooks,
restEndpoints, testsPassing), validate that each metric resolved to a non-zero
value and throw a descriptive error (or exit non-zero) if any are
null/undefined/0 so generation fails loudly (similar to how version is handled),
rather than using hardcoded defaults like 45/12/107/794.
Summary
Landing page hero kept reverting to V0.0.0 after releases. Reproduced on v0.9.1 deploy (attached screenshot confirms).
Root cause
`website/lib/meta.ts` resolved the repo at runtime via `import.meta.url`:
```ts
const here = dirname(fileURLToPath(import.meta.url));
const repoRoot = join(here, "..", "..");
// ...
const pkg = safeReadJson(join(repoRoot, "package.json"));
return { version: pkg?.version ?? "0.0.0", ... };
```
Locally `here` = `website/lib`, so `../..` correctly lands at the repo root. But Next.js 16 / Turbopack compiles server components and moves them into `.next/server/app/page/...`. At runtime `import.meta.url` resolves there, not in the source tree. `../..` stays inside `.next/server/`, `readFileSync(join(repoRoot, "package.json"))` throws, `safeReadJson` swallows it, and the `?? "0.0.0"` fallback renders. Earlier deploys happened to work because of how prior Turbopack versions laid out the bundles — not because the logic was correct.
Fix: bake meta at build time
Verified locally
```
$ npx next build
$ grep "ZERO EXTERNAL" .next/server/app/index.html
ZERO EXTERNAL DATABASES · v\",\"0.9.1\"]}]
```
Version baked into the static prerender. No fallback path left.
Test plan
Summary by CodeRabbit