fake-git-history
Version:
A command-line tool to generate your GitHub activity graph.
168 lines (143 loc) • 5.07 kB
JavaScript
const chalk = require("chalk");
const { format, getDay, differenceInDays, addDays } = require("date-fns");
/**
* Generate a visualization of the activity graph
* @param {Array} commitDateList - List of commit dates
* @param {Date} startDate - Start date
* @param {Date} endDate - End date
* @returns {String} - Visualization of the activity graph
*/
function generateActivityVisualization(commitDateList, startDate, endDate) {
// Count commits by day
const commitsByDay = {};
commitDateList.forEach(date => {
const dateKey = format(date, "YYYY-MM-DD");
if (!commitsByDay[dateKey]) {
commitsByDay[dateKey] = 0;
}
commitsByDay[dateKey]++;
});
// Get the max number of commits in a day
let maxCommitsInDay = 0;
Object.values(commitsByDay).forEach(count => {
if (count > maxCommitsInDay) {
maxCommitsInDay = count;
}
});
// Generate a list of all days between start and end date
const totalDays = differenceInDays(endDate, startDate) + 1;
const days = [];
for (let i = 0; i < totalDays; i++) {
days.push(addDays(startDate, i));
}
// Calculate the number of weeks
const totalWeeks = Math.ceil(totalDays / 7);
// Track month positions for labels
const monthLabelPositions = [];
let currentMonth = null;
days.forEach((day, index) => {
const month = format(day, "MMM");
const week = Math.floor(index / 7);
if (month !== currentMonth) {
monthLabelPositions.push({ month, week });
currentMonth = month;
}
});
// Build the visualization
const result = [];
// Add a title
result.push(
chalk.bold.green("This is what you will see on your GitHub profile:")
);
result.push("");
// Create month labels row
let monthRow = " "; // Space for day labels
for (let i = 0; i < monthLabelPositions.length; i++) {
const { month, week } = monthLabelPositions[i];
// Add the month label
monthRow += month;
if (i < monthLabelPositions.length - 1) {
// Add spaces to align with the next month or fill to the end
const nextMonthWeek =
monthLabelPositions.find(m => m.week > week)?.week || totalWeeks;
const spacesToAdd = (nextMonthWeek - week - 1) * 1.7;
monthRow += " ".repeat(spacesToAdd);
}
}
result.push(monthRow);
// Create day rows with contribution cells
const dayLabels = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
// GitHub-like intensity blocks using Unicode characters
const intensityBlocks = [
chalk.hex("#fdfdfd")("■"), // Empty/no commits (white square)
chalk.hex("#7feebb")("■"), // Few commits (light green)
chalk.hex("#4ac26b")("■"), // Some commits (medium green)
chalk.hex("#2da44e")("■"), // Many commits (darker green)
chalk.hex("#116329")("■") // Most commits (darkest green)
];
// Organize days by day of week and week number
const calendar = Array(7)
.fill()
.map(() => Array(totalWeeks).fill(null));
days.forEach((day, index) => {
const dayOfWeek = getDay(day); // 0 = Sunday, 1 = Monday, etc.
const week = Math.floor(index / 7);
calendar[dayOfWeek][week] = day;
});
// Generate rows for each day of the week
for (let dayOfWeek = 0; dayOfWeek < 7; dayOfWeek++) {
let row = chalk.bold(dayLabels[dayOfWeek]) + " ";
for (let week = 0; week < totalWeeks; week++) {
const day = calendar[dayOfWeek][week];
if (day) {
const dateKey = format(day, "YYYY-MM-DD");
// If there are no commits on this day
if (!commitsByDay[dateKey]) {
row += intensityBlocks[0] + " "; // No activity square
} else {
// There are commits on this day
const commitCount = commitsByDay[dateKey];
// Calculate intensity level (0-4)
const intensity = Math.min(
Math.ceil((commitCount / maxCommitsInDay) * 4),
4
);
row += intensityBlocks[intensity] + " ";
}
} else {
row += " "; // No day (outside the date range)
}
}
result.push(row);
}
result.push("");
// Add a legend
result.push(
`Legend: ${intensityBlocks[0]} No commits ${intensityBlocks[1]} Few ${intensityBlocks[2]} Some ${intensityBlocks[3]} Many ${intensityBlocks[4]} Most`
);
result.push("");
// Add statistics
result.push("Statistics");
result.push(`• Total commits: ${commitDateList.length}`);
result.push(
`• Date range: ${format(startDate, "YYYY-MM-DD")} to ${format(
endDate,
"YYYY-MM-DD"
)}`
);
result.push(`• Distribution: ${process.env.DISTRIBUTION || "uniform"}`);
result.push(`• Max commits in a day: ${maxCommitsInDay}`);
if (process.env.PREVIEW) {
result.push("");
result.push(
chalk.italic("Note: This is a preview only. No commits were created.")
);
result.push(
chalk.italic(
"To generate actual commits, run the command without the --preview flag."
)
);
}
return result.join("\n");
}
module.exports = generateActivityVisualization;