From 97b9a6c831aab58be985cc1e9cd16e0f32b71f5b Mon Sep 17 00:00:00 2001 From: Joe Mou Date: Sun, 25 Jan 2026 16:27:48 -0500 Subject: Refactor --- src/index.ts | 42 ++--------- src/scraper.ts | 214 ++++++++++++++++++++++++++------------------------------- 2 files changed, 104 insertions(+), 152 deletions(-) diff --git a/src/index.ts b/src/index.ts index 525ac74..fcd0069 100644 --- a/src/index.ts +++ b/src/index.ts @@ -14,45 +14,15 @@ const eta = new Eta({ views: path.join(__dirname, "..", "views") }); app.use("/static/*", serveStatic({ root: path.join(__dirname, "..") })); app.get("/:owner/:repo/tree/:branch/:path{.*}?", async (c) => { - try { - const owner = c.req.param("owner"); - const repo = c.req.param("repo"); - const branch = c.req.param("branch"); - const path = c.req.param("path") || ""; - - const data = await getGitHubTree(owner, repo, branch, path); - const html = await eta.renderAsync("tree.eta", data); - - return c.html(html); - } catch (error) { - const errorMessage = - error instanceof Error ? error.message : "Unknown error"; - return c.html( - `

Error

${errorMessage}

`, - 500, - ); - } + const { owner, repo, branch, path = "" } = c.req.param(); + const data = await getGitHubTree(owner, repo, branch, path); + return c.html(await eta.renderAsync("tree.eta", data)); }); app.get("/:owner/:repo/blob/:branch/:path{.*}", async (c) => { - try { - const owner = c.req.param("owner"); - const repo = c.req.param("repo"); - const branch = c.req.param("branch"); - const path = c.req.param("path"); - - const data = await getGitHubBlob(owner, repo, branch, path); - const html = await eta.renderAsync("blob.eta", data); - - return c.html(html); - } catch (error) { - const errorMessage = - error instanceof Error ? error.message : "Unknown error"; - return c.html( - `

Error

${errorMessage}

`, - 500, - ); - } + const { owner, repo, branch, path } = c.req.param(); + const data = await getGitHubBlob(owner, repo, branch, path); + return c.html(await eta.renderAsync("blob.eta", data)); }); if (process.env.LISTEN_FDS === "1") { diff --git a/src/scraper.ts b/src/scraper.ts index fa8d655..d6702cc 100644 --- a/src/scraper.ts +++ b/src/scraper.ts @@ -1,146 +1,137 @@ -export interface GitHubTreeItem { +interface TreeItem { + contentType: "directory" | "file"; name: string; path: string; - contentType: "directory" | "file"; } -export interface GitHubTree { - path: string; - branch: string; - items: GitHubTreeItem[]; +interface RawPayload { repo: { + ownerLogin: string; name: string; - owner: string; - isPublic: boolean; + }; + refInfo: { name: string }; + path: string; + tree?: { + items: TreeItem[]; + }; + blob?: { + language?: string; + rawLines?: string[]; + highlightedLines?: { + text?: string; + }[]; + size: number; }; } -export interface GitHubBlob { - path: string; - branch: string; - content: string; - rawContent: string; - language: string | null; - size: number; +interface GitHubCommon { repo: { - name: string; owner: string; - isPublic: boolean; + name: string; }; + branch: string; + path: string; } -export async function getGitHubTree( - owner: string, - repo: string, - branch: string, - path: string, -): Promise { - const url = `https://github.com/${owner}/${repo}/tree/${branch}/${path}`; +export interface GitHubTree extends GitHubCommon { + items: TreeItem[]; +} + +export interface GitHubBlob extends GitHubCommon { + language: string | null; + size: number; + content: string; + rawContent: string; +} +async function fetchGitHubPage(path: string): Promise { // GitHub throttles/blocks requests without realistic browser headers. // These headers make the request appear as a standard browser visit. - const response = await fetch(url, { - headers: { - "User-Agent": - "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36", - Accept: - "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", - "Accept-Language": "en-US,en;q=0.5", - }, - }); - const html = await response.text(); + const headers = { + "User-Agent": + "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36", + Accept: + "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", + "Accept-Language": "en-US,en;q=0.5", + }; + const response = await fetch(`https://github.com/${path}`, { headers }); + return response.text(); +} - // GitHub uses different formats for root vs subdirectories. - let payload; - - // Try root format first. - const rootFormatMatches = [ - ...html.matchAll( - /`, + "g", + ); + + match: for (const match of html.matchAll(regex)) { + let data; try { - const data = JSON.parse(match[1]); - if (data.props?.initialPayload?.tree) { - payload = data.props.initialPayload; - break; - } - } catch (e) { + data = JSON.parse(match[1]); + } catch { continue; } - } - - // Try subdirectory format. - if (!payload) { - const dirFormatMatch = html.match( - /