cli.js 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. #!/usr/bin/env node
  2. 'use strict'
  3. const addStream = require('add-stream')
  4. const conventionalChangelog = require('conventional-changelog')
  5. const fs = require('fs')
  6. const meow = require('meow')
  7. const tempfile = require('tempfile')
  8. const _ = require('lodash')
  9. const resolve = require('path').resolve
  10. const cli = meow(`
  11. Usage
  12. conventional-changelog
  13. Example
  14. conventional-changelog -i CHANGELOG.md --same-file
  15. Options
  16. -i, --infile Read the CHANGELOG from this file
  17. -o, --outfile Write the CHANGELOG to this file
  18. If unspecified, it prints to stdout
  19. -s, --same-file Outputting to the infile so you don't need to specify the same file as outfile
  20. -p, --preset Name of the preset you want to use. Must be one of the following:
  21. angular, atom, codemirror, conventionalcommits, ember, eslint, express, jquery or jshint
  22. -k, --pkg A filepath of where your package.json is located
  23. Default is the closest package.json from cwd
  24. -a, --append Should the newer release be appended to the older release
  25. Default: false
  26. -r, --release-count How many releases to be generated from the latest
  27. If 0, the whole changelog will be regenerated and the outfile will be overwritten
  28. Default: 1
  29. --skip-unstable If given, unstable tags will be skipped, e.g., x.x.x-alpha.1, x.x.x-rc.2
  30. -u, --output-unreleased Output unreleased changelog
  31. -v, --verbose Verbose output. Use this for debugging
  32. Default: false
  33. -n, --config A filepath of your config script
  34. Example of a config script: https://github.com/conventional-changelog/conventional-changelog/blob/master/packages/conventional-changelog-cli/test/fixtures/config.js
  35. -c, --context A filepath of a json that is used to define template variables
  36. -l, --lerna-package Generate a changelog for a specific lerna package (:pkg-name@1.0.0)
  37. -t, --tag-prefix Tag prefix to consider when reading the tags
  38. --commit-path Generate a changelog scoped to a specific directory
  39. `, {
  40. booleanDefault: undefined,
  41. flags: {
  42. infile: {
  43. alias: 'i',
  44. type: 'string'
  45. },
  46. outfile: {
  47. alias: 'o',
  48. type: 'string'
  49. },
  50. 'same-file': {
  51. alias: 's',
  52. type: 'boolean'
  53. },
  54. preset: {
  55. alias: 'p',
  56. type: 'string'
  57. },
  58. pkg: {
  59. alias: 'k',
  60. type: 'string'
  61. },
  62. append: {
  63. alias: 'a',
  64. type: 'boolean'
  65. },
  66. 'release-count': {
  67. alias: 'r',
  68. type: 'number'
  69. },
  70. 'skip-unstable': {
  71. type: 'boolean'
  72. },
  73. 'output-unreleased': {
  74. alias: 'u',
  75. type: 'boolean'
  76. },
  77. verbose: {
  78. alias: 'v',
  79. type: 'boolean'
  80. },
  81. config: {
  82. alias: 'n',
  83. type: 'string'
  84. },
  85. context: {
  86. alias: 'c',
  87. type: 'string'
  88. },
  89. 'lerna-package': {
  90. alias: 'l',
  91. type: 'string'
  92. },
  93. 'tag-prefix': {
  94. alias: 't',
  95. type: 'string'
  96. }
  97. }
  98. })
  99. let config
  100. const flags = cli.flags
  101. const infile = flags.infile
  102. let outfile = flags.outfile
  103. let sameFile = flags.sameFile
  104. const append = flags.append
  105. const releaseCount = flags.releaseCount
  106. const skipUnstable = flags.skipUnstable
  107. if (infile && infile === outfile) {
  108. sameFile = true
  109. } else if (sameFile) {
  110. if (infile) {
  111. outfile = infile
  112. } else {
  113. console.error('infile must be provided if same-file flag presents.')
  114. process.exit(1)
  115. }
  116. }
  117. let options = _.omitBy({
  118. preset: flags.preset,
  119. pkg: {
  120. path: flags.pkg
  121. },
  122. append: append,
  123. releaseCount: releaseCount,
  124. skipUnstable: skipUnstable,
  125. outputUnreleased: flags.outputUnreleased,
  126. lernaPackage: flags.lernaPackage,
  127. tagPrefix: flags.tagPrefix
  128. }, _.isUndefined)
  129. if (flags.verbose) {
  130. options.debug = console.info.bind(console)
  131. options.warn = console.warn.bind(console)
  132. }
  133. let templateContext
  134. let outStream
  135. try {
  136. if (flags.context) {
  137. templateContext = require(resolve(process.cwd(), flags.context))
  138. }
  139. if (flags.config) {
  140. config = require(resolve(process.cwd(), flags.config))
  141. options.config = config
  142. options = _.merge(options, config.options)
  143. } else {
  144. config = {}
  145. }
  146. } catch (err) {
  147. console.error('Failed to get file. ' + err)
  148. process.exit(1)
  149. }
  150. const gitRawCommitsOpts = _.merge({}, config.gitRawCommitsOpts || {})
  151. if (flags.commitPath) gitRawCommitsOpts.path = flags.commitPath
  152. const changelogStream = conventionalChangelog(options, templateContext, gitRawCommitsOpts, config.parserOpts, config.writerOpts)
  153. .on('error', function (err) {
  154. if (flags.verbose) {
  155. console.error(err.stack)
  156. } else {
  157. console.error(err.toString())
  158. }
  159. process.exit(1)
  160. })
  161. function noInputFile () {
  162. if (outfile) {
  163. outStream = fs.createWriteStream(outfile)
  164. } else {
  165. outStream = process.stdout
  166. }
  167. changelogStream
  168. .pipe(outStream)
  169. }
  170. if (infile && releaseCount !== 0) {
  171. const readStream = fs.createReadStream(infile)
  172. .on('error', function () {
  173. if (flags.verbose) {
  174. console.warn('infile does not exist.')
  175. }
  176. if (sameFile) {
  177. noInputFile()
  178. }
  179. })
  180. if (sameFile) {
  181. if (options.append) {
  182. changelogStream
  183. .pipe(fs.createWriteStream(outfile, {
  184. flags: 'a'
  185. }))
  186. } else {
  187. const tmp = tempfile()
  188. changelogStream
  189. .pipe(addStream(readStream))
  190. .pipe(fs.createWriteStream(tmp))
  191. .on('finish', function () {
  192. fs.createReadStream(tmp)
  193. .pipe(fs.createWriteStream(outfile))
  194. })
  195. }
  196. } else {
  197. if (outfile) {
  198. outStream = fs.createWriteStream(outfile)
  199. } else {
  200. outStream = process.stdout
  201. }
  202. let stream
  203. if (options.append) {
  204. stream = readStream
  205. .pipe(addStream(changelogStream))
  206. } else {
  207. stream = changelogStream
  208. .pipe(addStream(readStream))
  209. }
  210. stream
  211. .pipe(outStream)
  212. }
  213. } else {
  214. noInputFile()
  215. }