{"version":3,"file":"spawn.cjs","names":["spawn"],"sources":["../src/spawn.ts"],"sourcesContent":["import type {\n  SpawnOptions,\n  SpawnOptionsWithoutStdio,\n  SpawnOptionsWithStdioTuple,\n  SpawnSyncReturns,\n  StdioNull,\n  StdioPipe,\n} from 'node:child_process';\nimport { spawn } from 'node:child_process';\n\nimport { treeKill } from './treeKill.js';\n\n/**\n * Return type for spawnAsync function, based on SpawnSyncReturns but without output and error properties\n */\nexport type SpawnAsyncReturns = Omit<SpawnSyncReturns<string>, 'output' | 'error'>;\n\n/**\n * Options for spawnAsync function, extending various Node.js spawn options with additional functionality\n */\nexport type SpawnAsyncOptions = (\n  | SpawnOptionsWithoutStdio\n  | SpawnOptionsWithStdioTuple<StdioPipe, StdioPipe, StdioPipe>\n  | SpawnOptionsWithStdioTuple<StdioPipe, StdioPipe, StdioNull>\n  | SpawnOptionsWithStdioTuple<StdioPipe, StdioNull, StdioPipe>\n  | SpawnOptionsWithStdioTuple<StdioNull, StdioPipe, StdioPipe>\n  | SpawnOptionsWithStdioTuple<StdioPipe, StdioNull, StdioNull>\n  | SpawnOptionsWithStdioTuple<StdioNull, StdioPipe, StdioNull>\n  | SpawnOptionsWithStdioTuple<StdioNull, StdioNull, StdioPipe>\n  | SpawnOptionsWithStdioTuple<StdioNull, StdioNull, StdioNull>\n  | SpawnOptions\n) & {\n  /** Input string to write to the spawned process's stdin */\n  input?: string;\n  /** If true, stderr output will be merged into stdout */\n  mergeOutAndError?: boolean;\n  /** If true, the spawned process will be killed when the parent process exits */\n  killOnExit?: boolean;\n  /** If true, enables verbose logging of process operations */\n  verbose?: boolean;\n  /** If true, stdout data will be printed to console as it's received */\n  printingStdout?: boolean;\n  /** If true, stderr data will be printed to console as it's received */\n  printingStderr?: boolean;\n  /** If true, blank-only lines are skipped while printing stdout/stderr in realtime */\n  omitBlankLinesWhilePrinting?: boolean;\n};\n\n/**\n * Spawns a child process asynchronously and returns a promise that resolves with the process results\n *\n * This function provides a Promise-based wrapper around Node.js's spawn function with additional features:\n * - Automatic encoding of stdout/stderr as UTF-8\n * - Option to merge stderr into stdout\n * - Option to automatically kill the process on parent exit\n * - Option to provide input via stdin\n * - Verbose logging capability\n *\n * @param command - The command to run\n * @param args - List of string arguments\n * @param options - Configuration options for the spawned process\n * @returns Promise that resolves with the process results including pid, stdout, stderr, status, and signal\n * @throws Will reject the promise if the process fails to spawn or encounters an error\n *\n * @example\n * ```typescript\n * const result = await spawnAsync('ls', ['-la'], { verbose: true });\n * console.log(result.stdout);\n * ```\n */\nexport async function spawnAsync(\n  command: string,\n  args?: readonly string[],\n  options?: SpawnAsyncOptions\n): Promise<SpawnAsyncReturns> {\n  return new Promise((resolve, reject) => {\n    try {\n      const proc = spawn(command, args ?? [], options ?? {});\n      // `setEncoding` is undefined in Bun\n      // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n      proc.stdout?.setEncoding?.('utf8');\n      // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n      proc.stderr?.setEncoding?.('utf8');\n\n      let stdout = '';\n      let stderr = '';\n      const stdoutPrinter = createRealtimePrinter(process.stdout, options?.omitBlankLinesWhilePrinting);\n      const stderrPrinter = createRealtimePrinter(process.stderr, options?.omitBlankLinesWhilePrinting);\n      proc.stdout?.on('data', (data: string) => {\n        stdout += data;\n        if (options?.printingStdout) {\n          stdoutPrinter.write(data);\n        }\n      });\n      proc.stderr?.on('data', (data: string) => {\n        if (options?.mergeOutAndError) {\n          stdout += data;\n        } else {\n          stderr += data;\n        }\n        if (options?.printingStderr) {\n          stderrPrinter.write(data);\n        }\n      });\n\n      let stopped = false;\n      const stopProcess = (): void => {\n        if (stopped || !proc.pid) return;\n\n        stopped = true;\n        if (options?.verbose) {\n          console.info(`treeKill(${proc.pid})`);\n        }\n        try {\n          treeKill(proc.pid);\n        } catch (error) {\n          if (options?.verbose) {\n            console.warn(`Failed to treeKill(${proc.pid})`, error);\n          }\n        }\n      };\n      const cleanupSignals: NodeJS.Signals[] =\n        process.platform === 'win32' ? ['SIGINT', 'SIGTERM'] : ['SIGINT', 'SIGTERM', 'SIGQUIT'];\n      const signalHandlers = new Map<NodeJS.Signals, () => void>();\n      const removeKillOnExitHandlers = (): void => {\n        process.removeListener('beforeExit', stopProcess);\n        for (const [signal, handler] of signalHandlers) {\n          process.removeListener(signal, handler);\n        }\n        signalHandlers.clear();\n      };\n      if (options?.killOnExit) {\n        process.on('beforeExit', stopProcess);\n        for (const signal of cleanupSignals) {\n          const handleSignal = (): void => {\n            stopProcess();\n            removeKillOnExitHandlers();\n            if (process.listenerCount(signal) === 0) {\n              process.kill(process.pid, signal);\n            }\n          };\n          signalHandlers.set(signal, handleSignal);\n          process.on(signal, handleSignal);\n        }\n      }\n\n      proc.on('error', (error) => {\n        removeKillOnExitHandlers();\n        proc.removeAllListeners('close');\n        reject(error);\n      });\n      proc.on('close', (code: number | null, signal: NodeJS.Signals | null) => {\n        removeKillOnExitHandlers();\n        stdoutPrinter.flush();\n        stderrPrinter.flush();\n        if (proc.pid === undefined) {\n          reject(new Error('Process has no pid.'));\n        } else {\n          resolve({\n            pid: proc.pid,\n            stdout,\n            stderr,\n            status: code,\n            signal,\n          });\n        }\n      });\n\n      if (options?.input) {\n        proc.stdin?.write(options.input);\n        proc.stdin?.end();\n      }\n    } catch (error) {\n      // eslint-disable-next-line @typescript-eslint/prefer-promise-reject-errors\n      reject(error);\n    }\n  });\n}\n\nconst ANSI_ESCAPE_CODE_REGEXP = new RegExp(`${String.fromCodePoint(27)}\\\\[[0-?]*[ -/]*[@-~]`, 'g');\n\nfunction createRealtimePrinter(\n  stream: NodeJS.WriteStream,\n  omitBlankLines = false\n): { write: (data: string) => void; flush: () => void } {\n  if (!omitBlankLines) {\n    return {\n      write: (data) => stream.write(data),\n      flush: () => {},\n    };\n  }\n\n  let pending = '';\n  return {\n    write: (data) => {\n      pending += data;\n      const lines = pending.split(/\\r?\\n/);\n      pending = lines.pop() ?? '';\n      for (const line of lines) {\n        if (!isBlankLine(line)) {\n          stream.write(`${line}\\n`);\n        }\n      }\n    },\n    flush: () => {\n      if (!isBlankLine(pending)) {\n        stream.write(pending);\n      }\n      pending = '';\n    },\n  };\n}\n\nfunction isBlankLine(line: string): boolean {\n  return line.replaceAll(ANSI_ESCAPE_CODE_REGEXP, '').trim().length === 0;\n}\n"],"mappings":"mFAsEA,eAAsB,EACpB,EACA,EACA,EAC4B,CAC5B,OAAO,IAAI,SAAS,EAAS,IAAW,CACtC,GAAI,CACF,IAAM,GAAA,EAAOA,EAAAA,MAAAA,CAAM,EAAS,GAAQ,CAAC,EAAG,GAAW,CAAC,CAAC,EAGrD,EAAK,QAAQ,cAAc,MAAM,EAEjC,EAAK,QAAQ,cAAc,MAAM,EAEjC,IAAI,EAAS,GACT,EAAS,GACP,EAAgB,EAAsB,QAAQ,OAAQ,GAAS,2BAA2B,EAC1F,EAAgB,EAAsB,QAAQ,OAAQ,GAAS,2BAA2B,EAChG,EAAK,QAAQ,GAAG,OAAS,GAAiB,CACxC,GAAU,EACN,GAAS,gBACX,EAAc,MAAM,CAAI,CAE5B,CAAC,EACD,EAAK,QAAQ,GAAG,OAAS,GAAiB,CACpC,GAAS,iBACX,GAAU,EAEV,GAAU,EAER,GAAS,gBACX,EAAc,MAAM,CAAI,CAE5B,CAAC,EAED,IAAI,EAAU,GACR,MAA0B,CAC1B,QAAW,CAAC,EAAK,KAGrB,CADA,EAAU,GACN,GAAS,SACX,QAAQ,KAAK,YAAY,EAAK,IAAI,EAAE,EAEtC,GAAI,CACF,EAAA,SAAS,EAAK,GAAG,CACnB,OAAS,EAAO,CACV,GAAS,SACX,QAAQ,KAAK,sBAAsB,EAAK,IAAI,GAAI,CAAK,CAEzD,CARsC,CASxC,EACM,EACJ,QAAQ,WAAa,QAAU,CAAC,SAAU,SAAS,EAAI,CAAC,SAAU,UAAW,SAAS,EAClF,EAAiB,IAAI,IACrB,MAAuC,CAC3C,QAAQ,eAAe,aAAc,CAAW,EAChD,IAAK,GAAM,CAAC,EAAQ,KAAY,EAC9B,QAAQ,eAAe,EAAQ,CAAO,EAExC,EAAe,MAAM,CACvB,EACA,GAAI,GAAS,WAAY,CACvB,QAAQ,GAAG,aAAc,CAAW,EACpC,IAAK,IAAM,KAAU,EAAgB,CACnC,IAAM,MAA2B,CAC/B,EAAY,EACZ,EAAyB,EACrB,QAAQ,cAAc,CAAM,IAAM,GACpC,QAAQ,KAAK,QAAQ,IAAK,CAAM,CAEpC,EACA,EAAe,IAAI,EAAQ,CAAY,EACvC,QAAQ,GAAG,EAAQ,CAAY,CACjC,CACF,CAEA,EAAK,GAAG,QAAU,GAAU,CAC1B,EAAyB,EACzB,EAAK,mBAAmB,OAAO,EAC/B,EAAO,CAAK,CACd,CAAC,EACD,EAAK,GAAG,SAAU,EAAqB,IAAkC,CACvE,EAAyB,EACzB,EAAc,MAAM,EACpB,EAAc,MAAM,EAChB,EAAK,MAAQ,IAAA,GACf,EAAW,MAAM,qBAAqB,CAAC,EAEvC,EAAQ,CACN,IAAK,EAAK,IACV,SACA,SACA,OAAQ,EACR,QACF,CAAC,CAEL,CAAC,EAEG,GAAS,QACX,EAAK,OAAO,MAAM,EAAQ,KAAK,EAC/B,EAAK,OAAO,IAAI,EAEpB,OAAS,EAAO,CAEd,EAAO,CAAK,CACd,CACF,CAAC,CACH,CAEA,MAAM,EAA8B,OAAO,GAAG,OAAO,cAAc,EAAE,EAAE,sBAAuB,GAAG,EAEjG,SAAS,EACP,EACA,EAAiB,GACqC,CACtD,GAAI,CAAC,EACH,MAAO,CACL,MAAQ,GAAS,EAAO,MAAM,CAAI,EAClC,UAAa,CAAC,CAChB,EAGF,IAAI,EAAU,GACd,MAAO,CACL,MAAQ,GAAS,CACf,GAAW,EACX,IAAM,EAAQ,EAAQ,MAAM,OAAO,EACnC,EAAU,EAAM,IAAI,GAAK,GACzB,IAAK,IAAM,KAAQ,EACZ,EAAY,CAAI,GACnB,EAAO,MAAM,GAAG,EAAK,GAAG,CAG9B,EACA,UAAa,CACN,EAAY,CAAO,GACtB,EAAO,MAAM,CAAO,EAEtB,EAAU,EACZ,CACF,CACF,CAEA,SAAS,EAAY,EAAuB,CAC1C,OAAO,EAAK,WAAW,EAAyB,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,SAAW,CACxE"}