github-lang-stats 0.1.0
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/LICENSE +21 -0
- package/README.md +150 -0
- package/dist/aggregator.d.ts +3 -0
- package/dist/aggregator.d.ts.map +1 -0
- package/dist/aggregator.js +52 -0
- package/dist/aggregator.js.map +1 -0
- package/dist/cache.d.ts +32 -0
- package/dist/cache.d.ts.map +1 -0
- package/dist/cache.js +117 -0
- package/dist/cache.js.map +1 -0
- package/dist/github-client.d.ts +20 -0
- package/dist/github-client.d.ts.map +1 -0
- package/dist/github-client.js +237 -0
- package/dist/github-client.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +227 -0
- package/dist/index.js.map +1 -0
- package/dist/language-detector.d.ts +3 -0
- package/dist/language-detector.d.ts.map +1 -0
- package/dist/language-detector.js +157 -0
- package/dist/language-detector.js.map +1 -0
- package/dist/lib.d.ts +57 -0
- package/dist/lib.d.ts.map +1 -0
- package/dist/lib.js +98 -0
- package/dist/lib.js.map +1 -0
- package/dist/types.d.ts +53 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +59 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"github-client.js","sourceRoot":"","sources":["../src/github-client.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { existsSync, mkdirSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { dirname, resolve } from "node:path";
|
|
4
|
+
import checkbox from "@inquirer/checkbox";
|
|
5
|
+
import chalk from "chalk";
|
|
6
|
+
import { Command } from "commander";
|
|
7
|
+
import ora from "ora";
|
|
8
|
+
import { aggregate } from "./aggregator.js";
|
|
9
|
+
import { CacheStore, defaultCachePath } from "./cache.js";
|
|
10
|
+
import { GitHubClient } from "./github-client.js";
|
|
11
|
+
const DEFAULT_FROM_YEAR = new Date().getFullYear() - 10;
|
|
12
|
+
const program = new Command();
|
|
13
|
+
program
|
|
14
|
+
.name("gls")
|
|
15
|
+
.alias("github-lang-stats")
|
|
16
|
+
.description("Compute per-author GitHub language statistics by analysing commit file changes.\n" +
|
|
17
|
+
"Fetches only commits authored by --user, then inspects changed files via REST.\n" +
|
|
18
|
+
"Progress is cached locally so interrupted runs can be resumed.")
|
|
19
|
+
.version("1.0.0")
|
|
20
|
+
.requiredOption("-u, --user <username>", "GitHub username")
|
|
21
|
+
.requiredOption("-t, --token <pat>", "GitHub Personal Access Token (needs repo + read:user scopes)")
|
|
22
|
+
.option("-o, --output <path>", "Path to write JSON output (default: stdout)")
|
|
23
|
+
.option("--cache <path>", "Override cache file path")
|
|
24
|
+
.option("--no-cache", "Disable cache (start fresh each run)")
|
|
25
|
+
.option("--concurrency <n>", "Concurrent REST requests for commit details", "5")
|
|
26
|
+
.option("--from-year <year>", `Earliest year to include (default: ${DEFAULT_FROM_YEAR}, i.e. 10 years ago)`, String(DEFAULT_FROM_YEAR))
|
|
27
|
+
.option("--exclude-langs <langs>", "Comma-separated language names to exclude (e.g. JSON,YAML)")
|
|
28
|
+
.option("--select-repos", "Interactively select which repositories to analyse after fetching commit counts")
|
|
29
|
+
.option("--stats-only", "Skip fetching new data, just aggregate from existing cache")
|
|
30
|
+
.option("--reset", "Delete cache and start fresh")
|
|
31
|
+
.parse(process.argv);
|
|
32
|
+
const opts = program.opts();
|
|
33
|
+
const concurrency = parseInt(opts.concurrency, 10);
|
|
34
|
+
const fromYear = parseInt(opts.fromYear, 10);
|
|
35
|
+
const excludeLanguages = opts.excludeLangs
|
|
36
|
+
? opts.excludeLangs.split(",").map((s) => s.trim())
|
|
37
|
+
: [];
|
|
38
|
+
// ─── Setup ────────────────────────────────────────────────────────────────────
|
|
39
|
+
const client = new GitHubClient(opts.token);
|
|
40
|
+
const cachePath = opts.cache ?? defaultCachePath(opts.user);
|
|
41
|
+
const cache = new CacheStore(cachePath);
|
|
42
|
+
if (opts.reset) {
|
|
43
|
+
console.log(chalk.yellow(`Resetting cache at ${cachePath}`));
|
|
44
|
+
cache.reset();
|
|
45
|
+
cache.save();
|
|
46
|
+
}
|
|
47
|
+
console.log(chalk.bold(`\ngithub-lang-stats`) +
|
|
48
|
+
chalk.gray(` — user: ${opts.user}`) +
|
|
49
|
+
chalk.gray(` — from ${fromYear}`));
|
|
50
|
+
if (!opts.noCache)
|
|
51
|
+
console.log(chalk.gray(`Cache: ${cachePath}`));
|
|
52
|
+
console.log();
|
|
53
|
+
// ─── Phase 1: Discover repos ──────────────────────────────────────────────────
|
|
54
|
+
if (!opts.statsOnly) {
|
|
55
|
+
if (cache.repos.length === 0) {
|
|
56
|
+
const spinner = ora("Discovering contributed repositories (year-by-year)…").start();
|
|
57
|
+
let currentYear = 0;
|
|
58
|
+
const repos = await client.discoverContributedRepos(opts.user, fromYear, (year) => {
|
|
59
|
+
currentYear = year;
|
|
60
|
+
spinner.text = `Scanning contributions for ${year}…`;
|
|
61
|
+
});
|
|
62
|
+
cache.repos = repos;
|
|
63
|
+
if (!opts.noCache)
|
|
64
|
+
cache.save();
|
|
65
|
+
spinner.succeed(`Found ${chalk.bold(repos.length)} repositories (${fromYear}–${currentYear} scanned)`);
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
console.log(chalk.green(`✓`) +
|
|
69
|
+
` Using cached repo list: ${chalk.bold(cache.repos.length)} repos`);
|
|
70
|
+
}
|
|
71
|
+
// ─── Phase 2: Collect commit SHAs per repo ──────────────────────────────────
|
|
72
|
+
const incompleteRepos = cache.repos.filter((r) => !cache.isRepoComplete(r.owner, r.name));
|
|
73
|
+
if (incompleteRepos.length > 0) {
|
|
74
|
+
console.log(`\nCollecting commit SHAs for ${chalk.bold(incompleteRepos.length)} repos…`);
|
|
75
|
+
// Use the user's GitHub node ID for reliable author filtering
|
|
76
|
+
// (email-based filtering misses commits made with different local git emails)
|
|
77
|
+
const spinner0 = ora("Fetching GitHub user node ID…").start();
|
|
78
|
+
const authorId = await client.getUserNodeId(opts.user);
|
|
79
|
+
spinner0.succeed(`Author ID: ${chalk.gray(authorId)}`);
|
|
80
|
+
let repoIdx = 0;
|
|
81
|
+
for (const repo of incompleteRepos) {
|
|
82
|
+
repoIdx++;
|
|
83
|
+
const spinner = ora(`[${repoIdx}/${incompleteRepos.length}] ${repo.owner}/${repo.name}…`).start();
|
|
84
|
+
const existing = cache.getCommits(repo.owner, repo.name);
|
|
85
|
+
const shas = await client.collectCommitShas(repo.owner, repo.name, authorId, (count) => {
|
|
86
|
+
spinner.text = `[${repoIdx}/${incompleteRepos.length}] ${repo.owner}/${repo.name} — ${count} commits…`;
|
|
87
|
+
}, fromYear);
|
|
88
|
+
cache.addCommits(repo.owner, repo.name, shas);
|
|
89
|
+
cache.markRepoComplete(repo.owner, repo.name);
|
|
90
|
+
if (!opts.noCache)
|
|
91
|
+
cache.save();
|
|
92
|
+
const fresh = shas.length - existing.length;
|
|
93
|
+
spinner.succeed(`[${repoIdx}/${incompleteRepos.length}] ${repo.owner}/${repo.name} — ` +
|
|
94
|
+
`${chalk.bold(shas.length)} commits` +
|
|
95
|
+
(fresh > 0 ? chalk.gray(` (+${fresh} new)`) : ""));
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
console.log(`${chalk.green(`✓`)} All repo commit SHAs already cached`);
|
|
100
|
+
}
|
|
101
|
+
// ─── Phase 2.5: Interactive repo selection ────────────────────────────────────
|
|
102
|
+
if (opts.selectRepos) {
|
|
103
|
+
const repoEntries = cache.repos
|
|
104
|
+
.map((r) => ({
|
|
105
|
+
repo: r,
|
|
106
|
+
key: `${r.owner}/${r.name}`,
|
|
107
|
+
count: cache.getCommits(r.owner, r.name).length
|
|
108
|
+
}))
|
|
109
|
+
.sort((a, b) => b.count - a.count);
|
|
110
|
+
console.log(`\n${chalk.bold("Select repositories to analyse")} ` +
|
|
111
|
+
chalk.gray("(space=toggle, a=toggle all, i=invert, enter=confirm)\n"));
|
|
112
|
+
const selected = await checkbox({
|
|
113
|
+
message: `Choose repos (${repoEntries.length} total, sorted by commit count)`,
|
|
114
|
+
choices: repoEntries.map(({ key, count }) => ({
|
|
115
|
+
name: `${key.padEnd(55)} ${chalk.gray(`${String(count).padStart(5)} commits`)}`,
|
|
116
|
+
value: key,
|
|
117
|
+
checked: true
|
|
118
|
+
})),
|
|
119
|
+
pageSize: 20,
|
|
120
|
+
loop: false
|
|
121
|
+
});
|
|
122
|
+
if (selected.length === 0) {
|
|
123
|
+
console.log(chalk.red("No repositories selected — nothing to do."));
|
|
124
|
+
process.exit(0);
|
|
125
|
+
}
|
|
126
|
+
const selectedSet = new Set(selected);
|
|
127
|
+
cache.repos = cache.repos.filter((r) => selectedSet.has(`${r.owner}/${r.name}`));
|
|
128
|
+
console.log(chalk.green(`✓`) +
|
|
129
|
+
` Analysing ${chalk.bold(cache.repos.length)} selected repos\n`);
|
|
130
|
+
}
|
|
131
|
+
// ─── Phase 3: Fetch commit file details (REST) ──────────────────────────────
|
|
132
|
+
const pending = cache.pendingCommitCount();
|
|
133
|
+
const total = cache.totalCommitShas();
|
|
134
|
+
const done = total - pending;
|
|
135
|
+
if (pending > 0) {
|
|
136
|
+
console.log(`\nFetching commit details: ${chalk.bold(pending)} remaining` +
|
|
137
|
+
(done > 0 ? chalk.gray(` (${done} already cached)`) : "") +
|
|
138
|
+
`\n` +
|
|
139
|
+
chalk.gray(` Estimated time at 5k req/hr with concurrency=${concurrency}: ` +
|
|
140
|
+
`~${formatMinutes(pending / (5000 / 60 / 60) / concurrency)}`));
|
|
141
|
+
const workList = [];
|
|
142
|
+
for (const repo of cache.repos) {
|
|
143
|
+
const shas = cache.getCommits(repo.owner, repo.name);
|
|
144
|
+
for (const sha of shas) {
|
|
145
|
+
if (!cache.hasCommitDetail(sha)) {
|
|
146
|
+
workList.push({ owner: repo.owner, repo: repo.name, sha });
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
let fetched = 0;
|
|
151
|
+
let errors = 0;
|
|
152
|
+
let saveCounter = 0;
|
|
153
|
+
const bar = ora(`Fetching 0 / ${workList.length}…`).start();
|
|
154
|
+
// Process in batches of `concurrency`
|
|
155
|
+
for (let i = 0; i < workList.length; i += concurrency) {
|
|
156
|
+
const batch = workList.slice(i, i + concurrency);
|
|
157
|
+
const results = await Promise.allSettled(batch.map(({ owner, repo, sha }) => client.fetchCommitDetail(owner, repo, sha)));
|
|
158
|
+
for (let j = 0; j < batch.length; j++) {
|
|
159
|
+
const { sha } = batch[j];
|
|
160
|
+
const result = results[j];
|
|
161
|
+
if (result.status === "fulfilled") {
|
|
162
|
+
cache.setCommitDetail(sha, result.value);
|
|
163
|
+
fetched++;
|
|
164
|
+
}
|
|
165
|
+
else {
|
|
166
|
+
cache.setCommitDetail(sha, null);
|
|
167
|
+
errors++;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
saveCounter += batch.length;
|
|
171
|
+
if (!opts.noCache && saveCounter >= 50) {
|
|
172
|
+
cache.save();
|
|
173
|
+
saveCounter = 0;
|
|
174
|
+
}
|
|
175
|
+
const pct = Math.round(((fetched + errors) / workList.length) * 100);
|
|
176
|
+
const rl = client.getRateLimitInfo();
|
|
177
|
+
bar.text =
|
|
178
|
+
`Fetching ${fetched + errors} / ${workList.length} (${pct}%) — ` +
|
|
179
|
+
chalk.gray(`rate limit: ${rl.remaining} remaining`);
|
|
180
|
+
}
|
|
181
|
+
if (!opts.noCache)
|
|
182
|
+
cache.save();
|
|
183
|
+
bar.succeed(`Fetched ${chalk.bold(fetched)} commits (${errors} errors)`);
|
|
184
|
+
}
|
|
185
|
+
else {
|
|
186
|
+
console.log(`${chalk.green(`✓`)} All commit details already cached (${total} commits)`);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
// ─── Phase 4: Aggregate ───────────────────────────────────────────────────────
|
|
190
|
+
console.log(chalk.gray("\nAggregating…"));
|
|
191
|
+
const { commitsByRepo, commitDetails } = cache.getAggregationData();
|
|
192
|
+
const stats = aggregate(opts.user, commitsByRepo, commitDetails, excludeLanguages);
|
|
193
|
+
const json = JSON.stringify(stats, null, 2);
|
|
194
|
+
if (opts.output) {
|
|
195
|
+
const outPath = resolve(opts.output);
|
|
196
|
+
const outDir = dirname(outPath);
|
|
197
|
+
if (!existsSync(outDir))
|
|
198
|
+
mkdirSync(outDir, { recursive: true });
|
|
199
|
+
writeFileSync(outPath, json, "utf-8");
|
|
200
|
+
console.log(chalk.green(`\n✓ Output written to ${outPath}`));
|
|
201
|
+
}
|
|
202
|
+
else {
|
|
203
|
+
process.stdout.write(`${json}\n`);
|
|
204
|
+
}
|
|
205
|
+
// ─── Summary ─────────────────────────────────────────────────────────────────
|
|
206
|
+
console.log(chalk.bold("\nTop languages (lines changed):"));
|
|
207
|
+
const topN = Object.entries(stats.totals).slice(0, 10);
|
|
208
|
+
const maxLines = topN[0]?.[1] ?? 1;
|
|
209
|
+
for (const [lang, lines] of topN) {
|
|
210
|
+
const bar = "█".repeat(Math.round((lines / maxLines) * 20));
|
|
211
|
+
console.log(` ${lang.padEnd(20)} ${bar.padEnd(20)} ${formatNum(lines)}`);
|
|
212
|
+
}
|
|
213
|
+
console.log(chalk.gray(`\nProcessed ${stats.meta.totalCommitsProcessed} commits across ${stats.meta.totalRepos} repos`));
|
|
214
|
+
// ─── Helpers ─────────────────────────────────────────────────────────────────
|
|
215
|
+
function formatMinutes(minutes) {
|
|
216
|
+
if (minutes < 1)
|
|
217
|
+
return "< 1 min";
|
|
218
|
+
if (minutes < 60)
|
|
219
|
+
return `~${Math.round(minutes)} min`;
|
|
220
|
+
const h = Math.floor(minutes / 60);
|
|
221
|
+
const m = Math.round(minutes % 60);
|
|
222
|
+
return `~${h}h ${m}m`;
|
|
223
|
+
}
|
|
224
|
+
function formatNum(n) {
|
|
225
|
+
return n.toLocaleString("en-US");
|
|
226
|
+
}
|
|
227
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"language-detector.d.ts","sourceRoot":"","sources":["../src/language-detector.ts"],"names":[],"mappings":"AAgJA,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAW9D;AAED,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAE5D"}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Maps file extensions (and special filenames) to GitHub Linguist-style language names.
|
|
3
|
+
* Only includes languages commonly seen in software repos.
|
|
4
|
+
*/
|
|
5
|
+
const EXT_MAP = {
|
|
6
|
+
// TypeScript / JavaScript
|
|
7
|
+
".ts": "TypeScript",
|
|
8
|
+
".tsx": "TypeScript",
|
|
9
|
+
".mts": "TypeScript",
|
|
10
|
+
".cts": "TypeScript",
|
|
11
|
+
".js": "JavaScript",
|
|
12
|
+
".jsx": "JavaScript",
|
|
13
|
+
".mjs": "JavaScript",
|
|
14
|
+
".cjs": "JavaScript",
|
|
15
|
+
// Web
|
|
16
|
+
".html": "HTML",
|
|
17
|
+
".htm": "HTML",
|
|
18
|
+
".css": "CSS",
|
|
19
|
+
".scss": "SCSS",
|
|
20
|
+
".sass": "Sass",
|
|
21
|
+
".less": "Less",
|
|
22
|
+
".svelte": "Svelte",
|
|
23
|
+
".vue": "Vue",
|
|
24
|
+
".astro": "Astro",
|
|
25
|
+
// Backend / Systems
|
|
26
|
+
".py": "Python",
|
|
27
|
+
".rb": "Ruby",
|
|
28
|
+
".go": "Go",
|
|
29
|
+
".rs": "Rust",
|
|
30
|
+
".java": "Java",
|
|
31
|
+
".kt": "Kotlin",
|
|
32
|
+
".kts": "Kotlin",
|
|
33
|
+
".cs": "C#",
|
|
34
|
+
".cpp": "C++",
|
|
35
|
+
".cc": "C++",
|
|
36
|
+
".cxx": "C++",
|
|
37
|
+
".c": "C",
|
|
38
|
+
".h": "C",
|
|
39
|
+
".hpp": "C++",
|
|
40
|
+
".swift": "Swift",
|
|
41
|
+
".php": "PHP",
|
|
42
|
+
".scala": "Scala",
|
|
43
|
+
".clj": "Clojure",
|
|
44
|
+
".cljs": "ClojureScript",
|
|
45
|
+
".ex": "Elixir",
|
|
46
|
+
".exs": "Elixir",
|
|
47
|
+
".erl": "Erlang",
|
|
48
|
+
".hrl": "Erlang",
|
|
49
|
+
".hs": "Haskell",
|
|
50
|
+
".lhs": "Haskell",
|
|
51
|
+
".ml": "OCaml",
|
|
52
|
+
".mli": "OCaml",
|
|
53
|
+
".fs": "F#",
|
|
54
|
+
".fsi": "F#",
|
|
55
|
+
".fsx": "F#",
|
|
56
|
+
".dart": "Dart",
|
|
57
|
+
".lua": "Lua",
|
|
58
|
+
".r": "R",
|
|
59
|
+
".R": "R",
|
|
60
|
+
".m": "MATLAB",
|
|
61
|
+
".julia": "Julia",
|
|
62
|
+
".jl": "Julia",
|
|
63
|
+
".nim": "Nim",
|
|
64
|
+
".zig": "Zig",
|
|
65
|
+
".cr": "Crystal",
|
|
66
|
+
".d": "D",
|
|
67
|
+
// Shell / Scripts
|
|
68
|
+
".sh": "Shell",
|
|
69
|
+
".bash": "Shell",
|
|
70
|
+
".zsh": "Shell",
|
|
71
|
+
".fish": "Shell",
|
|
72
|
+
".ps1": "PowerShell",
|
|
73
|
+
".psm1": "PowerShell",
|
|
74
|
+
".bat": "Batchfile",
|
|
75
|
+
".cmd": "Batchfile",
|
|
76
|
+
// Data / Config
|
|
77
|
+
".json": "JSON",
|
|
78
|
+
".json5": "JSON5",
|
|
79
|
+
".jsonc": "JSON",
|
|
80
|
+
".yaml": "YAML",
|
|
81
|
+
".yml": "YAML",
|
|
82
|
+
".toml": "TOML",
|
|
83
|
+
".xml": "XML",
|
|
84
|
+
".csv": "CSV",
|
|
85
|
+
".tsv": "TSV",
|
|
86
|
+
".ini": "INI",
|
|
87
|
+
".env": "Shell",
|
|
88
|
+
// Infrastructure / Cloud
|
|
89
|
+
".tf": "HCL",
|
|
90
|
+
".tfvars": "HCL",
|
|
91
|
+
".hcl": "HCL",
|
|
92
|
+
".bicep": "Bicep",
|
|
93
|
+
".dockerfile": "Dockerfile",
|
|
94
|
+
// SQL
|
|
95
|
+
".sql": "SQL",
|
|
96
|
+
".pgsql": "SQL",
|
|
97
|
+
".mysql": "SQL",
|
|
98
|
+
// Documentation
|
|
99
|
+
".md": "Markdown",
|
|
100
|
+
".mdx": "MDX",
|
|
101
|
+
".rst": "reStructuredText",
|
|
102
|
+
".tex": "TeX",
|
|
103
|
+
// GraphQL / API
|
|
104
|
+
".graphql": "GraphQL",
|
|
105
|
+
".gql": "GraphQL",
|
|
106
|
+
".proto": "Protocol Buffer",
|
|
107
|
+
// Other
|
|
108
|
+
".nix": "Nix",
|
|
109
|
+
".pkl": "Pkl",
|
|
110
|
+
".wasm": "WebAssembly",
|
|
111
|
+
".wat": "WebAssembly"
|
|
112
|
+
};
|
|
113
|
+
/** Special filenames with no extension */
|
|
114
|
+
const FILENAME_MAP = {
|
|
115
|
+
Dockerfile: "Dockerfile",
|
|
116
|
+
Makefile: "Makefile",
|
|
117
|
+
Gemfile: "Ruby",
|
|
118
|
+
Rakefile: "Ruby",
|
|
119
|
+
Podfile: "Ruby",
|
|
120
|
+
Vagrantfile: "Ruby",
|
|
121
|
+
Brewfile: "Ruby",
|
|
122
|
+
".eslintrc": "JSON",
|
|
123
|
+
".prettierrc": "JSON",
|
|
124
|
+
".babelrc": "JSON",
|
|
125
|
+
".nvmrc": "Shell",
|
|
126
|
+
".node-version": "Shell"
|
|
127
|
+
};
|
|
128
|
+
/** Languages to exclude from the output (not meaningful for a dev's skill radar) */
|
|
129
|
+
const EXCLUDED_LANGUAGES = new Set([
|
|
130
|
+
"JSON",
|
|
131
|
+
"YAML",
|
|
132
|
+
"TOML",
|
|
133
|
+
"XML",
|
|
134
|
+
"CSV",
|
|
135
|
+
"TSV",
|
|
136
|
+
"INI",
|
|
137
|
+
"Markdown",
|
|
138
|
+
"MDX",
|
|
139
|
+
"reStructuredText",
|
|
140
|
+
"TeX"
|
|
141
|
+
]);
|
|
142
|
+
export function detectLanguage(filename) {
|
|
143
|
+
// Check special filenames first (basename only)
|
|
144
|
+
const basename = filename.split("/").pop() ?? filename;
|
|
145
|
+
if (FILENAME_MAP[basename])
|
|
146
|
+
return FILENAME_MAP[basename];
|
|
147
|
+
// Get extension
|
|
148
|
+
const dotIndex = basename.lastIndexOf(".");
|
|
149
|
+
if (dotIndex === -1)
|
|
150
|
+
return null;
|
|
151
|
+
const ext = basename.slice(dotIndex).toLowerCase();
|
|
152
|
+
return EXT_MAP[ext] ?? null;
|
|
153
|
+
}
|
|
154
|
+
export function isExcludedLanguage(language) {
|
|
155
|
+
return EXCLUDED_LANGUAGES.has(language);
|
|
156
|
+
}
|
|
157
|
+
//# sourceMappingURL=language-detector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"language-detector.js","sourceRoot":"","sources":["../src/language-detector.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,OAAO,GAA2B;IACvC,0BAA0B;IAC1B,KAAK,EAAE,YAAY;IACnB,MAAM,EAAE,YAAY;IACpB,MAAM,EAAE,YAAY;IACpB,MAAM,EAAE,YAAY;IACpB,KAAK,EAAE,YAAY;IACnB,MAAM,EAAE,YAAY;IACpB,MAAM,EAAE,YAAY;IACpB,MAAM,EAAE,YAAY;IACpB,MAAM;IACN,OAAO,EAAE,MAAM;IACf,MAAM,EAAE,MAAM;IACd,MAAM,EAAE,KAAK;IACb,OAAO,EAAE,MAAM;IACf,OAAO,EAAE,MAAM;IACf,OAAO,EAAE,MAAM;IACf,SAAS,EAAE,QAAQ;IACnB,MAAM,EAAE,KAAK;IACb,QAAQ,EAAE,OAAO;IACjB,oBAAoB;IACpB,KAAK,EAAE,QAAQ;IACf,KAAK,EAAE,MAAM;IACb,KAAK,EAAE,IAAI;IACX,KAAK,EAAE,MAAM;IACb,OAAO,EAAE,MAAM;IACf,KAAK,EAAE,QAAQ;IACf,MAAM,EAAE,QAAQ;IAChB,KAAK,EAAE,IAAI;IACX,MAAM,EAAE,KAAK;IACb,KAAK,EAAE,KAAK;IACZ,MAAM,EAAE,KAAK;IACb,IAAI,EAAE,GAAG;IACT,IAAI,EAAE,GAAG;IACT,MAAM,EAAE,KAAK;IACb,QAAQ,EAAE,OAAO;IACjB,MAAM,EAAE,KAAK;IACb,QAAQ,EAAE,OAAO;IACjB,MAAM,EAAE,SAAS;IACjB,OAAO,EAAE,eAAe;IACxB,KAAK,EAAE,QAAQ;IACf,MAAM,EAAE,QAAQ;IAChB,MAAM,EAAE,QAAQ;IAChB,MAAM,EAAE,QAAQ;IAChB,KAAK,EAAE,SAAS;IAChB,MAAM,EAAE,SAAS;IACjB,KAAK,EAAE,OAAO;IACd,MAAM,EAAE,OAAO;IACf,KAAK,EAAE,IAAI;IACX,MAAM,EAAE,IAAI;IACZ,MAAM,EAAE,IAAI;IACZ,OAAO,EAAE,MAAM;IACf,MAAM,EAAE,KAAK;IACb,IAAI,EAAE,GAAG;IACT,IAAI,EAAE,GAAG;IACT,IAAI,EAAE,QAAQ;IACd,QAAQ,EAAE,OAAO;IACjB,KAAK,EAAE,OAAO;IACd,MAAM,EAAE,KAAK;IACb,MAAM,EAAE,KAAK;IACb,KAAK,EAAE,SAAS;IAChB,IAAI,EAAE,GAAG;IACT,kBAAkB;IAClB,KAAK,EAAE,OAAO;IACd,OAAO,EAAE,OAAO;IAChB,MAAM,EAAE,OAAO;IACf,OAAO,EAAE,OAAO;IAChB,MAAM,EAAE,YAAY;IACpB,OAAO,EAAE,YAAY;IACrB,MAAM,EAAE,WAAW;IACnB,MAAM,EAAE,WAAW;IACnB,gBAAgB;IAChB,OAAO,EAAE,MAAM;IACf,QAAQ,EAAE,OAAO;IACjB,QAAQ,EAAE,MAAM;IAChB,OAAO,EAAE,MAAM;IACf,MAAM,EAAE,MAAM;IACd,OAAO,EAAE,MAAM;IACf,MAAM,EAAE,KAAK;IACb,MAAM,EAAE,KAAK;IACb,MAAM,EAAE,KAAK;IACb,MAAM,EAAE,KAAK;IACb,MAAM,EAAE,OAAO;IACf,yBAAyB;IACzB,KAAK,EAAE,KAAK;IACZ,SAAS,EAAE,KAAK;IAChB,MAAM,EAAE,KAAK;IACb,QAAQ,EAAE,OAAO;IACjB,aAAa,EAAE,YAAY;IAC3B,MAAM;IACN,MAAM,EAAE,KAAK;IACb,QAAQ,EAAE,KAAK;IACf,QAAQ,EAAE,KAAK;IACf,gBAAgB;IAChB,KAAK,EAAE,UAAU;IACjB,MAAM,EAAE,KAAK;IACb,MAAM,EAAE,kBAAkB;IAC1B,MAAM,EAAE,KAAK;IACb,gBAAgB;IAChB,UAAU,EAAE,SAAS;IACrB,MAAM,EAAE,SAAS;IACjB,QAAQ,EAAE,iBAAiB;IAC3B,QAAQ;IACR,MAAM,EAAE,KAAK;IACb,MAAM,EAAE,KAAK;IACb,OAAO,EAAE,aAAa;IACtB,MAAM,EAAE,aAAa;CACrB,CAAC;AAEF,0CAA0C;AAC1C,MAAM,YAAY,GAA2B;IAC5C,UAAU,EAAE,YAAY;IACxB,QAAQ,EAAE,UAAU;IACpB,OAAO,EAAE,MAAM;IACf,QAAQ,EAAE,MAAM;IAChB,OAAO,EAAE,MAAM;IACf,WAAW,EAAE,MAAM;IACnB,QAAQ,EAAE,MAAM;IAChB,WAAW,EAAE,MAAM;IACnB,aAAa,EAAE,MAAM;IACrB,UAAU,EAAE,MAAM;IAClB,QAAQ,EAAE,OAAO;IACjB,eAAe,EAAE,OAAO;CACxB,CAAC;AAEF,oFAAoF;AACpF,MAAM,kBAAkB,GAAG,IAAI,GAAG,CAAC;IAClC,MAAM;IACN,MAAM;IACN,MAAM;IACN,KAAK;IACL,KAAK;IACL,KAAK;IACL,KAAK;IACL,UAAU;IACV,KAAK;IACL,kBAAkB;IAClB,KAAK;CACL,CAAC,CAAC;AAEH,MAAM,UAAU,cAAc,CAAC,QAAgB;IAC9C,gDAAgD;IAChD,MAAM,QAAQ,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,IAAI,QAAQ,CAAC;IACvD,IAAI,YAAY,CAAC,QAAQ,CAAC;QAAE,OAAO,YAAY,CAAC,QAAQ,CAAC,CAAC;IAE1D,gBAAgB;IAChB,MAAM,QAAQ,GAAG,QAAQ,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;IAC3C,IAAI,QAAQ,KAAK,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IACjC,MAAM,GAAG,GAAG,QAAQ,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,WAAW,EAAE,CAAC;IAEnD,OAAO,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC;AAC7B,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,QAAgB;IAClD,OAAO,kBAAkB,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;AACzC,CAAC"}
|
package/dist/lib.d.ts
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { AggregatedStats } from "./types.js";
|
|
2
|
+
export type { AggregatedStats, CommitDetail, CommitFileDetail, RateLimitInfo, Repo } from "./types.js";
|
|
3
|
+
export interface GetGithubLangStatsOptions {
|
|
4
|
+
/** GitHub username to analyse */
|
|
5
|
+
user: string;
|
|
6
|
+
/** GitHub Personal Access Token (needs repo + read:user scopes) */
|
|
7
|
+
token: string;
|
|
8
|
+
/**
|
|
9
|
+
* Earliest year to include in the analysis.
|
|
10
|
+
* Defaults to **10 years ago** from the current year.
|
|
11
|
+
*/
|
|
12
|
+
fromYear?: number;
|
|
13
|
+
/** Languages to exclude (e.g. ["JSON", "YAML"]). */
|
|
14
|
+
excludeLanguages?: string[];
|
|
15
|
+
/** Max concurrent REST requests when fetching commit details. Defaults to 5. */
|
|
16
|
+
concurrency?: number;
|
|
17
|
+
/**
|
|
18
|
+
* Path to the cache file. Defaults to `.github-lang-stats-cache/<user>.json`
|
|
19
|
+
* in the current working directory.
|
|
20
|
+
*/
|
|
21
|
+
cachePath?: string;
|
|
22
|
+
/**
|
|
23
|
+
* Set to `false` to disable on-disk caching entirely.
|
|
24
|
+
* Defaults to `true`.
|
|
25
|
+
*/
|
|
26
|
+
useCache?: boolean;
|
|
27
|
+
/**
|
|
28
|
+
* Restrict analysis to these repos (`"owner/name"` format).
|
|
29
|
+
* When omitted, all discovered repos are processed.
|
|
30
|
+
*/
|
|
31
|
+
repos?: string[];
|
|
32
|
+
/** Optional progress callback invoked as each phase advances. */
|
|
33
|
+
onProgress?: (event: ProgressEvent) => void;
|
|
34
|
+
}
|
|
35
|
+
export type ProgressEvent = {
|
|
36
|
+
phase: "discover";
|
|
37
|
+
details: string;
|
|
38
|
+
} | {
|
|
39
|
+
phase: "shas";
|
|
40
|
+
repo: string;
|
|
41
|
+
count: number;
|
|
42
|
+
} | {
|
|
43
|
+
phase: "details";
|
|
44
|
+
fetched: number;
|
|
45
|
+
total: number;
|
|
46
|
+
} | {
|
|
47
|
+
phase: "aggregate";
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* Fetch GitHub language statistics for the given user programmatically.
|
|
51
|
+
*
|
|
52
|
+
* The function mirrors the full CLI pipeline (discover repos → collect commit
|
|
53
|
+
* SHAs → fetch commit file details → aggregate) but without any console output
|
|
54
|
+
* or interactive prompts, making it suitable for embedding in other tools.
|
|
55
|
+
*/
|
|
56
|
+
export declare function getGithubLangStats(options: GetGithubLangStatsOptions): Promise<AggregatedStats>;
|
|
57
|
+
//# sourceMappingURL=lib.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lib.d.ts","sourceRoot":"","sources":["../src/lib.ts"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAElD,YAAY,EACX,eAAe,EACf,YAAY,EACZ,gBAAgB,EAChB,aAAa,EACb,IAAI,EACJ,MAAM,YAAY,CAAC;AAEpB,MAAM,WAAW,yBAAyB;IACzC,iCAAiC;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,mEAAmE;IACnE,KAAK,EAAE,MAAM,CAAC;IACd;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,oDAAoD;IACpD,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC5B,gFAAgF;IAChF,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;OAGG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,iEAAiE;IACjE,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;CAC5C;AAED,MAAM,MAAM,aAAa,GACtB;IAAE,KAAK,EAAE,UAAU,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GACtC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAC9C;IAAE,KAAK,EAAE,SAAS,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GACpD;IAAE,KAAK,EAAE,WAAW,CAAA;CAAE,CAAC;AAE1B;;;;;;GAMG;AACH,wBAAsB,kBAAkB,CACvC,OAAO,EAAE,yBAAyB,GAChC,OAAO,CAAC,eAAe,CAAC,CAuH1B"}
|
package/dist/lib.js
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Programmatic API for github-lang-stats.
|
|
3
|
+
*
|
|
4
|
+
* @example
|
|
5
|
+
* ```ts
|
|
6
|
+
* import { getGithubLangStats } from "github-lang-stats";
|
|
7
|
+
*
|
|
8
|
+
* const stats = await getGithubLangStats({
|
|
9
|
+
* user: "octocat",
|
|
10
|
+
* token: process.env.GITHUB_TOKEN!,
|
|
11
|
+
* fromYear: 2020,
|
|
12
|
+
* });
|
|
13
|
+
* console.log(stats.totals);
|
|
14
|
+
* ```
|
|
15
|
+
*/
|
|
16
|
+
import { aggregate } from "./aggregator.js";
|
|
17
|
+
import { CacheStore, defaultCachePath } from "./cache.js";
|
|
18
|
+
import { GitHubClient } from "./github-client.js";
|
|
19
|
+
/**
|
|
20
|
+
* Fetch GitHub language statistics for the given user programmatically.
|
|
21
|
+
*
|
|
22
|
+
* The function mirrors the full CLI pipeline (discover repos → collect commit
|
|
23
|
+
* SHAs → fetch commit file details → aggregate) but without any console output
|
|
24
|
+
* or interactive prompts, making it suitable for embedding in other tools.
|
|
25
|
+
*/
|
|
26
|
+
export async function getGithubLangStats(options) {
|
|
27
|
+
const { user, token, fromYear = new Date().getFullYear() - 10, excludeLanguages = [], concurrency = 5, cachePath, useCache = true, repos: repoFilter, onProgress } = options;
|
|
28
|
+
const client = new GitHubClient(token);
|
|
29
|
+
const cPath = cachePath ?? defaultCachePath(user);
|
|
30
|
+
const cache = new CacheStore(cPath);
|
|
31
|
+
// ── Phase 1: Discover repos ─────────────────────────────────────────────
|
|
32
|
+
if (cache.repos.length === 0) {
|
|
33
|
+
onProgress?.({ phase: "discover", details: "Discovering repositories…" });
|
|
34
|
+
const allRepos = await client.discoverContributedRepos(user, fromYear, (year) => {
|
|
35
|
+
onProgress?.({
|
|
36
|
+
phase: "discover",
|
|
37
|
+
details: `Scanning contributions for ${year}…`
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
cache.repos = allRepos;
|
|
41
|
+
if (useCache)
|
|
42
|
+
cache.save();
|
|
43
|
+
}
|
|
44
|
+
const reposToProcess = repoFilter
|
|
45
|
+
? cache.repos.filter((r) => repoFilter.includes(`${r.owner}/${r.name}`))
|
|
46
|
+
: cache.repos;
|
|
47
|
+
// ── Phase 2: Collect commit SHAs ────────────────────────────────────────
|
|
48
|
+
const incompleteRepos = reposToProcess.filter((r) => !cache.isRepoComplete(r.owner, r.name));
|
|
49
|
+
if (incompleteRepos.length > 0) {
|
|
50
|
+
const authorId = await client.getUserNodeId(user);
|
|
51
|
+
for (const repo of incompleteRepos) {
|
|
52
|
+
const shas = await client.collectCommitShas(repo.owner, repo.name, authorId, (count) => {
|
|
53
|
+
onProgress?.({
|
|
54
|
+
phase: "shas",
|
|
55
|
+
repo: `${repo.owner}/${repo.name}`,
|
|
56
|
+
count
|
|
57
|
+
});
|
|
58
|
+
}, fromYear);
|
|
59
|
+
cache.addCommits(repo.owner, repo.name, shas);
|
|
60
|
+
cache.markRepoComplete(repo.owner, repo.name);
|
|
61
|
+
if (useCache)
|
|
62
|
+
cache.save();
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
const workList = [];
|
|
66
|
+
for (const repo of reposToProcess) {
|
|
67
|
+
const shas = cache.getCommits(repo.owner, repo.name);
|
|
68
|
+
for (const sha of shas) {
|
|
69
|
+
if (!cache.hasCommitDetail(sha)) {
|
|
70
|
+
workList.push({ owner: repo.owner, repo: repo.name, sha });
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
let fetched = 0;
|
|
75
|
+
for (let i = 0; i < workList.length; i += concurrency) {
|
|
76
|
+
const batch = workList.slice(i, i + concurrency);
|
|
77
|
+
const results = await Promise.allSettled(batch.map(({ owner, repo, sha }) => client.fetchCommitDetail(owner, repo, sha)));
|
|
78
|
+
for (let j = 0; j < batch.length; j++) {
|
|
79
|
+
const { sha } = batch[j];
|
|
80
|
+
const result = results[j];
|
|
81
|
+
cache.setCommitDetail(sha, result.status === "fulfilled" ? result.value : null);
|
|
82
|
+
fetched++;
|
|
83
|
+
}
|
|
84
|
+
if (useCache)
|
|
85
|
+
cache.save();
|
|
86
|
+
onProgress?.({ phase: "details", fetched, total: workList.length });
|
|
87
|
+
}
|
|
88
|
+
// ── Phase 4: Aggregate ──────────────────────────────────────────────────
|
|
89
|
+
onProgress?.({ phase: "aggregate" });
|
|
90
|
+
const filteredCommitsByRepo = {};
|
|
91
|
+
for (const repo of reposToProcess) {
|
|
92
|
+
const key = `${repo.owner}/${repo.name}`;
|
|
93
|
+
filteredCommitsByRepo[key] = cache.getCommits(repo.owner, repo.name);
|
|
94
|
+
}
|
|
95
|
+
const { commitDetails } = cache.getAggregationData();
|
|
96
|
+
return aggregate(user, filteredCommitsByRepo, commitDetails, excludeLanguages);
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=lib.js.map
|
package/dist/lib.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lib.js","sourceRoot":"","sources":["../src/lib.ts"],"names":[],"mappings":"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"}
|