UNPKG

9.76 kBJavaScriptView Raw
1'use strict'
2const dateFormat = require('dateformat')
3const getPkgRepo = require('get-pkg-repo')
4const gitSemverTags = require('git-semver-tags')
5const normalizePackageData = require('normalize-package-data')
6const Q = require('q')
7let gitRemoteOriginUrl
8try {
9 gitRemoteOriginUrl = require('git-remote-origin-url')
10} catch (err) {
11 gitRemoteOriginUrl = function () {
12 return Q.reject(err)
13 }
14}
15const readPkg = require('read-pkg')
16const readPkgUp = require('read-pkg-up')
17const URL = require('url').URL
18const _ = require('lodash')
19
20const rhosts = /github|bitbucket|gitlab/i
21
22function semverTagsPromise (options) {
23 return Q.Promise(function (resolve, reject) {
24 gitSemverTags({ lernaTags: !!options.lernaPackage, package: options.lernaPackage, tagPrefix: options.tagPrefix, skipUnstable: options.skipUnstable }, function (err, result) {
25 if (err) {
26 reject(err)
27 } else {
28 resolve(result)
29 }
30 })
31 })
32}
33
34function guessNextTag (previousTag, version) {
35 if (previousTag) {
36 if (previousTag[0] === 'v' && version[0] !== 'v') {
37 return 'v' + version
38 }
39
40 if (previousTag[0] !== 'v' && version[0] === 'v') {
41 return version.replace(/^v/, '')
42 }
43
44 return version
45 }
46
47 if (version[0] !== 'v') {
48 return 'v' + version
49 }
50
51 return version
52}
53
54function mergeConfig (options, context, gitRawCommitsOpts, parserOpts, writerOpts, gitRawExecOpts) {
55 let configPromise
56 let pkgPromise
57
58 context = context || {}
59 gitRawCommitsOpts = gitRawCommitsOpts || {}
60 gitRawExecOpts = gitRawExecOpts || {}
61
62 const rtag = options && options.tagPrefix ? new RegExp(`tag:\\s*[=]?${options.tagPrefix}(.+?)[,)]`, 'gi') : /tag:\s*[v=]?(.+?)[,)]/gi
63
64 options = _.merge({
65 pkg: {
66 transform: function (pkg) {
67 return pkg
68 }
69 },
70 append: false,
71 releaseCount: 1,
72 skipUnstable: false,
73 debug: function () {},
74 transform: function (commit, cb) {
75 if (_.isString(commit.gitTags)) {
76 const match = rtag.exec(commit.gitTags)
77 rtag.lastIndex = 0
78
79 if (match) {
80 commit.version = match[1]
81 }
82 }
83
84 if (commit.committerDate) {
85 commit.committerDate = dateFormat(commit.committerDate, 'yyyy-mm-dd', true)
86 }
87
88 cb(null, commit)
89 },
90 lernaPackage: null
91 }, options)
92
93 options.warn = options.warn || options.debug
94
95 if (options.config) {
96 if (_.isFunction(options.config)) {
97 configPromise = Q.nfcall(options.config)
98 } else {
99 configPromise = Q(options.config)
100 }
101 }
102
103 if (options.pkg) {
104 if (options.pkg.path) {
105 pkgPromise = Q(readPkg(options.pkg.path))
106 } else {
107 pkgPromise = Q(readPkgUp())
108 }
109 }
110
111 const gitRemoteOriginUrlPromise = Q(gitRemoteOriginUrl())
112
113 return Q.allSettled([configPromise, pkgPromise, semverTagsPromise(options), gitRemoteOriginUrlPromise])
114 .spread(function (configObj, pkgObj, tagsObj, gitRemoteOriginUrlObj) {
115 let config
116 let pkg
117 let fromTag
118 let repo
119
120 let hostOpts
121
122 let gitSemverTags = []
123
124 if (configPromise) {
125 if (configObj.state === 'fulfilled') {
126 config = configObj.value
127 } else {
128 options.warn('Error in config' + configObj.reason.toString())
129 config = {}
130 }
131 } else {
132 config = {}
133 }
134
135 context = _.assign(context, config.context)
136
137 if (options.pkg) {
138 if (pkgObj.state === 'fulfilled') {
139 if (options.pkg.path) {
140 pkg = pkgObj.value
141 } else {
142 pkg = pkgObj.value.pkg || {}
143 }
144
145 pkg = options.pkg.transform(pkg)
146 } else if (options.pkg.path) {
147 options.warn(pkgObj.reason.toString())
148 }
149 }
150
151 if ((!pkg || !pkg.repository || !pkg.repository.url) && gitRemoteOriginUrlObj.state === 'fulfilled') {
152 pkg = pkg || {}
153 pkg.repository = pkg.repository || {}
154 pkg.repository.url = gitRemoteOriginUrlObj.value
155 normalizePackageData(pkg)
156 }
157
158 if (pkg) {
159 context.version = context.version || pkg.version
160
161 try {
162 repo = getPkgRepo(pkg)
163 } catch (err) {
164 repo = {}
165 }
166
167 if (repo.browse) {
168 const browse = repo.browse()
169 if (!context.host) {
170 if (repo.domain) {
171 const parsedBrowse = new URL(browse)
172 if (parsedBrowse.origin.indexOf('//') !== -1) {
173 context.host = parsedBrowse.protocol + '//' + repo.domain
174 } else {
175 context.host = parsedBrowse.protocol + repo.domain
176 }
177 } else {
178 context.host = null
179 }
180 }
181 context.owner = context.owner || repo.user || ''
182 context.repository = context.repository || repo.project
183 context.repoUrl = browse
184 }
185
186 context.packageData = pkg
187 }
188
189 context.version = context.version || ''
190
191 if (tagsObj.state === 'fulfilled') {
192 gitSemverTags = context.gitSemverTags = tagsObj.value
193 fromTag = gitSemverTags[options.releaseCount - 1]
194 const lastTag = gitSemverTags[0]
195
196 if (lastTag === context.version || lastTag === 'v' + context.version) {
197 if (options.outputUnreleased) {
198 context.version = 'Unreleased'
199 } else {
200 options.outputUnreleased = false
201 }
202 }
203 }
204
205 if (!_.isBoolean(options.outputUnreleased)) {
206 options.outputUnreleased = true
207 }
208
209 if (context.host && (!context.issue || !context.commit || !parserOpts || !parserOpts.referenceActions)) {
210 let type
211
212 if (context.host) {
213 const match = context.host.match(rhosts)
214 if (match) {
215 type = match[0]
216 }
217 } else if (repo && repo.type) {
218 type = repo.type
219 }
220
221 if (type) {
222 hostOpts = require('../hosts/' + type)
223
224 context = _.assign({
225 issue: hostOpts.issue,
226 commit: hostOpts.commit
227 }, context)
228 } else {
229 options.warn('Host: "' + context.host + '" does not exist')
230 hostOpts = {}
231 }
232 } else {
233 hostOpts = {}
234 }
235
236 if (context.resetChangelog) {
237 fromTag = null
238 }
239
240 gitRawCommitsOpts = _.assign({
241 format: '%B%n-hash-%n%H%n-gitTags-%n%d%n-committerDate-%n%ci',
242 from: fromTag,
243 merges: false,
244 debug: options.debug
245 },
246 config.gitRawCommitsOpts,
247 gitRawCommitsOpts
248 )
249
250 if (options.append) {
251 gitRawCommitsOpts.reverse = gitRawCommitsOpts.reverse || true
252 }
253
254 parserOpts = _.assign(
255 {}, config.parserOpts, {
256 warn: options.warn
257 },
258 parserOpts)
259
260 if (hostOpts.referenceActions && parserOpts) {
261 parserOpts.referenceActions = hostOpts.referenceActions
262 }
263
264 if (_.isEmpty(parserOpts.issuePrefixes) && hostOpts.issuePrefixes) {
265 parserOpts.issuePrefixes = hostOpts.issuePrefixes
266 }
267
268 writerOpts = _.assign({
269 finalizeContext: function (context, writerOpts, filteredCommits, keyCommit, originalCommits) {
270 const firstCommit = originalCommits[0]
271 const lastCommit = originalCommits[originalCommits.length - 1]
272 const firstCommitHash = firstCommit ? firstCommit.hash : null
273 const lastCommitHash = lastCommit ? lastCommit.hash : null
274
275 if ((!context.currentTag || !context.previousTag) && keyCommit) {
276 const match = /tag:\s*(.+?)[,)]/gi.exec(keyCommit.gitTags)
277 const currentTag = context.currentTag
278 context.currentTag = currentTag || match ? match[1] : null
279 const index = gitSemverTags.indexOf(context.currentTag)
280
281 // if `keyCommit.gitTags` is not a semver
282 if (index === -1) {
283 context.currentTag = currentTag || null
284 } else {
285 const previousTag = context.previousTag = gitSemverTags[index + 1]
286
287 if (!previousTag) {
288 if (options.append) {
289 context.previousTag = context.previousTag || firstCommitHash
290 } else {
291 context.previousTag = context.previousTag || lastCommitHash
292 }
293 }
294 }
295 } else {
296 context.previousTag = context.previousTag || gitSemverTags[0]
297
298 if (context.version === 'Unreleased') {
299 if (options.append) {
300 context.currentTag = context.currentTag || lastCommitHash
301 } else {
302 context.currentTag = context.currentTag || firstCommitHash
303 }
304 } else if (!context.currentTag) {
305 if (options.lernaPackage) {
306 context.currentTag = options.lernaPackage + '@' + context.version
307 } else if (options.tagPrefix) {
308 context.currentTag = options.tagPrefix + context.version
309 } else {
310 context.currentTag = guessNextTag(gitSemverTags[0], context.version)
311 }
312 }
313 }
314
315 if (!_.isBoolean(context.linkCompare) && context.previousTag && context.currentTag) {
316 context.linkCompare = true
317 }
318
319 return context
320 },
321 debug: options.debug
322 },
323 config.writerOpts, {
324 reverse: options.append,
325 doFlush: options.outputUnreleased
326 },
327 writerOpts
328 )
329
330 return {
331 options: options,
332 context: context,
333 gitRawCommitsOpts: gitRawCommitsOpts,
334 parserOpts: parserOpts,
335 writerOpts: writerOpts,
336 gitRawExecOpts: gitRawExecOpts
337 }
338 })
339}
340
341module.exports = mergeConfig