cliui

repository·6.x·Indexed 41 days ago

https://github.com/poppinss/cliui

An opinionated UI kit for building consistent command-line interfaces. Version 6.8.1 provides tools for structured logging, tables, spinners, and widgets such as tasks, steps, and instructions. It includes a Logger for various severity levels, a TaskManager for asynchronous tasks with minimal or verbose rendering, and an Instructions class for boxed guided steps. The kit supports multiple operational modes: normal, silent, and raw (for testing via MemoryRenderer).

Tokens
11.9K
Snippets
51
Records
60
Agent score
97%

What's inside cliui

  1. Enable raw mode for testing UI output

    6.x

    When testing CLI commands, instantiate cliui with { raw: true }. This mode collects log messages in memory and disables color transformations (ANSI sequences), making it easy to assert against expected output.

    To retrieve logs, use ui.logger.getRenderer().getLogs().

    const ui = cliui({ raw: true })
    
    ui.logger.info('This is an info message')
    
    const logs = ui.logger.getRenderer().getLogs()
    // logs will contain the raw message without ANSI colors
    // e.g., [{ stream: 'stdout', message: '[ cyan(info) ] This is an info message' }]
  2. Basic usage of @poppinss/cliui

    6.x

    Import cliui and create an instance to access the logger and other UI components.

    import { cliui } from '@poppinss/cliui'
    const ui = cliui()
    
    ui.logger.info('This is an info message')
    // Output: [ info ] This is an info message
    import { cliui } from '@poppinss/cliui'
    const ui = cliui()
    
    ui.logger.info('This is an info message')
  3. Install @poppinss/cliui

    6.x

    Install the package from the npm registry to use the opinionated UI kit for command line applications.

    npm i @poppinss/cliui
    
    # or for yarn users
    yarn add @poppinss/cliui
    npm i @poppinss/cliui
  4. Test tasks output in raw mode

    6.x

    To test or assert against the output of the tasks widget, you must instantiate cliui in raw mode. This collects all log messages in memory instead of writing them to the terminal. You can then retrieve the logs using tasks.getRenderer().getLogs().

    const ui = cliui({ raw: true })
    const tasks = ui.tasks()
    
    await tasks
      .add('clone repo', async (task) => {
        return 'Completed'
      })
      .add('update package file', async (task) => {
        return task.error('Unable to update package file')
      })
      .add('install dependencies', async (task) => {
        return 'Installed'
      })
      .run()
    
    const logs = tasks.getRenderer().getLogs()
    console.log(logs)
  5. How TaskManager manages task state

    6.x

    The TaskManager tracks the lifecycle of the task sequence through an internal #state. You can check the current state using getState().

    Possible states:

    • idle: Initial state before run() is called.
    • running: The manager is currently executing tasks.
    • succeeded: All registered tasks completed successfully.
    • failed: A task failed or an error was thrown. The manager stops execution immediately.

    If a task fails, the error property on the TaskManager instance will contain the error that caused the failure.

  6. Prepare vs Render instructions

    6.x

    The Instructions class provides two ways to handle the final output:

    1. prepare(): Returns the final formatted string (including borders and padding) without actually printing it. This is useful if you need to manipulate the string or write it to a file.
    2. render(): Automatically calls prepare() and sends the resulting string to the configured renderer (defaulting to ConsoleRenderer).
    const instructions = new Instructions().heading('Title').add('Step')
    
    // Get the string for manual use
    const output = instructions.prepare()
    
    // Print directly to console
    instructions.render()
  7. Define TableHead and TableRow structures

    6.x

    Tables in @poppinss/cliui support complex cell configurations including spanning and alignment.

    TableHead

    An array of strings or configuration objects:

    • content: string (Required)
    • colSpan?: number (Optional)
    • hAlign?: 'left' | 'center' | 'right' (Horizontal alignment)
    • rowSpan?: number (Optional)
    • vAlign?: 'top' | 'center' | 'bottom' (Vertical alignment)

    TableRow

    An array of cells (similar to TableHead) or an object where keys are column names and values are arrays of strings: { [key: string]: string[] }.

  8. Use the Task class to manage CLI work units

    6.x

    The Task class represents an individual unit of work in a CLI. It manages the lifecycle and state of a task (e.g., idle, running, succeeded, failed) and provides hooks for listeners to react to state changes.

    Important: The Task class itself does not render anything to the console. It is a state container. You must use a separate renderer (provided by @poppinss/cliui) to actually display the task's progress and state in the terminal.

    import { Task } from '@poppinss/cliui'
    
    const task = new Task('Installing dependencies')
    
    task.start()
      .update('Downloading packages...')
      .markAsSucceeded('Installation complete!')
  9. Configure full-width and fluid columns in Table

    6.x

    When rendering tables in a terminal, you can use fullWidth mode to ensure the table spans the entire available width. In this mode, you can designate a specific column to be 'fluid', meaning it will expand to fill any leftover space after other columns have been sized.

    const table = new Table()
      .head(['ID', 'Name', 'Description'])
      .row(['1', 'Alice', 'A very long description that should expand'])
      .fullWidth(true)
      .fluidColumnIndex(2) // The 'Description' column will take up the remaining space
      .render()
    const table = new Table()
      .head(['ID', 'Name', 'Description'])
      .row(['1', 'Alice', 'A very long description that should expand'])
      .fullWidth(true)
      .fluidColumnIndex(2)
      .render()
  10. Use Logger actions for task outcomes

    6.x

    Logger actions are pre-styled logs used to show the outcome of a specific operation (e.g., file creation). Use logger.action(message) to start an action, then chain one of the following methods to finish it:

    • .succeeded(): Marks the action as successful.
    • .skipped(reason): Marks the action as skipped with an optional reason.
    • .failed(error): Marks the action as failed with an error.
    • .displayDuration(): (Optional) Displays the time taken for the action.
    logger
      .action('Creating config/auth.ts')
      .displayDuration()
      .succeeded()
    
    logger
      .action('Creating app/Models/User.ts')
      .skipped('File already exists')
    
    logger
      .action('Creating server.ts')
      .failed(new Error('File already exists'))
  11. Use the tasks widget to render a list of tasks

    6.x

    The tasks widget allows you to render a list of asynchronous tasks in the CLI. Each task is defined by a name and an async callback function.

    Key behaviors:

    • Success: The return value of the callback function is used as the success message.
    • Failure: You can mark a task as failed by throwing an Error or by calling task.error('error message') within the callback.
    • Execution: Use .run() to execute the queued tasks.
    const ui = cliui()
    const tasks = ui.tasks()
    
    await tasks
      .add('clone repo', async (task) => {
        return 'Completed'
      })
      .add('update package file', async (task) => {
        return task.error('Unable to update package file')
      })
      .add('install dependencies', async (task) => {
        return 'Installed'
      })
      .run()
  12. Render instructions and stickers

    6.x

    Both instructions and sticker widgets allow you to render a collection of lines in a styled box.

    • Instructions: Prefixes each line with an arrow >. Use ui.instructions().
    • Sticker: Does not use prefixes. Use ui.sticker().

    Both use .add(text) to append lines and .render() to display them.

    const instructions = ui.instructions()
    instructions
      .add(`cd ${ui.colors.cyan('hello-world')}`)
      .add(`Run ${ui.colors.cyan('node ace serve --watch')} to start the server`)
      .render()
    
    const sticker = ui.sticker()
    sticker
      .add('Started HTTP server')
      .add(`Local address: ${ui.colors.cyan('http://localhost:3333')}`)
      .render()