UNPKG

conventional-changelog-techor

Version:

Beautiful changelog based on Techor's conventional commits

271 lines (255 loc) 7.92 kB
'use strict'; var compareFunc = require('compare-func'); var techorConventionalCommits = require('techor-conventional-commits'); var https = require('node:https'); var parserOpts = { headerPattern: /^([A-Z]\w*)(?:\(([0-9A-Z`_#~].*)\))?: ([0-9A-Z`_#~].*)$/, headerCorrespondence: [ 'type', 'scope', 'subject' ], revertPattern: /^(?:Revert|Revert:)\s"?([\s\S]+?)"?\s*This reverts commit (\w*)\./i, revertCorrespondence: [ 'header', 'hash' ] }; var mainTemplate = `{{#each commitGroups}} {{#if title}} ### {{title}} {{/if}} {{#each scopes}} {{#if title}} ###### {{title}} {{/if}} {{#each commits}} {{> commit root=@root}} {{/each}} {{/each}} {{/each}} {{> footer}}`; var footerPartial = `{{#if noteGroups}} {{#each noteGroups}} ### {{title}} {{#each notes}} * {{#if commit.scope}}**{{commit.scope}}:** {{/if}}{{text}} {{/each}} {{/each}} {{/if}}`; var commitPartial = `- {{#if subject}} {{~subject}} {{~else}} {{~header}} {{~/if}} {{~!-- commit link --}} {{#if @root.linkReferences~}} <sub><sup>@{{author.login}} [{{shortHash}}]( {{~#if @root.repository}} {{~#if @root.host}} {{~@root.host}}/ {{~/if}} {{~#if @root.owner}} {{~@root.owner}}/ {{~/if}} {{~@root.repository}} {{~else}} {{~@root.repoUrl}} {{~/if}}/ {{~@root.commit}}/{{hash}})</sup></sub> {{~else}} <sub><sup>{{~shortHash}}</sup></sub> {{~/if}} {{~!-- commit references --}} {{~#if references~}} <sub><sup>{{~#each references}} {{#if @root.linkReferences~}} [ {{~#if this.owner}} {{~this.owner}}/ {{~/if}} {{~this.repository}}#{{this.issue}}]( {{~#if @root.repository}} {{~#if @root.host}} {{~@root.host}}/ {{~/if}} {{~#if this.repository}} {{~#if this.owner}} {{~this.owner}}/ {{~/if}} {{~this.repository}} {{~else}} {{~#if @root.owner}} {{~@root.owner}}/ {{~/if}} {{~@root.repository}} {{~/if}} {{~else}} {{~@root.repoUrl}} {{~/if}}/ {{~@root.issue}}/{{this.issue}}) {{~else}} {{~#if this.owner}} {{~this.owner}}/ {{~/if}} {{~this.repository}}#{{this.issue}} {{~/if}}{{/each}}</sup></sub> {{~/if}} `; var writerOpts = { transform: async (commit, context)=>{ const issues = []; if (commit.header) { commit.header = commit.header.replace(/->/g, '→').replace(/<-/g, '←'); } const conventionalCommit = techorConventionalCommits.commits.find(({ type })=>commit.type === type); if (commit.type === 'Revert' || commit.revert) { commit.type = techorConventionalCommits.commits.find(({ type })=>type === 'Revert').group; /** * From Revert: "Feat(Scope): First feature" * To Revert:《 Feat(Scope): First feature 》 */ commit.header = commit.header.replace(/(Revert|Revert:)\s"([\s\S]+?)"(.*)/, '$1 ``` $2 `text` ``` $3'); } else if (conventionalCommit && !conventionalCommit.hidden && conventionalCommit.group) { commit.type = conventionalCommit.group; } else { return; } if (commit.scope === '*') { commit.scope = ''; } if (typeof commit.hash === 'string') { commit.shortHash = commit.hash.substring(0, 7); } if (typeof commit.subject === 'string') { if (process.env.NODE_ENV !== 'test') { // get author by commit hash try { const response = await new Promise((resolve)=>{ const url = `https://api.github.com/repos/${context.owner}/${context.repository}/commits/${commit.hash}`; const headers = { 'User-Agent': context.owner }; if (process.env.GITHUB_TOKEN) { headers['Authorization'] = `token ${process.env.GITHUB_TOKEN}`; } https.get(url, { headers }, (response)=>{ let data = ''; response.on('data', (chunk)=>data += chunk); response.on('end', async ()=>{ resolve(data); }); }); }); commit.author = JSON.parse(response).author; } catch (error) { console.log(new Error(`Can't get author by commit hash ${commit.hash}`, { cause: error })); } } } // remove references that already appear in the subject commit.references = commit.references.filter((reference)=>{ if (issues.indexOf(reference.issue) === -1) { return true; } return false; }); return commit; }, groupBy: 'type', commitGroupsSort: (a, b)=>{ const commitGroupOrder = techorConventionalCommits.commits.map(({ group })=>group).reverse(); const gRankA = commitGroupOrder.indexOf(a.title); const gRankB = commitGroupOrder.indexOf(b.title); if (gRankA >= gRankB) { return -1; } else { return 1; } }, commitsSort: [ 'scope', 'subject' ], noteGroupsSort: 'title', notesSort: compareFunc, mainTemplate, commitPartial, headerPartial: '', footerPartial, finalizeContext: (context, options, commits)=>{ context.commitGroups.forEach((commitGroup)=>{ const scopes = []; commitGroup.commits.forEach((commit)=>{ const { scope } = commit; if (!scopes.includes(scope)) { scopes.push(scope); } }); commitGroup.scopes = scopes.map((eachScope)=>{ return { title: eachScope || '', commits: commitGroup.commits.filter((commit)=>commit.scope === eachScope) }; }).sort((a, b)=>{ if (a.title === '') { return -1 // 将 title 为 '' 的项排在前面 ; } if (b.title === '') { return 1 // 将 title 为 '' 的项排在前面 ; } return a.title.localeCompare(b.title) // 按照 name 的默认排序顺序排列 ; }); }); return context; } }; var conventionalChangelog = { parserOpts, writerOpts }; var recommendedBumpOpts = { parserOpts, whatBump: (commits)=>{ let level = null; let majorCount = 0; let MinorCount = 0; let patchCount = 0; commits.forEach(({ type })=>{ const conventionalCommit = techorConventionalCommits.commits.find(({ type: eachType })=>eachType === type); if (conventionalCommit) { switch(conventionalCommit.release){ case 'major': level = 0; majorCount++; break; case 'minor': level = 1; MinorCount++; break; case 'patch': level = 2; patchCount++; break; } } }); return { level: level, reason: `Major: ${majorCount}, Minor: ${MinorCount}, Patch: ${patchCount}` }; } }; async function createPreset() { return { conventionalChangelog, parserOpts, recommendedBumpOpts, writerOpts }; } module.exports = createPreset;