@r4lrgx/gitmoji
Version:
🧱 My customized Gitmoji Config - a fork of https://github.com/arvinxx/gitmoji-commit-workflow - just with several bug fixes and a cleaner look.
380 lines (372 loc) • 11.4 kB
JavaScript
import { readFileSync } from 'fs';
import { sep } from 'path';
import { fileURLToPath } from 'url';
import _ from 'lodash';
import pangu from 'pangu';
import fetch from 'sync-fetch';
// @r4lrgx/gitmoji v1.0.3
// MIT License
// src/changelog-config/handlers/displayScopeMap.ts
var displayScopeMap = (scope, customScopeMap) => {
if (!scope || !customScopeMap) return scope;
const custom = customScopeMap[scope];
if (custom) return custom;
const all = customScopeMap["*"];
if (all) {
return all.replace(/\*/g, scope);
}
return scope;
};
var { merge } = _;
var commitTypeMap = {
feat: {
emoji: "\u2728",
title: "Features",
subtitle: "New features and enhancements"
},
fix: {
emoji: "\u{1F41B}",
title: "Bug Fixes",
subtitle: "Resolved bugs and issues"
},
perf: {
emoji: "\u26A1",
title: "Performance Improvements",
subtitle: "Faster, leaner, better"
},
refactor: {
emoji: "\u267B",
title: "Code Refactoring",
subtitle: "Code structure improvements"
},
chore: {
emoji: "\u{1F527}",
title: "Chores",
subtitle: "Other tasks and maintenance"
},
docs: {
emoji: "\u{1F4DD}",
title: "Documentation",
subtitle: "Docs updates and improvements"
},
build: {
emoji: "\u{1F4E6}\uFE0F",
title: "Build System",
subtitle: "Changes to build tools and processes"
},
ci: {
emoji: "\u{1F477}",
title: "Continuous Integration",
subtitle: "CI config updates and automation"
},
test: {
emoji: "\u2705",
title: "Tests",
subtitle: "Added or updated tests"
},
style: {
emoji: "\u{1F3A8}",
title: "Styles",
subtitle: "Visual tweaks and formatting"
},
wip: {
emoji: "\u{1F691}\uFE0F",
title: "Cleaning",
subtitle: "Work in progress or cleanup"
},
revert: {
emoji: "\u23EA",
title: "Reverts",
subtitle: "Undone changes and rollbacks"
}
};
var defineCommitTypeMap = (customCommitTypeMap) => {
if (!customCommitTypeMap) return commitTypeMap;
return merge(customCommitTypeMap, commitTypeMap);
};
var displayCommitType = (commitType, config) => {
const { withEmoji = true, customCommitTypeMap = void 0 } = config;
const diplayCommitTypeMap = defineCommitTypeMap(customCommitTypeMap);
if (commitType in diplayCommitTypeMap) {
const item = diplayCommitTypeMap[commitType];
const { emoji, title } = item;
return `${withEmoji ? `${emoji} ` : ""}${title}`;
}
return commitType;
};
function gitHubLookup(email) {
const match = email.match(/^(\d*)\+?([a-zA-Z0-9\-]*)@users\.noreply\.github\.com$/);
let info = null;
if (match) {
const [_3, id, username] = match;
info = { id, username };
} else {
try {
const res = fetch(`https://api.github.com/search/commits?q=author-email:${encodeURIComponent(email)}`, {
headers: {
Accept: "application/vnd.github.cloak-preview+json"
}
});
if (!res.ok) return null;
const data = res.json();
if (data.items && data.items.length > 0) {
const username = data.items[0]?.author?.login;
const id = data.items[0]?.author?.id;
if (id && username) info = { id, username };
}
} catch (_err) {
return null;
}
}
if (!info?.id || !info?.username) return null;
try {
const res = fetch(`https://api.github.com/users/${info.username}`, {
headers: {
Accept: "application/vnd.github.cloak-preview+json"
}
});
if (!res.ok) return null;
const user = res.json();
return {
authorName: info.username,
authorAvatar: user.avatar_url,
authorUrl: user.html_url,
authorEmail: `${info.id}+${info.username}@users.noreply.github.com`
};
} catch (_err) {
return null;
}
}
// src/commit-types/index.ts
var commitTypes = [
// prettier
"feat",
"fix",
"perf",
"refactor",
"chore",
"docs",
"build",
"ci",
"test",
"style",
"wip",
"revert"
];
var commit_types_default = commitTypes;
// src/changelog-config/handlers/transformer.ts
var { cloneDeep } = _;
var users = /* @__PURE__ */ new Map();
var capitalize = (str) => {
if (!str?.length) return str;
return str.replace(/([a-zA-Z])/u, (firs) => firs.toUpperCase());
};
var transformer = (config) => (
/**
* Processes a single commit
* @param {Commit} commit - The commit object to transform
* @param {Context} context - The context object containing repository info
* @returns {Commit | void} The transformed commit or undefined if excluded
*/
(commit, context) => {
commit = cloneDeep(commit);
const issues = [];
let exclude = true;
commit.notes.forEach((note) => {
note.title = `${config.withEmoji === false ? "" : "\u{1F4A5}"} BREAKING CHANGES`;
exclude = false;
});
const { displayCommitTypes = commit_types_default } = config;
if (!displayCommitTypes.includes(commit.type) && exclude || !commit.type) return;
commit.type = displayCommitType(commit.type, config);
if (commit.scope) {
if (commit.scope === "*") {
commit.scope = "";
}
if (config.displayScopes) {
if (!config.displayScopes.includes(commit.scope)) return;
}
if (config.customScopeMap) {
commit.scope = displayScopeMap(commit.scope, config.customScopeMap);
}
commit.scope = capitalize(commit.scope);
}
if (commit.hash) {
commit.hash = commit.hash.substring(0, 7);
}
if (commit.subject && typeof commit.subject === "string") {
let repository = context.repository ? `${context.host}/${context.owner}/${context.repository}` : context.repoUrl;
if (repository) {
repository = `${repository}/issues/`;
commit.subject = commit.subject.replace(/#([0-9]+)/g, (_3, issue) => {
issues.push({ issue });
return `[#${issue}](${repository}${issue})`;
});
}
if (commit.host && typeof commit.host === "string") {
commit.subject = commit.subject.replace(/\B@([a-z0-9](?:-?[a-z0-9/]){0,38})/g, (_3, username) => {
if (username.includes("/")) {
return `@${username}`;
}
return `[@${username}](${context.host}/${username})`;
});
}
}
commit.references = commit.references.filter((reference) => {
const values = new Set(issues.map((item) => item.issue));
return !values.has(reference.issue);
});
if (commit.subject) {
commit.rawSubject = commit.subject;
commit.subject = pangu.spacing(capitalize(commit.subject));
}
const authorEmail = commit.author.email;
if (config.showAuthor || config.showAuthorAvatar) {
if (!users.has(authorEmail)) {
try {
const author2 = gitHubLookup(authorEmail);
users.set(authorEmail, author2);
} catch (_e) {
users.set(authorEmail, null);
}
}
const author = users.get(authorEmail);
Object.assign(commit, author);
}
return commit;
}
);
// src/changelog-config/context/index.ts
var users2 = /* @__PURE__ */ new Map();
var finalizeContext = (config) => (
/**
* Processes the changelog context
* @param {Context} context - The changelog context to enhance
* @returns {Context} The enhanced context or undefined if invalid
*/
(context) => {
const subCommitScope = (config.customScopeMap && config.customScopeMap["*"]) ?? null;
const commitTypeMap2 = defineCommitTypeMap(config.customCommitTypeMap);
context.commitGroups = context.commitGroups?.map((item) => {
const { subtitle } = Object.values(commitTypeMap2).find(({ emoji }) => item.title.includes(emoji));
let group;
let commits = item.commits.sort((a, b) => {
if (a.scope === subCommitScope && b.scope === subCommitScope) {
return 0;
} else if (a.scope === subCommitScope) {
return 1;
} else if (b.scope === subCommitScope) {
return -1;
} else {
return 0;
}
});
commits = commits.map((commit, index) => {
if (commit.scope) {
if (group !== commit.scope) {
group = commit.scope;
commit.first = true;
} else {
commit.first = false;
}
}
if (!commits[index + 1] || group !== commits[index + 1].scope) {
commit.last = true;
} else {
commit.last = false;
}
const { email } = commit.author;
if (config.showAuthor || config.showAuthorAvatar) {
if (!users2.has(email)) {
try {
const author = gitHubLookup(email);
users2.set(email, author);
} catch (_e) {
users2.set(email, null);
}
}
}
return commit;
});
return {
title: item.title,
subtitle,
commits
};
});
context.authors = Array.from(users2.values());
return context;
}
);
// src/changelog-config/writer.ts
var dirname = fileURLToPath(new URL(".", import.meta.url));
var reduceHeadingLevel = (skip, template) => {
if (skip) return template;
return template.replace(/(^|\n)(#{1,5})(?=\s)/g, (_match, prefix, hashes) => {
return `${prefix}${hashes}#`;
});
};
var readTemplate = (name) => {
return readFileSync(`${dirname}${sep}templates${sep}${name}.hbs`, "utf-8");
};
function createGitmojiWriterOpts(config) {
const [
// prettier
authorAvatar,
author,
backToTop,
commit,
footer,
headerNewlineTimestamp,
header,
summaryAvatar,
summaryTemplate,
template
] = [
// prettier
readTemplate("author-avatar"),
readTemplate("author"),
readTemplate("back-to-top"),
readTemplate("commit"),
readTemplate("footer"),
readTemplate("header-newline-timestamp"),
readTemplate("header"),
readTemplate("summary-avatar"),
readTemplate("summary-template"),
readTemplate("template")
];
const shouldReduce = !config.reduceHeadingLevel;
const mainTemplate = () => {
if (!config.showSummary) return template;
if (config.showAuthor && config.showAuthorAvatar) return summaryTemplate.replace(/{{gitUserInfo}}/g, summaryAvatar);
return summaryTemplate.replace(/{{gitUserInfo}}/g, "");
};
const headerPartial = () => {
if (!config.newlineTimestamp) return header;
return headerNewlineTimestamp;
};
const commitPartial = () => {
let gitUserInfo = "";
if (config.showAuthor) gitUserInfo = config.showAuthorAvatar ? authorAvatar : author;
return commit.replace(/{{gitUserInfo}}/g, gitUserInfo);
};
const footerPartial = () => {
if (config.addBackToTop) return footer.replace(/{{backToTop}}/g, backToTop);
return footer.replace(/{{backToTop}}/g, "");
};
return {
transform: transformer(config),
groupBy: "type",
commitGroupsSort: "title",
commitsSort: ["scope", "subject"],
noteGroupsSort: "title",
mainTemplate: reduceHeadingLevel(shouldReduce, mainTemplate()),
headerPartial: reduceHeadingLevel(shouldReduce, headerPartial()),
commitPartial: reduceHeadingLevel(shouldReduce, commitPartial()),
footerPartial: reduceHeadingLevel(shouldReduce, footerPartial()),
finalizeContext: finalizeContext(config)
};
}
export { createGitmojiWriterOpts as default };
//# sourceMappingURL=writer.js.map
//# sourceMappingURL=writer.js.map