Upgrade the package and runtime together
npm install node-cmd@6node-cmd 6 requires Node.js 22.12 or newer. Upgrade production, CI, development, containers, and deployment images before changing the package version.
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.
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.
Remove wrappers that existed only to reach Node options
Before
// application-owned wrapper around
// child_process just to set cwd/envVersion 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.
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.
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.
| Need | Method | Result |
|---|---|---|
| Callback + direct args | runFile | ChildProcess and callback |
| Promise + direct args | runFilePromise | {stdout, stderr} and promise.child |
| Sync + direct args | runFileSync | {data, err, stderr} |
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.
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.
Upgrade checklist
- Move every supported environment to Node.js 22.12 or newer.
- Install
node-cmd@6and regenerate the application lockfile. - Run existing callback and synchronous tests unchanged first.
- Remove custom option-forwarding and Promise wrappers where v6 replaces them.
- Convert variable shell arguments to
runFile*orrunStream. - Review timeout, cancellation,
maxBuffer, and output-consumption policies. - Exercise shell behavior on Windows, macOS, and Linux targets.
- Read the security model before accepting another user’s input.