npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

node-cmd

v6.0.1

Published

Node.js-only command-line power: run shell commands, launch executables, and control child processes with zero runtime dependencies.

Readme

node-cmd — command-line and process control for Node.js

node-cmd

Visit the node-cmd GitHub.io site

API reference · Testing & coverage · Benchmarks · Security · Migration · Changelog

CI npm version npm downloads license Node.js >=22.12 runtime dependencies tests coverage

Sponsor RIAEvangelist

Command-line power for Node.js. Run shell commands, launch executables, stream output, write to stdin, and control child processes. node-cmd has zero runtime dependencies and supports both Node.js CommonJS and native Node.js ES modules.

The original run() and runSync() APIs remain available. Version 6 adds forwarded execution options, Promise APIs, direct executable APIs that avoid a shell by default, an unbuffered spawn wrapper, and explicit cancellation support.

Runtime boundary

node-cmd is Node.js-only. It does not run in browsers, with or without a bundler. Its public API requires Node's built-in node:child_process module and operating-system process access. “Native ESM” in this project means native Node.js ESM.

No browser entry, browser shim, import map, playground, or native-Chrome suite is provided because browsers cannot expose child-process control. A Node-targeted bundler may externalize Node built-ins, but a browser-targeted bundle cannot grant browser JavaScript operating-system process privileges. Native-browser conformance and runtime-dependency conflict testing are not applicable; the package has zero runtime dependencies.

Why node-cmd?

Node already provides node:child_process; node-cmd turns its common execution paths into one small, consistent API. Use shell syntax when you need it, keep executable arguments separate when you do not, and choose callback, Promise, synchronous, buffered, or streaming control without a production dependency tree.

  • run* handles intentional shell commands; runFile* and runStream() are direct by default.
  • Existing run() and runSync() calls remain valid while modern options, cancellation, and immediate ChildProcess access stay available.
  • CommonJS and ESM load the same implementation on Node.js 22.12+, so runtime behavior is not duplicated between module systems.

Install

npm install node-cmd

node-cmd 6 requires Node.js 22.12 or newer.

Quick start

CommonJS

const cmd = require('node-cmd');

cmd.run('node --version', (error, data, stderr) => {
    if (error) {
        console.error(stderr || error.message);
        return;
    }

    console.log(data);
});

ES modules in Node.js

import { runPromise } from 'node-cmd';

const { stdout, stderr } = await runPromise('node --version');

if (stderr) {
    console.error(stderr);
}

console.log(stdout);

Run an executable without a shell

import { runFilePromise } from 'node-cmd';

const { stdout } = await runFilePromise(
    process.execPath,
    ['--version']
);

console.log(stdout);

runFile*() keeps the executable and its arguments separate and does not start a shell by default. Prefer it when any argument may contain untrusted or variable data.

Benchmarks

node-cmd delegates to the matching Node child-process primitive. A dependency-free harness measures JavaScript dispatch separately from real process completion so the wrapper’s cost is visible instead of being lost inside operating-system launch time.

node-cmd JavaScript dispatch benchmark against node:child_process

node-cmd empty-process completion benchmark against node:child_process

On the Node.js 22.12.0 reference run, common callback, Promise, direct-file, and streaming paths added median dispatch costs of 0.21–0.22 ns; synchronous result normalization added 7.56–10.40 ns. Empty-process completion took about 39–55 ms. All seven paired completion-time comparisons included zero in their 95% confidence intervals, so this run did not resolve a completion-time difference.

The largest measured dispatch delta was roughly 3.8 million times smaller than its matching child-process duration. Confidence intervals that include zero do not prove mathematical equivalence, and absolute launch time varies by machine and operating system. Read the methodology and exact tables, download the raw samples, or inspect the benchmark source. These results support negligible wrapper cost; they do not claim that node-cmd makes Node or the operating system launch a process faster.

API

| Method | Signature | Returns | |---|---|---| | run | run(command, options?, callback?) | ChildProcess | | runSync | runSync(command, options?) | { err, data, stderr } | | runPromise | runPromise(command, options?) | Promise<{ stdout, stderr }> | | runPromisified | Alias of runPromise | Promise<{ stdout, stderr }> | | runFile | runFile(file, args?, options?, callback?) | ChildProcess | | runFileSync | runFileSync(file, args?, options?) | { err, data, stderr } | | runFilePromise | runFilePromise(file, args?, options?) | Promise<{ stdout, stderr }> | | runFilePromisified | Alias of runFilePromise | Promise<{ stdout, stderr }> | | runStream | runStream(file, args?, options?) | ChildProcess |

The default export and CommonJS export expose the same methods. Native Node.js ESM also provides named exports.

run(command, options?, callback?)

Runs a command through the platform shell. The callback keeps the established Node-style shape:

cmd.run(
    'node --version',
    { cwd: process.cwd(), timeout: 10_000 },
    (error, data, stderr) => {
        if (error) {
            console.error(error);
            return;
        }

        console.log(data);
    }
);

The options object is optional, so existing run(command, callback) calls continue to work. The returned ChildProcess is available whether or not a callback is supplied.

runPromise(command, options?)

Runs a shell command and resolves with its buffered output:

const { stdout, stderr } = await cmd.runPromise('node --version', {
    timeout: 10_000
});

It rejects when the command cannot start, exits unsuccessfully, times out, is aborted, or exceeds maxBuffer. Rejection errors retain Node's child-process details, including stdout and stderr when Node provides them.

The returned Promise also exposes its immediate ChildProcess as .child when PID, events, or cancellation are needed before the buffered result settles:

const pending = cmd.runPromise('node --version');
console.log(pending.child.pid);
const result = await pending;

runPromisified is an exact compatibility alias of runPromise.

runSync(command, options?)

Runs a shell command synchronously and preserves the established result keys:

const result = cmd.runSync('node --version');

if (result.err) {
    console.error(result.stderr || result.err);
} else {
    console.log(result.data);
}
  • data contains standard output on success and is null on ordinary command failure.
  • err is null on success and describes an ordinary command failure.
  • stderr contains captured standard error when available.

Use synchronous execution only when blocking the Node.js event loop is acceptable.

Direct executable methods

The runFile*() methods execute a file with an argument array and buffer its output. Their callback, Promise, and synchronous result shapes match the corresponding shell-command methods.

cmd.runFile(
    process.execPath,
    ['--version'],
    { timeout: 10_000 },
    (error, data, stderr) => {
        if (error) {
            console.error(stderr || error.message);
            return;
        }

        console.log(data);
    }
);

runFilePromisified is an exact compatibility alias of runFilePromise.

The Promise returned by runFilePromise() or its alias likewise exposes the direct child as .child.

runStream(file, args?, options?)

Wraps Node's spawn() for long-running, high-output, or interactive programs. It does not buffer complete stdout or stderr and returns the ChildProcess immediately.

import { runStream } from 'node-cmd';

const child = runStream(process.execPath, ['--version']);

child.stdout.setEncoding('utf8');
child.stdout.on('data', (chunk) => process.stdout.write(chunk));
child.stderr.on('data', (chunk) => process.stderr.write(chunk));
child.on('close', (code, signal) => {
    console.log({ code, signal });
});

Arguments remain separate and no shell is used by default. Set options.shell only when shell parsing is intentional; doing so reintroduces platform-specific quoting and injection risks.

Options

Execution options are forwarded to Node's child_process APIs. Common options include:

| Option | Purpose | |---|---| | cwd | Working directory for the child process | | env | Environment variables supplied to the child process | | encoding | Buffered-output encoding; use 'buffer' or null for buffers | | timeout | Milliseconds before Node requests child termination | | signal | AbortSignal for asynchronous cancellation | | shell | Shell executable or shell enablement where supported | | maxBuffer | Maximum buffered stdout or stderr before termination | | killSignal | Signal used for timeout or cancellation | | windowsHide | Hide the subprocess window on Windows |

Not every Node option applies to every method. Synchronous calls cannot be cancelled with an AbortSignal, and runStream() is unbuffered so encoding and maxBuffer do not apply to it. Set an encoding directly on its stdout or stderr stream when text is wanted.

Setting shell: true on a direct-file or streaming call reintroduces shell parsing and its injection risks.

Child process control

run(), runFile(), and runStream() return Node's ChildProcess. Use it for streaming output, interactive input, PID inspection, events, and manual termination. Prefer runStream() when output can be large or the process is expected to stay open.

const child = cmd.runStream(process.execPath, ['--version']);

console.log(child.pid);
child.stdout.setEncoding('utf8');
child.stdout.on('data', (chunk) => process.stdout.write(chunk));
child.stderr.on('data', (chunk) => process.stderr.write(chunk));

Interactive input

const child = cmd.runStream(process.execPath, ['--interactive']);

child.stdout.setEncoding('utf8');
child.stdout.on('data', (chunk) => process.stdout.write(chunk));

child.stdin.write('console.log(6 * 7)\n');
child.stdin.write('.exit\n');
child.stdin.end();

Write to the returned child's stdin; callback output values are completed buffers, not process handles.

Cancellation

const controller = new AbortController();

cmd.run(
    'long-running-command',
    { signal: controller.signal },
    (error) => {
        if (error?.name === 'AbortError') {
            console.log('Command cancelled');
        }
    }
);

controller.abort();

You can also retain the returned child and call child.kill(). A shell command may create descendant processes; terminating the shell does not guarantee that every descendant is terminated on every operating system.

A timeout or kill signal requests termination; it is not a guaranteed hard deadline or a portable process-tree boundary.

Shell safety

run() and runPromise() intentionally execute shell command strings. Never concatenate untrusted input into those strings:

// Unsafe: userValue can change the command interpreted by the shell.
cmd.run(`tool --name ${userValue}`);

// Safer: the value remains one executable argument.
cmd.runFile('tool', ['--name', userValue]);

runFile*() and runStream() reduce shell-injection risk when their default no-shell behavior is preserved, but the called executable can still interpret arguments in unsafe ways. Validate inputs and use the smallest necessary environment and working directory. See SECURITY.md before running commands influenced by another user or service.

Cross-platform behavior

Shell syntax is platform-specific. Shell commands normally use /bin/sh on Unix-like systems and ComSpec on Windows, so quoting, environment expansion, separators, and built-in commands differ.

Prefer runFile*() or runStream() for portable executable calls. Windows .bat and .cmd files require a command shell; use run() or opt into a shell deliberately when invoking them.

node-cmd does not request administrator, root, or UAC elevation. Child processes inherit the privileges of the Node.js process that starts them.

Testing and coverage

The project uses vanilla-test 2.1.0 for both test execution and Node coverage. It is the only direct development dependency; the published node-cmd package keeps zero runtime dependencies.

The JavaScript suite contains 53 focused cases across five independently runnable sets. The Behavioral set composes public APIs into black-box consumer outcomes, while the other sets keep narrower contract ownership.

| Test set | Cases | Focus | | --- | ---: | --- | | Unit | 5 | CommonJS and ESM surface plus compatibility aliases | | Functional | 17 | Normal callback, Promise, synchronous, direct-file, and streaming behavior | | Behavioral | 5 | Shell composition, failure output, buffer limits, timeouts, and live output | | Integration | 8 | Process I/O, environment, cancellation, stderr isolation, and literal arguments | | Regression | 18 | Overloads, omitted values, buffers, validation, and error normalization | | Total | 53 | Public execution paths, compatibility edges, and consumer workflows |

| Gate | Current result | Required | | --- | ---: | ---: | | Full JavaScript suite | 53 / 53 passing | All passing | | Behavioral set | 5 / 5 passing | All passing | | Statements | 100% | 100% | | Branches | 100% | 100% | | Functions | 100% | 100% | | Lines | 100% | 100% |

Continuous integration runs the suite on Node.js 22.12 and Node.js 24 across Linux, macOS, and Windows. Coverage runs through vanilla-test coverage node, using native V8 execution without transforming node-cmd source. The generated engineer-readable HTML coverage report and ANSI-free test result artifact are published with the documentation site.

Development

npm ci
npm test
npm run test:unit
npm run test:functional
npm run test:behavioral
npm run test:integration
npm run test:regression
npm run test:runtime-contract
npm run coverage
npm run test:package
npm run benchmark
npm run benchmark:chart

The five test:* commands run one set independently; npm test and npm run coverage always run all 53 cases. Coverage writes the local HTML report to coverage/node/index.html. npm run benchmark prints a local comparison; pass --output <file> to retain its raw samples. npm run benchmark:chart regenerates the committed README charts from the reference JSON. npm run verify runs the full suite, coverage gates, packed-package smoke test, and static-site validation together.

When upgrading from v5, read MIGRATION.md. Release details are in CHANGELOG.md, and command-execution guidance is in SECURITY.md.

License

MIT