github-search-cli 9.4.2 → 9.4.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +26 -4
- package/README.md +8 -3
- package/bin/ghfind.js +98 -89
- package/completions/ghfind.bash +1 -1
- package/completions/ghfind.fish +3 -1
- package/completions/ghfind.zsh +6 -0
- package/dist/{chunk-IVUU2KGR.js → chunk-GYIL4S5X.js} +4 -1
- package/dist/cli.js +761 -342
- package/dist/{config-GNZOQRTH.js → config-OCIPF2IF.js} +1 -1
- package/package.json +6 -3
- package/release-notes/9.3.0.md +32 -29
- package/scripts/build.ts +34 -19
- package/scripts/generate-svg-demo.js +218 -168
- package/scripts/postinstall.js +176 -176
- package/src/bookmarks.ts +69 -69
- package/src/cli.ts +17 -1
- package/src/compare.ts +69 -47
- package/src/config.ts +70 -65
- package/src/deepdive.ts +149 -118
- package/src/doctor.ts +287 -287
- package/src/errors.ts +62 -59
- package/src/explore.ts +34 -29
- package/src/export.ts +73 -63
- package/src/help.ts +157 -157
- package/src/history.ts +69 -55
- package/src/init.ts +60 -55
- package/src/landing.ts +106 -104
- package/src/login.ts +186 -0
- package/src/markdown-render.ts +1 -6
- package/src/notifications.ts +75 -59
- package/src/open-url.ts +16 -16
- package/src/org.ts +43 -15
- package/src/output.ts +153 -137
- package/src/package-browser.ts +317 -305
- package/src/package.ts +149 -137
- package/src/pipe.ts +60 -54
- package/src/releases.ts +182 -144
- package/src/saved-searches.ts +72 -50
- package/src/search-state.ts +283 -229
- package/src/search.ts +678 -653
- package/src/session.ts +26 -24
- package/src/share.ts +61 -54
- package/src/status.ts +68 -61
- package/src/storage.ts +136 -106
- package/src/themes.ts +259 -116
- package/src/tips.ts +42 -42
- package/src/toast.ts +70 -69
- package/src/trending-parser.ts +104 -0
- package/src/trending.ts +481 -462
- package/src/tui.ts +149 -153
- package/src/types.ts +160 -158
- package/src/watch.ts +95 -74
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Defensive parser for `github.com/trending` HTML.
|
|
3
|
+
*
|
|
4
|
+
* Pure module: no I/O, no TUI/@opentui dependency, so it can be unit-tested
|
|
5
|
+
* against real captured markup (see tests/fixtures/trending.html). Throws a
|
|
6
|
+
* `ParseError` instead of silently returning an empty list, so a GitHub layout
|
|
7
|
+
* change surfaces to the user as a friendly "[r]etry" message.
|
|
8
|
+
*/
|
|
9
|
+
import { ParseError } from "./errors";
|
|
10
|
+
|
|
11
|
+
export interface RawTrendingRepo {
|
|
12
|
+
rank: number;
|
|
13
|
+
owner: string;
|
|
14
|
+
name: string;
|
|
15
|
+
stars: number;
|
|
16
|
+
starsToday: number;
|
|
17
|
+
language: string;
|
|
18
|
+
description: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** Collapse all whitespace runs inside tags/attributes to single spaces. */
|
|
22
|
+
function flattenHtml(html: string): string {
|
|
23
|
+
return html.replace(/>\s+</g, "><").replace(/\s+/g, " ");
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
interface RepoLink {
|
|
27
|
+
owner: string;
|
|
28
|
+
name: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function parseRepoLink(block: string): RepoLink | undefined {
|
|
32
|
+
// The title heading is the only anchor whose href is a bare /owner/name
|
|
33
|
+
// path followed by class="Link" (star/fork links carry sub-paths and
|
|
34
|
+
// different classes). Regex runs left-to-right, so the login/star-panel
|
|
35
|
+
// `/login?...&return_to=...` href fails the bare-path requirement and the
|
|
36
|
+
// stargazers/forks links don't carry the heading's class="Link".
|
|
37
|
+
const anchor = block.match(
|
|
38
|
+
/<a[^>]*?href="\/([A-Za-z0-9._-]+)\/([^\"?#]+)"[^>]*class="Link"/,
|
|
39
|
+
);
|
|
40
|
+
if (!anchor) return undefined;
|
|
41
|
+
return { owner: anchor[1], name: anchor[2] };
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function parseStars(block: string): number {
|
|
45
|
+
const starsMatch = block.match(
|
|
46
|
+
/href="\/[^\/\"]+\/[^\/\"]+\/stargazers"[^>]*>.*?<\/svg>\s*(\d[\d,]*)/,
|
|
47
|
+
);
|
|
48
|
+
return starsMatch ? parseInt(starsMatch[1].replace(/,/g, ""), 10) : 0;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function parseStarsToday(block: string): number {
|
|
52
|
+
const growthMatch = block.match(/(\d[\d,]*)\s+stars\s+(today|this\s+\w+)/);
|
|
53
|
+
return growthMatch ? parseInt(growthMatch[1].replace(/,/g, ""), 10) : 0;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function parseLanguage(block: string): string {
|
|
57
|
+
const langMatch = block.match(/itemprop="programmingLanguage">([^<]+)</);
|
|
58
|
+
return langMatch ? langMatch[1].trim() : "";
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function parseDescription(block: string): string {
|
|
62
|
+
const descMatch = block.match(
|
|
63
|
+
/<p[^>]*class="[^"]*color-fg-muted[^"]*"[^>]*>([^<]+)</,
|
|
64
|
+
);
|
|
65
|
+
return descMatch ? descMatch[1].trim() : "";
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function parseTrendingHtml(html: string): RawTrendingRepo[] {
|
|
69
|
+
const flat = flattenHtml(html);
|
|
70
|
+
const repos: RawTrendingRepo[] = [];
|
|
71
|
+
const articleRe = /<article class="Box-row">(.*?)<\/article>/gi;
|
|
72
|
+
let match: RegExpExecArray | null;
|
|
73
|
+
let rank = 0;
|
|
74
|
+
|
|
75
|
+
while ((match = articleRe.exec(flat)) !== null) {
|
|
76
|
+
rank++;
|
|
77
|
+
const block = match[1];
|
|
78
|
+
const link = parseRepoLink(block);
|
|
79
|
+
if (!link) continue;
|
|
80
|
+
repos.push({
|
|
81
|
+
rank,
|
|
82
|
+
owner: link.owner,
|
|
83
|
+
name: link.name,
|
|
84
|
+
stars: parseStars(block),
|
|
85
|
+
starsToday: parseStarsToday(block),
|
|
86
|
+
language: parseLanguage(block),
|
|
87
|
+
description: parseDescription(block),
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (rank === 0) {
|
|
92
|
+
throw new ParseError(
|
|
93
|
+
"github.com/trending",
|
|
94
|
+
"no article rows found — response may be an error page or a rewrite",
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
if (repos.length === 0) {
|
|
98
|
+
throw new ParseError(
|
|
99
|
+
"github.com/trending",
|
|
100
|
+
"article rows present but no repository heading could be parsed",
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
return repos;
|
|
104
|
+
}
|