OverviewFilesCommitsIssuesDocsReleasesPeers

pgh / lib / _compat-exec.js

52 lines · javascript ref:
/**
 * Cross-runtime exec helpers.
 *
 * bare-subprocess 6.0.0 exports `spawn` + `spawnSync` but NOT `execSync`
 * or `execFileSync` (Bare keeps the subprocess API minimal). Node's
 * child_process has all four. This module provides execSync/execFileSync
 * with drop-in semantics, implemented on top of spawnSync — which exists
 * in both runtimes.
 *
 * Drop-in semantics:
 *   - Throws on non-zero exit (status + signal preserved on err)
 *   - Returns stdout Buffer/string when stdio:'pipe' (default)
 *   - Returns null when stdio:'inherit'
 *   - Honours { cwd, env, encoding, maxBuffer } as spawnSync does
 *
 * Use these instead of child_process.execSync / execFileSync so the
 * call sites work on both Bare and Node without per-site branching.
 */

import { spawnSync } from '#node:child_process'

function _check (cmd, result) {
  if (result.error) throw result.error
  if (result.status !== 0) {
    const err = new Error('Command failed: ' + cmd)
    err.status = result.status
    err.signal = result.signal
    err.stderr = result.stderr
    err.stdout = result.stdout
    throw err
  }
}

export function execSync (cmd, opts = {}) {
  const result = spawnSync('sh', ['-c', cmd], { stdio: 'pipe', ...opts })
  _check(cmd, result)
  return opts.stdio === 'inherit' ? null : result.stdout
}

export function execFileSync (file, args = [], opts = {}) {
  // execFileSync's signature is (file, args?, opts?). If `args` is
  // omitted and opts is passed positionally, the caller's array form
  // shifts. Match Node's behaviour: detect when args is the opts object.
  if (args && !Array.isArray(args) && typeof args === 'object') {
    opts = args
    args = []
  }
  const result = spawnSync(file, args, { stdio: 'pipe', ...opts })
  _check(file + ' ' + args.join(' '), result)
  return opts.stdio === 'inherit' ? null : result.stdout
}