unbash: Fast Bash Parser

2.2.0 · active · verified Sun Apr 19

unbash is a fast, zero-dependency library written in TypeScript for parsing Bash scripts into a structured Abstract Syntax Tree (AST). Currently stable at version 2.2.0, it differentiates itself by offering a pure JavaScript/TypeScript implementation without WASM or native bindings, providing a fully typed API, and delivering high performance, often outperforming alternatives by a significant margin. Its release cadence follows a typical Semantic Versioning approach, with updates for features and bug fixes. Key differentiators include its AST-centric output (unlike CST-focused parsers), tolerant parsing that collects errors rather than throwing exceptions, and built-in support for advanced Bash syntax such as process substitutions, coproc, `[[ ]]` test expressions, `(( ))` arithmetic evaluations, and extglob. It is designed for environments requiring a lightweight, synchronous parsing solution. While it excels at AST generation and speed, it does not offer incremental parsing, full token preservation for CSTs, or multi-shell dialect support (e.g., pure POSIX sh), features found in libraries like tree-sitter-bash or sh-syntax. It requires Node.js v14 or higher and maintains a small bundle size (13KB gzipped).

Common errors

Warnings

Install

Imports

Quickstart

Parses a sample Bash script into an AST, checks for collected parsing errors, and then prints the AST back into a formatted Bash script string, demonstrating core functionality and tolerant error handling.

import { parse, type Script } from "unbash";
import { print } from "unbash/print";

// Define a Bash script string to parse.
const bashScript = 'if [ -f "$1" ]; then\n  echo "File exists: $1"\n  cat "$1"\nelse\n  echo "File not found: $1"\nfi';

// Parse the script into an AST (Abstract Syntax Tree).
// unbash performs tolerant parsing, meaning it attempts to create an AST even with syntax errors,
// collecting any issues in the 'errors' property of the returned object.
const ast: Script = parse(bashScript);

// Check if any parsing errors occurred.
if (ast.errors && ast.errors.length > 0) {
  console.warn("Parsing errors detected:", ast.errors);
  // Depending on your application, you might want to stop here or attempt to recover.
} else {
  console.log("Successfully parsed script into AST (truncated):\n", JSON.stringify(ast, null, 2).substring(0, 500) + "...");
}

// Use the built-in printer to convert the AST back into a Bash script string.
// Note: The `print` function is opinionated and will reformat the script,
// not necessarily preserving original whitespace, comments, or exact layout.
const printedScript = print(ast);
console.log("\nRe-printed script from AST:\n", printedScript);

// Demonstrate parsing a script with an obvious error to show tolerant parsing
const erroneousScript = 'for i in 1 2 3; do echo $i fi;'; // Missing 'done'
const erroneousAst = parse(erroneousScript);
if (erroneousAst.errors && erroneousAst.errors.length > 0) {
  console.error("\nDetected errors in erroneous script example:", erroneousAst.errors);
}

view raw JSON →