Upgrade v5 → v6

Migration guide

Version 6 keeps the original callback and synchronous APIs while raising the runtime floor and adding explicit Promise, direct-executable, and streaming paths.

01 / Install

Upgrade the package and runtime together

npm install node-cmd@6

node-cmd 6 requires Node.js 22.12 or newer. Upgrade production, CI, development, containers, and deployment images before changing the package version.

Major-version boundary. The classic call shapes remain, but the supported Node range changes from legacy Node releases to modern Node 22.12+.
02 / Imports

Keep CommonJS or adopt native Node.js ESM

Existing CommonJS

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

cmd.run('node --version', callback);

Native Node.js ESM

import cmd, { runPromise } from 'node-cmd';

const result = await runPromise(
    'node --version'
);

The package root is preferred. Existing require('node-cmd/cmd') and require('node-cmd/cmd.js') imports remain supported. A new node-cmd/cmd.mjs entry is also exported.

03 / Compatibility

Existing callback and synchronous calls remain valid

const child = cmd.run('npm test', (error, data, stderr) => {
    // same callback shape as v5
});

const result = cmd.runSync('node --version');
// { data, err, stderr }

run() still returns the ChildProcess. runSync() retains the legacy keys and captures stderr instead of leaking it to the parent process by default.

04 / Options

Remove wrappers that existed only to reach Node options

Before

// application-owned wrapper around
// child_process just to set cwd/env

Version 6

cmd.run('npm test', {
    cwd: './workspace',
    env: process.env,
    timeout: 30_000
}, callback);

Relevant options—including cwd, env, encoding, timeout, signal, shell, maxBuffer, killSignal, and windowsHide—are forwarded to the underlying Node method.

05 / Promises

Replace manual Promise wrappers

Before

const output = await new Promise(
    (resolve, reject) => cmd.run(
        command,
        (error, data, stderr) => {
            if (error) reject(error);
            else resolve({ stdout: data, stderr });
        }
    )
);

Version 6

const task = cmd.runPromise(command);
console.log(task.child.pid);

const { stdout, stderr } = await task;

runPromisified is an exact alias, so code written against that proposed name continues to work. Rejections retain Node’s error metadata and collected output.

06 / Direct execution

Move variable arguments out of shell strings

Shell-concatenated input

// Avoid this for variable data
cmd.run(`tool --name ${userValue}`);

Exact argument boundary

await cmd.runFilePromise(
    'tool',
    ['--name', userValue]
);

Select runFile, runFilePromise, or runFileSync when output should be buffered. They map to Node’s execFile family and avoid a shell by default.

NeedMethodResult
Callback + direct argsrunFileChildProcess and callback
Promise + direct argsrunFilePromise{stdout, stderr} and promise.child
Sync + direct argsrunFileSync{data, err, stderr}
07 / Streaming

Use runStream instead of buffering long-lived output

const child = cmd.runStream(
    'node',
    ['server.js'],
    { stdio: ['pipe', 'pipe', 'pipe'] }
);

child.stdout.pipe(process.stdout);
child.stderr.pipe(process.stderr);
child.stdin.write('ready\n');

runStream wraps spawn(), returns the live ChildProcess, and keeps direct arguments separate. It does not resolve a final stdout/stderr object. Set encoding and backpressure behavior on individual streams.

08 / Cancellation

Replace application-specific cancellation wiring

const controller = new AbortController();
const task = cmd.runPromise(command, {
    signal: controller.signal,
    timeout: 30_000
});

controller.abort();

The Promise rejects when Node processes the abort. A timeout requests termination; it is not a guaranteed deadline or a process-tree supervisor. Continue using the returned child for explicit lifecycle handling when needed.

09 / Checklist

Upgrade checklist

  1. Move every supported environment to Node.js 22.12 or newer.
  2. Install node-cmd@6 and regenerate the application lockfile.
  3. Run existing callback and synchronous tests unchanged first.
  4. Remove custom option-forwarding and Promise wrappers where v6 replaces them.
  5. Convert variable shell arguments to runFile* or runStream.
  6. Review timeout, cancellation, maxBuffer, and output-consumption policies.
  7. Exercise shell behavior on Windows, macOS, and Linux targets.
  8. Read the security model before accepting another user’s input.