Cross-spawn is a Node.js library that provides cross-platform solutions for child_process.spawn and child_process.spawnSync. It resolves Windows-specific issues including PATHEXT resolution, shebang support, proper command escaping, and ENOENT error handling while maintaining the same API as Node.js built-ins.
npm install cross-spawnconst spawn = require('cross-spawn');ES6 modules:
import spawn from 'cross-spawn';Named imports:
const { spawn, sync } = require('cross-spawn');import { spawn, sync } from 'cross-spawn';const spawn = require('cross-spawn');
// Asynchronous spawn - returns ChildProcess
const child = spawn('npm', ['list', '-g', '-depth', '0'], { stdio: 'inherit' });
child.on('close', (code) => {
console.log(`Child process exited with code ${code}`);
});
// Synchronous spawn - returns SpawnSyncResult
const result = spawn.sync('npm', ['list', '-g', '-depth', '0'], { stdio: 'inherit' });
console.log('Exit code:', result.status);Cross-spawn is built around several key components that work together to provide cross-platform process spawning:
lib/parse.js): Normalizes and parses spawn arguments, handles Windows-specific command resolution and escapinglib/util/resolveCommand.js): Locates executable files using PATH and PATHEXT, handles custom working directorieslib/util/readShebang.js): Reads and processes shebang headers from executable files on Windowslib/util/escape.js): Properly escapes commands and arguments for Windows cmd.exe executionlib/enoent.js): Provides consistent ENOENT (command not found) error reporting across platformsindex.js): Exposes the public API while delegating to Node.js built-in child_process functionsThe library works by intercepting spawn calls, analyzing the target platform, and applying necessary transformations before delegating to Node.js built-in functions.
Cross-platform asynchronous process spawning with Windows compatibility fixes.
/**
* Spawn a child process asynchronously with cross-platform compatibility
* @param {string} command - Command to execute
* @param {string[]} [args] - Array of arguments
* @param {object} [options] - Spawn options (same as child_process.spawn)
* @returns {ChildProcess} Child process instance
*/
function spawn(command, args, options);The returned ChildProcess object is the same as Node.js child_process.spawn, providing all standard events (exit, close, error, etc.) and streams (stdin, stdout, stderr).
Usage Examples:
const spawn = require('cross-spawn');
// Basic command execution
const child = spawn('echo', ['Hello World']);
// Command with options
const child = spawn('npm', ['install'], {
cwd: '/path/to/project',
stdio: 'inherit'
});
// Handle events
child.on('close', (code, signal) => {
console.log(`Process exited with code ${code} and signal ${signal}`);
});
child.on('error', (err) => {
console.error('Failed to start process:', err);
});Cross-platform synchronous process spawning with Windows compatibility fixes.
/**
* Spawn a child process synchronously with cross-platform compatibility
* @param {string} command - Command to execute
* @param {string[]} [args] - Array of arguments
* @param {object} [options] - Spawn options (same as child_process.spawnSync)
* @returns {SpawnSyncResult} Result object with status, stdout, stderr, etc.
*/
function sync(command, args, options);Usage Examples:
const { sync } = require('cross-spawn');
// Basic synchronous execution
const result = sync('echo', ['Hello World'], { encoding: 'utf8' });
console.log('Output:', result.stdout);
console.log('Exit code:', result.status);
// Handle errors
const result = sync('nonexistent-command');
if (result.error) {
console.error('Command failed:', result.error.message);
}Cross-spawn automatically handles several platform-specific differences:
Windows-specific enhancements:
.com, .exe, .bat, .cmd files based on Windows PATHEXT environment variable#!/usr/bin/env shebangs on Windows (Unix behavior emulation)^ characterCross-platform consistency:
/**
* Result object returned by sync() function
*/
interface SpawnSyncResult {
/** Process ID of the spawned process */
pid: number;
/** Buffer containing stdout data (if stdio not redirected) */
output: Buffer[];
/** String containing stdout data (if encoding specified) */
stdout: string | Buffer;
/** String containing stderr data (if encoding specified) */
stderr: string | Buffer;
/** Exit status code (null if process was killed by signal) */
status: number | null;
/** Signal that terminated the process (null if exited normally) */
signal: string | null;
/** Error object if spawn failed */
error?: Error;
}Cross-spawn accepts the same options as Node.js built-in spawn functions:
/**
* Options for spawn and sync functions (extends Node.js spawn options)
*/
interface SpawnOptions {
/** Current working directory of the child process */
cwd?: string;
/** Environment key-value pairs */
env?: Record<string, string>;
/** Array or string stdio configuration */
stdio?: 'pipe' | 'ignore' | 'inherit' | Array<string | number | Stream | null | undefined>;
/** Detach the child process */
detached?: boolean;
/** User identity to run as */
uid?: number;
/** Group identity to run as */
gid?: number;
/** Use shell to execute command (cross-spawn enhancements disabled) */
shell?: boolean | string;
/** Signal to use to kill the child process */
killSignal?: string | number;
/** Timeout for sync operations */
timeout?: number;
/** Max buffer size for stdout/stderr in sync operations */
maxBuffer?: number;
/** Encoding for sync string output */
encoding?: string;
/** Windows-specific: pass arguments verbatim */
windowsVerbatimArguments?: boolean;
/** Windows-specific: hide console window */
windowsHide?: boolean;
}Cross-spawn provides consistent error handling across platforms:
const spawn = require('cross-spawn');
// Async error handling
const child = spawn('nonexistent-command');
child.on('error', (err) => {
if (err.code === 'ENOENT') {
console.error('Command not found');
}
});
// Sync error handling
const result = spawn.sync('nonexistent-command');
if (result.error && result.error.code === 'ENOENT') {
console.error('Command not found');
}ENOENT: Command not found or file does not existEACCES: Permission deniedEMFILE: Too many open filesENOMEM: Cannot allocate memoryCross-spawn exposes internal functions for testing and advanced use cases. These should not be used in production code.
/**
* Internal argument parsing function (exposed for testing)
* @param {string} command - Command to execute
* @param {string[]} [args] - Array of arguments
* @param {object} [options] - Spawn options
* @returns {object} Parsed command object with normalized arguments
*/
function _parse(command, args, options);
/**
* Internal ENOENT error handling utilities (exposed for testing)
*/
const _enoent = {
/** Hook into child process to emit proper ENOENT errors */
hookChildProcess: function(cp: ChildProcess, parsed: object): void,
/** Verify if exit status indicates ENOENT for async spawn */
verifyENOENT: function(status: number, parsed: object): Error | null,
/** Verify if exit status indicates ENOENT for sync spawn */
verifyENOENTSync: function(status: number, parsed: object): Error | null,
/** Create properly formatted ENOENT error objects */
notFoundError: function(original: object, syscall: string): Error
};These exports can be accessed as:
const spawn = require('cross-spawn');
const parsed = spawn._parse('command', ['arg1', 'arg2']);
const enoentUtils = spawn._enoent;Cross-spawn provides several Windows-specific enhancements:
.com, .exe, .bat, .cmd)#!/usr/bin/env <program> format)When using options.shell, cross-spawn's enhancements are disabled to match Node.js behavior:
// Cross-spawn enhancements active (recommended)
spawn('npm', ['install']);
// Cross-spawn enhancements disabled (matches Node.js behavior)
spawn('npm install', { shell: true });Shebang support on Windows is limited to the format #!/usr/bin/env <program> where <program> cannot contain arguments.
Cross-spawn is a drop-in replacement for Node.js built-in spawn functions:
// Replace this
const { spawn, spawnSync } = require('child_process');
// With this
const spawn = require('cross-spawn');
const spawnSync = spawn.sync; // or const { sync: spawnSync } = require('cross-spawn');
// All existing code works unchanged
const child = spawn('command', ['args'], options);
const result = spawnSync('command', ['args'], options);