cli.js 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. #!/usr/bin/env node
  2. 'use strict'
  3. const conventionalCommitsParser = require('./')
  4. const forEach = require('lodash').forEach
  5. const fs = require('fs')
  6. const isTextPath = require('is-text-path')
  7. const JSONStream = require('JSONStream')
  8. const meow = require('meow')
  9. const readline = require('readline')
  10. const split = require('split2')
  11. const through = require('through2')
  12. const filePaths = []
  13. let separator = '\n\n\n'
  14. const cli = meow(`
  15. Practice writing commit messages or parse messages from files.
  16. If used without specifying a text file path, you will enter an interactive shell.
  17. Otherwise the commit messages in the files are parsed and printed
  18. By default, commits will be split by three newlines ('\\n\\n\\n') or you can specify a separator.
  19. Usage
  20. conventional-commits-parser [<commit-separator>]
  21. conventional-commits-parser [<commit-separator>] <path> [<path> ...]
  22. cat <path> | conventional-commits-parser [<commit-separator>]
  23. Example
  24. conventional-commits-parser
  25. conventional-commits-parser log.txt
  26. cat log.txt | conventional-commits-parser
  27. conventional-commits-parser log2.txt '===' >> parsed.txt
  28. Options
  29. -p, --header-pattern Regex to match header pattern
  30. -c, --header-correspondence Comma separated parts used to define what capturing group of 'headerPattern' captures what
  31. -r, --reference-actions Comma separated keywords that used to reference issues
  32. -i, --issue-prefixes Comma separated prefixes of an issue
  33. --issue-prefixes-case-sensitive Treat issue prefixes as case sensitive
  34. -n, --note-keywords Comma separated keywords for important notes
  35. -f, --field-pattern Regex to match other fields
  36. --revert-pattern Regex to match revert pattern
  37. --revert-correspondence Comma separated fields used to define what the commit reverts
  38. -v, --verbose Verbose output
  39. `, {
  40. flags: {
  41. 'header-pattern': {
  42. alias: 'p',
  43. type: 'string'
  44. },
  45. 'header-correspondence': {
  46. alias: 'c',
  47. type: 'string'
  48. },
  49. 'reference-actions': {
  50. alias: 'r',
  51. type: 'string'
  52. },
  53. 'issue-prefixes': {
  54. alias: 'i',
  55. type: 'string'
  56. },
  57. 'issue-prefixes-case-sensitive': {
  58. type: 'boolean'
  59. },
  60. 'note-keywords': {
  61. alias: 'n',
  62. type: 'string'
  63. },
  64. 'field-pattern': {
  65. alias: 'f',
  66. type: 'string'
  67. },
  68. 'revert-pattern': {
  69. type: 'string'
  70. },
  71. 'revert-correspondence': {
  72. type: 'string'
  73. },
  74. verbose: {
  75. alias: 'v',
  76. type: 'boolean'
  77. }
  78. }
  79. })
  80. forEach(cli.input, function (arg) {
  81. if (isTextPath(arg)) {
  82. filePaths.push(arg)
  83. } else {
  84. separator = arg
  85. }
  86. })
  87. const length = filePaths.length
  88. const options = cli.flags
  89. if (options.verbose) {
  90. options.warn = console.log.bind(console)
  91. }
  92. function processFile (fileIndex) {
  93. const filePath = filePaths[fileIndex]
  94. fs.createReadStream(filePath)
  95. .on('error', function (err) {
  96. console.warn('Failed to read file ' + filePath + '\n' + err)
  97. if (++fileIndex < length) {
  98. processFile(fileIndex)
  99. }
  100. })
  101. .pipe(split(separator))
  102. .pipe(conventionalCommitsParser(options))
  103. .pipe(JSONStream.stringify())
  104. .on('end', function () {
  105. if (++fileIndex < length) {
  106. processFile(fileIndex)
  107. }
  108. })
  109. .pipe(process.stdout)
  110. }
  111. if (process.stdin.isTTY) {
  112. if (length > 0) {
  113. processFile(0)
  114. } else {
  115. let commit = ''
  116. const stream = through()
  117. const rl = readline.createInterface({
  118. input: process.stdin,
  119. output: process.stdout,
  120. terminal: true
  121. })
  122. stream.pipe(conventionalCommitsParser(options))
  123. .pipe(JSONStream.stringify('', '', ''))
  124. .pipe(through(function (chunk, enc, cb) {
  125. if (chunk.toString() === '""') {
  126. cb(null, 'Commit cannot be parsed\n')
  127. } else {
  128. cb(null, chunk + '\n')
  129. }
  130. }))
  131. .pipe(process.stdout)
  132. rl.on('line', function (line) {
  133. commit += line + '\n'
  134. if (commit.indexOf(separator) === -1) {
  135. return
  136. }
  137. stream.write(commit)
  138. commit = ''
  139. })
  140. }
  141. } else {
  142. options.warn = true
  143. process.stdin
  144. .pipe(split(separator))
  145. .pipe(conventionalCommitsParser(options))
  146. .on('error', function (err) {
  147. console.error(err.toString())
  148. process.exit(1)
  149. })
  150. .pipe(JSONStream.stringify())
  151. .pipe(process.stdout)
  152. }