1 | 'use strict'
|
2 | const dateFormat = require('dateformat')
|
3 | const getPkgRepo = require('get-pkg-repo')
|
4 | const gitSemverTags = require('git-semver-tags')
|
5 | const normalizePackageData = require('normalize-package-data')
|
6 | const Q = require('q')
|
7 | let gitRemoteOriginUrl
|
8 | try {
|
9 | gitRemoteOriginUrl = require('git-remote-origin-url')
|
10 | } catch (err) {
|
11 | gitRemoteOriginUrl = function () {
|
12 | return Q.reject(err)
|
13 | }
|
14 | }
|
15 | const readPkg = require('read-pkg')
|
16 | const readPkgUp = require('read-pkg-up')
|
17 | const URL = require('url').URL
|
18 | const _ = require('lodash')
|
19 |
|
20 | const rhosts = /github|bitbucket|gitlab/i
|
21 |
|
22 | function 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 |
|
34 | function 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 |
|
54 | function 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 |
|
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 |
|
341 | module.exports = mergeConfig
|