This is a "fork" of cross-spawn ( a cross-platform solution to node's spawn and spawnSync ) which ports its codebase to modern ESM and TypeScript.
This package is a drop-in replacement for cross-spawn which tries to behave the same as the original package. Please refer to the Migration guide for further explanation.
With NPM:
npm install cross-spawn-esmWith Yarn:
yarn add cross-spawn-esmWith PNPM:
pnpm add cross-spawn-esmWith Bun:
bun add cross-spawn-esmWith Deno:
deno add cross-spawn-esmUse it exactly the same way as node's spawn and spawnSync ( a drop-in replacement for them ) with the same arguments and options. There
is no default export; always use named imports.
import { spawn, spawnSync } from "cross-spawn-esm";
// Spawn NPM asynchronously
const child = spawn("npm", ["list", "-g", "-depth", "0"], { stdio: "inherit" });
// Spawn NPM synchronously
const result = spawnSync("npm", ["list", "-g", "-depth", "0"], { stdio: "inherit" });- Overall smaller bundle size (~50% smaller)
- Tree-shaking friendly
- Zero dependencies
- No need for an additional types package (
@types/cross-spawn) - Modern codebase and active maintenance
- Better documentation
Porting from cross-spawn to cross-spawn-esm is mostly a matter of changing how you import the package and how you call its sync API.
Uninstall cross-spawn (and @types/cross-spawn if you had it) and install cross-spawn-esm:
npm remove cross-spawn @types/cross-spawn
npm install cross-spawn-esmBefore:
import spawn from "cross-spawn";
const child = spawn("npm", ["list", "-g", "-depth", "0"], { stdio: "inherit" });
const result = spawn.sync("npm", ["list", "-g", "-depth", "0"], { stdio: "inherit" });After:
import { spawn, spawnSync } from "cross-spawn-esm";
const child = spawn("npm", ["list", "-g", "-depth", "0"], { stdio: "inherit" });
const result = spawnSync("npm", ["list", "-g", "-depth", "0"], { stdio: "inherit" });If you were relying on the hidden internals:
Before: spawn._parse(...), spawn._enoent.verifyENOENT(...)
After:
import { _parse, _enoent } from "cross-spawn-esm";_enoent.verifyENOENT(status, parsed, syscall)now takes an explicitsyscallargument ("spawn"or"spawnSync"), where the original shipped two separate functions (verifyENOENT/verifyENOENTSync)._enoent.notFoundError(...)now returns a valid NodeJS.ErrnoException._parsenow contains bothparseandparseNonShellfunctions:
Before: const parsed = _parse(...)
After: const parsed = _parse.parse(...)
- New
_utilsobject exposes all the lower-level helpers (shebangCommand,readShebang,detectShebang,resolveCommand,resolveCommandAttempt,escapeLineBreaks,escapeMetaChars,escapeCommand,escapeArgument,pathKey) If you were relying on the original cross-spawn dependencies ( path-key and shebang-command ), their improved versions can be found in_utils.
cross-spawn-esm ships its own type definitions, so the third-party @types/cross-spawn package is no longer needed.
Beyond the API surface, a few implementation details intentionally differ:
original.argsis an independent snapshot:parseclones the args and givesoriginal.argsits own copy, so shebang rewiring andcmd.exeescaping never mutate it.- Broader shebang support: Shebang detection on Windows reads the shebang of the resolved script and rewires the command to its
interpreter.
#!/usr/bin/env <program>is resolved fromPATHand spawned directly. Any other shebang (#!/bin/sh,#!/bin/bash -e) is reduced to the interpreter's basename (plus its single argument, if any) and falls back to thecmd.exewrapper, which works when that interpreter is onPATH.
The following behaviors are intentionally kept identical to cross-spawn:
- When
options.shellis used, parsing, escaping, and shebang enhancements are disabled - matching both the original and Node.js behavior. - Windows-only ENOENT detection: when the process exits with code
1and the command could not be resolved, anerrorevent (async) orresult.error(sync) is produced.
| Feature | cross-spawn | cross-spawn-esm |
|---|---|---|
| Module format | CommonJS | ESM ("type": "module") |
| Async spawn | spawn (default export) |
spawn (named export) |
| Sync spawn | spawn.sync |
spawnSync |
| Parse internals | spawn._parse |
_parse |
| ENOENT internals | spawn._enoent |
_enoent |
| Other internals | not exposed | _utils |
| TypeScript types | @types/cross-spawn |
built-in |
Released under the MIT License.