A tool for writing better scripts by bridging JavaScript and shell commands with cross-platform wrappers around child_process
Interactive utilities for user input, output formatting, visual feedback, and data processing in command-line scripts.
Display formatted output and information to users.
/**
* Print formatted output to console
* @param args - Values to print (or template literal with interpolations)
*/
function echo(...args: any[]): void;
function echo(pieces: TemplateStringsArray, ...args: any[]): void;Usage Examples:
import { echo, $ } from "zx";
// Simple output
echo('Hello World');
echo('Deployment status:', 'success');
// Template literal format
const status = 'completed';
const time = new Date().toISOString();
echo`Deployment ${status} at ${time}`;
// With ProcessOutput
const result = await $`git branch --show-current`;
echo`Current branch: ${result}`;
// Multiple values
echo('Server:', 'production', 'Port:', 8080);Get interactive input from users with optional choices and completion.
/**
* Prompt user for input with optional choices and completion
* @param query - Question to ask the user
* @param options - Configuration options
* @returns Promise resolving to user's input
*/
function question(
query?: string,
options?: {
/** Array of suggested choices for tab completion */
choices?: string[];
/** Input stream (default: process.stdin) */
input?: NodeJS.ReadStream;
/** Output stream (default: process.stdout) */
output?: NodeJS.WriteStream;
}
): Promise<string>;Usage Examples:
import { question, echo } from "zx";
// Simple question
const name = await question('What is your name? ');
echo`Hello, ${name}!`;
// With choices (tab completion)
const environment = await question('Deploy to which environment? ', {
choices: ['development', 'staging', 'production']
});
// Confirmation prompt
const proceed = await question('Continue with deployment? (y/n) ');
if (proceed.toLowerCase() !== 'y') {
echo('Deployment cancelled');
process.exit(0);
}
// Custom streams
import fs from 'fs';
const input = fs.createReadStream('./input.txt');
const response = await question('Question: ', { input });Read and process data from standard input streams.
/**
* Read all data from standard input
* @param stream - Input stream (default: process.stdin)
* @returns Promise resolving to input content as string
*/
function stdin(stream?: Readable): Promise<string>;Usage Examples:
import { stdin, echo, $ } from "zx";
// Read from standard input
echo('Please enter your data (Ctrl+D to finish):');
const input = await stdin();
echo`You entered: ${input}`;
// Process piped input
const data = await stdin();
const lines = data.trim().split('\n');
echo`Received ${lines.length} lines`;
// Use with custom stream
import fs from 'fs';
const fileStream = fs.createReadStream('./data.txt');
const fileContent = await stdin(fileStream);
echo`File content: ${fileContent}`;
// Combine with shell commands
const logData = await $`tail -f /var/log/system.log`.pipe(process.stdout);Provide visual feedback during long-running operations.
/**
* Show a spinner during async operation execution
* @param title - Spinner text (optional)
* @param callback - Async function to execute
* @returns Promise resolving to callback result
*/
function spinner<T>(title: string, callback: () => T): Promise<T>;
function spinner<T>(callback: () => T): Promise<T>;Usage Examples:
import { spinner, $, sleep } from "zx";
// Simple spinner
const result = await spinner('Deploying application...', async () => {
await $`docker build -t myapp .`;
await $`docker push myapp`;
return 'success';
});
// Spinner without title
await spinner(async () => {
await sleep('2s');
await $`npm install`;
});
// Complex operations
const deployment = await spinner('Setting up infrastructure...', async () => {
// Multiple operations
await $`terraform init`;
await $`terraform plan -out=plan.out`;
await $`terraform apply plan.out`;
return {
status: 'deployed',
timestamp: new Date().toISOString()
};
});
echo`Deployment completed: ${deployment.status}`;
// Conditional spinner (disabled in CI)
const buildResult = await spinner('Building project...', async () => {
return await $`npm run build`;
});
// Spinner automatically disabled if process.env.CI is setParse and process command-line arguments.
/**
* Parse command-line arguments with options
* @param args - Arguments array (default: process.argv.slice(2))
* @param opts - Parsing options
* @param defs - Default values
* @returns Parsed arguments object
*/
function parseArgv(
args?: string[],
opts?: ArgvOpts,
defs?: Record<string, any>
): minimist.ParsedArgs;
/**
* Update the global argv object with new arguments
* @param args - New arguments to parse
* @param opts - Parsing options
*/
function updateArgv(args?: string[], opts?: ArgvOpts): void;
/**
* Global parsed arguments object
*/
declare const argv: minimist.ParsedArgs;
interface ArgvOpts extends minimist.Opts {
/** Convert kebab-case to camelCase */
camelCase?: boolean;
/** Parse string 'true'/'false' to boolean */
parseBoolean?: boolean;
}Usage Examples:
import { parseArgv, argv, echo } from "zx";
// Using global argv
echo`Script called with:`, argv;
console.log('Environment:', argv.env || 'development');
console.log('Verbose mode:', argv.verbose || false);
// Custom argument parsing
const args = parseArgv(['--port', '3000', '--host', 'localhost', '--verbose'], {
default: { port: 8080 },
boolean: ['verbose'],
string: ['host']
});
console.log(args); // { port: 3000, host: 'localhost', verbose: true, _: [] }
// With camelCase conversion
const camelArgs = parseArgv(['--api-key', 'secret', '--max-retries', '3'], {
camelCase: true,
parseBoolean: true
});
console.log(camelArgs.apiKey); // 'secret'
console.log(camelArgs.maxRetries); // 3
// Update global argv
updateArgv(['--env', 'production'], { camelCase: true });
echo`Environment: ${argv.env}`;
// Default values
const config = parseArgv(process.argv.slice(2), {
default: {
port: 8080,
host: '0.0.0.0',
env: 'development'
},
boolean: ['verbose', 'debug'],
string: ['host', 'env'],
number: ['port']
});Asynchronous delays and timing utilities.
/**
* Asynchronous sleep/delay
* @param duration - Sleep duration (string like '2s', '500ms' or number in ms)
* @returns Promise that resolves after the specified duration
*/
function sleep(duration: Duration): Promise<void>;
type Duration = string | number;Usage Examples:
import { sleep, echo, $ } from "zx";
// String duration formats
await sleep('2s'); // 2 seconds
await sleep('500ms'); // 500 milliseconds
await sleep('1m'); // 1 minute
await sleep('1.5s'); // 1.5 seconds
// Numeric duration (milliseconds)
await sleep(1000); // 1 second
// In scripts
echo('Starting deployment...');
await sleep('1s');
await $`docker build -t myapp .`;
echo('Build complete, waiting before push...');
await sleep('2s');
await $`docker push myapp`;
echo('Deployment complete!');
// Retry with delays
for (let i = 0; i < 3; i++) {
try {
await $`curl -f https://api.example.com/health`;
echo('Service is healthy');
break;
} catch (error) {
echo(`Health check failed (attempt ${i + 1}), retrying...`);
await sleep('5s');
}
}/**
* Duration specification as string or milliseconds
*/
type Duration = string | number;
/**
* Parsed command-line arguments
*/
interface ParsedArgs {
/** Non-option arguments */
_: string[];
/** Named arguments */
[key: string]: any;
}
/**
* minimist parsing options extended with zx-specific options
*/
interface ArgvOpts extends minimist.Opts {
/** Convert kebab-case to camelCase */
camelCase?: boolean;
/** Parse 'true'/'false' strings to booleans */
parseBoolean?: boolean;
/** Default values for arguments */
default?: Record<string, any>;
/** Keys that should be treated as booleans */
boolean?: string | string[];
/** Keys that should be treated as strings */
string?: string | string[];
/** Keys that should be treated as numbers */
number?: string | string[];
/** Argument aliases */
alias?: Record<string, string | string[]>;
}
/**
* Node.js readable stream interface
*/
interface Readable extends NodeJS.ReadableStream {
setEncoding(encoding: BufferEncoding): this;
[Symbol.asyncIterator](): AsyncIterableIterator<any>;
}Install with Tessl CLI
npx tessl i tessl/npm-zx