shell-quote
Parses and quotes POSIX shell command strings, guarding against shell injection when building commands programmatically.
Repository Health
Technical Analysis
shell-quote is a zero-dependency Node.js utility with two complementary functions: quote(), which takes an array of arguments and escapes them into a single POSIX-shell-safe string, and parse(), which takes a shell command string and tokenizes it back into an array of arguments, handling single/double quotes, backslash escapes, environment variable interpolation, glob patterns, comments, and control operators like ||, &&, and |. It implements just enough of POSIX/bash quoting semantics to safely round-trip user- or config-supplied command strings without shelling out to an actual shell parser.
The library is widely used as a defensive building block anywhere a Node.js program needs to construct or interpret shell command lines from untrusted or dynamic input — task runners, build tools, CLI wrappers, and process-spawning utilities pull in shell-quote specifically to avoid hand-rolling shell-escaping logic, a notorious source of injection bugs. Because both quote() and parse() are pure functions with full TypeScript definitions and no runtime dependencies, it drops into any codebase with no extra installation weight or supply-chain surface.
What You Get
- quote(args) - Escapes an array of strings (or operator/glob/comment objects) into a single POSIX-shell-safe command string.
- parse(cmd, env, opts) - Tokenizes a shell command string into arguments, expanding
$VAR/${VAR}references against anenvobject or lookup function. - Control operator awareness - Recognizes shell operators like
||,&&,;,|,<,>, and<(as distinct{ op }tokens rather than plain strings. - Full TypeScript types - Ships
index.d.ts/parse.d.ts/quote.d.tswith no external@typespackage needed. - Zero runtime dependencies - No transitive packages, keeping install size and supply-chain surface minimal.
Common Use Cases
- Safely building shell commands from user input - Escaping filenames, flags, or config values before interpolating them into a command string.
- Parsing user- or config-supplied command strings - Splitting a single command-line string (e.g. from a config file) into an argv-style array for
spawn/execFile. - Implementing custom shell-like DSLs - Task runners and build tools use
parseto interpret pipeline/redirection syntax (|,>,&&) without embedding a real shell. - Environment variable interpolation - Expanding
$VAR/${VAR}references in a command string against a supplied environment object during parsing.
Under The Hood
Architecture
The package has a trivial two-module architecture: index.js re-exports quote.js and parse.js, each a single pure function with no internal object graph, class hierarchy, or shared mutable state. quote.js maps over an input array applying regex-driven escaping rules per token, while parse.js runs a hand-written scanner (a chunker regex plus a character-by-character state machine tracking quote/escape context) that emits a flat token array, with a separate reduce step to re-inject objects returned by a function-typed env callback via a randomized token-delimiter trick. There’s no dependency injection and no data flow beyond input string/array to output array/string — about as flat and low-blast-radius as a library gets.
Tech Stack
Plain CommonJS JavaScript targeting Node >=0.4, with zero runtime dependencies; devDependencies are entirely tooling — tape+nyc for tests, eslint for linting, tsc/@arethetypeswrong/cli to validate the checked-in .d.ts files against consumers, auto-changelog for changelog generation, and publish-safety guardrails (safe-publish-latest, npmignore, in-publish) typical of this maintainer’s package ecosystem. No build step is needed since hand-written JS ships directly alongside hand-written type declarations.
Code Quality
Tests live under test/ as one file per concern (quote, parse, env, env with a function, operators, comments, array-splitting), run with tape through nyc for coverage, with linting wired to run before tests so failures block the suite. Error handling in quote.js/parse.js is explicit and typed via thrown errors with descriptive messages for invalid token shapes and malformed ${...} substitutions rather than silent failure. JSDoc annotations back the hand-written type declarations, and CI runs lint, type-check, and test across a wide matrix of Node versions on every push.
API Design
The public surface is deliberately tiny — two functions, quote(args) and parse(cmd, env, opts) — requiring zero setup to get started, and the README explicitly documents footgun-avoidance (e.g. never re-quoting the output, steering Windows users toward execFile/spawn instead of shell strings). Type definitions are hand-authored and richly documented, including an unusual tuple-join type augmentation for literal string arrays, showing above-average attention to consumer type ergonomics; the main friction point is that parse’s object-token shapes require callers to branch on shape themselves since no discriminated helper is exported.
Used by 12 apps in this directory
Scalar
Developer Tools
Beautiful, interactive OpenAPI documentation with a built-in offline-first API client and multi-language code generation — all in one open-source platform.
superset
AI Code Assistants · AI Development
Orchestrate an army of AI coding agents—Claude Code, Codex, Gemini CLI, and more—running simultaneously in isolated git worktrees from a single Electron desktop app.
Teable
Databases · No Code Platforms
A no-code PostgreSQL database with spreadsheet UX, real-time collaboration, and native AI agents — built for teams that outgrow Airtable.