mrkeyoor.com_
Wed 23 Sept 00:35 UTC
npmCLI & Toolingupdated 22 Sept 2026

spawndamnit review

spawndamnit 3.0.1 is a small CommonJS layer over `child_process.spawn()`. One call returns an object that behaves as both a Promise and an EventEmitter: it emits stdout and stderr chunks while the command runs, then resolves with the exit code and complete output Buffers. It uses cross-spawn for executable lookup and signal-exit to send termination signals to tracked children when the parent ends. Version 3.0.1 updates cross-spawn to fix a dependency vulnerability; it does not add timeouts, cancellation, types, or a richer command API.

Verdict

spawndamnit 3.0.1 installed in 1 second and occupied 1 MB in our sandbox with 0 audit findings, but it buffers all child output and exposes no timeout or direct cancellation. Keep it for small CommonJS commands that need live chunks plus final Buffers; choose execa or built-in spawn when process control and bounded output matter.

We installed it

Lab card: what happened when we installed spawndamnitScreenshot of spawndamnit documentation
Install✓ · 1s8 packages on disk · 1 MB
ImportESM import works · require() works · CommonJS package
Browsern/acould not be bundled for the browser (Node-only code, most likely)
Typesno TypeScript types found
Known vulns00 critical · 0 high · 0 moderate · 0 low (npm audit)

Answers from our run

Does spawndamnit install cleanly?

Yes. In a fresh container with an empty cache, npm install spawndamnit finished in 1 seconds, leaving 8 packages and 1 MB on disk. npm audit reported no known vulnerabilities.

Can spawndamnit run in a browser?

Not directly: esbuild could not bundle it for the browser in our run, which normally means it depends on Node built-ins. Use it on the server, or find a browser-targeted alternative.

Does spawndamnit work with both ESM and CommonJS?

Yes. Both import 'spawndamnit' and require('spawndamnit') worked in Node 22 in our run. The package is published as CommonJS.

Does spawndamnit include TypeScript types?

No type declarations were found in our install, so TypeScript users need their own declarations.

spawndamnit or execa: which should you use?

execa: Use it for typed modern process execution with rich errors, cancellation, timeouts, piping, and output controls. spawndamnit 3.0.1 installed in 1 second and occupied 1 MB in our sandbox with 0 audit findings, but it buffers all child output and exposes no timeout or direct cancellation.

When should you not use spawndamnit?

Child output may be large or endless. The implementation retains stdout and stderr chunks in memory and offers no byte limit or streaming-only mode.

API stability4/5The package still centers on one function returning the same Promise and EventEmitter hybrid, with `stdout` and `stderr` events followed by `{ code, stdout, stderr }`. Version 2 deliberately made nonzero exits resolve, and version 3.0.1 refreshed dependencies without changing that shape. The small surface is unlikely to surprise established callers, but the absence of declarations leaves option and result mistakes for runtime.
Docs2/5The README states 4 behaviors and gives one example that demonstrates chunk events, awaiting, and the final result. It does not document the forwarded spawn options, resolved nonzero exits, spawn-error rejection, Buffer encoding, inherited stdio, global child tracking, memory growth, shell safety, or missing cancellation. The implementation is short enough to inspect, but source reading should not be required to learn the failure contract.
Maintenance2/5Version 3.0.1 shipped on November 18, 2024 specifically to raise cross-spawn after a vulnerability, and GitHub was pushed the same day. The repository is not archived and reports 9 open issues and pull requests. That security refresh is useful evidence of occasional care, yet nearly all preceding code activity dates to 2018 and no release has followed in about 21 months.
Ecosystem3/5npm counted 4,240,376 downloads in the latest completed week, with much of that reach coming through established toolchains. cross-spawn supplies familiar Windows command resolution, and standard spawn options pass through. The wrapper has no plugins, types, adapters, browser target, or shared interface with fuller runners. Its value is a narrow convenience around two widely understood Node primitives.

Use it if

  • A CommonJS CLI wants one awaitable wrapper that also emits live stdout and stderr chunks.
  • The final output is small enough to buffer and must be available after the child closes.
  • Windows-compatible command lookup from cross-spawn is useful, while direct ChildProcess control is unnecessary.
  • Every caller can check `code` because a normal nonzero exit is data rather than a rejected Promise.
Skip it if

Setup reality

We installed spawndamnit 3.0.1 in 1 second in a fresh Node 22 Bookworm sandbox. The install left 8 packages and 1 MB on disk. The package has 2 direct dependencies, no peers, and a 28 KB unpacked size. npm audit found 0 known vulnerabilities. Package metadata states SEE LICENSE IN LICENSE rather than an SPDX identifier. Our check found no bundled TypeScript declarations.

The runtime is CommonJS without an exports map. Both require() and ESM import worked in our Node 22 sandbox. An esbuild browser bundle failed because the wrapper depends on child processes, signals, and Node streams; this is CLI and server tooling only. There is no config file, credential, native build, or service. The third argument is passed to cross-spawn, so ordinary spawn options such as cwd, env, and stdio apply even though the short README does not enumerate them.

Attach stdout and stderr listeners immediately after calling the function, before awaiting, because a fast child can emit at once. Output is also accumulated into Buffers for the final result. There is no cap, so a verbose build or long-lived watcher can grow the parent until memory is exhausted. With stdio: 'inherit', there are no piped child streams; live output goes directly to the terminal and the returned Buffers remain empty.

A child that starts and exits with code 1 resolves normally. A command that cannot start rejects through the child error event. Check both paths. Pass arguments as an array and keep shell false when any value is untrusted. signal-exit tracks children globally and sends SIGTERM when the parent exits, but the wrapper does not expose one child's PID or cancellation method. Use built-in spawn or execa when shutdown, timeout, signal escalation, or output bounds are part of correctness.

Patterns

Run a command and enforce success run-command

const spawn = require('spawndamnit');

const result = await spawn('git', ['status', '--short']);
if (result.code !== 0) {
  throw new Error(result.stderr.toString('utf8') || `git exited ${result.code}`);
}
console.log(result.stdout.toString('utf8'));

A normal nonzero exit resolves in version 3.0.1. Check `code` before consuming stdout as a successful result.

Forward chunks while awaiting the result mirror-live-output

const task = spawn('npm', ['run', 'build']);
task.on('stdout', (chunk) => process.stdout.write(chunk));
task.on('stderr', (chunk) => process.stderr.write(chunk));

const result = await task;
process.exitCode = result.code ?? 1;

Listeners must be attached immediately. Every chunk is still retained for the final Buffers, so live forwarding does not bound memory.

Run inside another project set-working-directory

const result = await spawn('npm', ['test'], {
  cwd: '/srv/project',
  env: process.env,
});

The third object is forwarded to cross-spawn even though the README does not list its standard spawn options.

Add one child environment variable extend-environment

await spawn(process.execPath, ['worker.js'], {
  env: {
    ...process.env,
    NODE_ENV: 'production',
  },
});

Spread the current environment unless isolation is deliberate. Omitting it can remove PATH and credentials the child expects.

Give an interactive child the terminal inherit-terminal

const result = await spawn('npm', ['init'], {
  stdio: 'inherit',
});
if (result.code !== 0) process.exitCode = result.code;

Inherited stdout and stderr are not pipes, so no chunk events fire and the returned output Buffers are empty.

Separate spawn errors from exit codes handle-missing-executable

try {
  const result = await spawn(command, args);
  if (result.code !== 0) console.error('exit', result.code);
} catch (error) {
  if (error.code === 'ENOENT') console.error('command not found');
  else throw error;
}

The Promise rejects when the child cannot start. Once started, even exit code 1 produces a resolved result.

Use the CommonJS entry from ESM import-from-esm

import spawn from 'spawndamnit';

const result = await spawn(process.execPath, ['--version']);
if (result.code !== 0) throw new Error(`node exited ${result.code}`);

Our Node 22 ESM import worked through CommonJS interop. The package has no native ESM entry or exports map.

Launch the current Node executable run-current-node

const result = await spawn(process.execPath, [
  '-e',
  'process.stdout.write(process.version)',
]);
console.log(result.stdout.toString('utf8'));

`process.execPath` avoids assuming that the executable is named `node` or discoverable through the child PATH.

Pass untrusted data as one argument avoid-shell-parsing

const result = await spawn('git', ['show', '--stat', '--', userRef], {
  shell: false,
});
if (result.code !== 0) throw new Error(result.stderr.toString());

An argument array avoids shell interpolation. The `--` marker also stops Git from interpreting `userRef` as an option.

Decode JSON only after success parse-json-output

const result = await spawn('npm', ['view', 'react', '--json']);
if (result.code !== 0) throw new Error(result.stderr.toString('utf8'));
const metadata = JSON.parse(result.stdout.toString('utf8'));

Final output values are Buffers. Decode explicitly and check the exit code before attempting JSON parsing.

Keep the ChildProcess handle for cancellation use-built-in-spawn

import { spawn as nodeSpawn } from 'node:child_process';

const child = nodeSpawn(command, args, { stdio: 'pipe' });
const timer = setTimeout(() => child.kill('SIGTERM'), 30_000);
child.once('close', () => clearTimeout(timer));

spawndamnit does not expose the child handle or a 30-second timeout option. Built-in spawn does, but you must collect output and handle errors yourself.

Alternatives

PackageRegistryPick it when
execanpmUse it for typed modern process execution with rich errors, cancellation, timeouts, piping, and output controls.
cross-spawnnpmUse it when Windows command lookup is the only helper needed and you want the real ChildProcess handle.
zxnpmUse it for script-oriented automation built around template commands and included shell utilities.
tinyshnpmUse it for a smaller shell-style helper when its command semantics match a simple script.

More cli & tooling guides

chalk · commander · typescript · esbuild · yargs · click · the whole shelf →

How this guide is made: grounded in the library's documentation, release notes, changelog, and issue history, on a fixed rubric — not a hands-on install of every release. The 50 most-downloaded entries are additionally install-verified in clean containers. Corrections: contact the desk.