1 | 'use strict'
|
2 | const conventionalCommitsFilter = require('conventional-commits-filter')
|
3 | const Handlebars = require('handlebars')
|
4 | const semver = require('semver')
|
5 | const stringify = require('json-stringify-safe')
|
6 |
|
7 | function compileTemplates (templates) {
|
8 | const main = templates.mainTemplate
|
9 | const headerPartial = templates.headerPartial
|
10 | const commitPartial = templates.commitPartial
|
11 | const footerPartial = templates.footerPartial
|
12 | const partials = templates.partials
|
13 |
|
14 | if (typeof headerPartial === 'string') {
|
15 | Handlebars.registerPartial('header', headerPartial)
|
16 | }
|
17 |
|
18 | if (typeof commitPartial === 'string') {
|
19 | Handlebars.registerPartial('commit', commitPartial)
|
20 | }
|
21 |
|
22 | if (typeof footerPartial === 'string') {
|
23 | Handlebars.registerPartial('footer', footerPartial)
|
24 | }
|
25 |
|
26 | if (partials) {
|
27 | Object.entries(partials).forEach(function ([name, partial]) {
|
28 | if (typeof partial === 'string') {
|
29 | Handlebars.registerPartial(name, partial)
|
30 | }
|
31 | })
|
32 | }
|
33 |
|
34 | return Handlebars.compile(main, {
|
35 | noEscape: true
|
36 | })
|
37 | }
|
38 |
|
39 | function functionify (strOrArr) {
|
40 | if (strOrArr && typeof strOrArr !== 'function') {
|
41 | return (a, b) => {
|
42 | let str1 = ''
|
43 | let str2 = ''
|
44 | if (Array.isArray(strOrArr)) {
|
45 | for (const key of strOrArr) {
|
46 | str1 += a[key] || ''
|
47 | str2 += b[key] || ''
|
48 | }
|
49 | } else {
|
50 | str1 += a[strOrArr]
|
51 | str2 += b[strOrArr]
|
52 | }
|
53 | return str1.localeCompare(str2)
|
54 | }
|
55 | } else {
|
56 | return strOrArr
|
57 | }
|
58 | }
|
59 |
|
60 | function getCommitGroups (groupBy, commits, groupsSort, commitsSort) {
|
61 | const commitGroups = []
|
62 | const commitGroupsObj = commits.reduce(function (groups, commit) {
|
63 | const key = commit[groupBy] || ''
|
64 |
|
65 | if (groups[key]) {
|
66 | groups[key].push(commit)
|
67 | } else {
|
68 | groups[key] = [commit]
|
69 | }
|
70 |
|
71 | return groups
|
72 | }, {})
|
73 |
|
74 | Object.entries(commitGroupsObj).forEach(function ([title, commits]) {
|
75 | if (title === '') {
|
76 | title = false
|
77 | }
|
78 |
|
79 | if (commitsSort) {
|
80 | commits.sort(commitsSort)
|
81 | }
|
82 |
|
83 | commitGroups.push({
|
84 | title: title,
|
85 | commits: commits
|
86 | })
|
87 | })
|
88 |
|
89 | if (groupsSort) {
|
90 | commitGroups.sort(groupsSort)
|
91 | }
|
92 |
|
93 | return commitGroups
|
94 | }
|
95 |
|
96 | function getNoteGroups (notes, noteGroupsSort, notesSort) {
|
97 | const retGroups = []
|
98 |
|
99 | notes.forEach(function (note) {
|
100 | const title = note.title
|
101 | let titleExists = false
|
102 |
|
103 | retGroups.forEach(function (group) {
|
104 | if (group.title === title) {
|
105 | titleExists = true
|
106 | group.notes.push(note)
|
107 | return false
|
108 | }
|
109 | })
|
110 |
|
111 | if (!titleExists) {
|
112 | retGroups.push({
|
113 | title: title,
|
114 | notes: [note]
|
115 | })
|
116 | }
|
117 | })
|
118 |
|
119 | if (noteGroupsSort) {
|
120 | retGroups.sort(noteGroupsSort)
|
121 | }
|
122 |
|
123 | if (notesSort) {
|
124 | retGroups.forEach(function (group) {
|
125 | group.notes.sort(notesSort)
|
126 | })
|
127 | }
|
128 |
|
129 | return retGroups
|
130 | }
|
131 |
|
132 | function get (context, path) {
|
133 | const parts = path.split('.')
|
134 |
|
135 | return parts.reduce((context, key) =>
|
136 | context ? context[key] : context
|
137 | , context)
|
138 | }
|
139 |
|
140 | function immutableSet (context, path, value) {
|
141 | const parts = Array.isArray(path) ? path.slice() : path.split('.')
|
142 | const key = parts.shift()
|
143 |
|
144 | if (!key) {
|
145 | return context
|
146 | }
|
147 |
|
148 | return {
|
149 | ...context,
|
150 | [key]: parts.length ? immutableSet(context[key], parts, value) : value
|
151 | }
|
152 | }
|
153 |
|
154 | function processCommit (chunk, transform, context) {
|
155 | let commit
|
156 |
|
157 | try {
|
158 | chunk = JSON.parse(chunk)
|
159 | } catch (e) {}
|
160 |
|
161 | commit = {
|
162 | ...chunk
|
163 | }
|
164 |
|
165 | if (typeof transform === 'function') {
|
166 | commit = transform(commit, context)
|
167 |
|
168 | if (commit) {
|
169 | commit.raw = chunk
|
170 | }
|
171 |
|
172 | return commit
|
173 | }
|
174 |
|
175 | if (transform) {
|
176 | Object.entries(transform).forEach(function ([path, el]) {
|
177 | let value = get(commit, path)
|
178 |
|
179 | if (typeof el === 'function') {
|
180 | value = el(value, path)
|
181 | } else {
|
182 | value = el
|
183 | }
|
184 |
|
185 | commit = immutableSet(commit, path, value)
|
186 | })
|
187 | }
|
188 |
|
189 | commit.raw = chunk
|
190 |
|
191 | return commit
|
192 | }
|
193 |
|
194 | function getExtraContext (commits, notes, options) {
|
195 | const context = {}
|
196 |
|
197 |
|
198 | context.commitGroups = getCommitGroups(options.groupBy, commits, options.commitGroupsSort, options.commitsSort)
|
199 |
|
200 |
|
201 | context.noteGroups = getNoteGroups(notes, options.noteGroupsSort, options.notesSort)
|
202 |
|
203 | return context
|
204 | }
|
205 |
|
206 | function generate (options, commits, context, keyCommit) {
|
207 | const notes = []
|
208 | let filteredCommits
|
209 | const compiled = compileTemplates(options)
|
210 |
|
211 | if (options.ignoreReverted) {
|
212 | filteredCommits = conventionalCommitsFilter(commits)
|
213 | } else {
|
214 | filteredCommits = commits.slice()
|
215 | }
|
216 |
|
217 | filteredCommits = filteredCommits.map((commit) => ({
|
218 | ...commit,
|
219 | notes: commit.notes.map((note) => {
|
220 | const commitNote = {
|
221 | ...note,
|
222 | commit
|
223 | }
|
224 |
|
225 | notes.push(commitNote)
|
226 |
|
227 | return commitNote
|
228 | })
|
229 | }))
|
230 |
|
231 | context = {
|
232 | ...context,
|
233 | ...keyCommit,
|
234 | ...getExtraContext(filteredCommits, notes, options)
|
235 | }
|
236 |
|
237 | if (keyCommit && keyCommit.committerDate) {
|
238 | context.date = keyCommit.committerDate
|
239 | }
|
240 |
|
241 | if (context.version && semver.valid(context.version)) {
|
242 | context.isPatch = context.isPatch || semver.patch(context.version) !== 0
|
243 | }
|
244 |
|
245 | context = options.finalizeContext(context, options, filteredCommits, keyCommit, commits)
|
246 | options.debug('Your final context is:\n' + stringify(context, null, 2))
|
247 |
|
248 | return compiled(context)
|
249 | }
|
250 |
|
251 | module.exports = {
|
252 | compileTemplates: compileTemplates,
|
253 | functionify: functionify,
|
254 | getCommitGroups: getCommitGroups,
|
255 | getNoteGroups: getNoteGroups,
|
256 | processCommit: processCommit,
|
257 | getExtraContext: getExtraContext,
|
258 | generate: generate
|
259 | }
|