Comprehensive logging utilities supporting both static and streaming output with different message types and custom symbols.
Standard logging functions for different message types with consistent visual styling.
/**
* Static logging utilities for different message types
*/
interface LogSystem {
/** Display a general message with optional custom symbol */
message: (message?: string, options?: LogMessageOptions) => void;
/** Display an info message with blue info symbol */
info: (message: string) => void;
/** Display a success message with green checkmark symbol */
success: (message: string) => void;
/** Display a step message with green step symbol */
step: (message: string) => void;
/** Display a warning message with yellow warning symbol */
warn: (message: string) => void;
/** Alias for warn() */
warning: (message: string) => void;
/** Display an error message with red error symbol */
error: (message: string) => void;
}
interface LogMessageOptions {
/** Custom symbol to display before the message */
symbol?: string;
}Usage Examples:
import { log } from "@clack/prompts";
import color from "picocolors"; // Optional for custom symbols
// Basic logging
log.info("Starting application setup");
log.success("Database connection established");
log.step("Installing dependencies");
log.warn("Using deprecated configuration format");
log.error("Failed to connect to remote server");
// General message logging
log.message("Processing user data");
// Custom symbol logging
log.message("Custom operation completed", {
symbol: color.cyan("β
")
});
// Multi-line messages
log.info("Configuration loaded successfully\nUsing development environment");
log.error(`Build failed with errors:
- Missing required dependency
- Invalid configuration file
- Permission denied`);Asynchronous logging functions for streaming iterables and real-time output.
/**
* Streaming logging utilities for iterables and async iterables
*/
interface StreamSystem {
/** Stream a general message with optional custom symbol */
message: (iterable: Iterable<string> | AsyncIterable<string>, options?: LogMessageOptions) => Promise<void>;
/** Stream an info message with blue info symbol */
info: (iterable: Iterable<string> | AsyncIterable<string>) => Promise<void>;
/** Stream a success message with green checkmark symbol */
success: (iterable: Iterable<string> | AsyncIterable<string>) => Promise<void>;
/** Stream a step message with green step symbol */
step: (iterable: Iterable<string> | AsyncIterable<string>) => Promise<void>;
/** Stream a warning message with yellow warning symbol */
warn: (iterable: Iterable<string> | AsyncIterable<string>) => Promise<void>;
/** Alias for warn() */
warning: (iterable: Iterable<string> | AsyncIterable<string>) => Promise<void>;
/** Stream an error message with red error symbol */
error: (iterable: Iterable<string> | AsyncIterable<string>) => Promise<void>;
}Usage Examples:
import { stream } from "@clack/prompts";
import color from "picocolors";
// Streaming from generators
await stream.info((function* () {
yield "Connecting to API";
yield "...";
yield " connected!";
})());
// Streaming from async generators
async function* buildProgress() {
yield "Building components";
await new Promise(resolve => setTimeout(resolve, 1000));
yield "\nOptimizing assets";
await new Promise(resolve => setTimeout(resolve, 500));
yield "\nBuild complete!";
}
await stream.success(buildProgress());
// Streaming arrays
const steps = ["Step 1", "Step 2", "Step 3"];
await stream.step(steps);
// Custom symbol streaming
await stream.message((function* () {
yield "Deploying to production";
yield "...";
yield " deployed!";
})(), { symbol: color.magenta("π") });
// Real-time log streaming
async function* processLogs() {
const logs = [
"Starting process",
"Loading configuration",
"Connecting to database",
"Processing data",
"Generating report",
"Process complete"
];
for (const log of logs) {
yield log + "\n";
await new Promise(resolve => setTimeout(resolve, 500));
}
}
await stream.info(processLogs());import { stream } from "@clack/prompts";
// Streaming from fetch response
async function streamApiResponse(url: string) {
const response = await fetch(url);
const reader = response.body?.getReader();
if (!reader) return;
async function* streamResponse() {
try {
while (true) {
const { done, value } = await reader.read();
if (done) break;
yield new TextDecoder().decode(value);
}
} finally {
reader.releaseLock();
}
}
await stream.info(streamResponse());
}
// Streaming command output
import { spawn } from "child_process";
async function streamCommandOutput(command: string, args: string[]) {
const child = spawn(command, args);
async function* streamStdout() {
for await (const chunk of child.stdout) {
yield chunk.toString();
}
}
async function* streamStderr() {
for await (const chunk of child.stderr) {
yield chunk.toString();
}
}
// Stream both stdout and stderr
await Promise.all([
stream.info(streamStdout()),
stream.error(streamStderr())
]);
}
// Streaming with progress updates
async function* downloadProgress(totalSize: number) {
let downloaded = 0;
while (downloaded < totalSize) {
downloaded += Math.random() * 1000000; // Simulate download
const percent = Math.min(Math.round((downloaded / totalSize) * 100), 100);
yield `Downloading... ${percent}%\n`;
if (downloaded >= totalSize) {
yield "Download complete!";
break;
}
await new Promise(resolve => setTimeout(resolve, 100));
}
}
await stream.success(downloadProgress(10000000));The logging system automatically handles line wrapping based on terminal width:
import { log } from "@clack/prompts";
// Long messages wrap appropriately
log.info("This is a very long message that will automatically wrap to the next line when it exceeds the terminal width, maintaining proper indentation and visual consistency throughout the entire message.");import { log } from "@clack/prompts";
// Explicit line breaks
log.success(`Operation completed successfully!
Results:
- 15 files processed
- 3 errors fixed
- 0 warnings remaining`);
// Streaming multi-line content
await stream.info((function* () {
yield "Multi-line content:\n";
yield "Line 1\n";
yield "Line 2\n";
yield "Line 3";
})());import { log, stream } from "@clack/prompts";
import color from "picocolors";
// Custom symbols for different operations
const symbols = {
download: color.blue("β¬"),
upload: color.green("β¬"),
process: color.yellow("β"),
complete: color.green("β"),
failed: color.red("β")
};
log.message("Downloading package", { symbol: symbols.download });
log.message("Processing files", { symbol: symbols.process });
log.message("Upload complete", { symbol: symbols.complete });
// Custom streaming symbols
await stream.message((function* () {
yield "Syncing data";
yield "...";
yield " synchronized!";
})(), { symbol: color.cyan("β²") });import { spinner, log } from "@clack/prompts";
const s = spinner();
s.start("Processing");
try {
// Work here
s.stop("Processing complete");
log.success("All operations completed successfully");
} catch (error) {
s.stop("Processing failed", 1);
log.error(`Operation failed: ${error.message}`);
}import { intro, outro, log, text, isCancel, cancel } from "@clack/prompts";
intro("Configuration Setup");
log.info("Welcome to the configuration wizard");
const config = await text({
message: "Enter configuration value:",
});
if (isCancel(config)) {
cancel("Setup cancelled");
process.exit(0);
}
log.step("Validating configuration");
log.success("Configuration validated successfully");
outro("Setup complete!");import { log } from "@clack/prompts";
try {
// Risky operation
throw new Error("Something went wrong");
} catch (error) {
log.error(`Operation failed: ${error.message}`);
if (error.stack) {
log.message(`Stack trace:\n${error.stack}`, {
symbol: " " // Indent without symbol
});
}
log.info("Please check your configuration and try again");
}info for general information, success for completions, warn for non-fatal issues, error for failures\n explicitly for multi-line content