UNPKG

5.61 kBJavaScriptView Raw
1'use strict'
2const conventionalCommitsFilter = require('conventional-commits-filter')
3const Handlebars = require('handlebars')
4const semver = require('semver')
5const stringify = require('json-stringify-safe')
6
7function 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
39function 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
60function 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
96function 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
132function get (context, path) {
133 const parts = path.split('.')
134
135 return parts.reduce((context, key) =>
136 context ? context[key] : context
137 , context)
138}
139
140function 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
154function 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
194function getExtraContext (commits, notes, options) {
195 const context = {}
196
197 // group `commits` by `options.groupBy`
198 context.commitGroups = getCommitGroups(options.groupBy, commits, options.commitGroupsSort, options.commitsSort)
199
200 // group `notes` for footer
201 context.noteGroups = getNoteGroups(notes, options.noteGroupsSort, options.notesSort)
202
203 return context
204}
205
206function 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
251module.exports = {
252 compileTemplates: compileTemplates,
253 functionify: functionify,
254 getCommitGroups: getCommitGroups,
255 getNoteGroups: getNoteGroups,
256 processCommit: processCommit,
257 getExtraContext: getExtraContext,
258 generate: generate
259}