exec.ts 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. import { promisify } from 'util'
  2. import {
  3. exec as execCb,
  4. execFile as execFileCb,
  5. spawn,
  6. ChildProcess,
  7. ExecOptions,
  8. StdioOptions,
  9. SpawnOptions,
  10. } from 'child_process'
  11. const exec = promisify(execCb)
  12. const execFile = promisify(execFileCb)
  13. export function execCapture(
  14. command: string | string[],
  15. options: ExecOptions = {},
  16. ): Promise<string> {
  17. if (typeof command === 'string') {
  18. return exec(command, options)
  19. .then((res) => res.stdout)
  20. } else if (Array.isArray(command)) {
  21. return execFile(command[0], command.slice(1), options)
  22. .then((res) => res.stdout)
  23. } else {
  24. throw new Error('Invalid command type for execCapture()')
  25. }
  26. }
  27. export function execLive(
  28. command: string | string[],
  29. options: SpawnOptions = {},
  30. ): Promise<void> {
  31. return execWithStdio(command, options, 'inherit')
  32. }
  33. export function execSilent(
  34. command: string | string[],
  35. options: SpawnOptions = {},
  36. ): Promise<void> {
  37. return execWithStdio(command, options, 'ignore')
  38. }
  39. // TODO: just return the childProcess
  40. export function spawnLive(
  41. command: string | string[],
  42. options: SpawnOptions = {},
  43. ): () => void {
  44. const child = spawnWithStdio(command, options, 'inherit')
  45. return () => {
  46. child.disconnect && child.disconnect()
  47. }
  48. }
  49. // TODO: just return the childProcess
  50. export function spawnSilent(
  51. command: string | string[],
  52. options: SpawnOptions = {},
  53. ): () => void {
  54. const child = spawnWithStdio(command, options, 'ignore')
  55. return () => {
  56. child.disconnect && child.disconnect()
  57. }
  58. }
  59. function execWithStdio(
  60. command: string | string[],
  61. options: SpawnOptions,
  62. stdio: StdioOptions,
  63. ): Promise<void> {
  64. const childProcess = spawnWithStdio(command, options, stdio)
  65. return new Promise((resolve, reject) => {
  66. childProcess.on('close', (exitCode) => {
  67. if (exitCode === 0) {
  68. resolve()
  69. } else {
  70. reject(new SpawnError(command, exitCode))
  71. }
  72. })
  73. })
  74. }
  75. function spawnWithStdio(
  76. command: string | string[],
  77. options: SpawnOptions,
  78. stdio: StdioOptions,
  79. ): ChildProcess {
  80. let commandPath: string
  81. let commandArgs: string[]
  82. let shell: boolean
  83. if (typeof command === 'string') {
  84. commandPath = command
  85. commandArgs = []
  86. shell = true
  87. } else if (Array.isArray(command)) {
  88. commandPath = command[0]
  89. commandArgs = command.slice(1)
  90. shell = false
  91. } else {
  92. throw new Error('Invalid command type for execLive()')
  93. }
  94. return spawn(commandPath, commandArgs, {
  95. ...options,
  96. shell,
  97. stdio,
  98. })
  99. }
  100. export class SpawnError extends Error {
  101. constructor(
  102. public command: string | string[],
  103. public exitCode: number | null,
  104. ) {
  105. super(`Exited ${JSON.stringify(command)} with error code ${exitCode}`)
  106. }
  107. }