@donativo/roux 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.
Files changed (95) hide show
  1. package/README.md +335 -0
  2. package/dist/analysis/age.d.ts +3 -0
  3. package/dist/analysis/age.js +32 -0
  4. package/dist/analysis/age.js.map +1 -0
  5. package/dist/analysis/authors.d.ts +3 -0
  6. package/dist/analysis/authors.js +18 -0
  7. package/dist/analysis/authors.js.map +1 -0
  8. package/dist/analysis/churn.d.ts +5 -0
  9. package/dist/analysis/churn.js +39 -0
  10. package/dist/analysis/churn.js.map +1 -0
  11. package/dist/analysis/communication.d.ts +3 -0
  12. package/dist/analysis/communication.js +44 -0
  13. package/dist/analysis/communication.js.map +1 -0
  14. package/dist/analysis/coupling.d.ts +4 -0
  15. package/dist/analysis/coupling.js +84 -0
  16. package/dist/analysis/coupling.js.map +1 -0
  17. package/dist/analysis/entity-effort.d.ts +3 -0
  18. package/dist/analysis/entity-effort.js +36 -0
  19. package/dist/analysis/entity-effort.js.map +1 -0
  20. package/dist/analysis/entity-ownership.d.ts +3 -0
  21. package/dist/analysis/entity-ownership.js +35 -0
  22. package/dist/analysis/entity-ownership.js.map +1 -0
  23. package/dist/analysis/fragmentation.d.ts +3 -0
  24. package/dist/analysis/fragmentation.js +53 -0
  25. package/dist/analysis/fragmentation.js.map +1 -0
  26. package/dist/analysis/identity.d.ts +3 -0
  27. package/dist/analysis/identity.js +12 -0
  28. package/dist/analysis/identity.js.map +1 -0
  29. package/dist/analysis/index.d.ts +4 -0
  30. package/dist/analysis/index.js +59 -0
  31. package/dist/analysis/index.js.map +1 -0
  32. package/dist/analysis/main-dev-by-revs.d.ts +3 -0
  33. package/dist/analysis/main-dev-by-revs.js +36 -0
  34. package/dist/analysis/main-dev-by-revs.js.map +1 -0
  35. package/dist/analysis/main-dev.d.ts +3 -0
  36. package/dist/analysis/main-dev.js +38 -0
  37. package/dist/analysis/main-dev.js.map +1 -0
  38. package/dist/analysis/messages.d.ts +3 -0
  39. package/dist/analysis/messages.js +27 -0
  40. package/dist/analysis/messages.js.map +1 -0
  41. package/dist/analysis/refactoring-main-dev.d.ts +3 -0
  42. package/dist/analysis/refactoring-main-dev.js +37 -0
  43. package/dist/analysis/refactoring-main-dev.js.map +1 -0
  44. package/dist/analysis/revisions.d.ts +3 -0
  45. package/dist/analysis/revisions.js +16 -0
  46. package/dist/analysis/revisions.js.map +1 -0
  47. package/dist/analysis/summary.d.ts +3 -0
  48. package/dist/analysis/summary.js +12 -0
  49. package/dist/analysis/summary.js.map +1 -0
  50. package/dist/analysis/types.d.ts +18 -0
  51. package/dist/analysis/types.js +8 -0
  52. package/dist/analysis/types.js.map +1 -0
  53. package/dist/app.d.ts +12 -0
  54. package/dist/app.js +54 -0
  55. package/dist/app.js.map +1 -0
  56. package/dist/cli.d.ts +1 -0
  57. package/dist/cli.js +79 -0
  58. package/dist/cli.js.map +1 -0
  59. package/dist/git.d.ts +7 -0
  60. package/dist/git.js +24 -0
  61. package/dist/git.js.map +1 -0
  62. package/dist/index.d.ts +8 -0
  63. package/dist/index.js +6 -0
  64. package/dist/index.js.map +1 -0
  65. package/dist/output/csv.d.ts +1 -0
  66. package/dist/output/csv.js +19 -0
  67. package/dist/output/csv.js.map +1 -0
  68. package/dist/output/json.d.ts +1 -0
  69. package/dist/output/json.js +4 -0
  70. package/dist/output/json.js.map +1 -0
  71. package/dist/parsers/git.d.ts +2 -0
  72. package/dist/parsers/git.js +41 -0
  73. package/dist/parsers/git.js.map +1 -0
  74. package/dist/parsers/git2.d.ts +2 -0
  75. package/dist/parsers/git2.js +72 -0
  76. package/dist/parsers/git2.js.map +1 -0
  77. package/dist/parsers/index.d.ts +7 -0
  78. package/dist/parsers/index.js +7 -0
  79. package/dist/parsers/index.js.map +1 -0
  80. package/dist/parsers/types.d.ts +9 -0
  81. package/dist/parsers/types.js +2 -0
  82. package/dist/parsers/types.js.map +1 -0
  83. package/dist/transforms/grouper.d.ts +11 -0
  84. package/dist/transforms/grouper.js +57 -0
  85. package/dist/transforms/grouper.js.map +1 -0
  86. package/dist/transforms/team-mapper.d.ts +7 -0
  87. package/dist/transforms/team-mapper.js +51 -0
  88. package/dist/transforms/team-mapper.js.map +1 -0
  89. package/dist/transforms/temporal-grouper.d.ts +2 -0
  90. package/dist/transforms/temporal-grouper.js +42 -0
  91. package/dist/transforms/temporal-grouper.js.map +1 -0
  92. package/dist/utils/dataset.d.ts +2 -0
  93. package/dist/utils/dataset.js +34 -0
  94. package/dist/utils/dataset.js.map +1 -0
  95. package/package.json +53 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entity-effort.js","sourceRoot":"","sources":["../../src/analysis/entity-effort.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAE3C,MAAM,UAAU,YAAY,CAC1B,IAAoB,EACpB,QAAyB;IAEzB,MAAM,QAAQ,GAAG,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;IACzC,MAAM,MAAM,GAA8B,EAAE,CAAC;IAE7C,KAAK,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,IAAI,QAAQ,EAAE,CAAC;QACtC,2DAA2D;QAC3D,MAAM,UAAU,GAAG,IAAI,GAAG,EAAuB,CAAC;QAClD,KAAK,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC;YACrB,MAAM,IAAI,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,IAAI,GAAG,EAAU,CAAC;YAC3D,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;YAChB,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QACjC,CAAC;QAED,sCAAsC;QACtC,IAAI,SAAS,GAAG,CAAC,CAAC;QAClB,KAAK,MAAM,IAAI,IAAI,UAAU,CAAC,MAAM,EAAE,EAAE,CAAC;YACvC,SAAS,IAAI,IAAI,CAAC,IAAI,CAAC;QACzB,CAAC;QAED,6BAA6B;QAC7B,MAAM,UAAU,GAA8B,EAAE,CAAC;QACjD,KAAK,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,IAAI,UAAU,EAAE,CAAC;YACxC,UAAU,CAAC,IAAI,CAAC;gBACd,MAAM,EAAE,MAAgB;gBACxB,MAAM;gBACN,aAAa,EAAE,IAAI,CAAC,IAAI;gBACxB,YAAY,EAAE,SAAS;aACxB,CAAC,CAAC;QACL,CAAC;QAED,yCAAyC;QACzC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAE,CAAC,CAAC,aAAa,CAAY,GAAI,CAAC,CAAC,aAAa,CAAY,CAAC,CAAC;QACvF,MAAM,CAAC,IAAI,CAAC,GAAG,UAAU,CAAC,CAAC;IAC7B,CAAC;IAED,yEAAyE;IACzE,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAE,CAAC,CAAC,MAAiB,CAAC,aAAa,CAAC,CAAC,CAAC,MAAgB,CAAC,CAAC,CAAC;IAE9E,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { Modification } from "../parsers/types";
2
+ import type { AnalysisOptions } from "./types";
3
+ export declare function entityOwnership(data: Modification[], _options: AnalysisOptions): Record<string, unknown>[];
@@ -0,0 +1,35 @@
1
+ import { groupBy } from "../utils/dataset";
2
+ /** Normalize binary (-1) to 0 */
3
+ function loc(value) {
4
+ return value < 0 ? 0 : value;
5
+ }
6
+ export function entityOwnership(data, _options) {
7
+ const byEntity = groupBy(data, "entity");
8
+ const result = [];
9
+ for (const [entity, mods] of byEntity) {
10
+ const byAuthor = new Map();
11
+ for (const m of mods) {
12
+ const stats = byAuthor.get(m.author) ?? { added: 0, deleted: 0 };
13
+ stats.added += loc(m.locAdded);
14
+ stats.deleted += loc(m.locDeleted);
15
+ byAuthor.set(m.author, stats);
16
+ }
17
+ for (const [author, stats] of byAuthor) {
18
+ result.push({
19
+ entity: entity,
20
+ author,
21
+ added: stats.added,
22
+ deleted: stats.deleted,
23
+ });
24
+ }
25
+ }
26
+ // Sort by entity asc, then by added desc within entity
27
+ result.sort((a, b) => {
28
+ const entityCmp = a.entity.localeCompare(b.entity);
29
+ if (entityCmp !== 0)
30
+ return entityCmp;
31
+ return b.added - a.added;
32
+ });
33
+ return result;
34
+ }
35
+ //# sourceMappingURL=entity-ownership.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entity-ownership.js","sourceRoot":"","sources":["../../src/analysis/entity-ownership.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAE3C,iCAAiC;AACjC,SAAS,GAAG,CAAC,KAAa;IACxB,OAAO,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;AAC/B,CAAC;AAED,MAAM,UAAU,eAAe,CAC7B,IAAoB,EACpB,QAAyB;IAEzB,MAAM,QAAQ,GAAG,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;IACzC,MAAM,MAAM,GAA8B,EAAE,CAAC;IAE7C,KAAK,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,IAAI,QAAQ,EAAE,CAAC;QACtC,MAAM,QAAQ,GAAG,IAAI,GAAG,EAA8C,CAAC;QACvE,KAAK,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC;YACrB,MAAM,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;YACjE,KAAK,CAAC,KAAK,IAAI,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;YAC/B,KAAK,CAAC,OAAO,IAAI,GAAG,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC;YACnC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;QAChC,CAAC;QACD,KAAK,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,IAAI,QAAQ,EAAE,CAAC;YACvC,MAAM,CAAC,IAAI,CAAC;gBACV,MAAM,EAAE,MAAgB;gBACxB,MAAM;gBACN,KAAK,EAAE,KAAK,CAAC,KAAK;gBAClB,OAAO,EAAE,KAAK,CAAC,OAAO;aACvB,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,uDAAuD;IACvD,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;QACnB,MAAM,SAAS,GAAI,CAAC,CAAC,MAAiB,CAAC,aAAa,CAAC,CAAC,CAAC,MAAgB,CAAC,CAAC;QACzE,IAAI,SAAS,KAAK,CAAC;YAAE,OAAO,SAAS,CAAC;QACtC,OAAQ,CAAC,CAAC,KAAgB,GAAI,CAAC,CAAC,KAAgB,CAAC;IACnD,CAAC,CAAC,CAAC;IAEH,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { Modification } from "../parsers/types";
2
+ import type { AnalysisOptions } from "./types";
3
+ export declare function fragmentation(data: Modification[], _options: AnalysisOptions): Record<string, unknown>[];
@@ -0,0 +1,53 @@
1
+ import { groupBy } from "../utils/dataset";
2
+ /**
3
+ * Format to 2 significant figures as a string, matching code-maat's
4
+ * ratio->centi-float-precision. Uses toPrecision(2) for non-zero values,
5
+ * which produces strings like "0.51", "0.091", "0.0050".
6
+ * For zero, returns "0.0" to match Clojure's (double 0) formatting.
7
+ */
8
+ function formatPrecision2(v) {
9
+ if (v === 0)
10
+ return "0.0";
11
+ const s = v.toPrecision(2);
12
+ // toPrecision may return scientific notation for very small numbers
13
+ return String(parseFloat(s));
14
+ }
15
+ export function fragmentation(data, _options) {
16
+ const byEntity = groupBy(data, "entity");
17
+ const intermediate = [];
18
+ for (const [entity, mods] of byEntity) {
19
+ // Count unique revisions per author
20
+ const authorRevs = new Map();
21
+ for (const m of mods) {
22
+ const revs = authorRevs.get(m.author) ?? new Set();
23
+ revs.add(m.rev);
24
+ authorRevs.set(m.author, revs);
25
+ }
26
+ // total-revs = sum of all author-revs
27
+ let totalRevs = 0;
28
+ for (const revs of authorRevs.values()) {
29
+ totalRevs += revs.size;
30
+ }
31
+ // fractal = 1 - sum((author_revs / total_revs)^2)
32
+ let sumSquares = 0;
33
+ for (const revs of authorRevs.values()) {
34
+ const fraction = revs.size / totalRevs;
35
+ sumSquares += fraction * fraction;
36
+ }
37
+ // Use high-precision intermediate to avoid FP accumulation errors
38
+ const fractal = 1 - parseFloat(sumSquares.toPrecision(10));
39
+ intermediate.push({
40
+ entity: entity,
41
+ fractal,
42
+ totalRevs,
43
+ });
44
+ }
45
+ // Sort by fractal value descending (numerically), then by totalRevs descending
46
+ intermediate.sort((a, b) => b.fractal - a.fractal || b.totalRevs - a.totalRevs);
47
+ return intermediate.map((r) => ({
48
+ entity: r.entity,
49
+ "fractal-value": formatPrecision2(r.fractal),
50
+ "total-revs": r.totalRevs,
51
+ }));
52
+ }
53
+ //# sourceMappingURL=fragmentation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fragmentation.js","sourceRoot":"","sources":["../../src/analysis/fragmentation.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAW,MAAM,kBAAkB,CAAC;AAEpD;;;;;GAKG;AACH,SAAS,gBAAgB,CAAC,CAAS;IACjC,IAAI,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IAC1B,MAAM,CAAC,GAAG,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC;IAC3B,oEAAoE;IACpE,OAAO,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;AAC/B,CAAC;AAED,MAAM,UAAU,aAAa,CAC3B,IAAoB,EACpB,QAAyB;IAEzB,MAAM,QAAQ,GAAG,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;IACzC,MAAM,YAAY,GAA6D,EAAE,CAAC;IAElF,KAAK,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,IAAI,QAAQ,EAAE,CAAC;QACtC,oCAAoC;QACpC,MAAM,UAAU,GAAG,IAAI,GAAG,EAAuB,CAAC;QAClD,KAAK,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC;YACrB,MAAM,IAAI,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,IAAI,GAAG,EAAU,CAAC;YAC3D,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;YAChB,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QACjC,CAAC;QAED,sCAAsC;QACtC,IAAI,SAAS,GAAG,CAAC,CAAC;QAClB,KAAK,MAAM,IAAI,IAAI,UAAU,CAAC,MAAM,EAAE,EAAE,CAAC;YACvC,SAAS,IAAI,IAAI,CAAC,IAAI,CAAC;QACzB,CAAC;QAED,kDAAkD;QAClD,IAAI,UAAU,GAAG,CAAC,CAAC;QACnB,KAAK,MAAM,IAAI,IAAI,UAAU,CAAC,MAAM,EAAE,EAAE,CAAC;YACvC,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,GAAG,SAAS,CAAC;YACvC,UAAU,IAAI,QAAQ,GAAG,QAAQ,CAAC;QACpC,CAAC;QACD,kEAAkE;QAClE,MAAM,OAAO,GAAG,CAAC,GAAG,UAAU,CAAC,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,CAAC;QAE3D,YAAY,CAAC,IAAI,CAAC;YAChB,MAAM,EAAE,MAAgB;YACxB,OAAO;YACP,SAAS;SACV,CAAC,CAAC;IACL,CAAC;IAED,+EAA+E;IAC/E,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,GAAG,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,SAAS,GAAG,CAAC,CAAC,SAAS,CAAC,CAAC;IAEhF,OAAO,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAC9B,MAAM,EAAE,CAAC,CAAC,MAAM;QAChB,eAAe,EAAE,gBAAgB,CAAC,CAAC,CAAC,OAAO,CAAC;QAC5C,YAAY,EAAE,CAAC,CAAC,SAAS;KAC1B,CAAC,CAAC,CAAC;AACN,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { Modification } from "../parsers/types";
2
+ import type { AnalysisOptions } from "./types";
3
+ export declare function identity(data: Modification[], _options: AnalysisOptions): Record<string, unknown>[];
@@ -0,0 +1,12 @@
1
+ export function identity(data, _options) {
2
+ return data.map((m) => ({
3
+ author: m.author,
4
+ rev: m.rev,
5
+ date: m.date,
6
+ entity: m.entity,
7
+ message: m.message ?? "-",
8
+ "loc-added": m.locAdded < 0 ? "-" : m.locAdded,
9
+ "loc-deleted": m.locDeleted < 0 ? "-" : m.locDeleted,
10
+ }));
11
+ }
12
+ //# sourceMappingURL=identity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"identity.js","sourceRoot":"","sources":["../../src/analysis/identity.ts"],"names":[],"mappings":"AAGA,MAAM,UAAU,QAAQ,CACtB,IAAoB,EACpB,QAAyB;IAEzB,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACtB,MAAM,EAAE,CAAC,CAAC,MAAM;QAChB,GAAG,EAAE,CAAC,CAAC,GAAG;QACV,IAAI,EAAE,CAAC,CAAC,IAAI;QACZ,MAAM,EAAE,CAAC,CAAC,MAAM;QAChB,OAAO,EAAE,CAAC,CAAC,OAAO,IAAI,GAAG;QACzB,WAAW,EAAE,CAAC,CAAC,QAAQ,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ;QAC9C,aAAa,EAAE,CAAC,CAAC,UAAU,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,UAAU;KACrD,CAAC,CAAC,CAAC;AACN,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { AnalysisFn } from "./types";
2
+ /** Column headers for each analysis, used when results are empty */
3
+ export declare const analysisHeaders: Record<string, string[]>;
4
+ export declare const analyses: Record<string, AnalysisFn>;
@@ -0,0 +1,59 @@
1
+ import { summary } from "./summary";
2
+ import { authors } from "./authors";
3
+ import { revisions } from "./revisions";
4
+ import { coupling, sumOfCoupling } from "./coupling";
5
+ import { absoluteChurn, churnByEntity, churnByAuthor } from "./churn";
6
+ import { age } from "./age";
7
+ import { entityOwnership } from "./entity-ownership";
8
+ import { mainDev } from "./main-dev";
9
+ import { mainDevByRevs } from "./main-dev-by-revs";
10
+ import { refactoringMainDev } from "./refactoring-main-dev";
11
+ import { entityEffort } from "./entity-effort";
12
+ import { fragmentation } from "./fragmentation";
13
+ import { communication } from "./communication";
14
+ import { messages } from "./messages";
15
+ import { identity } from "./identity";
16
+ /** Column headers for each analysis, used when results are empty */
17
+ export const analysisHeaders = {
18
+ summary: ["statistic", "value"],
19
+ authors: ["entity", "n-authors", "n-revs"],
20
+ revisions: ["entity", "n-revs"],
21
+ coupling: ["entity", "coupled", "degree", "average-revs"],
22
+ soc: ["entity", "soc"],
23
+ "abs-churn": ["date", "added", "deleted", "commits"],
24
+ "entity-churn": ["entity", "added", "deleted", "commits"],
25
+ "author-churn": ["author", "added", "deleted", "commits"],
26
+ age: ["entity", "age-months"],
27
+ "entity-ownership": ["entity", "author", "added", "deleted"],
28
+ "main-dev": ["entity", "main-dev", "added", "total-added", "ownership"],
29
+ "main-dev-by-revs": ["entity", "main-dev", "added", "total-added", "ownership"],
30
+ "refactoring-main-dev": ["entity", "main-dev", "removed", "total-removed", "ownership"],
31
+ "entity-effort": ["entity", "author", "author-revs", "total-revs"],
32
+ fragmentation: ["entity", "fractal-value", "total-revs"],
33
+ communication: ["author", "peer", "shared", "average", "strength"],
34
+ messages: ["entity", "matches"],
35
+ identity: ["author", "rev", "date", "entity", "message", "loc-added", "loc-deleted"],
36
+ };
37
+ export const analyses = {
38
+ // Phase 1
39
+ summary,
40
+ authors,
41
+ revisions,
42
+ coupling,
43
+ soc: sumOfCoupling,
44
+ "abs-churn": absoluteChurn,
45
+ "entity-churn": churnByEntity,
46
+ "author-churn": churnByAuthor,
47
+ // Phase 2
48
+ age,
49
+ "entity-ownership": entityOwnership,
50
+ "main-dev": mainDev,
51
+ "main-dev-by-revs": mainDevByRevs,
52
+ "refactoring-main-dev": refactoringMainDev,
53
+ "entity-effort": entityEffort,
54
+ fragmentation,
55
+ communication,
56
+ messages,
57
+ identity,
58
+ };
59
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/analysis/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { Modification } from "../parsers/types";
2
+ import type { AnalysisOptions } from "./types";
3
+ export declare function mainDevByRevs(data: Modification[], _options: AnalysisOptions): Record<string, unknown>[];
@@ -0,0 +1,36 @@
1
+ import { groupBy } from "../utils/dataset";
2
+ function formatOwnership(ratio) {
3
+ const rounded = Math.round(ratio * 100) / 100;
4
+ const s = String(rounded);
5
+ return s.includes(".") ? s : s + ".0";
6
+ }
7
+ export function mainDevByRevs(data, _options) {
8
+ const byEntity = groupBy(data, "entity");
9
+ const result = [];
10
+ for (const [entity, mods] of byEntity) {
11
+ const byAuthor = groupBy(mods, "author");
12
+ let bestAuthor = "";
13
+ let bestRevs = 0;
14
+ let totalRevs = 0;
15
+ for (const [author, authorMods] of byAuthor) {
16
+ const uniqueRevs = new Set(authorMods.map((m) => m.rev)).size;
17
+ totalRevs += uniqueRevs;
18
+ if (uniqueRevs >= bestRevs) {
19
+ bestRevs = uniqueRevs;
20
+ bestAuthor = author;
21
+ }
22
+ }
23
+ if (totalRevs > 0) {
24
+ result.push({
25
+ entity: entity,
26
+ "main-dev": bestAuthor,
27
+ added: bestRevs,
28
+ "total-added": totalRevs,
29
+ ownership: formatOwnership(bestRevs / totalRevs),
30
+ });
31
+ }
32
+ }
33
+ result.sort((a, b) => a.entity.localeCompare(b.entity));
34
+ return result;
35
+ }
36
+ //# sourceMappingURL=main-dev-by-revs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"main-dev-by-revs.js","sourceRoot":"","sources":["../../src/analysis/main-dev-by-revs.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAE3C,SAAS,eAAe,CAAC,KAAa;IACpC,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,GAAG,CAAC,GAAG,GAAG,CAAC;IAC9C,MAAM,CAAC,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC;IAC1B,OAAO,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;AACxC,CAAC;AAED,MAAM,UAAU,aAAa,CAC3B,IAAoB,EACpB,QAAyB;IAEzB,MAAM,QAAQ,GAAG,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;IACzC,MAAM,MAAM,GAA8B,EAAE,CAAC;IAE7C,KAAK,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,IAAI,QAAQ,EAAE,CAAC;QACtC,MAAM,QAAQ,GAAG,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;QACzC,IAAI,UAAU,GAAG,EAAE,CAAC;QACpB,IAAI,QAAQ,GAAG,CAAC,CAAC;QACjB,IAAI,SAAS,GAAG,CAAC,CAAC;QAElB,KAAK,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,IAAI,QAAQ,EAAE,CAAC;YAC5C,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC;YAC9D,SAAS,IAAI,UAAU,CAAC;YACxB,IAAI,UAAU,IAAI,QAAQ,EAAE,CAAC;gBAC3B,QAAQ,GAAG,UAAU,CAAC;gBACtB,UAAU,GAAG,MAAgB,CAAC;YAChC,CAAC;QACH,CAAC;QAED,IAAI,SAAS,GAAG,CAAC,EAAE,CAAC;YAClB,MAAM,CAAC,IAAI,CAAC;gBACV,MAAM,EAAE,MAAgB;gBACxB,UAAU,EAAE,UAAU;gBACtB,KAAK,EAAE,QAAQ;gBACf,aAAa,EAAE,SAAS;gBACxB,SAAS,EAAE,eAAe,CAAC,QAAQ,GAAG,SAAS,CAAC;aACjD,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAClB,CAAC,CAAC,MAAiB,CAAC,aAAa,CAAC,CAAC,CAAC,MAAgB,CAAC,CACvD,CAAC;IACF,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { Modification } from "../parsers/types";
2
+ import type { AnalysisOptions } from "./types";
3
+ export declare function mainDev(data: Modification[], _options: AnalysisOptions): Record<string, unknown>[];
@@ -0,0 +1,38 @@
1
+ import { groupBy } from "../utils/dataset";
2
+ /** Format ownership ratio to match code-maat's Clojure double formatting */
3
+ function formatOwnership(ratio) {
4
+ const rounded = Math.round(ratio * 100) / 100;
5
+ const s = String(rounded);
6
+ return s.includes(".") ? s : s + ".0";
7
+ }
8
+ export function mainDev(data, _options) {
9
+ const byEntity = groupBy(data, "entity");
10
+ const result = [];
11
+ for (const [entity, mods] of byEntity) {
12
+ const byAuthor = groupBy(mods, "author");
13
+ let bestAuthor = "";
14
+ let bestAdded = 0;
15
+ let totalAdded = 0;
16
+ for (const [author, authorMods] of byAuthor) {
17
+ const added = authorMods.reduce((sum, m) => sum + Math.max(0, m.locAdded), 0);
18
+ totalAdded += added;
19
+ if (added >= bestAdded) {
20
+ bestAdded = added;
21
+ bestAuthor = author;
22
+ }
23
+ }
24
+ const ownership = totalAdded > 0
25
+ ? formatOwnership(bestAdded / totalAdded)
26
+ : "0.0";
27
+ result.push({
28
+ entity: entity,
29
+ "main-dev": bestAuthor,
30
+ added: bestAdded,
31
+ "total-added": totalAdded,
32
+ ownership,
33
+ });
34
+ }
35
+ result.sort((a, b) => a.entity.localeCompare(b.entity));
36
+ return result;
37
+ }
38
+ //# sourceMappingURL=main-dev.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"main-dev.js","sourceRoot":"","sources":["../../src/analysis/main-dev.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAE3C,4EAA4E;AAC5E,SAAS,eAAe,CAAC,KAAa;IACpC,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,GAAG,CAAC,GAAG,GAAG,CAAC;IAC9C,MAAM,CAAC,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC;IAC1B,OAAO,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;AACxC,CAAC;AAED,MAAM,UAAU,OAAO,CACrB,IAAoB,EACpB,QAAyB;IAEzB,MAAM,QAAQ,GAAG,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;IACzC,MAAM,MAAM,GAA8B,EAAE,CAAC;IAE7C,KAAK,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,IAAI,QAAQ,EAAE,CAAC;QACtC,MAAM,QAAQ,GAAG,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;QACzC,IAAI,UAAU,GAAG,EAAE,CAAC;QACpB,IAAI,SAAS,GAAG,CAAC,CAAC;QAClB,IAAI,UAAU,GAAG,CAAC,CAAC;QAEnB,KAAK,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,IAAI,QAAQ,EAAE,CAAC;YAC5C,MAAM,KAAK,GAAG,UAAU,CAAC,MAAM,CAC7B,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,EACzC,CAAC,CACF,CAAC;YACF,UAAU,IAAI,KAAK,CAAC;YACpB,IAAI,KAAK,IAAI,SAAS,EAAE,CAAC;gBACvB,SAAS,GAAG,KAAK,CAAC;gBAClB,UAAU,GAAG,MAAgB,CAAC;YAChC,CAAC;QACH,CAAC;QAED,MAAM,SAAS,GAAG,UAAU,GAAG,CAAC;YAC9B,CAAC,CAAC,eAAe,CAAC,SAAS,GAAG,UAAU,CAAC;YACzC,CAAC,CAAC,KAAK,CAAC;QACV,MAAM,CAAC,IAAI,CAAC;YACV,MAAM,EAAE,MAAgB;YACxB,UAAU,EAAE,UAAU;YACtB,KAAK,EAAE,SAAS;YAChB,aAAa,EAAE,UAAU;YACzB,SAAS;SACV,CAAC,CAAC;IACL,CAAC;IAED,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAClB,CAAC,CAAC,MAAiB,CAAC,aAAa,CAAC,CAAC,CAAC,MAAgB,CAAC,CACvD,CAAC;IACF,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { Modification } from "../parsers/types";
2
+ import type { AnalysisOptions } from "./types";
3
+ export declare function messages(data: Modification[], options: AnalysisOptions): Record<string, unknown>[];
@@ -0,0 +1,27 @@
1
+ import { groupBy, orderBy } from "../utils/dataset";
2
+ export function messages(data, options) {
3
+ if (!options.expressionToMatch) {
4
+ throw new Error("Messages analysis requires --expression-to-match (-e) option");
5
+ }
6
+ // Entries with message "-" are dash placeholders (format has no real messages)
7
+ const dashEntries = data.filter((m) => m.message === "-");
8
+ // If ALL entries have dash placeholder messages, the format doesn't support messages
9
+ if (dashEntries.length > 0 && dashEntries.length === data.length) {
10
+ throw new Error("Cannot do a messages analysis without commit messages. " +
11
+ "The input log format may not include messages.");
12
+ }
13
+ // Filter out dash placeholders before matching
14
+ const withMessages = data.filter((m) => m.message && m.message !== "-");
15
+ const regex = new RegExp(options.expressionToMatch);
16
+ const byEntity = groupBy(withMessages, "entity");
17
+ const result = [];
18
+ for (const [entity, mods] of byEntity) {
19
+ const matches = mods.filter((m) => m.message && regex.test(m.message))
20
+ .length;
21
+ if (matches > 0) {
22
+ result.push({ entity: entity, matches });
23
+ }
24
+ }
25
+ return orderBy(result, "matches", "desc");
26
+ }
27
+ //# sourceMappingURL=messages.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"messages.js","sourceRoot":"","sources":["../../src/analysis/messages.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAEpD,MAAM,UAAU,QAAQ,CACtB,IAAoB,EACpB,OAAwB;IAExB,IAAI,CAAC,OAAO,CAAC,iBAAiB,EAAE,CAAC;QAC/B,MAAM,IAAI,KAAK,CACb,8DAA8D,CAC/D,CAAC;IACJ,CAAC;IAED,+EAA+E;IAC/E,MAAM,WAAW,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,GAAG,CAAC,CAAC;IAE1D,qFAAqF;IACrF,IAAI,WAAW,CAAC,MAAM,GAAG,CAAC,IAAI,WAAW,CAAC,MAAM,KAAK,IAAI,CAAC,MAAM,EAAE,CAAC;QACjE,MAAM,IAAI,KAAK,CACb,yDAAyD;YACzD,gDAAgD,CACjD,CAAC;IACJ,CAAC;IAED,+CAA+C;IAC/C,MAAM,YAAY,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,OAAO,KAAK,GAAG,CAAC,CAAC;IAExE,MAAM,KAAK,GAAG,IAAI,MAAM,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAC;IACpD,MAAM,QAAQ,GAAG,OAAO,CAAC,YAAY,EAAE,QAAQ,CAAC,CAAC;IACjD,MAAM,MAAM,GAA8B,EAAE,CAAC;IAE7C,KAAK,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,IAAI,QAAQ,EAAE,CAAC;QACtC,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;aACnE,MAAM,CAAC;QACV,IAAI,OAAO,GAAG,CAAC,EAAE,CAAC;YAChB,MAAM,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,MAAgB,EAAE,OAAO,EAAE,CAAC,CAAC;QACrD,CAAC;IACH,CAAC;IAED,OAAO,OAAO,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,CAAC,CAAC;AAC5C,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { Modification } from "../parsers/types";
2
+ import type { AnalysisOptions } from "./types";
3
+ export declare function refactoringMainDev(data: Modification[], _options: AnalysisOptions): Record<string, unknown>[];
@@ -0,0 +1,37 @@
1
+ import { groupBy } from "../utils/dataset";
2
+ function formatOwnership(ratio) {
3
+ const rounded = Math.round(ratio * 100) / 100;
4
+ const s = String(rounded);
5
+ return s.includes(".") ? s : s + ".0";
6
+ }
7
+ export function refactoringMainDev(data, _options) {
8
+ const byEntity = groupBy(data, "entity");
9
+ const result = [];
10
+ for (const [entity, mods] of byEntity) {
11
+ const byAuthor = groupBy(mods, "author");
12
+ let bestAuthor = "";
13
+ let bestDeleted = 0;
14
+ let totalDeleted = 0;
15
+ for (const [author, authorMods] of byAuthor) {
16
+ const deleted = authorMods.reduce((sum, m) => sum + Math.max(0, m.locDeleted), 0);
17
+ totalDeleted += deleted;
18
+ if (deleted >= bestDeleted) {
19
+ bestDeleted = deleted;
20
+ bestAuthor = author;
21
+ }
22
+ }
23
+ const ownership = totalDeleted > 0
24
+ ? formatOwnership(bestDeleted / totalDeleted)
25
+ : "0.0";
26
+ result.push({
27
+ entity: entity,
28
+ "main-dev": bestAuthor,
29
+ removed: bestDeleted,
30
+ "total-removed": totalDeleted,
31
+ ownership,
32
+ });
33
+ }
34
+ result.sort((a, b) => a.entity.localeCompare(b.entity));
35
+ return result;
36
+ }
37
+ //# sourceMappingURL=refactoring-main-dev.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"refactoring-main-dev.js","sourceRoot":"","sources":["../../src/analysis/refactoring-main-dev.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAE3C,SAAS,eAAe,CAAC,KAAa;IACpC,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,GAAG,CAAC,GAAG,GAAG,CAAC;IAC9C,MAAM,CAAC,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC;IAC1B,OAAO,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;AACxC,CAAC;AAED,MAAM,UAAU,kBAAkB,CAChC,IAAoB,EACpB,QAAyB;IAEzB,MAAM,QAAQ,GAAG,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;IACzC,MAAM,MAAM,GAA8B,EAAE,CAAC;IAE7C,KAAK,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,IAAI,QAAQ,EAAE,CAAC;QACtC,MAAM,QAAQ,GAAG,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;QACzC,IAAI,UAAU,GAAG,EAAE,CAAC;QACpB,IAAI,WAAW,GAAG,CAAC,CAAC;QACpB,IAAI,YAAY,GAAG,CAAC,CAAC;QAErB,KAAK,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,IAAI,QAAQ,EAAE,CAAC;YAC5C,MAAM,OAAO,GAAG,UAAU,CAAC,MAAM,CAC/B,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,UAAU,CAAC,EAC3C,CAAC,CACF,CAAC;YACF,YAAY,IAAI,OAAO,CAAC;YACxB,IAAI,OAAO,IAAI,WAAW,EAAE,CAAC;gBAC3B,WAAW,GAAG,OAAO,CAAC;gBACtB,UAAU,GAAG,MAAgB,CAAC;YAChC,CAAC;QACH,CAAC;QAED,MAAM,SAAS,GAAG,YAAY,GAAG,CAAC;YAChC,CAAC,CAAC,eAAe,CAAC,WAAW,GAAG,YAAY,CAAC;YAC7C,CAAC,CAAC,KAAK,CAAC;QACV,MAAM,CAAC,IAAI,CAAC;YACV,MAAM,EAAE,MAAgB;YACxB,UAAU,EAAE,UAAU;YACtB,OAAO,EAAE,WAAW;YACpB,eAAe,EAAE,YAAY;YAC7B,SAAS;SACV,CAAC,CAAC;IACL,CAAC;IAED,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAClB,CAAC,CAAC,MAAiB,CAAC,aAAa,CAAC,CAAC,CAAC,MAAgB,CAAC,CACvD,CAAC;IACF,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { Modification } from "../parsers/types";
2
+ import type { AnalysisOptions } from "./types";
3
+ export declare function revisions(data: Modification[], options: AnalysisOptions): Record<string, unknown>[];
@@ -0,0 +1,16 @@
1
+ import { groupBy, orderBy } from "../utils/dataset";
2
+ export function revisions(data, options) {
3
+ const byEntity = groupBy(data, "entity");
4
+ const result = [];
5
+ for (const [entity, mods] of byEntity) {
6
+ const nRevs = mods.length;
7
+ if (nRevs >= options.minRevs) {
8
+ result.push({
9
+ entity: entity,
10
+ "n-revs": nRevs,
11
+ });
12
+ }
13
+ }
14
+ return orderBy(result, "n-revs", "desc");
15
+ }
16
+ //# sourceMappingURL=revisions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"revisions.js","sourceRoot":"","sources":["../../src/analysis/revisions.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAEpD,MAAM,UAAU,SAAS,CACvB,IAAoB,EACpB,OAAwB;IAExB,MAAM,QAAQ,GAAG,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;IACzC,MAAM,MAAM,GAA8B,EAAE,CAAC;IAE7C,KAAK,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,IAAI,QAAQ,EAAE,CAAC;QACtC,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC;QAC1B,IAAI,KAAK,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;YAC7B,MAAM,CAAC,IAAI,CAAC;gBACV,MAAM,EAAE,MAAgB;gBACxB,QAAQ,EAAE,KAAK;aAChB,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,OAAO,OAAO,CAAC,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC;AAC3C,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { Modification } from "../parsers/types";
2
+ import type { AnalysisOptions } from "./types";
3
+ export declare function summary(data: Modification[], _options: AnalysisOptions): Record<string, unknown>[];
@@ -0,0 +1,12 @@
1
+ export function summary(data, _options) {
2
+ const commits = new Set(data.map((m) => m.rev));
3
+ const entities = new Set(data.map((m) => m.entity));
4
+ const authors = new Set(data.map((m) => m.author));
5
+ return [
6
+ { statistic: "number-of-commits", value: commits.size },
7
+ { statistic: "number-of-entities", value: entities.size },
8
+ { statistic: "number-of-entities-changed", value: data.length },
9
+ { statistic: "number-of-authors", value: authors.size },
10
+ ];
11
+ }
12
+ //# sourceMappingURL=summary.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"summary.js","sourceRoot":"","sources":["../../src/analysis/summary.ts"],"names":[],"mappings":"AAGA,MAAM,UAAU,OAAO,CACrB,IAAoB,EACpB,QAAyB;IAEzB,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IAChD,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;IACpD,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;IAEnD,OAAO;QACL,EAAE,SAAS,EAAE,mBAAmB,EAAE,KAAK,EAAE,OAAO,CAAC,IAAI,EAAE;QACvD,EAAE,SAAS,EAAE,oBAAoB,EAAE,KAAK,EAAE,QAAQ,CAAC,IAAI,EAAE;QACzD,EAAE,SAAS,EAAE,4BAA4B,EAAE,KAAK,EAAE,IAAI,CAAC,MAAM,EAAE;QAC/D,EAAE,SAAS,EAAE,mBAAmB,EAAE,KAAK,EAAE,OAAO,CAAC,IAAI,EAAE;KACxD,CAAC;AACJ,CAAC"}
@@ -0,0 +1,18 @@
1
+ import type { Modification } from "../parsers/types";
2
+ export interface AnalysisOptions {
3
+ minRevs: number;
4
+ minSharedRevs: number;
5
+ minCoupling: number;
6
+ maxCoupling: number;
7
+ maxChangesetSize: number;
8
+ verboseResults?: boolean;
9
+ ageTimeNow?: string;
10
+ expressionToMatch?: string;
11
+ groupFile?: string;
12
+ teamMapFile?: string;
13
+ temporalPeriod?: number;
14
+ outputFormat?: "csv" | "json";
15
+ logFormat?: "git2" | "git";
16
+ }
17
+ export declare const defaultOptions: AnalysisOptions;
18
+ export type AnalysisFn = (data: Modification[], options: AnalysisOptions) => Record<string, unknown>[];
@@ -0,0 +1,8 @@
1
+ export const defaultOptions = {
2
+ minRevs: 5,
3
+ minSharedRevs: 5,
4
+ minCoupling: 30,
5
+ maxCoupling: 100,
6
+ maxChangesetSize: 30,
7
+ };
8
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/analysis/types.ts"],"names":[],"mappings":"AAkBA,MAAM,CAAC,MAAM,cAAc,GAAoB;IAC7C,OAAO,EAAE,CAAC;IACV,aAAa,EAAE,CAAC;IAChB,WAAW,EAAE,EAAE;IACf,WAAW,EAAE,GAAG;IAChB,gBAAgB,EAAE,EAAE;CACrB,CAAC"}
package/dist/app.d.ts ADDED
@@ -0,0 +1,12 @@
1
+ import { type AnalysisOptions } from "./analysis/types";
2
+ export interface AppOptions extends Partial<AnalysisOptions> {
3
+ log?: string;
4
+ repo?: string;
5
+ input?: string;
6
+ analysis: string;
7
+ rows?: number;
8
+ after?: string;
9
+ before?: string;
10
+ rev?: string;
11
+ }
12
+ export declare function run(opts: AppOptions): string;
package/dist/app.js ADDED
@@ -0,0 +1,54 @@
1
+ import { readFileSync } from "fs";
2
+ import { parsers } from "./parsers";
3
+ import { analyses, analysisHeaders } from "./analysis";
4
+ import { defaultOptions } from "./analysis/types";
5
+ import { toCSV } from "./output/csv";
6
+ import { toJSON } from "./output/json";
7
+ import { generateGitLog } from "./git";
8
+ import { parseGroupConfig, applyGrouping } from "./transforms/grouper";
9
+ import { parseTeamConfig, applyTeamMapping } from "./transforms/team-mapper";
10
+ import { applyTemporalGrouping } from "./transforms/temporal-grouper";
11
+ function getLogText(opts) {
12
+ if (opts.input)
13
+ return opts.input;
14
+ if (opts.log)
15
+ return readFileSync(opts.log, "utf-8");
16
+ return generateGitLog({ repo: opts.repo, after: opts.after, before: opts.before, rev: opts.rev });
17
+ }
18
+ export function run(opts) {
19
+ const analysisFn = analyses[opts.analysis];
20
+ if (!analysisFn) {
21
+ const available = Object.keys(analyses).join(", ");
22
+ throw new Error(`Unknown analysis "${opts.analysis}". Available: ${available}`);
23
+ }
24
+ const text = getLogText(opts);
25
+ // Filter out undefined values so defaults aren't overridden
26
+ const defined = Object.fromEntries(Object.entries(opts).filter(([, v]) => v !== undefined));
27
+ const options = { ...defaultOptions, ...defined };
28
+ const format = options.logFormat ?? "git2";
29
+ const parser = parsers[format];
30
+ if (!parser) {
31
+ throw new Error(`Unknown log format "${format}". Available: ${Object.keys(parsers).join(", ")}`);
32
+ }
33
+ let modifications = parser(text);
34
+ if (options.groupFile) {
35
+ const specs = parseGroupConfig(options.groupFile);
36
+ modifications = applyGrouping(modifications, specs);
37
+ }
38
+ if (options.temporalPeriod) {
39
+ modifications = applyTemporalGrouping(modifications, options.temporalPeriod);
40
+ }
41
+ if (options.teamMapFile) {
42
+ const teamMap = parseTeamConfig(options.teamMapFile);
43
+ modifications = applyTeamMapping(modifications, teamMap);
44
+ }
45
+ let result = analysisFn(modifications, options);
46
+ if (opts.rows && opts.rows > 0) {
47
+ result = result.slice(0, opts.rows);
48
+ }
49
+ if (options.outputFormat === "json") {
50
+ return toJSON(result);
51
+ }
52
+ return toCSV(result, analysisHeaders[opts.analysis]);
53
+ }
54
+ //# sourceMappingURL=app.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app.js","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"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"}
package/dist/cli.d.ts ADDED
@@ -0,0 +1 @@
1
+ export {};