A cross platform solution to node's spawn and spawnSync (ESM fork of cross-spawn)
TypeScript
1
49 commits
updated Sep 18, 2026
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-esm
With Yarn:
yarn add cross-spawn-esm
With PNPM:
pnpm add cross-spawn-esm
With Bun:
bun add cross-spawn-esm
With Deno:
deno add cross-spawn-esm
Use 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" });
@types/cross-spawn)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-esm
Before:
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 explicit syscall argument ("spawn" or "spawnSync"), where the original
shipped two separate functions (verifyENOENT / verifyENOENTSync)._enoent.notFoundError(...) now returns a valid NodeJS.ErrnoException._parse now contains both parse and parseNonShell functions:Before: const parsed = _parse(...)
After: const parsed = _parse.parse(...)
_utils object 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.args is an independent snapshot: parse clones the args and gives original.args its own copy, so shebang rewiring and
cmd.exe escaping never mutate it.#!/usr/bin/env <program> is resolved from PATH and 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 the cmd.exe wrapper, which works when that
interpreter is on PATH.The following behaviors are intentionally kept identical to cross-spawn:
options.shell is used, parsing, escaping, and shebang enhancements are disabled - matching both the original and Node.js behavior.1 and the command could not be resolved, an error event (async) or
result.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.
46 commits
3 commits
TypeScript
100.0%
A cross platform solution to node's spawn and spawnSync (ESM fork of cross-spawn)
TypeScript
1
49 commits
updated Sep 18, 2026
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-esm
With Yarn:
yarn add cross-spawn-esm
With PNPM:
pnpm add cross-spawn-esm
With Bun:
bun add cross-spawn-esm
With Deno:
deno add cross-spawn-esm
Use 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" });
@types/cross-spawn)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-esm
Before:
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 explicit syscall argument ("spawn" or "spawnSync"), where the original
shipped two separate functions (verifyENOENT / verifyENOENTSync)._enoent.notFoundError(...) now returns a valid NodeJS.ErrnoException._parse now contains both parse and parseNonShell functions:Before: const parsed = _parse(...)
After: const parsed = _parse.parse(...)
_utils object 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.args is an independent snapshot: parse clones the args and gives original.args its own copy, so shebang rewiring and
cmd.exe escaping never mutate it.#!/usr/bin/env <program> is resolved from PATH and 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 the cmd.exe wrapper, which works when that
interpreter is on PATH.The following behaviors are intentionally kept identical to cross-spawn:
options.shell is used, parsing, escaping, and shebang enhancements are disabled - matching both the original and Node.js behavior.1 and the command could not be resolved, an error event (async) or
result.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.
46 commits
3 commits
TypeScript
100.0%