Skip to content

Implement automated ring.json generation #14

Description

@AJaccP

🧠 Context

The webring embed widget fetches a small JSON file listing the ring members so it can compute prev/random/next. Today that file is a hand-written placeholder at public/ring.json (with a public/ring-README.txt explaining it's temporary). This ticket replaces it with a version generated at build time from the webring content collection, so the ring data is always derived from the real member entries — no hand-maintained list.

The generated file:

  • Includes only members with inRing === true (some members are listed in the directory but opt out of the ring).
  • Contains just the fields the widget needs: name and url.
  • Is ordered by graduation year ascending, with name as a stable tiebreak for members sharing a year (deterministic ordering matters — it defines the prev/next traversal).
  • Is emitted at the same path, /ring.json, with the same array-of-{name, url} shape as the placeholder — so anything already developed against the placeholder keeps working unchanged.

The graph visualization reads the webring collection directly at build; ring.json exists specifically as the external embed's data source. This ticket is independent of the embed work — that builds against whatever is served at /ring.json (placeholder or generated).

Files you'll touch:

  • src/pages/ring.json.ts (new — the static endpoint that generates the file)
  • public/ring.json (delete — replaced by the endpoint)
  • public/ring-README.txt (delete — no longer a placeholder to explain)

Don't touch:

  • The webring schema in src/content.config.ts.
  • webring.astro, RingGraph.astro, or other components.

🛠️ Implementation Plan

Note: Any code snippets may not work immediately, adjust as needed.

  1. Add a static JSON endpoint. Create src/pages/ring.json.ts. In a static build, an Astro endpoint at this path emits a real /ring.json file. Pull the collection, filter to inRing, sort, and project to the needed fields:

    import type { APIRoute } from 'astro';
    import { getCollection } from 'astro:content';
    
    export const GET: APIRoute = async () => {
      const members = await getCollection('webring');
    
      // Build `ring` from `members`:
      //   - .filter() to keep only inRing === true
      //   - .sort() by graduation year ascending, then name as a stable tiebreak
      //     (year difference, falling back to localeCompare on name)
      //   - .map() each down to just { name, url }
      // This order defines prev/next traversal in the embed — keep it deterministic.
      const ring = /* ... */;
    
      return new Response(JSON.stringify(ring, null, 2), {
        headers: { 'Content-Type': 'application/json' },
      });
    };
  2. Remove the placeholders. Delete public/ring.json and public/ring-README.txt. (Two files can't serve the same /ring.json path — the old static file must go so the generated endpoint owns it.)

  3. Verify. The generated file lands in the build output (dist/ring.json), not in public/public/ is an input folder, and the endpoint is served at the /ring.json URL (dynamically in pnpm dev, emitted to dist/ on pnpm build). Run pnpm build and confirm dist/ring.json exists, is valid JSON, contains only inRing: true members as { name, url }, and is ordered by grad year. Toggle a member's inRing to false in a content entry and rebuild to confirm they drop out. (If the site later sets a base path, the file is served under that base — that's the embed's concern, not this ticket's.)


✅ Acceptance Criteria

  • /ring.json is generated at build from the webring collection (no hand-maintained list).
  • Only inRing === true members appear, each as { name, url }.
  • Ordering is graduation year ascending, then name — deterministic.
  • The output path (/ring.json) and array shape match the old placeholder, so existing consumers keep working.
  • public/ring.json and public/ring-README.txt are deleted.
  • pnpm format:check, pnpm check, and pnpm build all pass.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    Status
    Ready

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions