repo-wrapped 0.0.3 → 0.0.4
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/dist/commands/generate.js +104 -95
- package/dist/constants/chronotypes.js +23 -23
- package/dist/constants/colors.js +18 -18
- package/dist/constants/index.js +18 -18
- package/dist/formatters/index.js +17 -17
- package/dist/formatters/timeFormatter.js +28 -29
- package/dist/generators/html/templates/achievementsSection.js +42 -43
- package/dist/generators/html/templates/commitQualitySection.js +25 -26
- package/dist/generators/html/templates/contributionGraph.js +47 -48
- package/dist/generators/html/templates/impactSection.js +19 -20
- package/dist/generators/html/templates/knowledgeSection.js +86 -87
- package/dist/generators/html/templates/streakSection.js +8 -9
- package/dist/generators/html/templates/timePatternsSection.js +45 -46
- package/dist/generators/html/utils/colorUtils.js +21 -21
- package/dist/generators/html/utils/commitMapBuilder.js +23 -24
- package/dist/generators/html/utils/dateRangeCalculator.js +56 -57
- package/dist/generators/html/utils/developerStatsCalculator.js +28 -29
- package/dist/generators/html/utils/scriptLoader.js +15 -16
- package/dist/generators/html/utils/styleLoader.js +17 -18
- package/dist/generators/html/utils/weekGrouper.js +27 -28
- package/dist/index.js +78 -78
- package/dist/types/index.js +2 -2
- package/dist/utils/achievementDefinitions.js +433 -433
- package/dist/utils/achievementEngine.js +169 -170
- package/dist/utils/commitQualityAnalyzer.js +367 -368
- package/dist/utils/fileHotspotAnalyzer.js +269 -270
- package/dist/utils/gitParser.js +136 -126
- package/dist/utils/htmlGenerator.js +232 -233
- package/dist/utils/impactAnalyzer.js +247 -248
- package/dist/utils/knowledgeDistributionAnalyzer.js +373 -374
- package/dist/utils/matrixGenerator.js +349 -350
- package/dist/utils/slideGenerator.js +170 -171
- package/dist/utils/streakCalculator.js +134 -135
- package/dist/utils/timePatternAnalyzer.js +304 -305
- package/dist/utils/wrappedDisplay.js +124 -115
- package/dist/utils/wrappedGenerator.js +376 -377
- package/dist/utils/wrappedHtmlGenerator.js +105 -106
- package/package.json +10 -10
|
@@ -1,270 +1,269 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
//
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
//
|
|
79
|
-
const
|
|
80
|
-
//
|
|
81
|
-
const
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
const
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
dirStats.
|
|
141
|
-
dirStats.
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
.
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
author,
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
.
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
let
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
.
|
|
241
|
-
.
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
case '
|
|
256
|
-
case '
|
|
257
|
-
case '
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
function getOwnershipIcon(type) {
|
|
263
|
-
switch (type) {
|
|
264
|
-
case 'single-owner': return '🔴';
|
|
265
|
-
case 'shared': return '🟡';
|
|
266
|
-
case 'collaborative': return '🟢';
|
|
267
|
-
default: return '⚪';
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
exports.getOwnershipIcon = getOwnershipIcon;
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.analyzeFileHotspots = analyzeFileHotspots;
|
|
4
|
+
exports.getRiskLevelColor = getRiskLevelColor;
|
|
5
|
+
exports.getOwnershipIcon = getOwnershipIcon;
|
|
6
|
+
const child_process_1 = require("child_process");
|
|
7
|
+
function analyzeFileHotspots(repoPath, startDate, endDate) {
|
|
8
|
+
try {
|
|
9
|
+
// Get file changes with author info from git log
|
|
10
|
+
const since = startDate.toISOString().split('T')[0];
|
|
11
|
+
const until = endDate.toISOString().split('T')[0];
|
|
12
|
+
let gitLog;
|
|
13
|
+
try {
|
|
14
|
+
gitLog = (0, child_process_1.execSync)(`git log --name-only --format="%H|%an|%ad" --date=short --since="${since}" --until="${until}"`, { cwd: repoPath, encoding: 'utf-8', maxBuffer: 10 * 1024 * 1024 }).trim();
|
|
15
|
+
}
|
|
16
|
+
catch (gitError) {
|
|
17
|
+
// Git command failed, likely not in a git repo or no commits in range
|
|
18
|
+
return getEmptyAnalysis();
|
|
19
|
+
}
|
|
20
|
+
if (!gitLog) {
|
|
21
|
+
return getEmptyAnalysis();
|
|
22
|
+
}
|
|
23
|
+
// Parse git log output
|
|
24
|
+
const fileMap = new Map();
|
|
25
|
+
const lines = gitLog.split('\n');
|
|
26
|
+
let currentCommit = null;
|
|
27
|
+
for (const line of lines) {
|
|
28
|
+
const trimmed = line.trim();
|
|
29
|
+
if (trimmed.includes('|')) {
|
|
30
|
+
// Commit header line
|
|
31
|
+
const [hash, author, date] = trimmed.split('|');
|
|
32
|
+
currentCommit = { hash, author, date };
|
|
33
|
+
}
|
|
34
|
+
else if (trimmed && currentCommit) {
|
|
35
|
+
// File path line
|
|
36
|
+
const filePath = trimmed;
|
|
37
|
+
// Skip binary files, lock files, and common generated files
|
|
38
|
+
if (shouldIgnoreFile(filePath)) {
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
if (!fileMap.has(filePath)) {
|
|
42
|
+
fileMap.set(filePath, {
|
|
43
|
+
path: filePath,
|
|
44
|
+
changeCount: 0,
|
|
45
|
+
authors: [],
|
|
46
|
+
authorDistribution: {},
|
|
47
|
+
firstChange: new Date(currentCommit.date),
|
|
48
|
+
lastChange: new Date(currentCommit.date),
|
|
49
|
+
complexityScore: 0
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
const fileStats = fileMap.get(filePath);
|
|
53
|
+
fileStats.changeCount++;
|
|
54
|
+
// Track authors
|
|
55
|
+
if (!fileStats.authors.includes(currentCommit.author)) {
|
|
56
|
+
fileStats.authors.push(currentCommit.author);
|
|
57
|
+
}
|
|
58
|
+
fileStats.authorDistribution[currentCommit.author] =
|
|
59
|
+
(fileStats.authorDistribution[currentCommit.author] || 0) + 1;
|
|
60
|
+
// Update dates
|
|
61
|
+
const commitDate = new Date(currentCommit.date);
|
|
62
|
+
if (commitDate < fileStats.firstChange) {
|
|
63
|
+
fileStats.firstChange = commitDate;
|
|
64
|
+
}
|
|
65
|
+
if (commitDate > fileStats.lastChange) {
|
|
66
|
+
fileStats.lastChange = commitDate;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
// Calculate complexity scores
|
|
71
|
+
for (const fileStats of fileMap.values()) {
|
|
72
|
+
fileStats.complexityScore = calculateComplexityScore(fileStats);
|
|
73
|
+
}
|
|
74
|
+
// Get top files
|
|
75
|
+
const topFiles = Array.from(fileMap.values())
|
|
76
|
+
.sort((a, b) => b.changeCount - a.changeCount)
|
|
77
|
+
.slice(0, 20);
|
|
78
|
+
// Analyze directories
|
|
79
|
+
const topDirectories = analyzeDirectories(fileMap);
|
|
80
|
+
// Identify technical debt
|
|
81
|
+
const technicalDebt = identifyTechnicalDebt(Array.from(fileMap.values()));
|
|
82
|
+
// Analyze ownership risks
|
|
83
|
+
const ownershipRisks = analyzeOwnership(Array.from(fileMap.values()));
|
|
84
|
+
return {
|
|
85
|
+
topFiles,
|
|
86
|
+
topDirectories,
|
|
87
|
+
technicalDebt,
|
|
88
|
+
ownershipRisks,
|
|
89
|
+
totalFilesAnalyzed: fileMap.size
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
catch (error) {
|
|
93
|
+
console.error('Error analyzing file hotspots:', error);
|
|
94
|
+
return getEmptyAnalysis();
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
function shouldIgnoreFile(path) {
|
|
98
|
+
const ignoredExtensions = ['.lock', '.log', '.png', '.jpg', '.jpeg', '.gif', '.svg', '.ico', '.woff', '.woff2', '.ttf', '.eot'];
|
|
99
|
+
const ignoredPatterns = ['package-lock.json', 'yarn.lock', 'pnpm-lock.yaml', 'node_modules/', 'dist/', 'build/', '.next/', 'coverage/'];
|
|
100
|
+
// Check extensions
|
|
101
|
+
if (ignoredExtensions.some(ext => path.toLowerCase().endsWith(ext))) {
|
|
102
|
+
return true;
|
|
103
|
+
}
|
|
104
|
+
// Check patterns
|
|
105
|
+
if (ignoredPatterns.some(pattern => path.includes(pattern))) {
|
|
106
|
+
return true;
|
|
107
|
+
}
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
110
|
+
function calculateComplexityScore(fileStats) {
|
|
111
|
+
let score = 0;
|
|
112
|
+
// High churn
|
|
113
|
+
if (fileStats.changeCount > 100)
|
|
114
|
+
score += 3;
|
|
115
|
+
else if (fileStats.changeCount > 50)
|
|
116
|
+
score += 2;
|
|
117
|
+
else if (fileStats.changeCount > 25)
|
|
118
|
+
score += 1;
|
|
119
|
+
// Many authors
|
|
120
|
+
if (fileStats.authors.length > 10)
|
|
121
|
+
score += 2;
|
|
122
|
+
else if (fileStats.authors.length > 5)
|
|
123
|
+
score += 1;
|
|
124
|
+
// Time since last change (older = potentially more stable, but also potentially abandoned)
|
|
125
|
+
const daysSinceLastChange = Math.floor((Date.now() - fileStats.lastChange.getTime()) / (1000 * 60 * 60 * 24));
|
|
126
|
+
if (daysSinceLastChange > 180)
|
|
127
|
+
score += 1; // No changes in 6 months
|
|
128
|
+
return Math.min(10, score);
|
|
129
|
+
}
|
|
130
|
+
function analyzeDirectories(fileMap) {
|
|
131
|
+
const dirMap = new Map();
|
|
132
|
+
// Group files by directory
|
|
133
|
+
for (const file of fileMap.values()) {
|
|
134
|
+
const dirPath = file.path.includes('/')
|
|
135
|
+
? file.path.substring(0, file.path.lastIndexOf('/'))
|
|
136
|
+
: '.';
|
|
137
|
+
if (!dirMap.has(dirPath)) {
|
|
138
|
+
dirMap.set(dirPath, { files: [], totalChanges: 0 });
|
|
139
|
+
}
|
|
140
|
+
const dirStats = dirMap.get(dirPath);
|
|
141
|
+
dirStats.files.push(file);
|
|
142
|
+
dirStats.totalChanges += file.changeCount;
|
|
143
|
+
}
|
|
144
|
+
// Create directory stats
|
|
145
|
+
const directories = [];
|
|
146
|
+
for (const [path, data] of dirMap) {
|
|
147
|
+
const mostActive = data.files.sort((a, b) => b.changeCount - a.changeCount)[0];
|
|
148
|
+
directories.push({
|
|
149
|
+
path,
|
|
150
|
+
totalChanges: data.totalChanges,
|
|
151
|
+
fileCount: data.files.length,
|
|
152
|
+
avgChangesPerFile: data.totalChanges / data.files.length,
|
|
153
|
+
mostActiveFile: mostActive.path
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
return directories
|
|
157
|
+
.sort((a, b) => b.totalChanges - a.totalChanges)
|
|
158
|
+
.slice(0, 10);
|
|
159
|
+
}
|
|
160
|
+
function identifyTechnicalDebt(files) {
|
|
161
|
+
const debtFiles = [];
|
|
162
|
+
for (const file of files) {
|
|
163
|
+
const score = file.complexityScore;
|
|
164
|
+
// Only flag files with complexity score >= 4
|
|
165
|
+
if (score < 4)
|
|
166
|
+
continue;
|
|
167
|
+
let riskLevel = 'low';
|
|
168
|
+
if (score >= 8)
|
|
169
|
+
riskLevel = 'critical';
|
|
170
|
+
else if (score >= 6)
|
|
171
|
+
riskLevel = 'high';
|
|
172
|
+
else if (score >= 4)
|
|
173
|
+
riskLevel = 'medium';
|
|
174
|
+
const recommendations = [];
|
|
175
|
+
if (file.changeCount > 50) {
|
|
176
|
+
recommendations.push('High change frequency - consider refactoring for stability');
|
|
177
|
+
}
|
|
178
|
+
if (file.authors.length > 8) {
|
|
179
|
+
recommendations.push('Many different authors - ensure clear ownership');
|
|
180
|
+
}
|
|
181
|
+
const daysSinceLastChange = Math.floor((Date.now() - file.lastChange.getTime()) / (1000 * 60 * 60 * 24));
|
|
182
|
+
if (daysSinceLastChange > 180) {
|
|
183
|
+
recommendations.push('No recent changes - may be stable or abandoned');
|
|
184
|
+
}
|
|
185
|
+
debtFiles.push({
|
|
186
|
+
file: file.path,
|
|
187
|
+
riskLevel,
|
|
188
|
+
changeCount: file.changeCount,
|
|
189
|
+
authorCount: file.authors.length,
|
|
190
|
+
lastModified: file.lastChange,
|
|
191
|
+
score,
|
|
192
|
+
recommendations
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
return debtFiles.sort((a, b) => b.score - a.score).slice(0, 10);
|
|
196
|
+
}
|
|
197
|
+
function analyzeOwnership(files) {
|
|
198
|
+
const ownershipData = [];
|
|
199
|
+
for (const file of files) {
|
|
200
|
+
// Only analyze files with multiple commits
|
|
201
|
+
if (file.changeCount < 3)
|
|
202
|
+
continue;
|
|
203
|
+
// Calculate owner percentages
|
|
204
|
+
const owners = Object.entries(file.authorDistribution)
|
|
205
|
+
.map(([author, commits]) => ({
|
|
206
|
+
author,
|
|
207
|
+
commits,
|
|
208
|
+
percentage: (commits / file.changeCount) * 100
|
|
209
|
+
}))
|
|
210
|
+
.sort((a, b) => b.commits - a.commits)
|
|
211
|
+
.slice(0, 5); // Top 5 contributors
|
|
212
|
+
// Determine ownership type
|
|
213
|
+
let ownershipType = 'collaborative';
|
|
214
|
+
let busFactorRisk = 0;
|
|
215
|
+
if (owners.length === 1 || owners[0].percentage >= 90) {
|
|
216
|
+
ownershipType = 'single-owner';
|
|
217
|
+
busFactorRisk = 9;
|
|
218
|
+
}
|
|
219
|
+
else if (owners[0].percentage >= 70) {
|
|
220
|
+
ownershipType = 'single-owner';
|
|
221
|
+
busFactorRisk = 7;
|
|
222
|
+
}
|
|
223
|
+
else if (owners[0].percentage >= 50) {
|
|
224
|
+
ownershipType = 'shared';
|
|
225
|
+
busFactorRisk = 4;
|
|
226
|
+
}
|
|
227
|
+
else {
|
|
228
|
+
ownershipType = 'collaborative';
|
|
229
|
+
busFactorRisk = 2;
|
|
230
|
+
}
|
|
231
|
+
ownershipData.push({
|
|
232
|
+
file: file.path,
|
|
233
|
+
owners,
|
|
234
|
+
ownershipType,
|
|
235
|
+
busFactorRisk
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
// Return files with ownership risks
|
|
239
|
+
return ownershipData
|
|
240
|
+
.filter(o => o.busFactorRisk >= 7)
|
|
241
|
+
.sort((a, b) => b.busFactorRisk - a.busFactorRisk)
|
|
242
|
+
.slice(0, 10);
|
|
243
|
+
}
|
|
244
|
+
function getEmptyAnalysis() {
|
|
245
|
+
return {
|
|
246
|
+
topFiles: [],
|
|
247
|
+
topDirectories: [],
|
|
248
|
+
technicalDebt: [],
|
|
249
|
+
ownershipRisks: [],
|
|
250
|
+
totalFilesAnalyzed: 0
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
function getRiskLevelColor(level) {
|
|
254
|
+
switch (level) {
|
|
255
|
+
case 'critical': return '🔴';
|
|
256
|
+
case 'high': return '🟠';
|
|
257
|
+
case 'medium': return '🟡';
|
|
258
|
+
case 'low': return '🟢';
|
|
259
|
+
default: return '⚪';
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
function getOwnershipIcon(type) {
|
|
263
|
+
switch (type) {
|
|
264
|
+
case 'single-owner': return '🔴';
|
|
265
|
+
case 'shared': return '🟡';
|
|
266
|
+
case 'collaborative': return '🟢';
|
|
267
|
+
default: return '⚪';
|
|
268
|
+
}
|
|
269
|
+
}
|