DefaultCommandBase.ts 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. import ExitCodes from '../ExitCodes'
  2. import Command from '@oclif/command'
  3. import inquirer, { DistinctQuestion } from 'inquirer'
  4. import chalk from 'chalk'
  5. import inquirerDatepicker from 'inquirer-datepicker-prompt'
  6. /**
  7. * Abstract base class for pretty much all commands
  8. * (prevents console.log from hanging the process and unifies the default exit code)
  9. */
  10. export default abstract class DefaultCommandBase extends Command {
  11. protected indentGroupsOpened = 0
  12. protected jsonPrettyIdent = ''
  13. openIndentGroup() {
  14. console.group()
  15. ++this.indentGroupsOpened
  16. }
  17. closeIndentGroup() {
  18. console.groupEnd()
  19. --this.indentGroupsOpened
  20. }
  21. async simplePrompt(question: DistinctQuestion) {
  22. const { result } = await inquirer.prompt([
  23. {
  24. ...question,
  25. name: 'result',
  26. // prefix = 2 spaces for each group - 1 (because 1 is always added by default)
  27. prefix: Array.from(new Array(this.indentGroupsOpened))
  28. .map(() => ' ')
  29. .join('')
  30. .slice(1),
  31. },
  32. ])
  33. return result
  34. }
  35. async requireConfirmation(
  36. message = 'Are you sure you want to execute this action?',
  37. defaultVal = false
  38. ): Promise<void> {
  39. if (process.env.AUTO_CONFIRM === 'true' || parseInt(process.env.AUTO_CONFIRM || '')) {
  40. return
  41. }
  42. const { confirmed } = await inquirer.prompt([{ type: 'confirm', name: 'confirmed', message, default: defaultVal }])
  43. if (!confirmed) {
  44. this.exit(ExitCodes.OK)
  45. }
  46. }
  47. private jsonPrettyIndented(line: string) {
  48. return `${this.jsonPrettyIdent}${line}`
  49. }
  50. private jsonPrettyOpen(char: '{' | '[') {
  51. this.jsonPrettyIdent += ' '
  52. return chalk.gray(char) + '\n'
  53. }
  54. private jsonPrettyClose(char: '}' | ']') {
  55. this.jsonPrettyIdent = this.jsonPrettyIdent.slice(0, -4)
  56. return this.jsonPrettyIndented(chalk.gray(char))
  57. }
  58. private jsonPrettyKeyVal(key: string, val: any): string {
  59. return this.jsonPrettyIndented(chalk.magentaBright(`${key}: ${this.jsonPrettyAny(val)}`))
  60. }
  61. private jsonPrettyObj(obj: { [key: string]: any }): string {
  62. return (
  63. this.jsonPrettyOpen('{') +
  64. Object.keys(obj)
  65. .map((k) => this.jsonPrettyKeyVal(k, obj[k]))
  66. .join(',\n') +
  67. '\n' +
  68. this.jsonPrettyClose('}')
  69. )
  70. }
  71. private jsonPrettyArr(arr: any[]): string {
  72. return (
  73. this.jsonPrettyOpen('[') +
  74. arr.map((v) => this.jsonPrettyIndented(this.jsonPrettyAny(v))).join(',\n') +
  75. '\n' +
  76. this.jsonPrettyClose(']')
  77. )
  78. }
  79. private jsonPrettyAny(val: any): string {
  80. if (Array.isArray(val)) {
  81. return this.jsonPrettyArr(val)
  82. } else if (typeof val === 'object' && val !== null) {
  83. return this.jsonPrettyObj(val)
  84. } else if (typeof val === 'string') {
  85. return chalk.green(`"${val}"`)
  86. }
  87. // Number, boolean etc.
  88. return chalk.cyan(val)
  89. }
  90. jsonPrettyPrint(json: string) {
  91. try {
  92. const parsed = JSON.parse(json)
  93. console.log(this.jsonPrettyAny(parsed))
  94. } catch (e) {
  95. console.log(this.jsonPrettyAny(json))
  96. }
  97. }
  98. async finally(err: any) {
  99. // called after run and catch regardless of whether or not the command errored
  100. // We'll force exit here, in case there is no error, to prevent console.log from hanging the process
  101. if (!err) this.exit(ExitCodes.OK)
  102. if (err && process.env.DEBUG === 'true') {
  103. console.log(err)
  104. }
  105. super.finally(err)
  106. }
  107. async init() {
  108. inquirer.registerPrompt('datetime', inquirerDatepicker)
  109. }
  110. }