Shell parser/AST for POSIX, Bash, mksh, and zsh in TypeScript.
TypeScript shell parser inspired by mvdan/sh. Parses POSIX/Bash shell commands into a typed AST.
Zero dependencies. Single exported function. ~28 KB bundled.
import { parse } from "@aliou/sh";
const { ast } = parse('echo "hello $USER" | grep hello');
// ast.type === "Program"
// ast.body[0].command.type === "Pipeline"
The parser returns a Program node containing Statement nodes. Each statement wraps a Command, which is one of:
SimpleCommand — words, assignments, redirectsPipeline, Logical (&&, ||)IfClause, WhileClause, ForClause, SelectClause, CaseClauseFunctionDecl, Subshell, BlockTestClause ([[ ]]), ArithCmd ((( ))), CoprocClause, TimeClauseDeclClause (declare, local, export, readonly, typeset, nameref)LetClause (let), CStyleLoop (for (( ; ; )))Words contain typed parts: Literal, SglQuoted, DblQuoted, ParamExp, CmdSubst, ArithExp, ProcSubst.
import { parse, type SimpleCommand } from "@aliou/sh";
function extractCommandNames(node: unknown): string[] {
if (!node || typeof node !== "object") return [];
const n = node as Record<string, unknown>;
const names: string[] = [];
if (n.type === "SimpleCommand") {
const cmd = n as unknown as SimpleCommand;
if (cmd.words?.length) {
const first = cmd.words[0];
if (first.parts.length === 1 && first.parts[0].type === "Literal") {
names.push(first.parts[0].value);
}
}
}
for (const val of Object.values(n)) {
if (Array.isArray(val)) {
for (const item of val) names.push(...extractCommandNames(item));
} else if (val && typeof val === "object") {
names.push(...extractCommandNames(val));
}
}
return names;
}
const { ast } = parse("grep -rn npm package.json | head -5");
extractCommandNames(ast); // ["grep", "head"]
&&, ||)$var, ${var:-default})$(cmd), `cmd`), arithmetic expansion ($((expr)))<(cmd), >(cmd))<<, <<-), herestrings (<<<)>, >>, <, >&, <&, <>, >|, &>, &>>)FOO=bar cmd), append assignments (FOO+=bar)arr=(a b c), arr=([0]=x [1]=y))declare, local, export, readonly, typeset, nameref)let expressions (let i++ j=2)if/elif/else/fi, while/until, for/in, for ((...)), select/in, case/esacfoo() {}, function foo {})(), blocks {}[[ ]] test expressions, (( )) arithmetic commandscoproc, time, negation (!)keepComments option), backslash line continuations, background (&), semicolonspnpm add github:aliou/sh
Requires Nix (provides Node 22 and pnpm):
nix develop
pnpm install # install deps
pnpm test # run tests (vitest)
pnpm typecheck # tsc --noEmit
pnpm check # biome format + lint
pnpm build # rolldown + tsc declarations
Git hooks (via husky):
Work in progress. Covers the Bash subset needed for AST-based command analysis (command classification, variable mutation tracking, guardrail enforcement). Not yet a complete POSIX/Bash parser — notably missing: position tracking in AST nodes, extended globbing, and full arithmetic expression parsing.
UNLICENSED