bun/no-unportable-commands
Disallow starting a process with a command that does not work the same on every system
A command that works on macOS and Linux can fail on Windows:
npmis a script there (npm.cmd), and you can't start a script without a shell.ls,rmandgrepdon't exist.&&and|are shell syntax, and the shell isn't the same on every system.
The rule checks exec, spawn and execFile from node:child_process, their Sync versions, Bun.spawn and Bun.spawnSync, when the command is a string in the call. It also reports any string that contains ps with options, such as "ps -ef", because ps takes different options on each system.
Configure#
Off by default. To turn it on:
[lint.rules]
"bun/no-unportable-commands" = "error" # or "warn"Every option, set to its default. The ones that are commented out are unset by default.
[lint.rules]
"bun/no-unportable-commands" = ["error", {
scripts = ["npm", "npx", "pnpm", "pnpx", "yarn"],
# posixOnly = <list of strings>,
launchers = [], # list of strings
}]Examples#
import { exec, spawn } from "node:child_process";
spawn("npm"On Windows, npm is the script npm.cmd, which cannot run without a shell., ["install"]);
spawn("rm"rm does not exist on Windows., ["-rf", "dist"]);
exec("bun run build && bun run deploy"&& is shell syntax, and the shell differs between systems.);Bun.spawn(["cat"cat does not exist on Windows., "notes.txt"]);const listProcesses = "ps -ef"ps options differ between systems, and ps does not exist on Windows.;import { spawn } from "node:child_process";
import { rm } from "node:fs/promises";
spawn("npm", ["install"], { shell: true });
await rm("dist", { recursive: true, force: true });Bun Shell has its own rm, ls, cat, && and |, which work the same on every system:
import { $ } from "bun";
await $`rm -rf dist`;
await $`bun run build && bun run deploy`;Options#
| Option | Default | What it does |
|---|---|---|
scripts | ["npm", "npx", "pnpm", "pnpx", "yarn"] | Commands that are .cmd scripts on Windows. Reported when they're started without a shell. |
posixOnly | awk, cat, chmod, cp, grep, kill, ls, mv, rm, sed, sh, touch and 21 more | Commands that Windows doesn't have. Reported with or without a shell. |
launchers | [] | Names of your own functions that start a process. Their first argument is checked like the first argument of spawn. |
Setting a list replaces the default, so repeat the defaults you want to keep.
{ "scripts": ["npm", "tsc"] }1 problemimport { spawnSync } from "node:child_process";
spawnSync("tsc"On Windows, tsc is the script tsc.cmd, which cannot run without a shell., ["--noEmit"]);{ "launchers": ["run"] }1 problemawait run("grep"grep does not exist on Windows., ["-r", "TODO", "src"]);When not to use it#
Leave it off if your code never runs on Windows.