repo-dive 0.16.4 → 0.17.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/README.md
CHANGED
|
@@ -68,7 +68,7 @@ Collectors so far:
|
|
|
68
68
|
- **commit-meta** — identities, dates, parents, subject and trailers (incl. AI co-authors)
|
|
69
69
|
- **churn** — lines added/deleted per commit, by file extension
|
|
70
70
|
- **file-types** — file count and bytes per extension at each commit's tree
|
|
71
|
-
- **directives** — eslint-disable comments by rule (block disables tracked as gray areas) and `@ts-ignore`/`@ts-expect-error`/`@ts-nocheck`
|
|
71
|
+
- **directives** — `eslint-disable` and `oxlint-disable` comments by linter and rule (block disables tracked as gray areas) and `@ts-ignore`/`@ts-expect-error`/`@ts-nocheck`
|
|
72
72
|
- **dependencies** — resolved package totals from lockfiles, per package manager (pnpm, npm, yarn classic and yarn berry), plus direct/dev/optional dependencies and manifest counts read straight from `package.json` files; version-aware, monorepo-aware and extensible to more managers
|
|
73
73
|
- **todo-comments** — TODO/FIXME/HACK/XXX counts
|
|
74
74
|
- **languages** — lines and file count per language across a commit's source files (lockfiles, minified bundles and generated data excluded)
|
package/dist/cli.js
CHANGED
|
@@ -42805,7 +42805,7 @@ var runWith = (command, config) => {
|
|
|
42805
42805
|
};
|
|
42806
42806
|
var package_default = {
|
|
42807
42807
|
name: "repo-dive",
|
|
42808
|
-
version: "0.
|
|
42808
|
+
version: "0.17.0",
|
|
42809
42809
|
description: "Dive into a git repo's history: per-commit snapshots, an indexed metrics catalog and an interactive dashboard",
|
|
42810
42810
|
keywords: [
|
|
42811
42811
|
"git",
|
|
@@ -49682,6 +49682,36 @@ var scanTreeWithBlobCache = fnUntraced(function* ({ repoRoot, catalogPath, sha,
|
|
|
49682
49682
|
});
|
|
49683
49683
|
//#endregion
|
|
49684
49684
|
//#region src/cli/shared/collectors/directives.ts
|
|
49685
|
+
/**
|
|
49686
|
+
* The linters whose suppression comments are counted, by comment prefix.
|
|
49687
|
+
* oxlint honours `eslint-*` comments too, so the prefix records how a comment
|
|
49688
|
+
* is spelled, not which tool reads it.
|
|
49689
|
+
*/
|
|
49690
|
+
var linters = ["eslint", "oxlint"];
|
|
49691
|
+
/**
|
|
49692
|
+
* Where each linter's counts sit in {@link DirectivesOutput}. Flat rather than
|
|
49693
|
+
* nested by linter, so outputs collected before oxlint was counted (eslint
|
|
49694
|
+
* keys only) still normalize.
|
|
49695
|
+
*/
|
|
49696
|
+
var outputKeys = {
|
|
49697
|
+
eslint: {
|
|
49698
|
+
nextLine: "eslintNextLine",
|
|
49699
|
+
line: "eslintLine",
|
|
49700
|
+
blocks: "eslintBlocks"
|
|
49701
|
+
},
|
|
49702
|
+
oxlint: {
|
|
49703
|
+
nextLine: "oxlintNextLine",
|
|
49704
|
+
line: "oxlintLine",
|
|
49705
|
+
blocks: "oxlintBlocks"
|
|
49706
|
+
}
|
|
49707
|
+
};
|
|
49708
|
+
var emptyBlockCounts = () => ({
|
|
49709
|
+
count: 0,
|
|
49710
|
+
closedCount: 0,
|
|
49711
|
+
unboundedCount: 0,
|
|
49712
|
+
coveredLines: 0,
|
|
49713
|
+
byRule: {}
|
|
49714
|
+
});
|
|
49685
49715
|
var emptyOutput = () => ({
|
|
49686
49716
|
eslintNextLine: {
|
|
49687
49717
|
count: 0,
|
|
@@ -49691,24 +49721,25 @@ var emptyOutput = () => ({
|
|
|
49691
49721
|
count: 0,
|
|
49692
49722
|
byRule: {}
|
|
49693
49723
|
},
|
|
49694
|
-
eslintBlocks:
|
|
49724
|
+
eslintBlocks: emptyBlockCounts(),
|
|
49725
|
+
oxlintNextLine: {
|
|
49726
|
+
count: 0,
|
|
49727
|
+
byRule: {}
|
|
49728
|
+
},
|
|
49729
|
+
oxlintLine: {
|
|
49695
49730
|
count: 0,
|
|
49696
|
-
closedCount: 0,
|
|
49697
|
-
unboundedCount: 0,
|
|
49698
|
-
coveredLines: 0,
|
|
49699
49731
|
byRule: {}
|
|
49700
49732
|
},
|
|
49733
|
+
oxlintBlocks: emptyBlockCounts(),
|
|
49701
49734
|
tsDirectives: {
|
|
49702
49735
|
ignore: 0,
|
|
49703
49736
|
expectError: 0,
|
|
49704
49737
|
nocheck: 0
|
|
49705
49738
|
}
|
|
49706
49739
|
});
|
|
49707
|
-
/** Extracts rule names
|
|
49708
|
-
var parseRules = (
|
|
49709
|
-
|
|
49710
|
-
if (keywordIndex === -1) return [];
|
|
49711
|
-
let rest = content.slice(keywordIndex + keyword.length);
|
|
49740
|
+
/** Extracts the rule names after a directive, stopping at a comment terminator or `--`. */
|
|
49741
|
+
var parseRules = (afterDirective) => {
|
|
49742
|
+
let rest = afterDirective;
|
|
49712
49743
|
for (const terminator of [
|
|
49713
49744
|
"*/",
|
|
49714
49745
|
"-->",
|
|
@@ -49722,34 +49753,59 @@ var parseRules = (content, keyword) => {
|
|
|
49722
49753
|
var addRules = (byRule, rules) => {
|
|
49723
49754
|
for (const rule of rules.length > 0 ? rules : ["(all)"]) byRule[rule] = (byRule[rule] ?? 0) + 1;
|
|
49724
49755
|
};
|
|
49725
|
-
var quickPattern = /eslint-disable|
|
|
49756
|
+
var quickPattern = /(?:eslint|oxlint)-(?:disable|enable)|@ts-(?:ignore|expect-error|nocheck)/;
|
|
49757
|
+
/**
|
|
49758
|
+
* A lint directive and its kind. The first match on a line wins, so a comment
|
|
49759
|
+
* that mentions the other linter's directive in its rationale is still
|
|
49760
|
+
* attributed to the linter it opens with.
|
|
49761
|
+
*/
|
|
49762
|
+
var lintDirectivePattern = /(eslint|oxlint)-(disable-next-line|disable-line|disable|enable)/;
|
|
49726
49763
|
/** Scans one file's content; results are cached per blob by the tree scanner. */
|
|
49727
49764
|
var scanFileForDirectives = (content) => {
|
|
49728
49765
|
const output = emptyOutput();
|
|
49729
49766
|
if (!quickPattern.test(content)) return output;
|
|
49730
|
-
|
|
49767
|
+
const openBlockLines = /* @__PURE__ */ new Map();
|
|
49731
49768
|
for (const [index, line] of content.split("\n").entries()) {
|
|
49732
49769
|
if (!quickPattern.test(line)) continue;
|
|
49770
|
+
const lintDirective = lintDirectivePattern.exec(line);
|
|
49733
49771
|
if (line.includes("@ts-ignore")) output.tsDirectives.ignore += 1;
|
|
49734
49772
|
else if (line.includes("@ts-expect-error")) output.tsDirectives.expectError += 1;
|
|
49735
49773
|
else if (line.includes("@ts-nocheck")) output.tsDirectives.nocheck += 1;
|
|
49736
|
-
else if (
|
|
49737
|
-
|
|
49738
|
-
|
|
49739
|
-
|
|
49740
|
-
|
|
49741
|
-
|
|
49742
|
-
|
|
49743
|
-
|
|
49744
|
-
|
|
49745
|
-
|
|
49746
|
-
|
|
49747
|
-
|
|
49748
|
-
|
|
49749
|
-
|
|
49750
|
-
|
|
49751
|
-
|
|
49752
|
-
|
|
49774
|
+
else if (lintDirective !== null) {
|
|
49775
|
+
const [directive, prefix, kind] = lintDirective;
|
|
49776
|
+
const linter = prefix === "oxlint" ? "oxlint" : "eslint";
|
|
49777
|
+
const keys = outputKeys[linter];
|
|
49778
|
+
const rules = parseRules(line.slice(lintDirective.index + directive.length));
|
|
49779
|
+
switch (kind) {
|
|
49780
|
+
case "disable-next-line":
|
|
49781
|
+
output[keys.nextLine].count += 1;
|
|
49782
|
+
addRules(output[keys.nextLine].byRule, rules);
|
|
49783
|
+
break;
|
|
49784
|
+
case "disable-line":
|
|
49785
|
+
output[keys.line].count += 1;
|
|
49786
|
+
addRules(output[keys.line].byRule, rules);
|
|
49787
|
+
break;
|
|
49788
|
+
case "disable":
|
|
49789
|
+
output[keys.blocks].count += 1;
|
|
49790
|
+
addRules(output[keys.blocks].byRule, rules);
|
|
49791
|
+
openBlockLines.set(linter, index);
|
|
49792
|
+
break;
|
|
49793
|
+
case "enable": {
|
|
49794
|
+
const openBlockLine = openBlockLines.get(linter);
|
|
49795
|
+
if (openBlockLine !== void 0) {
|
|
49796
|
+
output[keys.blocks].closedCount += 1;
|
|
49797
|
+
output[keys.blocks].coveredLines += Math.max(0, index - openBlockLine - 1);
|
|
49798
|
+
openBlockLines.delete(linter);
|
|
49799
|
+
}
|
|
49800
|
+
break;
|
|
49801
|
+
}
|
|
49802
|
+
}
|
|
49803
|
+
}
|
|
49804
|
+
}
|
|
49805
|
+
for (const linter of linters) {
|
|
49806
|
+
const blocks = output[outputKeys[linter].blocks];
|
|
49807
|
+
blocks.unboundedCount = blocks.count - blocks.closedCount;
|
|
49808
|
+
}
|
|
49753
49809
|
return output;
|
|
49754
49810
|
};
|
|
49755
49811
|
var mergeRuleCounts = (target, source) => {
|
|
@@ -49759,18 +49815,21 @@ var mergeRuleCounts = (target, source) => {
|
|
|
49759
49815
|
var mergeDirectives = (fileResults) => {
|
|
49760
49816
|
const merged = emptyOutput();
|
|
49761
49817
|
for (const result of fileResults) {
|
|
49762
|
-
const
|
|
49763
|
-
|
|
49764
|
-
|
|
49765
|
-
|
|
49766
|
-
|
|
49767
|
-
|
|
49768
|
-
|
|
49769
|
-
|
|
49770
|
-
|
|
49771
|
-
|
|
49772
|
-
|
|
49773
|
-
|
|
49818
|
+
for (const linter of linters) {
|
|
49819
|
+
const keys = outputKeys[linter];
|
|
49820
|
+
for (const key of [keys.nextLine, keys.line]) {
|
|
49821
|
+
const counts = recordAt(result, key);
|
|
49822
|
+
merged[key].count += numberAt(counts, "count");
|
|
49823
|
+
mergeRuleCounts(merged[key].byRule, counts["byRule"]);
|
|
49824
|
+
}
|
|
49825
|
+
const blocks = recordAt(result, keys.blocks);
|
|
49826
|
+
const mergedBlocks = merged[keys.blocks];
|
|
49827
|
+
mergedBlocks.count += numberAt(blocks, "count");
|
|
49828
|
+
mergedBlocks.closedCount += numberAt(blocks, "closedCount");
|
|
49829
|
+
mergedBlocks.unboundedCount += numberAt(blocks, "unboundedCount");
|
|
49830
|
+
mergedBlocks.coveredLines += numberAt(blocks, "coveredLines");
|
|
49831
|
+
mergeRuleCounts(mergedBlocks.byRule, blocks["byRule"]);
|
|
49832
|
+
}
|
|
49774
49833
|
const ts = recordAt(result, "tsDirectives");
|
|
49775
49834
|
merged.tsDirectives.ignore += numberAt(ts, "ignore");
|
|
49776
49835
|
merged.tsDirectives.expectError += numberAt(ts, "expectError");
|
|
@@ -49780,8 +49839,8 @@ var mergeDirectives = (fileResults) => {
|
|
|
49780
49839
|
};
|
|
49781
49840
|
var directivesCollector = {
|
|
49782
49841
|
name: "directives",
|
|
49783
|
-
description: "ESLint suppression comments (by rule; block disables as gray areas) and @ts-ignore/@ts-expect-error/@ts-nocheck counts",
|
|
49784
|
-
version: "
|
|
49842
|
+
description: "ESLint and oxlint suppression comments (by linter and rule; block disables as gray areas) and @ts-ignore/@ts-expect-error/@ts-nocheck counts",
|
|
49843
|
+
version: "2",
|
|
49785
49844
|
strategy: "tree",
|
|
49786
49845
|
defaultSampling: "all",
|
|
49787
49846
|
collect: ({ repoRoot, catalogPath, sha, cacheKey }) => scanTreeWithBlobCache({
|
|
@@ -49794,22 +49853,28 @@ var directivesCollector = {
|
|
|
49794
49853
|
}).pipe(map$6((files) => mergeDirectives(files.map((file) => file.result)))),
|
|
49795
49854
|
normalize: (raw) => {
|
|
49796
49855
|
const facts = [];
|
|
49797
|
-
for (const
|
|
49798
|
-
[
|
|
49799
|
-
[
|
|
49800
|
-
|
|
49801
|
-
|
|
49802
|
-
|
|
49803
|
-
|
|
49804
|
-
|
|
49805
|
-
|
|
49806
|
-
|
|
49807
|
-
|
|
49808
|
-
|
|
49809
|
-
|
|
49810
|
-
|
|
49811
|
-
|
|
49812
|
-
|
|
49856
|
+
for (const linter of linters) {
|
|
49857
|
+
const keys = outputKeys[linter];
|
|
49858
|
+
const kinds = [
|
|
49859
|
+
[keys.nextLine, "next-line"],
|
|
49860
|
+
[keys.line, "line"],
|
|
49861
|
+
[keys.blocks, "block"]
|
|
49862
|
+
];
|
|
49863
|
+
for (const [key, type] of kinds) for (const [rule, count] of Object.entries(recordAt(recordAt(raw, key), "byRule"))) if (typeof count === "number") facts.push({
|
|
49864
|
+
metric: "directives.lint",
|
|
49865
|
+
value: count,
|
|
49866
|
+
categories: {
|
|
49867
|
+
linter,
|
|
49868
|
+
type,
|
|
49869
|
+
rule
|
|
49870
|
+
}
|
|
49871
|
+
});
|
|
49872
|
+
facts.push({
|
|
49873
|
+
metric: "directives.lintBlockCoveredLines",
|
|
49874
|
+
value: numberAt(recordAt(raw, keys.blocks), "coveredLines"),
|
|
49875
|
+
categories: { linter }
|
|
49876
|
+
});
|
|
49877
|
+
}
|
|
49813
49878
|
const ts = recordAt(raw, "tsDirectives");
|
|
49814
49879
|
for (const type of [
|
|
49815
49880
|
"ignore",
|
|
@@ -52054,15 +52119,17 @@ var sumMetric = (commit, metric, filter) => {
|
|
|
52054
52119
|
for (const facts of commit.factsByCollector.values()) for (const fact of facts) if (fact.metric === metric && (filter === void 0 || filter(fact.categories ?? {}))) total += fact.value;
|
|
52055
52120
|
return total;
|
|
52056
52121
|
};
|
|
52057
|
-
|
|
52122
|
+
/** Sums a metric's values by a key derived from each fact's categories. */
|
|
52123
|
+
var groupMetricBy = (commit, metric, keyOf) => {
|
|
52058
52124
|
const grouped = {};
|
|
52059
52125
|
for (const facts of commit.factsByCollector.values()) for (const fact of facts) {
|
|
52060
52126
|
if (fact.metric !== metric) continue;
|
|
52061
|
-
const key = fact.categories
|
|
52127
|
+
const key = keyOf(fact.categories ?? {});
|
|
52062
52128
|
grouped[key] = (grouped[key] ?? 0) + fact.value;
|
|
52063
52129
|
}
|
|
52064
52130
|
return grouped;
|
|
52065
52131
|
};
|
|
52132
|
+
var groupMetric = (commit, metric, categoryKey) => groupMetricBy(commit, metric, (categories) => categories[categoryKey] ?? "(unknown)");
|
|
52066
52133
|
var hasMetric = (commit, metric) => commit.factsByCollector.values().some((facts) => facts.some((fact) => fact.metric === metric));
|
|
52067
52134
|
/** Re-keys a numeric record, summing values whose new keys collide. */
|
|
52068
52135
|
var sumByKey = (record, keyOf) => {
|
|
@@ -52127,15 +52194,6 @@ var buildDashboardData = (repoRoot, commits, config, remoteUrl, lineageOf) => {
|
|
|
52127
52194
|
};
|
|
52128
52195
|
};
|
|
52129
52196
|
const span = spanOf(commits);
|
|
52130
|
-
const commitRows = commits.map((commit) => ({
|
|
52131
|
-
sha: commit.sha.slice(0, 10),
|
|
52132
|
-
date: commit.authoredAt,
|
|
52133
|
-
author: commit.authorEmail,
|
|
52134
|
-
kind: config.resolveContributor(commit.authorEmail, commit.authorName).kind,
|
|
52135
|
-
ai: coAuthorsOf(commit).some((identity) => resolveCoAuthor(identity).resolved.kind === "ai"),
|
|
52136
|
-
added: sumMetric(commit, "churn.added"),
|
|
52137
|
-
deleted: sumMetric(commit, "churn.deleted")
|
|
52138
|
-
}));
|
|
52139
52197
|
const languages = composeSeries((commit) => hasMetric(commit, "languages.lines"), (commit) => ({
|
|
52140
52198
|
sha: commit.sha.slice(0, 10),
|
|
52141
52199
|
date: commit.committedAt,
|
|
@@ -52149,15 +52207,18 @@ var buildDashboardData = (repoRoot, commits, config, remoteUrl, lineageOf) => {
|
|
|
52149
52207
|
totalBytes: sumMetric(commit, "files.bytes")
|
|
52150
52208
|
}));
|
|
52151
52209
|
const directives = composeSeries((commit) => hasMetric(commit, "directives.ts"), (commit) => {
|
|
52152
|
-
const
|
|
52210
|
+
const lint = groupMetricBy(commit, "directives.lint", (categories) => `${categories["linter"]} ${categories["type"]}`);
|
|
52153
52211
|
const ts = groupMetric(commit, "directives.ts", "type");
|
|
52154
52212
|
return {
|
|
52155
52213
|
sha: commit.sha.slice(0, 10),
|
|
52156
52214
|
date: commit.committedAt,
|
|
52157
|
-
eslintNextLine:
|
|
52158
|
-
eslintLine:
|
|
52159
|
-
eslintBlocks:
|
|
52160
|
-
|
|
52215
|
+
eslintNextLine: lint["eslint next-line"] ?? 0,
|
|
52216
|
+
eslintLine: lint["eslint line"] ?? 0,
|
|
52217
|
+
eslintBlocks: lint["eslint block"] ?? 0,
|
|
52218
|
+
oxlintNextLine: lint["oxlint next-line"] ?? 0,
|
|
52219
|
+
oxlintLine: lint["oxlint line"] ?? 0,
|
|
52220
|
+
oxlintBlocks: lint["oxlint block"] ?? 0,
|
|
52221
|
+
blockCoveredLines: sumMetric(commit, "directives.lintBlockCoveredLines"),
|
|
52161
52222
|
tsIgnore: ts["ignore"] ?? 0,
|
|
52162
52223
|
tsExpectError: ts["expectError"] ?? 0,
|
|
52163
52224
|
tsNocheck: ts["nocheck"] ?? 0,
|
|
@@ -52177,8 +52238,8 @@ var buildDashboardData = (repoRoot, commits, config, remoteUrl, lineageOf) => {
|
|
|
52177
52238
|
byPackageManager: groupMetric(commit, "dependencies.resolved", "packageManager")
|
|
52178
52239
|
};
|
|
52179
52240
|
});
|
|
52180
|
-
const latestWithDirectives = commits.findLast((commit) => hasMetric(commit, "directives.
|
|
52181
|
-
const topRules = latestWithDirectives ? Object.entries(groupMetric(latestWithDirectives, "directives.
|
|
52241
|
+
const latestWithDirectives = commits.findLast((commit) => hasMetric(commit, "directives.lint"));
|
|
52242
|
+
const topRules = latestWithDirectives ? Object.entries(groupMetric(latestWithDirectives, "directives.lint", "rule")).toSorted(([, left], [, right]) => right - left).slice(0, 20).map(([rule, count]) => ({
|
|
52182
52243
|
rule,
|
|
52183
52244
|
count
|
|
52184
52245
|
})) : [];
|
|
@@ -52243,7 +52304,7 @@ var buildDashboardData = (repoRoot, commits, config, remoteUrl, lineageOf) => {
|
|
|
52243
52304
|
const fileSurvival = survivalSeriesOf("survival.files");
|
|
52244
52305
|
const contributorMap = /* @__PURE__ */ new Map();
|
|
52245
52306
|
/**
|
|
52246
|
-
* Fetches or creates a contributor's
|
|
52307
|
+
* Fetches or creates a contributor's entry.
|
|
52247
52308
|
*
|
|
52248
52309
|
* Humans key off their canonical email alone, so an alias group folds every
|
|
52249
52310
|
* spelling of one person together. Bots and AI agents key off their name too:
|
|
@@ -52252,65 +52313,44 @@ var buildDashboardData = (repoRoot, commits, config, remoteUrl, lineageOf) => {
|
|
|
52252
52313
|
* identity worth telling apart. Giving such a group a `displayName` in the
|
|
52253
52314
|
* config merges them back into one.
|
|
52254
52315
|
*/
|
|
52255
|
-
const
|
|
52316
|
+
const contributorFor = (resolved, observedName) => {
|
|
52256
52317
|
const name = nameOf(resolved, observedName);
|
|
52257
52318
|
const email = resolved.canonicalEmail.toLowerCase();
|
|
52258
52319
|
const key = resolved.kind === "human" ? email : `${name.toLowerCase()}\u{1F}${email}`;
|
|
52259
|
-
const
|
|
52320
|
+
const contributor = contributorMap.get(key) ?? {
|
|
52321
|
+
index: contributorMap.size,
|
|
52260
52322
|
email: resolved.canonicalEmail,
|
|
52261
52323
|
name,
|
|
52262
52324
|
url: resolved.url,
|
|
52263
|
-
kind: resolved.kind
|
|
52264
|
-
commits: 0,
|
|
52265
|
-
added: 0,
|
|
52266
|
-
deleted: 0,
|
|
52267
|
-
assistedBy: {},
|
|
52268
|
-
assisted: {}
|
|
52325
|
+
kind: resolved.kind
|
|
52269
52326
|
};
|
|
52270
|
-
|
|
52271
|
-
contributorMap.set(key,
|
|
52272
|
-
return
|
|
52327
|
+
contributor.name = name;
|
|
52328
|
+
contributorMap.set(key, contributor);
|
|
52329
|
+
return contributor;
|
|
52273
52330
|
};
|
|
52274
|
-
|
|
52275
|
-
const row = commitRows[index];
|
|
52276
|
-
if (!row) continue;
|
|
52331
|
+
const commitRows = commits.map((commit) => {
|
|
52277
52332
|
const author = config.resolveContributor(commit.authorEmail, commit.authorName);
|
|
52278
|
-
const
|
|
52279
|
-
|
|
52280
|
-
authorBucket.added += row.added;
|
|
52281
|
-
authorBucket.deleted += row.deleted;
|
|
52333
|
+
const contributor = contributorFor(author, commit.authorName).index;
|
|
52334
|
+
const coAuthors = coAuthorsOf(commit).map(resolveCoAuthor);
|
|
52282
52335
|
const helpers = /* @__PURE__ */ new Set();
|
|
52283
|
-
for (const
|
|
52284
|
-
|
|
52285
|
-
|
|
52286
|
-
|
|
52287
|
-
|
|
52288
|
-
|
|
52289
|
-
|
|
52290
|
-
|
|
52291
|
-
|
|
52292
|
-
|
|
52293
|
-
|
|
52294
|
-
|
|
52295
|
-
|
|
52296
|
-
|
|
52297
|
-
|
|
52298
|
-
|
|
52299
|
-
|
|
52300
|
-
|
|
52301
|
-
human: 0,
|
|
52302
|
-
bot: 0,
|
|
52303
|
-
ai: 0
|
|
52304
|
-
};
|
|
52305
|
-
const contributors = contributorMap.values().toArray().toSorted((left, right) => involvementOf(right) - involvementOf(left)).filter((bucket) => {
|
|
52306
|
-
if (keptPerKind[bucket.kind] >= perKindCap) return false;
|
|
52307
|
-
keptPerKind[bucket.kind] += 1;
|
|
52308
|
-
return true;
|
|
52309
|
-
}).map((bucket) => ({
|
|
52310
|
-
...bucket,
|
|
52311
|
-
assistedBy: countsOrUndefined(bucket.assistedBy),
|
|
52312
|
-
assisted: countsOrUndefined(bucket.assisted)
|
|
52313
|
-
}));
|
|
52336
|
+
for (const { name, resolved } of coAuthors) if (resolved.kind !== author.kind) helpers.add(contributorFor(resolved, name).index);
|
|
52337
|
+
return {
|
|
52338
|
+
sha: commit.sha.slice(0, 10),
|
|
52339
|
+
date: commit.authoredAt,
|
|
52340
|
+
contributor,
|
|
52341
|
+
kind: author.kind,
|
|
52342
|
+
ai: coAuthors.some(({ resolved }) => resolved.kind === "ai"),
|
|
52343
|
+
added: sumMetric(commit, "churn.added"),
|
|
52344
|
+
deleted: sumMetric(commit, "churn.deleted"),
|
|
52345
|
+
...helpers.size > 0 && { helpers: [...helpers] }
|
|
52346
|
+
};
|
|
52347
|
+
});
|
|
52348
|
+
const contributors = contributorMap.values().map(({ email, name, url, kind }) => ({
|
|
52349
|
+
email,
|
|
52350
|
+
name,
|
|
52351
|
+
url,
|
|
52352
|
+
kind
|
|
52353
|
+
})).toArray();
|
|
52314
52354
|
return {
|
|
52315
52355
|
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
52316
52356
|
config: {
|