'use strict' const compareFunc = require('compare-func') const conventionalCommitsFilter = require('conventional-commits-filter') const Handlebars = require('handlebars') const semver = require('semver') const _ = require('lodash') const stringify = require('json-stringify-safe') function compileTemplates (templates) { const main = templates.mainTemplate const headerPartial = templates.headerPartial const commitPartial = templates.commitPartial const footerPartial = templates.footerPartial const partials = templates.partials if (_.isString(headerPartial)) { Handlebars.registerPartial('header', headerPartial) } if (_.isString(commitPartial)) { Handlebars.registerPartial('commit', commitPartial) } if (_.isString(footerPartial)) { Handlebars.registerPartial('footer', footerPartial) } _.forEach(partials, function (partial, name) { if (_.isString(partial)) { Handlebars.registerPartial(name, partial) } }) return Handlebars.compile(main, { noEscape: true }) } function functionify (strOrArr) { if (strOrArr && !_.isFunction(strOrArr)) { return compareFunc(strOrArr) } return strOrArr } function getCommitGroups (groupBy, commits, groupsSort, commitsSort) { const commitGroups = [] const commitGroupsObj = _.groupBy(commits, function (commit) { return commit[groupBy] || '' }) _.forEach(commitGroupsObj, function (commits, title) { if (title === '') { title = false } if (commitsSort) { commits.sort(commitsSort) } commitGroups.push({ title: title, commits: commits }) }) if (groupsSort) { commitGroups.sort(groupsSort) } return commitGroups } function getNoteGroups (notes, noteGroupsSort, notesSort) { const retGroups = [] _.forEach(notes, function (note) { const title = note.title let titleExists = false _.forEach(retGroups, function (group) { if (group.title === title) { titleExists = true group.notes.push(note) return false } }) if (!titleExists) { retGroups.push({ title: title, notes: [note] }) } }) if (noteGroupsSort) { retGroups.sort(noteGroupsSort) } if (notesSort) { _.forEach(retGroups, function (group) { group.notes.sort(notesSort) }) } return retGroups } function processCommit (chunk, transform, context) { let commit try { chunk = JSON.parse(chunk) } catch (e) {} commit = _.cloneDeep(chunk) if (_.isFunction(transform)) { commit = transform(commit, context) if (commit) { commit.raw = chunk } return commit } _.forEach(transform, function (el, path) { let value = _.get(commit, path) if (_.isFunction(el)) { value = el(value, path) } else { value = el } _.set(commit, path, value) }) commit.raw = chunk return commit } function getExtraContext (commits, notes, options) { const context = {} // group `commits` by `options.groupBy` context.commitGroups = getCommitGroups(options.groupBy, commits, options.commitGroupsSort, options.commitsSort) // group `notes` for footer context.noteGroups = getNoteGroups(notes, options.noteGroupsSort, options.notesSort) return context } function generate (options, commits, context, keyCommit) { let notes = [] let filteredCommits const compiled = compileTemplates(options) if (options.ignoreReverted) { filteredCommits = conventionalCommitsFilter(commits) } else { filteredCommits = _.clone(commits) } _.forEach(filteredCommits, function (commit) { _.map(commit.notes, function (note) { note.commit = commit return note }) notes = notes.concat(commit.notes) }) context = _.merge({}, context, keyCommit, getExtraContext(filteredCommits, notes, options)) if (keyCommit && keyCommit.committerDate) { context.date = keyCommit.committerDate } if (context.version && semver.valid(context.version)) { context.isPatch = context.isPatch || semver.patch(context.version) !== 0 } context = options.finalizeContext(context, options, filteredCommits, keyCommit, commits) options.debug('Your final context is:\n' + stringify(context, null, 2)) return compiled(context) } module.exports = { compileTemplates: compileTemplates, functionify: functionify, getCommitGroups: getCommitGroups, getNoteGroups: getNoteGroups, processCommit: processCommit, getExtraContext: getExtraContext, generate: generate }