From 499bba129c842c00c1a28d85bc044ab609aee44e Mon Sep 17 00:00:00 2001 From: Joe Mou Date: Mon, 21 Sep 2026 17:55:06 -0400 Subject: Add discussions Discussions are served from GitHub's older server-rendered HTML, with no embeddedData JSON to read, so the scraper walks the DOM the way the pull request and wiki code does. A discussion's timeline opens with its own post and continues with its comments, each carrying its replies nested inside it, which is where the recursion in _discussionComment.eta comes from. Categories, upvotes, and the answer a Q&A discussion is marked with all come along. Only what GitHub renders up front is shown: neither the next page of discussions nor the replies it hides behind a "Show N previous replies" button are fetched, so a comment says how many replies it is missing. Co-Authored-By: Claude Opus 5 --- src/app.test.ts | 156 +++++++++++++++++++++++++++++++++++ src/app.ts | 26 ++++++ src/scraper.test.ts | 131 +++++++++++++++++++++++++++++ src/scraper.ts | 232 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 545 insertions(+) (limited to 'src') diff --git a/src/app.test.ts b/src/app.test.ts index 450c1cc..3fdacd5 100644 --- a/src/app.test.ts +++ b/src/app.test.ts @@ -358,6 +358,162 @@ describe("wiki", () => { }); }); +describe("discussions", () => { + const repo = { owner: "jj-vcs", name: "jj" }; + const categories = [ + { slug: "general", name: "General", emoji: "\u{1F4AC}" }, + { slug: "q-a", name: "Q&A", emoji: "\u{1F64F}" }, + ]; + const comment = { + anchor: "discussioncomment-2258263", + author: "martinvonz", + createdAt: "2022-02-26T21:56:47Z", + labels: ["Maintainer"], + bodyHtml: "

Good point.

", + hiddenReason: null, + isAnswer: false, + replies: [], + hiddenReplies: 0, + }; + const discussion = { + repo, + number: 88, + titleHtml: ""at least" .DS_Store was added back", + author: "arxanas", + category: categories[1], + answerAnchor: null, + body: { ...comment, anchor: "discussion-3903561", author: "arxanas" }, + comments: [comment], + }; + + it("should link every category beside the list", () => { + const html = eta.render("discussions.eta", { + repo, + categories, + category: categories[1], + discussions: [], + q: "hooks", + }); + + assert.match(html, /href="\/jj-vcs\/jj\/discussions">All<\/a>/); + assert.match(html, /href="\/jj-vcs\/jj\/discussions\/categories\/q-a">\u{1F64F} Q&A<\/a>/u); + // The search box keeps the query the list was narrowed by. + assert.match(html, /]*value="hooks">/); + assert.match(html, /