index.js 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. 'use strict'
  2. const addStream = require('add-stream')
  3. const gitRawCommits = require('git-raw-commits')
  4. const conventionalCommitsParser = require('conventional-commits-parser')
  5. const conventionalChangelogWriter = require('conventional-changelog-writer')
  6. const _ = require('lodash')
  7. const stream = require('stream')
  8. const through = require('through2')
  9. const execFileSync = require('child_process').execFileSync
  10. const mergeConfig = require('./lib/merge-config')
  11. function conventionalChangelog (options, context, gitRawCommitsOpts, parserOpts, writerOpts, gitRawExecOpts) {
  12. writerOpts = writerOpts || {}
  13. const readable = new stream.Readable({
  14. objectMode: writerOpts.includeDetails
  15. })
  16. readable._read = function () { }
  17. let commitsErrorThrown = false
  18. let commitsStream = new stream.Readable({
  19. objectMode: true
  20. })
  21. commitsStream._read = function () { }
  22. function commitsRange (from, to) {
  23. return gitRawCommits(_.merge({}, gitRawCommitsOpts, {
  24. from: from,
  25. to: to
  26. }))
  27. .on('error', function (err) {
  28. if (!commitsErrorThrown) {
  29. setImmediate(commitsStream.emit.bind(commitsStream), 'error', err)
  30. commitsErrorThrown = true
  31. }
  32. })
  33. }
  34. mergeConfig(options, context, gitRawCommitsOpts, parserOpts, writerOpts, gitRawExecOpts)
  35. .then(function (data) {
  36. options = data.options
  37. context = data.context
  38. gitRawCommitsOpts = data.gitRawCommitsOpts
  39. parserOpts = data.parserOpts
  40. writerOpts = data.writerOpts
  41. gitRawExecOpts = data.gitRawExecOpts
  42. try {
  43. execFileSync('git', ['rev-parse', '--verify', 'HEAD'], {
  44. stdio: 'ignore'
  45. })
  46. let reverseTags = context.gitSemverTags.slice(0).reverse()
  47. reverseTags.push('HEAD')
  48. if (gitRawCommitsOpts.from) {
  49. if (reverseTags.indexOf(gitRawCommitsOpts.from) !== -1) {
  50. reverseTags = reverseTags.slice(reverseTags.indexOf(gitRawCommitsOpts.from))
  51. } else {
  52. reverseTags = [gitRawCommitsOpts.from, 'HEAD']
  53. }
  54. }
  55. let streams = reverseTags.map((to, i) => {
  56. const from = i > 0
  57. ? reverseTags[i - 1]
  58. : ''
  59. return commitsRange(from, to)
  60. })
  61. if (gitRawCommitsOpts.from) {
  62. streams = streams.splice(1)
  63. }
  64. if (gitRawCommitsOpts.reverse) {
  65. streams.reverse()
  66. }
  67. streams.reduce((prev, next) => next.pipe(addStream(prev)))
  68. .on('data', function (data) {
  69. setImmediate(commitsStream.emit.bind(commitsStream), 'data', data)
  70. })
  71. .on('end', function () {
  72. setImmediate(commitsStream.emit.bind(commitsStream), 'end')
  73. })
  74. } catch (_e) {
  75. commitsStream = gitRawCommits(gitRawCommitsOpts, gitRawExecOpts)
  76. }
  77. commitsStream
  78. .on('error', function (err) {
  79. err.message = 'Error in git-raw-commits: ' + err.message
  80. setImmediate(readable.emit.bind(readable), 'error', err)
  81. })
  82. .pipe(conventionalCommitsParser(parserOpts))
  83. .on('error', function (err) {
  84. err.message = 'Error in conventional-commits-parser: ' + err.message
  85. setImmediate(readable.emit.bind(readable), 'error', err)
  86. })
  87. // it would be better if `gitRawCommits` could spit out better formatted data
  88. // so we don't need to transform here
  89. .pipe(through.obj(function (chunk, enc, cb) {
  90. try {
  91. options.transform.call(this, chunk, cb)
  92. } catch (err) {
  93. cb(err)
  94. }
  95. }))
  96. .on('error', function (err) {
  97. err.message = 'Error in options.transform: ' + err.message
  98. setImmediate(readable.emit.bind(readable), 'error', err)
  99. })
  100. .pipe(conventionalChangelogWriter(context, writerOpts))
  101. .on('error', function (err) {
  102. err.message = 'Error in conventional-changelog-writer: ' + err.message
  103. setImmediate(readable.emit.bind(readable), 'error', err)
  104. })
  105. .pipe(through({
  106. objectMode: writerOpts.includeDetails
  107. }, function (chunk, enc, cb) {
  108. try {
  109. readable.push(chunk)
  110. } catch (err) {
  111. setImmediate(function () {
  112. throw err
  113. })
  114. }
  115. cb()
  116. }, function (cb) {
  117. readable.push(null)
  118. cb()
  119. }))
  120. })
  121. .catch(function (err) {
  122. setImmediate(readable.emit.bind(readable), 'error', err)
  123. })
  124. return readable
  125. }
  126. module.exports = conventionalChangelog