Skip to content

feat(site): give /stats complete metadata and a social card - #26

Merged
TheOrcDev merged 1 commit into
mainfrom
feat/stats-metadata
Aug 4, 2026
Merged

feat(site): give /stats complete metadata and a social card#26
TheOrcDev merged 1 commit into
mainfrom
feat/stats-metadata

Conversation

@TheOrcDev

@TheOrcDev TheOrcDev commented Aug 4, 2026

Copy link
Copy Markdown
Owner

/stats was the one public page that hand-rolled its own Metadata object instead of going through createPageMetadata:

export const metadata: Metadata = {
  description: "npm downloads, version adoption, and repository stats…",
  title: "Stats",
};

That left it with no canonical URL, no OpenGraph block, no Twitter card, and no social image — so sharing the link anywhere rendered it untitled and imageless. It was also missing from the sitemap.

What's here

  • Metadata — routed through createPageMetadata, same as every other page, so canonical / OpenGraph / Twitter all derive from one description. Title becomes Shadscan usage stats (the h1 stays Stats; its e2e spec only asserts the heading).
  • app/stats/opengraph-image.tsx — a card that actually cites the numbers it advertises: downloads, versions published, latest release. It reuses the existing SocialCard, so it sits in the same visual family as the other five.
  • lib/stats-social-copy.ts — the copy-building logic lives here rather than in the route, so the number formatting and the npm-is-down fallback are unit-testable without rendering a PNG.
  • Sitemap/stats added at daily, matching how often the numbers move.

The image carries its own revalidate = 3600 so it refreshes in step with the page rather than freezing at build time. It's the only OG route with a revalidate, which the build output now reflects:

├ ○ /stats                                 1h      1y
├ ○ /stats/opengraph-image                 1h      1y

Verification

Gate Result
ultracite check clean
tsc --noEmit clean
vitest run test/shadscan-web 171 passed (7 new)
playwright test 67 passed (2 new)
next build /stats/opengraph-image prerendered, 1h revalidate

New e2e asserts the full metadata block for /stats and that the card is a 1200×630 PNG. Rendered card checked visually.

One caveat on coverage: the npm-unavailable fallback is verified by unit test, not end to end. getNpmStats runs inside unstable_cache, which sits outside the request context the Playwright test proxy patches, so the OG route reads through to real npm regardless of the mock. The fallback string is deterministic and SocialCard renders arbitrary strings, so the risk is confined to copy, not rendering.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added the Stats page to the site sitemap.
    • Added a dedicated social preview image for Stats pages, including download and release information.
    • Improved Stats page metadata for richer search and social sharing previews.
    • Added fallback messaging when usage statistics are unavailable.
  • Tests

    • Expanded metadata and social-card coverage for the Stats page.
    • Added validation for download formatting, release details, and fallback scenarios.

/stats hand-rolled a bare Metadata object with only a title and a
description, so it was the one public page with no canonical URL, no
OpenGraph tags, no Twitter card, and no social image. Sharing it anywhere
produced an untitled, imageless link.

- Route it through createPageMetadata like every other page, which fills
  in the canonical, OpenGraph, and Twitter blocks from one description
- Add /stats/opengraph-image, which cites the live numbers it is a card
  for: downloads, versions published, and the latest release
- Extract the card copy into lib/stats-social-copy.ts so the numbers and
  the npm-is-down fallback are unit-testable independent of rendering
- Publish /stats in the sitemap, where it was missing

The image revalidates hourly alongside the page, so a shared card never
cites numbers the page has already moved past.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@vercel

vercel Bot commented Aug 4, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
shadscan Ready Ready Preview Aug 4, 2026 2:23pm

Request Review

@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The stats page now uses shared social metadata, exposes a generated Open Graph image, formats NPM statistics into social-card copy, adds sitemap coverage, and validates the metadata and image output.

Changes

Stats social metadata

Layer / File(s) Summary
Stats social-card copy and formatting
lib/stats-social-copy.ts, test/shadscan-web/stats-social-copy.test.ts
Adds social-card copy generation with download formatting, conditional version details, fallback text, and scenario coverage.
Stats metadata and Open Graph image route
app/stats/page.tsx, app/stats/opengraph-image.tsx
Uses createPageMetadata() with stats image fields and adds a 1200×630 PNG route with hourly revalidation.
Sitemap and metadata validation
app/sitemap.ts, test/e2e/metadata.spec.ts
Adds /stats sitemap coverage and validates stats metadata plus Open Graph image format and dimensions.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant OpenGraphImage
  participant NPMStatsFetcher
  participant createStatsSocialCopy
  participant SocialCard
  OpenGraphImage->>NPMStatsFetcher: Fetch NPM statistics
  NPMStatsFetcher-->>OpenGraphImage: Return statistics or null
  OpenGraphImage->>createStatsSocialCopy: Generate card copy
  createStatsSocialCopy-->>OpenGraphImage: Return headline, detail, and footer
  OpenGraphImage->>SocialCard: Render card copy
Loading

Possibly related PRs

  • TheOrcDev/shadscan#9: Adds analogous page-specific Open Graph image and metadata coverage for another route.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: adding complete metadata and social card support to the /stats page.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/stats-metadata

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
app/stats/opengraph-image.tsx (1)

6-12: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Optional: unify the export style for image-route constants.

revalidate is exported directly at its declaration (Line 16), but alt, contentType, and size are declared as plain const and exported together at the bottom (Line 28). Export all four the same way, either all inline or all grouped, to keep the file's convention consistent.

♻️ Proposed consistency fix
-const size = {
+export const size = {
   height: 630,
   width: 1200,
 };

-const alt = "shadscan usage stats: npm downloads and version adoption";
-const contentType = "image/png";
+export const alt = "shadscan usage stats: npm downloads and version adoption";
+export const contentType = "image/png";
-export { alt, contentType, size };

Also applies to: 28-28

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/stats/opengraph-image.tsx` around lines 6 - 12, Unify the export style
for the image-route constants in opengraph-image by making size, alt,
contentType, and revalidate use the same declaration or grouped-export pattern.
Update the declarations and bottom export in the route while preserving all
existing values.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@app/stats/opengraph-image.tsx`:
- Around line 6-12: Unify the export style for the image-route constants in
opengraph-image by making size, alt, contentType, and revalidate use the same
declaration or grouped-export pattern. Update the declarations and bottom export
in the route while preserving all existing values.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: b33c391a-18e3-406b-a8c9-d2ae09e449aa

📥 Commits

Reviewing files that changed from the base of the PR and between 959b153 and 6668b1b.

📒 Files selected for processing (6)
  • app/sitemap.ts
  • app/stats/opengraph-image.tsx
  • app/stats/page.tsx
  • lib/stats-social-copy.ts
  • test/e2e/metadata.spec.ts
  • test/shadscan-web/stats-social-copy.test.ts

@TheOrcDev
TheOrcDev merged commit c9df491 into main Aug 4, 2026
8 checks passed
@TheOrcDev
TheOrcDev deleted the feat/stats-metadata branch August 4, 2026 14:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant