import { describe, expect, it } from "vite-plus/test"; import { commandProgramName } from "./commandLabel.ts"; describe("commandProgramName", () => { it.each([ ["/bin/zsh -lc 'vp test run apps/web/src/session-logic.test.ts'", "vp"], ["/bin/zsh -lc 'git diff --check'", "git"], ['/bin/zsh -lc "npx -y react-doctor@latest apps/web"', "npx"], ["/bin/zsh -lc 'rg -n \"registerHooks|worker\" apps/web/src'", "rg"], ["/bin/bash --noprofile --norc -l -c 'sed -n 1,270p file.ts'", "sed"], ["/bin/bash -o pipefail -lc 'vp test run'", "vp"], ["/bin/bash --rcfile /tmp/config -c 'git status'", "git"], ["sh -ec 'node scripts/check.js'", "node"], ["fish --command 'rg --files'", "rg"], ["zsh -lc 'CI=1 env -u DEBUG sudo -u root vp test run'", "vp"], ["env CI=1 /bin/zsh -lc '\"/Applications/My Tools/bin/check\" --verbose'", "check"], ["bash -lc \"zsh -c 'git status'\"", "git"], ['"C:\\Program Files\\Git\\bin\\bash.exe" -lc "git status"', "git"], ["/bin/zsh -lc 'git status\nsed -n '\"'1,20p' apps/web/src/components/DiffPanel.tsx\"", "git"], ])("unwraps shell scripts without executing them: %s", (command, program) => { expect(commandProgramName(command)).toBe(program); }); it.each([ ["vp test run", "vp"], ["sudo -u root pnpm test", "pnpm"], ["env --split-string='CI=1 node scripts/check.js'", "node"], ['"C:\\Program Files\\nodejs\\node.exe" script.js', "node.exe"], ["/bin/zsh", "zsh"], ["/bin/bash -l", "bash"], ["zsh script.sh -c 'git status'", "zsh"], ["bash -- -c 'git status'", "bash"], ["bash --rcfile config.sh", "bash"], ["my-shell -c 'git status'", "my-shell"], ["$HOME/.bun/bin/bun test", "bun"], ['"$ANDROID_HOME/emulator/emulator" -list-avds', "emulator"], ["${ROOT}/bin/tool --version", "tool"], ])("preserves ordinary programs and actual shell launches: %s", (command, program) => { expect(commandProgramName(command)).toBe(program); }); it.each([ "", "zsh -lc", "zsh -lc ''", "zsh -lc 'git status", "zsh -lc 'env'", 'zsh -lc "git \\\"', ])("falls back for missing or malformed scripts: %s", (command) => { expect(commandProgramName(command)).toBeNull(); }); it.each([ "if test -f package.json; then vp test; fi", "[ -f package.json ]", "[[ -f package.json ]]", "test -f package.json", 'for file in *; do echo "$file"; done', "while true; do sleep 1; done", "until false; do sleep 1; done", "case $name in test) vp test;; esac", 'select item in one two; do echo "$item"; done', "function check() { vp test; }", "check() { vp test; }", "k(){ echo ok; }; k", "{ vp test; }", "(vp test)", "(( count += 1 ))", "! vp test", ":", ". ./script.sh", "source ./script.sh", "eval 'vp test'", "cd packages/client-runtime", "export NODE_ENV=test", "local name=value", "set -e", "alias ll='ls -la'", "repeat 3 echo ok", "and vp test", "return 1", "break", "continue", "true", "false", ])("falls back for shell syntax and internal control commands: %s", (command) => { expect(commandProgramName(command)).toBeNull(); }); it.each([ ['rg -n "if|for|while" src', "rg"], ["printf '%s\\n' 'a;b|c'", "printf"], ["node -e \"if (true) console.log('ok')\"", "node"], ["echo '$(git status)'", "echo"], ["vp test && git status", "vp"], ["vp test || git status", "vp"], ["rg needle src | head", "rg"], ["vp test; git status", "vp"], ["vp test &", "vp"], ["vp test\ngit status", "vp"], ['echo "$(git status)"', "echo"], ["echo `git status`", "echo"], ["cat <(rg needle src)", "cat"], ])("uses the first executable-looking program: %s", (command, program) => { expect(commandProgramName(command)).toBe(program); }); it.each([ ["cd packages/client-runtime && vp test run", "vp"], ['cd "a path with spaces"; git status', "git"], ["cd apps/web\npnpm test", "pnpm"], ["cd first && cd second && bun test", "bun"], ["CI=1 cd apps/web && npm test", "npm"], ["PATH+=:/tools npm test", "npm"], ["PATH+=:/tools && npm test", "npm"], ['TMP=$(mktemp -d); cd "$TMP"; npm pack ./package', "npm"], ["cd $(find . -type d | head -1) && git status", "git"], ["cd `find . -type d | head -1` && node script.js", "node"], ["cd /tmp 2>&1 && npm test", "npm"], ["cd /tmp 2<&0 && pnpm test", "pnpm"], ["cd /tmp &>/dev/null && bun test", "bun"], ["cd work |& npm test", "npm"], ["cd /tmp && # use the selected workspace\nnpm test", "npm"], ["export CI=1; # first note\n# second note\npnpm test", "pnpm"], ["cd&&npm test", "npm"], ["export CI=1;pnpm test", "pnpm"], ["cd ${ROOT:-path;with;semicolons} && bun test", "bun"], ["cd ${ROOT:-path&&fallback} && node app.js", "node"], ["cd @(first|second) && npm test", "npm"], ["cd /tmp \\\n&& npm test", "npm"], ["/bin/zsh -lc 'cd apps/web && vp test run'", "vp"], ])("skips leading cd commands and uses the next useful program: %s", (command, program) => { expect(commandProgramName(command)).toBe(program); }); it.each([ ["source ~/.nvm/nvm.sh && nvm use", "nvm"], [". ./.env && pnpm test", "pnpm"], ["export CI=1 && vp test run", "vp"], ["unset DEBUG; node app.js", "node"], ["export CI=1 && cd apps/web && pnpm test", "pnpm"], ["/bin/zsh -lc 'source ~/.nvm/nvm.sh && nvm use'", "nvm"], ])("skips shell setup commands and uses the next useful program: %s", (command, program) => { expect(commandProgramName(command)).toBe(program); }); it.each([ ["set -eu; npm test", "npm"], [": && npm test", "npm"], ["true && npm test", "npm"], ["false || npm test", "npm"], ["false; npm test", "npm"], ["sudo -n true && npm test", "npm"], ["sudo -n true; echo checked", "echo"], ["test -d node_modules || vp i", "vp"], ["[ -d node_modules ] || vp i", "vp"], ])("skips non-descriptive shell commands before a useful program: %s", (command, program) => { expect(commandProgramName(command)).toBe(program); }); it.each( ["cd /tmp", "export CI=1", "unset DEBUG", "source env.sh", ". env.sh"].flatMap((setup) => ["&&", " || ", ";", "\n", "|", " |& ", " & "].map( (operator) => [`${setup}${operator}npm test`, "npm"] as const, ), ), )("handles shell setup followed by every command separator: %s", (command, program) => { expect(commandProgramName(command)).toBe(program); }); it.each([ ["command git status", "git"], ["command -p git status", "git"], ["command -- git status", "git"], ["builtin printf ok", "printf"], ["builtin -- echo ok", "echo"], ["command cd /tmp && npm test", "npm"], ["builtin cd /tmp && pnpm test", "pnpm"], ["exec node app.js", "node"], ["exec -cl -a worker node app.js", "node"], ["exec env CI=1 /opt/tools/check --verbose", "check"], ['exec "C:\\Program Files\\nodejs\\node.exe" app.js', "node.exe"], ["exec sh -c 'cd /tmp && npm test'", "npm"], ["exec sh -c 'cd /tmp\nnpm test'", "npm"], ["exec bash -c 'set -e\nnpm test'", "npm"], ])("unwraps shell command wrappers: %s", (command, program) => { expect(commandProgramName(command)).toBe(program); }); it.each([ ["timeout 10 pnpm test", "pnpm"], ["timeout 10s python3 script.py", "python3"], ["gtimeout 1.5 node app.js", "node"], ["nohup npx expo start >/tmp/metro.log 2>&1 &", "npx"], ["nohup -- env CI=1 bun test", "bun"], ["arch -x86_64 ./build/app-under-test", "app-under-test"], ["arch -arch arm64 /opt/tools/check", "check"], ["bundle exec pod install", "pod"], ["timeout 30 nohup env CI=1 node app.js", "node"], ["timeout 60 script -q /dev/null env CI=1 node app.js", "node"], ])("unwraps process-launch wrappers: %s", (command, program) => { expect(commandProgramName(command)).toBe(program); }); it.each([ ["& 'C:\\Program Files\\nodejs\\node.exe' script.js", "node.exe"], ['& "$env:WINDIR\\Microsoft.NET\\Framework64\\v4\\csc.exe" file.cs', "csc.exe"], ])("resolves literal PowerShell call operators: %s", (command, program) => { expect(commandProgramName(command)).toBe(program); }); it.each([ ["$env:CI='1'; npm test", "npm"], ["$value = 'configured'; node app.js", "node"], ["$process = Get-Process node; $process.Id", "Get-Process"], ["$tmp = Join-Path $env:TEMP repo; git clone example", "Join-Path"], ["$html = (Invoke-WebRequest https://example.com).Content", "Invoke-WebRequest"], ["$process = Start-Process -FilePath .\\app.exe -PassThru; $process.Id", "app.exe"], ])("labels commands inside simple PowerShell assignments: %s", (command, program) => { expect(commandProgramName(command)).toBe(program); }); it.each([ ["cmd /c bcdedit /enum", "bcdedit"], ["cmd /c cd C:\\work && npm test", "npm"], ['cmd.exe /d /s /c "cd C:\\work && npm test"', "npm"], [ 'powershell.exe -NoProfile -ExecutionPolicy Bypass -File "C:\\work\\scripts\\doctor.ps1"', "doctor.ps1", ], ['pwsh -NoProfile -Command "Set-Location C:\\work; pnpm test"', "pnpm"], ["pwsh -Command Set-Location C:\\work; pnpm test", "pnpm"], ])("unwraps Windows shell launchers: %s", (command, program) => { expect(commandProgramName(command)).toBe(program); }); it.each([ ["Set-Location C:\\work; npm test", "npm"], ["Push-Location C:\\work; node app.js", "node"], ["Start-Process -FilePath node -ArgumentList server.js", "node"], ["Start-Process -ArgumentList '-FilePath helper' node", "node"], ["Start-Process -ErrorAction Stop -FilePath node", "node"], ["Start-Process -NoNewWindow -WorkingDirectory C:\\work node", "node"], ['Start-Process "C:\\Program Files\\Example\\app.exe"', "app.exe"], ['Start-Process -FilePath ".\\dist\\Example App.exe" -PassThru', "Example App.exe"], [".\\.venv\\Scripts\\python.exe script.py", "python.exe"], ["$env:LOCALAPPDATA\\Programs\\tool.exe --version", "tool.exe"], ['"=== CHECK FILE ==="; Get-Content file.txt', "Get-Content"], ["$x = @'\ndata'; Get-Fake\n'@\nGet-Process", "Get-Process"], ["@'\nprint('ok; still data')\n'@ | python -", "python"], ])("handles common PowerShell setup and launch commands: %s", (command, program) => { expect(commandProgramName(command)).toBe(program); }); it.each([ ["timeout --help", "timeout"], ["nohup --version", "nohup"], ["arch", "arch"], ["bundle install", "bundle"], ["script output.log", "script"], ["/usr/bin/timeout 10 node app.js", "timeout"], ])("keeps process-launch wrappers when no safe payload is present: %s", (command, program) => { expect(commandProgramName(command)).toBe(program); }); it.each([ "command -v git", "command -V git", "command -a git", "command -pv git", "builtin -p", "exec", "exec > output.log", "exec --", "exec cd /tmp && npm test", "exec env CI=1 cd /tmp && npm test", "exec CI=1 npm test", "env CI=1 cd /tmp && npm test", "env CI=1; npm test", "sudo cd /tmp && npm test", "command CI=1 npm test", "command false && npm test", "cmd /c false && npm test", "cd /tmp <<EOF\nunterminated heredoc", "cd /tmp && >/tmp/log", "(xcrun simctl io booted recordVideo /tmp/video.mp4 &) ; wait", "export CI=1 && (bundle exec pod install || pod install)", "$PY scripts/check.py", "${TOOL} --version", "%TOOL% --version", "!TOOL! --version", "& $tool --version", "& { Get-Process }", "$value = 'configured'", "$headers = @{ 'Accept' = 'application/json'; 'Content-Type' = 'application/json' }; Invoke-WebRequest https://example.com", '"sha256(value)=$hash"', "broken{", "@echo off", ":: comment", "time -- npm test", "time -v npm test", "coproc npm test", "coproc worker { npm test; }", "cd [first|second] && pnpm test", "npm) --version", "try { Invoke-WebRequest https://example.com } catch { Write-Error $_ }", "for($i=0; $i -lt 2; $i++){ Start-Sleep 1 }", "# comment only", ])("does not treat shell lookup and commandless wrapper forms as executions: %s", (command) => { expect(commandProgramName(command)).toBeNull(); }); it.each([ ["parallel -j4", "parallel"], ["hash --help", "hash"], ["process --help", "process"], ["rem comment", "rem"], ["Exec node app.js", "Exec"], ["CD /tmp && npm test", "CD"], ])("does not hide legitimate or case-distinct program names: %s", (command, program) => { expect(commandProgramName(command)).toBe(program); }); it.each([ ["env sh -c 'cd /tmp && npm test'", "npm"], ["sudo zsh -lc 'export CI=1 && pnpm test'", "pnpm"], ])("parses shell setup inside an explicitly launched shell: %s", (command, program) => { expect(commandProgramName(command)).toBe(program); }); it.each([ ["nocorrect pnpm test", "pnpm"], ["noglob bun test", "bun"], ["time node app.js", "node"], ["time -p deno test", "deno"], ["time nocorrect npm test", "npm"], ])("skips shell precommand modifiers: %s", (command, program) => { expect(commandProgramName(command)).toBe(program); }); it("uses the command after leading shell comments", () => { expect(commandProgramName("# first comment\n # second comment\ngit status")).toBe("git"); }); it.each([ ["CI=1 # note\nnpm test", "npm"], ["CI=1 # it's configured\nnpm test", "npm"], ['CI=1 # "unterminated quote\nbun test', "bun"], [">/tmp/log # note\npnpm test", "pnpm"], [">/tmp/log # it's configured\ndeno test", "deno"], ])("skips comments after commandless shell setup: %s", (command, program) => { expect(commandProgramName(command)).toBe(program); }); it.each([ ["./cd /tmp && npm test", "cd"], ["/opt/exec node app.js", "exec"], ["/usr/bin/time npm test", "time"], ["/usr/bin/test -f package.json", "test"], ])("does not treat qualified paths as shell syntax: %s", (command, program) => { expect(commandProgramName(command)).toBe(program); }); it("skips a shell array assignment before the command", () => { expect(commandProgramName("items=(one two); npm test")).toBe("npm"); }); it.each([ ['EMU="/opt/android/emulator"; "$EMU" -list-avds', "emulator"], ["AAPT=/opt/android/aapt2\n$AAPT dump badging app.apk", "aapt2"], [ 'SSH=(ssh -i /tmp/key -o IdentitiesOnly=yes); HOST=user@example; "${SSH[@]}" "$HOST" uptime', "ssh", ], ["SCP=(/usr/bin/scp -i /tmp/key); if true; then ${SCP[@]} file user@example:/tmp; fi", "scp"], ['TOOL="/Applications/My Tool/bin/check"; "$TOOL" --verbose', "check"], ])("resolves literal command aliases from earlier shell segments: %s", (command, program) => { expect(commandProgramName(command)).toBe(program); }); it.each([ 'TOOL=$(pick-command); "$TOOL" --version', 'TOOL="git status"; "$TOOL"', "# TOOL=git\n$TOOL status", 'TOOL=git true; "$TOOL" status', 'TOOL=git; TOOL=$(pick-command); "$TOOL" status', 'TOOL=git; unset TOOL; "$TOOL" status', ])("does not evaluate dynamic or non-persistent command aliases: %s", (command) => { expect(commandProgramName(command)).toBeNull(); }); it("does not retain aliases assigned inside control flow", () => { expect(commandProgramName('TOOL=git; if false; then\nTOOL=npm\nfi\n"$TOOL" status')).toBe( "git", ); }); it.each([ ["ROOT=${BASE:-path with spaces}; npm test", "npm"], ["ROOT=`printf 'path with spaces'`; pnpm test", "pnpm"], ])("keeps expansions inside assignment words: %s", (command, program) => { expect(commandProgramName(command)).toBe(program); }); it.each([ [">/tmp/log && npm test", "npm"], ["2>/tmp/error.log; pnpm test", "pnpm"], ["cd /tmp && >/tmp/log npm test", "npm"], ["cd /tmp && > /tmp/log pnpm test", "pnpm"], ["cd /tmp && 2>&1 bun test", "bun"], ["cd /tmp && 2>& 1 node app.js", "node"], ["cd /tmp && &>/tmp/log git status", "git"], ["cd /tmp && *>>/tmp/log vp test", "vp"], ["cd /tmp && {output}>/tmp/log deno test", "deno"], ["cd /tmp && <<<input ruby script.rb", "ruby"], ])("skips redirections before the next command: %s", (command, program) => { expect(commandProgramName(command)).toBe(program); }); it.each([ ["cd /tmp <<EOF\nnot-a-command\nEOF\nnpm test", "npm"], ["cd /tmp <<'EOF'\nnot-a-command\nEOF\npnpm test", "pnpm"], ["cd /tmp <<'EOF'\nnot-a-command\\\nEOF\npnpm test", "pnpm"], ["cd /tmp <<-EOF\n\tnot-a-command\n\tEOF\nbun test", "bun"], ["cd /tmp <<A <<B\none\nA\ntwo\nB\ngit status", "git"], ["cd /tmp <<EOF; # setup\nnot-a-command\nEOF\nnpm test", "npm"], ["cd /tmp <<EOF && pnpm test\nnot-a-command\nEOF\nbun test", "pnpm"], ])("skips heredoc bodies before finding the next command: %s", (command, program) => { expect(commandProgramName(command)).toBe(program); }); it.each(["cd apps/web && [ -f package.json ]", "cd apps/web || exit 1", "cd one && cd two"])( "falls back when no useful program follows cd: %s", (command) => { expect(commandProgramName(command)).toBeNull(); }, ); it.each([ "if test -f package.json\nthen\n npm test\nfi", "[[ -d first && -d second ]] && npm test", 'for file in *\ndo\n echo "$file"\ndone', "while true\ndo\n sleep 1\ndone", "cd /tmp; build () { npm test; }", ])("does not label commands inside multiline shell control flow: %s", (command) => { expect(commandProgramName(command)).toBeNull(); }); it("bounds nested shell unwrapping", () => { let command = "git status"; for (let depth = 0; depth < 9; depth += 1) { command = `sh -c '${command.replaceAll("'", "'\\''")}'`; } expect(commandProgramName(command)).toBeNull(); }); it("does not spend the shell nesting budget on setup commands", () => { const command = [ ...Array.from({ length: 10 }, (_, index) => `export VALUE_${index}=configured`), "npm test", ].join("\n"); expect(commandProgramName(command)).toBe("npm"); }); it("bounds the number of top-level setup segments", () => { const command = [ ...Array.from({ length: 2_000 }, (_, index) => `export VALUE_${index}=configured`), "npm test", ].join(";"); expect(commandProgramName(command)).toBeNull(); }); it("bounds nested command wrappers", () => { expect(commandProgramName(`${"command ".repeat(9)}git status`)).toBeNull(); });});
import { describe, expect, it } from "vite-plus/test";
import { commandProgramName } from "./commandLabel.ts";
describe("commandProgramName", () => {
it.each([
["/bin/zsh -lc 'vp test run apps/web/src/session-logic.test.ts'", "vp"],
["/bin/zsh -lc 'git diff --check'", "git"],
['/bin/zsh -lc "npx -y react-doctor@latest apps/web"', "npx"],
["/bin/zsh -lc 'rg -n \"registerHooks|worker\" apps/web/src'", "rg"],
["/bin/bash --noprofile --norc -l -c 'sed -n 1,270p file.ts'", "sed"],
["/bin/bash -o pipefail -lc 'vp test run'", "vp"],
["/bin/bash --rcfile /tmp/config -c 'git status'", "git"],
["sh -ec 'node scripts/check.js'", "node"],
["fish --command 'rg --files'", "rg"],
["zsh -lc 'CI=1 env -u DEBUG sudo -u root vp test run'", "vp"],
["env CI=1 /bin/zsh -lc '\"/Applications/My Tools/bin/check\" --verbose'", "check"],
["bash -lc \"zsh -c 'git status'\"", "git"],
['"C:\\Program Files\\Git\\bin\\bash.exe" -lc "git status"', "git"],
["/bin/zsh -lc 'git status\nsed -n '\"'1,20p' apps/web/src/components/DiffPanel.tsx\"", "git"],
])("unwraps shell scripts without executing them: %s", (command, program) => {
expect(commandProgramName(command)).toBe(program);
});
it.each([
["vp test run", "vp"],
["sudo -u root pnpm test", "pnpm"],
["env --split-string='CI=1 node scripts/check.js'", "node"],
['"C:\\Program Files\\nodejs\\node.exe" script.js', "node.exe"],
["/bin/zsh", "zsh"],
["/bin/bash -l", "bash"],
["zsh script.sh -c 'git status'", "zsh"],
["bash -- -c 'git status'", "bash"],
["bash --rcfile config.sh", "bash"],
["my-shell -c 'git status'", "my-shell"],
["$HOME/.bun/bin/bun test", "bun"],
['"$ANDROID_HOME/emulator/emulator" -list-avds', "emulator"],
["${ROOT}/bin/tool --version", "tool"],
])("preserves ordinary programs and actual shell launches: %s", (command, program) => {
expect(commandProgramName(command)).toBe(program);
});
it.each([
"",
"zsh -lc",
"zsh -lc ''",
"zsh -lc 'git status",
"zsh -lc 'env'",
'zsh -lc "git \\\"',
])("falls back for missing or malformed scripts: %s", (command) => {
expect(commandProgramName(command)).toBeNull();
});
it.each([
"if test -f package.json; then vp test; fi",
"[ -f package.json ]",
"[[ -f package.json ]]",
"test -f package.json",
'for file in *; do echo "$file"; done',
"while true; do sleep 1; done",
"until false; do sleep 1; done",
"case $name in test) vp test;; esac",
'select item in one two; do echo "$item"; done',
"function check() { vp test; }",
"check() { vp test; }",
"k(){ echo ok; }; k",
"{ vp test; }",
"(vp test)",
"(( count += 1 ))",
"! vp test",
":",
". ./script.sh",
"source ./script.sh",
"eval 'vp test'",
"cd packages/client-runtime",
"export NODE_ENV=test",
"local name=value",
"set -e",
"alias ll='ls -la'",
"repeat 3 echo ok",
"and vp test",
"return 1",
"break",
"continue",
"true",
"false",
])("falls back for shell syntax and internal control commands: %s", (command) => {
expect(commandProgramName(command)).toBeNull();
});
it.each([
['rg -n "if|for|while" src', "rg"],
["printf '%s\\n' 'a;b|c'", "printf"],
["node -e \"if (true) console.log('ok')\"", "node"],
["echo '$(git status)'", "echo"],
["vp test && git status", "vp"],
["vp test || git status", "vp"],
["rg needle src | head", "rg"],
["vp test; git status", "vp"],
["vp test &", "vp"],
["vp test\ngit status", "vp"],
['echo "$(git status)"', "echo"],
["echo `git status`", "echo"],
["cat <(rg needle src)", "cat"],
])("uses the first executable-looking program: %s", (command, program) => {
expect(commandProgramName(command)).toBe(program);
});
it.each([
["cd packages/client-runtime && vp test run", "vp"],
['cd "a path with spaces"; git status', "git"],
["cd apps/web\npnpm test", "pnpm"],
["cd first && cd second && bun test", "bun"],
["CI=1 cd apps/web && npm test", "npm"],
["PATH+=:/tools npm test", "npm"],
["PATH+=:/tools && npm test", "npm"],
['TMP=$(mktemp -d); cd "$TMP"; npm pack ./package', "npm"],
["cd $(find . -type d | head -1) && git status", "git"],
["cd `find . -type d | head -1` && node script.js", "node"],
["cd /tmp 2>&1 && npm test", "npm"],
["cd /tmp 2<&0 && pnpm test", "pnpm"],
["cd /tmp &>/dev/null && bun test", "bun"],
["cd work |& npm test", "npm"],
["cd /tmp && # use the selected workspace\nnpm test", "npm"],
["export CI=1; # first note\n# second note\npnpm test", "pnpm"],
["cd&&npm test", "npm"],
["export CI=1;pnpm test", "pnpm"],
["cd ${ROOT:-path;with;semicolons} && bun test", "bun"],
["cd ${ROOT:-path&&fallback} && node app.js", "node"],
["cd @(first|second) && npm test", "npm"],
["cd /tmp \\\n&& npm test", "npm"],
["/bin/zsh -lc 'cd apps/web && vp test run'", "vp"],
])("skips leading cd commands and uses the next useful program: %s", (command, program) => {
expect(commandProgramName(command)).toBe(program);
});
it.each([
["source ~/.nvm/nvm.sh && nvm use", "nvm"],
[". ./.env && pnpm test", "pnpm"],
["export CI=1 && vp test run", "vp"],
["unset DEBUG; node app.js", "node"],
["export CI=1 && cd apps/web && pnpm test", "pnpm"],
["/bin/zsh -lc 'source ~/.nvm/nvm.sh && nvm use'", "nvm"],
])("skips shell setup commands and uses the next useful program: %s", (command, program) => {
expect(commandProgramName(command)).toBe(program);
});
it.each([
["set -eu; npm test", "npm"],
[": && npm test", "npm"],
["true && npm test", "npm"],
["false || npm test", "npm"],
["false; npm test", "npm"],
["sudo -n true && npm test", "npm"],
["sudo -n true; echo checked", "echo"],
["test -d node_modules || vp i", "vp"],
["[ -d node_modules ] || vp i", "vp"],
])("skips non-descriptive shell commands before a useful program: %s", (command, program) => {
expect(commandProgramName(command)).toBe(program);
});
it.each(
["cd /tmp", "export CI=1", "unset DEBUG", "source env.sh", ". env.sh"].flatMap((setup) =>
["&&", " || ", ";", "\n", "|", " |& ", " & "].map(
(operator) => [`${setup}${operator}npm test`, "npm"] as const,
),
),
)("handles shell setup followed by every command separator: %s", (command, program) => {
expect(commandProgramName(command)).toBe(program);
});
it.each([
["command git status", "git"],
["command -p git status", "git"],
["command -- git status", "git"],
["builtin printf ok", "printf"],
["builtin -- echo ok", "echo"],
["command cd /tmp && npm test", "npm"],
["builtin cd /tmp && pnpm test", "pnpm"],
["exec node app.js", "node"],
["exec -cl -a worker node app.js", "node"],
["exec env CI=1 /opt/tools/check --verbose", "check"],
['exec "C:\\Program Files\\nodejs\\node.exe" app.js', "node.exe"],
["exec sh -c 'cd /tmp && npm test'", "npm"],
["exec sh -c 'cd /tmp\nnpm test'", "npm"],
["exec bash -c 'set -e\nnpm test'", "npm"],
])("unwraps shell command wrappers: %s", (command, program) => {
expect(commandProgramName(command)).toBe(program);
});
it.each([
["timeout 10 pnpm test", "pnpm"],
["timeout 10s python3 script.py", "python3"],
["gtimeout 1.5 node app.js", "node"],
["nohup npx expo start >/tmp/metro.log 2>&1 &", "npx"],
["nohup -- env CI=1 bun test", "bun"],
["arch -x86_64 ./build/app-under-test", "app-under-test"],
["arch -arch arm64 /opt/tools/check", "check"],
["bundle exec pod install", "pod"],
["timeout 30 nohup env CI=1 node app.js", "node"],
["timeout 60 script -q /dev/null env CI=1 node app.js", "node"],
])("unwraps process-launch wrappers: %s", (command, program) => {
expect(commandProgramName(command)).toBe(program);
});
it.each([
["& 'C:\\Program Files\\nodejs\\node.exe' script.js", "node.exe"],
['& "$env:WINDIR\\Microsoft.NET\\Framework64\\v4\\csc.exe" file.cs', "csc.exe"],
])("resolves literal PowerShell call operators: %s", (command, program) => {
expect(commandProgramName(command)).toBe(program);
});
it.each([
["$env:CI='1'; npm test", "npm"],
["$value = 'configured'; node app.js", "node"],
["$process = Get-Process node; $process.Id", "Get-Process"],
["$tmp = Join-Path $env:TEMP repo; git clone example", "Join-Path"],
["$html = (Invoke-WebRequest https://example.com).Content", "Invoke-WebRequest"],
["$process = Start-Process -FilePath .\\app.exe -PassThru; $process.Id", "app.exe"],
])("labels commands inside simple PowerShell assignments: %s", (command, program) => {
expect(commandProgramName(command)).toBe(program);
});
it.each([
["cmd /c bcdedit /enum", "bcdedit"],
["cmd /c cd C:\\work && npm test", "npm"],
['cmd.exe /d /s /c "cd C:\\work && npm test"', "npm"],
[
'powershell.exe -NoProfile -ExecutionPolicy Bypass -File "C:\\work\\scripts\\doctor.ps1"',
"doctor.ps1",
],
['pwsh -NoProfile -Command "Set-Location C:\\work; pnpm test"', "pnpm"],
["pwsh -Command Set-Location C:\\work; pnpm test", "pnpm"],
])("unwraps Windows shell launchers: %s", (command, program) => {
expect(commandProgramName(command)).toBe(program);
});
it.each([
["Set-Location C:\\work; npm test", "npm"],
["Push-Location C:\\work; node app.js", "node"],
["Start-Process -FilePath node -ArgumentList server.js", "node"],
["Start-Process -ArgumentList '-FilePath helper' node", "node"],
["Start-Process -ErrorAction Stop -FilePath node", "node"],
["Start-Process -NoNewWindow -WorkingDirectory C:\\work node", "node"],
['Start-Process "C:\\Program Files\\Example\\app.exe"', "app.exe"],
['Start-Process -FilePath ".\\dist\\Example App.exe" -PassThru', "Example App.exe"],
[".\\.venv\\Scripts\\python.exe script.py", "python.exe"],
["$env:LOCALAPPDATA\\Programs\\tool.exe --version", "tool.exe"],
['"=== CHECK FILE ==="; Get-Content file.txt', "Get-Content"],
["$x = @'\ndata'; Get-Fake\n'@\nGet-Process", "Get-Process"],
["@'\nprint('ok; still data')\n'@ | python -", "python"],
])("handles common PowerShell setup and launch commands: %s", (command, program) => {
expect(commandProgramName(command)).toBe(program);
});
it.each([
["timeout --help", "timeout"],
["nohup --version", "nohup"],
["arch", "arch"],
["bundle install", "bundle"],
["script output.log", "script"],
["/usr/bin/timeout 10 node app.js", "timeout"],
])("keeps process-launch wrappers when no safe payload is present: %s", (command, program) => {
expect(commandProgramName(command)).toBe(program);
});
it.each([
"command -v git",
"command -V git",
"command -a git",
"command -pv git",
"builtin -p",
"exec",
"exec > output.log",
"exec --",
"exec cd /tmp && npm test",
"exec env CI=1 cd /tmp && npm test",
"exec CI=1 npm test",
"env CI=1 cd /tmp && npm test",
"env CI=1; npm test",
"sudo cd /tmp && npm test",
"command CI=1 npm test",
"command false && npm test",
"cmd /c false && npm test",
"cd /tmp <<EOF\nunterminated heredoc",
"cd /tmp && >/tmp/log",
"(xcrun simctl io booted recordVideo /tmp/video.mp4 &) ; wait",
"export CI=1 && (bundle exec pod install || pod install)",
"$PY scripts/check.py",
"${TOOL} --version",
"%TOOL% --version",
"!TOOL! --version",
"& $tool --version",
"& { Get-Process }",
"$value = 'configured'",
"$headers = @{ 'Accept' = 'application/json'; 'Content-Type' = 'application/json' }; Invoke-WebRequest https://example.com",
'"sha256(value)=$hash"',
"broken{",
"@echo off",
":: comment",
"time -- npm test",
"time -v npm test",
"coproc npm test",
"coproc worker { npm test; }",
"cd [first|second] && pnpm test",
"npm) --version",
"try { Invoke-WebRequest https://example.com } catch { Write-Error $_ }",
"for($i=0; $i -lt 2; $i++){ Start-Sleep 1 }",
"# comment only",
])("does not treat shell lookup and commandless wrapper forms as executions: %s", (command) => {
expect(commandProgramName(command)).toBeNull();
});
it.each([
["parallel -j4", "parallel"],
["hash --help", "hash"],
["process --help", "process"],
["rem comment", "rem"],
["Exec node app.js", "Exec"],
["CD /tmp && npm test", "CD"],
])("does not hide legitimate or case-distinct program names: %s", (command, program) => {
expect(commandProgramName(command)).toBe(program);
});
it.each([
["env sh -c 'cd /tmp && npm test'", "npm"],
["sudo zsh -lc 'export CI=1 && pnpm test'", "pnpm"],
])("parses shell setup inside an explicitly launched shell: %s", (command, program) => {
expect(commandProgramName(command)).toBe(program);
});
it.each([
["nocorrect pnpm test", "pnpm"],
["noglob bun test", "bun"],
["time node app.js", "node"],
["time -p deno test", "deno"],
["time nocorrect npm test", "npm"],
])("skips shell precommand modifiers: %s", (command, program) => {
expect(commandProgramName(command)).toBe(program);
});
it("uses the command after leading shell comments", () => {
expect(commandProgramName("# first comment\n # second comment\ngit status")).toBe("git");
});
it.each([
["CI=1 # note\nnpm test", "npm"],
["CI=1 # it's configured\nnpm test", "npm"],
['CI=1 # "unterminated quote\nbun test', "bun"],
[">/tmp/log # note\npnpm test", "pnpm"],
[">/tmp/log # it's configured\ndeno test", "deno"],
])("skips comments after commandless shell setup: %s", (command, program) => {
expect(commandProgramName(command)).toBe(program);
});
it.each([
["./cd /tmp && npm test", "cd"],
["/opt/exec node app.js", "exec"],
["/usr/bin/time npm test", "time"],
["/usr/bin/test -f package.json", "test"],
])("does not treat qualified paths as shell syntax: %s", (command, program) => {
expect(commandProgramName(command)).toBe(program);
});
it("skips a shell array assignment before the command", () => {
expect(commandProgramName("items=(one two); npm test")).toBe("npm");
});
it.each([
['EMU="/opt/android/emulator"; "$EMU" -list-avds', "emulator"],
["AAPT=/opt/android/aapt2\n$AAPT dump badging app.apk", "aapt2"],
[
'SSH=(ssh -i /tmp/key -o IdentitiesOnly=yes); HOST=user@example; "${SSH[@]}" "$HOST" uptime',
"ssh",
],
["SCP=(/usr/bin/scp -i /tmp/key); if true; then ${SCP[@]} file user@example:/tmp; fi", "scp"],
['TOOL="/Applications/My Tool/bin/check"; "$TOOL" --verbose', "check"],
])("resolves literal command aliases from earlier shell segments: %s", (command, program) => {
expect(commandProgramName(command)).toBe(program);
});
it.each([
'TOOL=$(pick-command); "$TOOL" --version',
'TOOL="git status"; "$TOOL"',
"# TOOL=git\n$TOOL status",
'TOOL=git true; "$TOOL" status',
'TOOL=git; TOOL=$(pick-command); "$TOOL" status',
'TOOL=git; unset TOOL; "$TOOL" status',
])("does not evaluate dynamic or non-persistent command aliases: %s", (command) => {
expect(commandProgramName(command)).toBeNull();
});
it("does not retain aliases assigned inside control flow", () => {
expect(commandProgramName('TOOL=git; if false; then\nTOOL=npm\nfi\n"$TOOL" status')).toBe(
"git",
);
});
it.each([
["ROOT=${BASE:-path with spaces}; npm test", "npm"],
["ROOT=`printf 'path with spaces'`; pnpm test", "pnpm"],
])("keeps expansions inside assignment words: %s", (command, program) => {
expect(commandProgramName(command)).toBe(program);
});
it.each([
[">/tmp/log && npm test", "npm"],
["2>/tmp/error.log; pnpm test", "pnpm"],
["cd /tmp && >/tmp/log npm test", "npm"],
["cd /tmp && > /tmp/log pnpm test", "pnpm"],
["cd /tmp && 2>&1 bun test", "bun"],
["cd /tmp && 2>& 1 node app.js", "node"],
["cd /tmp && &>/tmp/log git status", "git"],
["cd /tmp && *>>/tmp/log vp test", "vp"],
["cd /tmp && {output}>/tmp/log deno test", "deno"],
["cd /tmp && <<<input ruby script.rb", "ruby"],
])("skips redirections before the next command: %s", (command, program) => {
expect(commandProgramName(command)).toBe(program);
});
it.each([
["cd /tmp <<EOF\nnot-a-command\nEOF\nnpm test", "npm"],
["cd /tmp <<'EOF'\nnot-a-command\nEOF\npnpm test", "pnpm"],
["cd /tmp <<'EOF'\nnot-a-command\\\nEOF\npnpm test", "pnpm"],
["cd /tmp <<-EOF\n\tnot-a-command\n\tEOF\nbun test", "bun"],
["cd /tmp <<A <<B\none\nA\ntwo\nB\ngit status", "git"],
["cd /tmp <<EOF; # setup\nnot-a-command\nEOF\nnpm test", "npm"],
["cd /tmp <<EOF && pnpm test\nnot-a-command\nEOF\nbun test", "pnpm"],
])("skips heredoc bodies before finding the next command: %s", (command, program) => {
expect(commandProgramName(command)).toBe(program);
});
it.each(["cd apps/web && [ -f package.json ]", "cd apps/web || exit 1", "cd one && cd two"])(
"falls back when no useful program follows cd: %s",
(command) => {
expect(commandProgramName(command)).toBeNull();
},
);
it.each([
"if test -f package.json\nthen\n npm test\nfi",
"[[ -d first && -d second ]] && npm test",
'for file in *\ndo\n echo "$file"\ndone',
"while true\ndo\n sleep 1\ndone",
"cd /tmp; build () { npm test; }",
])("does not label commands inside multiline shell control flow: %s", (command) => {
expect(commandProgramName(command)).toBeNull();
});
it("bounds nested shell unwrapping", () => {
let command = "git status";
for (let depth = 0; depth < 9; depth += 1) {
command = `sh -c '${command.replaceAll("'", "'\\''")}'`;
}
expect(commandProgramName(command)).toBeNull();
});
it("does not spend the shell nesting budget on setup commands", () => {
const command = [
...Array.from({ length: 10 }, (_, index) => `export VALUE_${index}=configured`),
"npm test",
].join("\n");
expect(commandProgramName(command)).toBe("npm");
});
it("bounds the number of top-level setup segments", () => {
const command = [
...Array.from({ length: 2_000 }, (_, index) => `export VALUE_${index}=configured`),
"npm test",
].join(";");
expect(commandProgramName(command)).toBeNull();
});
it("bounds nested command wrappers", () => {
expect(commandProgramName(`${"command ".repeat(9)}git status`)).toBeNull();
});
});