Skip to content

CliOutput.ts

Formats CLI help and errors as text.

This module turns help documents, CLI errors, grouped errors, and version information into strings. It does not write those strings to the terminal itself. It includes the Formatter interface, the formatter service, a layer for custom formatters, and the default formatter with configurable color support.

Since v4.0.0



Creates a default formatter with configurable options.

Example (Creating default formatters)

import { Effect } from "effect"
import { CliError, CliOutput } from "effect/unstable/cli"
// Create a formatter without colors for tests or CI environments
const noColorFormatter = CliOutput.defaultFormatter({ colors: false })
// Create a formatter with colors forced on
const colorFormatter = CliOutput.defaultFormatter({ colors: true })
// Auto-detect colors based on terminal support (default behavior)
const autoFormatter = CliOutput.defaultFormatter()
const program = Effect.gen(function* () {
const formatter = colorFormatter
// Format an error with proper styling
const error = new CliError.InvalidValue({
option: "foo",
value: "bar",
expected: "baz",
kind: "flag"
})
const errorText = formatter.formatError(error)
console.log(errorText)
// Format version information
const versionText = formatter.formatVersion("my-tool", "1.2.3")
console.log(versionText)
})

Signature

declare const defaultFormatter: (options?: { colors?: boolean }) => Formatter

Source

Since v4.0.0

Creates a Layer that provides a custom Formatter implementation.

Example (Providing a custom formatter)

import { Console, Effect } from "effect"
import { CliOutput } from "effect/unstable/cli"
// Create a custom formatter without colors
const noColorFormatter = CliOutput.defaultFormatter({ colors: false })
const NoColorLayer = CliOutput.layer(noColorFormatter)
// Create a program that uses the custom formatter
const program = Effect.gen(function* () {
const formatter = yield* CliOutput.Formatter
const versionText = formatter.formatVersion("my-cli", "1.0.0")
yield* Console.log(`Using custom formatter: ${versionText}`)
}).pipe(Effect.provide(NoColorLayer))
// You can also create completely custom formatters
const jsonFormatter: CliOutput.Formatter = {
formatHelpDoc: (doc) => JSON.stringify(doc, null, 2),
formatCliError: (error) => JSON.stringify({ error: error.message }),
formatError: (error) => JSON.stringify({ type: "error", message: error.message }),
formatVersion: (name, version) => JSON.stringify({ name, version }),
formatErrors: (errors) => JSON.stringify(errors.map((error) => error.message))
}
const JsonLayer = CliOutput.layer(jsonFormatter)

Signature

declare const layer: (formatter: Formatter) => Layer.Layer<never>

Source

Since v4.0.0

Defines the service interface for formatting CLI output including help, errors, and version info. This allows customization of output formatting, including color support.

Example (Customizing CLI output formatting)

import { Effect } from "effect"
import { CliOutput } from "effect/unstable/cli"
// Create a custom formatter implementation
const customFormatter: CliOutput.Formatter = {
formatHelpDoc: (doc) => `Custom Help: ${doc.usage}`,
formatCliError: (error) => `Error: ${error.message}`,
formatError: (error) => `[ERROR] ${error.message}`,
formatVersion: (name, version) => `${name} (${version})`,
formatErrors: (errors) => errors.map((error) => error.message).join("\\n")
}
// Use the custom formatter in a program
const program = Effect.gen(function* () {
const formatter = yield* CliOutput.Formatter
const helpText = formatter.formatVersion("myapp", "1.0.0")
console.log(helpText)
}).pipe(Effect.provide(CliOutput.layer(customFormatter)))

Signature

export interface Formatter {
/**
* Formats a HelpDoc structure into a readable string format.
*
* **Example** (Formatting help documents)
*
* ```ts
* import { Option as O } from "effect"
* import { CliOutput } from "effect/unstable/cli"
* import type { HelpDoc } from "effect/unstable/cli"
*
* const helpDoc: HelpDoc = {
* usage: "myapp [options] <file>",
* description: "Process files with various options",
* flags: [
* {
* name: "verbose",
* aliases: ["-v"],
* type: "boolean",
* description: O.some("Enable verbose output"),
* required: false
* }
* ],
* args: [
* {
* name: "file",
* type: "string",
* description: O.some("Input file to process"),
* required: true,
* variadic: false
* }
* ]
* }
*
* const formatter = CliOutput.defaultFormatter()
* const helpText = formatter.formatHelpDoc(helpDoc)
* console.log(helpText)
* // Outputs formatted help with sections: DESCRIPTION, USAGE, ARGUMENTS, FLAGS
* ```
*
* @since 4.0.0
*/
readonly formatHelpDoc: (doc: HelpDoc) => string
/**
* Formats a CLI error for display. Default implementation mirrors the error message.
*
* **Example** (Formatting CLI errors)
*
* ```ts
* import { Data } from "effect"
* import { CliOutput } from "effect/unstable/cli"
*
* class InvalidOption extends Data.TaggedError("InvalidOption")<{
* readonly message: string
* }> {}
*
* const formatter = CliOutput.defaultFormatter()
* const error = new InvalidOption({ message: "Unknown flag '--invalid'" })
* const errorMessage = formatter.formatCliError(error)
* console.log(errorMessage) // "Unknown flag '--invalid'"
* ```
*
* @since 4.0.0
*/
readonly formatCliError: (error: CliError.CliError) => string
/**
* Formats an error section with proper styling and color reset.
*
* **Example** (Formatting error sections)
*
* ```ts
* import { Data } from "effect"
* import { CliOutput } from "effect/unstable/cli"
*
* class ValidationError extends Data.TaggedError("ValidationError")<{
* readonly message: string
* }> {}
*
* const colorFormatter = CliOutput.defaultFormatter({ colors: true })
* const noColorFormatter = CliOutput.defaultFormatter({ colors: false })
*
* const error = new ValidationError({ message: "Value must be positive" })
*
* const coloredError = colorFormatter.formatError(error)
* console.log(coloredError) // "\n\x1b[1m\x1b[31mERROR\x1b[0m\n Value must be positive\x1b[0m"
*
* const plainError = noColorFormatter.formatError(error)
* console.log(plainError) // "\nERROR\n Value must be positive"
* ```
*
* @since 4.0.0
*/
readonly formatError: (error: CliError.CliError) => string
/**
* Formats version output for display.
*
* **Example** (Formatting version output)
*
* ```ts
* import { CliOutput } from "effect/unstable/cli"
*
* const colorFormatter = CliOutput.defaultFormatter({ colors: true })
* const noColorFormatter = CliOutput.defaultFormatter({ colors: false })
*
* const appName = "my-awesome-tool"
* const version = "1.2.3"
*
* const coloredVersion = colorFormatter.formatVersion(appName, version)
* console.log(coloredVersion) // "\x1b[1mmy-awesome-tool\x1b[0m \x1b[2mv\x1b[0m\x1b[1m1.2.3\x1b[0m"
*
* const plainVersion = noColorFormatter.formatVersion(appName, version)
* console.log(plainVersion) // "my-awesome-tool v1.2.3"
* ```
*
* @since 4.0.0
*/
readonly formatVersion: (name: string, version: string) => string
/**
* Formats multiple CLI errors for display, grouping by error type.
*
* **Example** (Formatting grouped errors)
*
* ```ts
* import { CliError, CliOutput } from "effect/unstable/cli"
*
* const formatter = CliOutput.defaultFormatter({ colors: false })
*
* const errors = [
* new CliError.UnrecognizedOption({
* option: "--foo",
* suggestions: ["--force"]
* }),
* new CliError.UnrecognizedOption({ option: "--bar", suggestions: [] }),
* new CliError.MissingOption({ option: "--required" })
* ]
*
* const output = formatter.formatErrors(errors)
* // Groups errors by type and displays all at once
* ```
*
* @since 4.0.0
*/
readonly formatErrors: (errors: ReadonlyArray<CliError.CliError>) => string
}

Source

Since v4.0.0

Service reference for the CLI output formatter. Provides a default implementation that can be overridden for custom formatting or testing.

Example (Accessing the output formatter)

import { Effect } from "effect"
import { CliOutput } from "effect/unstable/cli"
// Access the formatter service
const program = Effect.gen(function* () {
const formatter = yield* CliOutput.Formatter
// Format version information
const versionText = formatter.formatVersion("my-cli", "2.1.0")
console.log(versionText) // "my-cli v2.1.0" (with colors if supported)
return versionText
})
// Run with default formatter
const result = Effect.runSync(program)

Signature

declare const Formatter: Context.Reference<Formatter>

Source

Since v4.0.0