A tool for writing better scripts by bridging JavaScript and shell commands with cross-platform wrappers around child_process
General-purpose utilities for common scripting tasks including retry logic, error handling, string processing, and vendor library access.
Implement retry logic with various backoff strategies for handling transient failures.
/**
* Retry a function with exponential backoff
* @param count - Number of retry attempts
* @param callback - Function to retry
* @returns Promise resolving to callback result
*/
function retry<T>(count: number, callback: () => T): Promise<T>;
/**
* Retry a function with custom backoff strategy
* @param count - Number of retry attempts
* @param duration - Fixed delay or generator for dynamic delays
* @param callback - Function to retry
* @returns Promise resolving to callback result
*/
function retry<T>(
count: number,
duration: Duration | Generator<number>,
callback: () => T
): Promise<T>;
/**
* Generate exponential backoff delays
* @param max - Maximum delay (default: '60s')
* @param delay - Initial delay (default: '100ms')
* @returns Generator yielding delay values in milliseconds
*/
function expBackoff(
max?: Duration,
delay?: Duration
): Generator<number, void, unknown>;Usage Examples:
import { retry, expBackoff, $, echo, fetch } from "zx";
// Simple retry
const result = await retry(3, async () => {
const response = await fetch('https://unreliable-api.example.com/data');
if (!response.ok) throw new Error(`HTTP ${response.status}`);
return response.json();
});
// Retry with fixed delay
const fileContent = await retry(5, '2s', async () => {
return await $`cat /proc/meminfo`;
});
// Retry with exponential backoff
const backoffGen = expBackoff('30s', '500ms');
const stableResult = await retry(10, backoffGen, async () => {
const healthCheck = await $`curl -f https://service.example.com/health`;
return healthCheck.stdout;
});
// Custom retry with logging
const deployment = await retry(3, '5s', async () => {
try {
echo('Attempting deployment...');
await $`kubectl apply -f deployment.yaml`;
await $`kubectl rollout status deployment/myapp`;
echo('Deployment successful');
return 'success';
} catch (error) {
echo`Deployment failed: ${error.message}`;
throw error;
}
});
// Complex backoff strategy
function* customBackoff() {
const delays = [1000, 2000, 5000, 10000, 30000]; // Custom delays
for (const delay of delays) {
yield delay;
}
while (true) {
yield 30000; // Max delay for remaining attempts
}
}
const criticalOperation = await retry(20, customBackoff(), async () => {
return await $`important-operation`;
});Utilities for processing and formatting strings in shell contexts.
/**
* Quote shell arguments to prevent injection
* @param arg - String to quote for shell safety
* @returns Properly quoted string
*/
function quote(arg: string): string;
/**
* Quote arguments for PowerShell
* @param arg - String to quote for PowerShell safety
* @returns Properly quoted string for PowerShell
*/
function quotePowerShell(arg: string): string;Usage Examples:
import { quote, quotePowerShell, $, echo } from "zx";
// Safe shell argument quoting
const userInput = "file with spaces & special chars";
const quoted = quote(userInput);
echo`Quoted: ${quoted}`;
// Use in commands
const filename = "my file.txt";
await $`cat ${quote(filename)}`; // Safer than direct interpolation
// PowerShell quoting
const psArg = "C:\\Program Files\\My App\\data.txt";
const psQuoted = quotePowerShell(psArg);
echo`PowerShell quoted: ${psQuoted}`;
// Platform-specific quoting
const path = "/path/with spaces/file.txt";
const safePath = process.platform === 'win32'
? quotePowerShell(path)
: quote(path);
await $`ls ${safePath}`;Error classes and utilities for better error reporting.
/**
* Enhanced error class for command failures
*/
class Fail extends Error {
constructor(message?: string);
/** Format exit error message */
static formatExitMessage(code: number, signal: string, stderr: string): string;
/** Format general error message */
static formatErrorMessage(error: Error): string;
/** Format detailed error information */
static formatErrorDetails(error: Error): string;
/** Get exit code information */
static getExitCodeInfo(code: number): string;
}Usage Examples:
import { Fail, $, echo } from "zx";
// Custom error handling
function validateInput(input) {
if (!input || input.trim().length === 0) {
throw new Fail('Input cannot be empty');
}
return input.trim();
}
try {
const userInput = validateInput('');
} catch (error) {
if (error instanceof Fail) {
echo`Validation error: ${error.message}`;
}
}
// Process error formatting
try {
await $`exit 1`;
} catch (error) {
echo('Exit message:', Fail.formatExitMessage(1, null, 'Command failed'));
echo('Error details:', Fail.formatErrorDetails(error));
}Structured logging with different output modes and filtering.
/**
* Logging function with structured output
* @param entry - Log entry object or message
*/
function log(entry: LogEntry | any): void;
interface LogEntry {
/** Log entry type/category */
kind: string;
/** Whether to show verbose output */
verbose?: boolean;
/** Error message */
error?: string;
/** Additional metadata */
[key: string]: any;
}Usage Examples:
import { log, $ } from "zx";
// Structured logging
log({
kind: 'deployment',
status: 'started',
environment: 'production',
verbose: true
});
// Error logging
log({
kind: 'error',
message: 'Database connection failed',
error: 'Connection timeout',
retries: 3
});
// Simple message logging
log('Starting backup process...');
// Conditional verbose logging
log({
kind: 'debug',
message: 'Processing file batch',
count: 150,
verbose: $.verbose
});Access to commonly used libraries bundled with zx.
/**
* Command line argument parser
*/
declare const minimist: typeof import('minimist') & {
(args: string[], opts?: minimist.Opts): minimist.ParsedArgs;
};
/**
* Environment variable loader with dotenv config functionality
*/
declare const dotenv: {
config(options?: { path?: string }): { parsed?: Record<string, string> };
};
/**
* Enhanced file system operations (fs-extra)
*/
declare const fs: typeof import('fs-extra');
/**
* YAML parser and serializer
*/
declare const YAML: {
parse(text: string): any;
stringify(object: any): string;
};
/**
* File globbing and pattern matching (globby)
*/
declare const glob: {
(patterns: string | readonly string[], options?: GlobbyOptions): Promise<string[]>;
sync: (patterns: string | readonly string[], options?: GlobbyOptions) => string[];
globby: typeof glob;
convertPathToPattern: (path: string) => string;
isDynamicPattern: (pattern: string) => boolean;
};
/**
* Terminal string styling (chalk)
*/
declare const chalk: ChalkInstance;
/**
* Find executable in PATH
*/
declare const which: {
(cmd: string): Promise<string>;
sync: (cmd: string) => string;
};
/**
* Process information utilities
*/
declare const ps: {
findAll(): Promise<Array<{ pid: number; name: string; cmd: string }>>;
};Usage Examples:
import {
minimist, dotenv, fs, YAML, glob, chalk, which, ps, echo
} from "zx";
// Command line parsing
const args = minimist(process.argv.slice(2), {
boolean: ['verbose', 'dry-run'],
string: ['env', 'config'],
default: { env: 'development' }
});
echo`Environment: ${args.env}`;
// Environment variables
dotenv.config({ path: '.env.local' });
echo`Database URL: ${process.env.DATABASE_URL}`;
// File system operations
await fs.ensureDir('build/assets');
const packageInfo = await fs.readJson('package.json');
echo`Project: ${packageInfo.name}`;
// YAML processing
const config = YAML.parse(await fs.readFile('config.yaml', 'utf8'));
echo`Config loaded: ${config.app.name}`;
// File globbing
const jsFiles = await glob('src/**/*.js');
echo`Found ${jsFiles.length} JavaScript files`;
// Terminal styling
echo(chalk.green('✓ Success'), chalk.blue('Deployment completed'));
echo(chalk.red.bold('Error:'), chalk.yellow('Configuration missing'));
// Executable discovery
const dockerPath = await which('docker');
if (dockerPath) {
echo`Docker found at: ${dockerPath}`;
} else {
echo(chalk.red('Docker not found in PATH'));
}
// Process information
const processes = await ps.findAll();
const nodeProcesses = processes.filter(p => p.name.includes('node'));
echo`Found ${nodeProcesses.length} Node.js processes`;Access package version information and dependency versions.
/**
* ZX package version string
*/
declare const VERSION: string;
/**
* Alias for VERSION
*/
declare const version: string;
/**
* Version information for zx and bundled dependencies
*/
declare const versions: Record<string, string>;Usage Examples:
import { VERSION, version, versions, echo } from "zx";
echo`Running ZX version: ${VERSION}`;
echo`Version (alias): ${version}`;
// Version comparison
const [major, minor, patch] = VERSION.split('.').map(n => parseInt(n));
if (major >= 8) {
echo('Using modern ZX features');
}
// Dependency versions
echo`Bundled dependencies:`;
for (const [dep, ver] of Object.entries(versions)) {
echo` ${dep}: ${ver}`;
}
// Check specific dependency version
echo`Using chalk version: ${versions.chalk}`;
echo`Using minimist version: ${versions.minimist}`;Enhanced argument parsing with additional processing options.
/**
* Global parsed arguments (automatically populated)
*/
declare const argv: minimist.ParsedArgs;
/**
* Update global argv with new arguments
* @param args - Arguments to parse
* @param opts - Parsing options
*/
function updateArgv(args?: string[], opts?: ArgvOpts): void;
interface ArgvOpts extends minimist.Opts {
/** Convert kebab-case to camelCase */
camelCase?: boolean;
/** Parse 'true'/'false' strings to booleans */
parseBoolean?: boolean;
}Usage Examples:
import { argv, updateArgv, echo } from "zx";
// Access global arguments
echo`Script arguments:`, argv;
if (argv.help || argv.h) {
echo('Usage: script.mjs [options]');
echo('Options:');
echo(' --env, -e Environment (default: development)');
echo(' --verbose, -v Enable verbose logging');
echo(' --help, -h Show this help');
process.exit(0);
}
// Update arguments dynamically
updateArgv(['--database-url', 'postgres://localhost/mydb'], {
camelCase: true
});
echo`Database URL: ${argv.databaseUrl}`;
// Boolean parsing
updateArgv(['--enable-cache', 'true', '--debug', 'false'], {
parseBoolean: true
});
echo`Cache enabled: ${argv.enableCache}`; // true (boolean)
echo`Debug mode: ${argv.debug}`; // false (boolean)/**
* Duration specification as string or number
*/
type Duration = string | number;
/**
* Log entry structure
*/
interface LogEntry {
kind: string;
verbose?: boolean;
error?: string;
[key: string]: any;
}
/**
* Parsed command line arguments
*/
interface ParsedArgs {
_: string[];
[key: string]: any;
}
/**
* Argument parsing options
*/
interface ArgvOpts extends minimist.Opts {
camelCase?: boolean;
parseBoolean?: boolean;
}Install with Tessl CLI
npx tessl i tessl/npm-zx