Skip to content

supercharge/console-io

Repository files navigation



Console IO

Opinionated UI kit for CLI inputs and outputs.


Installation · Table of Contents Usage



Latest Version Monthly downloads

Follow @marcuspoehls and @superchargejs for updates!


Supercharge: Console IO Logging

Introduction

The @supercharge/console-io package is a CLI UI kit. It provides an opinionated styling for console outputs and allows you to easily retrieve user retrieve input from the terminal.

Installation

npm i @supercharge/console-io

Table of Contents

Usage

Using @supercharge/console-io is pretty straightforward.

const { ConsoleInput, ConsoleOutput } = require('@supercharge/console-io')

async function askForAndPrintTheName () {
  const input = new ConsoleInput()
  const output = new ConsoleOutput()

  // asking the for input
  const name = await io.ask('What’s your name')

  // printing output to the terminal
  output.success('Hello', name)
}

Input

The package exports a ConsoleInput class. This class allows you to prompt users for input on the command line.

For example, you may ask the user for text input or to confirm/decline a given question.

Here’s how you create a console input instance called input. The following console input examples refer to this input instance when calling the individual methods.

const { ConsoleInput } = require('@supercharge/console-io')

const input = new ConsoleInput()

input.ask(message, builder?)

Prompts the user for text input:

const name = await input.ask('What’s your name')

The ask method accepts an optional builder callback as the second argument. This builder callback allows you to refine the question. Refining the question can be defining a default value or transforming the answer:

const name = await input.ask('What’s your name', builder => {
  builder
    .defaultValue('Marcus')
    .transform(answer => String(answer).toUpperCase())
})

// `name` when pressing enter using the default value: MARCUS
// `name` when providing 'test' as the value: TEST

input.confirm(message, builder?)

Prompts the user for a confirmation returning true or false:

const proceed = await input.confirm('This deletes all files. Proceed?')

The confirm method accepts a builder callback as the second argument. This builder callback allows you to refine the question. Refining the question can be defining a default value or transforming the answer:

const proceed = await input.confirm('This deletes all files. Proceed?', builder => {
  builder
    .defaultValue(false)
    .transform(answer => answer ? 1 : 0) // transforms `true` to `1` and `false` to `0`
})

// `proceed` when pressing enter using the default value: 0
// `proceed` when selecting the truthy value: 1

input.password(message, builder?)

Prompts the user for a password input. The typed input is masked with stars:

const password = await input.password('Provide your password')

The password method accepts a builder callback as the second argument. This builder callback allows you to refine the password prompt: you can make the input visible, like the password prompts on Linux systems:

const password = await input.password('Provide your password (not visible when typing)', builder => {
  builder
    .invisible()
    .transform(name => String(name).toLowerCase())
})

// `password` when typing "Supercharge": "supercharge"

input.secure(message)

Prompts the user for a secure input which is not visible when the user types the input:

const secret = await input.secure('Provide your password')

The secure method accepts is a shortcut to an invisible password prompts. It doesn’t show the input, not even masked with stars.

input.choice(message, builder)

Prompts the user to select from a list of choices. Use the builder in the callback function to create and configure the available choices:

const choice = await input.choice('Choose your favorite framework', builder => {
  builder.add('Supercharge').withValue('supercharge')
  builder.add('Express').withValue('express').disabled()
})

// `choice` when selecting "Supercharge": "supercharge"

Output

The package exports a ConsoleOutput class. This class allows you to print expressive and colored messages to the terminal.

For example, you may use console output to create expressive and colored terminal messages.

Here’s how you create a console output instance called output. The following console output examples refer to this output instance when calling the individual methods:

const { ConsoleOutput } = require('@supercharge/console-io')

const output = new ConsoleOutput()

Logging

The console output exposes a handful methods. Here’s a preview how the console output looks like:

Supercharge: Console IO Output

output.log(message)

Prints a log message to stdout. Works like console.log but uses the log renderer. Log renderers can be swapped for better testing:

output.log('Supercharge is sweet!')

output.logError(message)

Prints a log message to stderr. Works like console.error but uses the log renderer. Log renderers can be swapped for better testing:

output.log('Supercharge is sweet!')

output.blankLine()

Prints an empty line to the terminal. Useful to create whitespace:

output.blankLine()

output.info(message)

Prints the given info message to the terminal. Info messages will be prefixed with an “INFO” tag on a cyan background:

output.info('info message')

output.warn(message)

Prints the given warning message to the terminal. Warning messages will be prefixed with a “WARN” tag on a yellow background:

output.info('warning message')

output.debug(message)

Prints the given debug message to the terminal. Debug messages will be prefixed with a “DEBUG” tag on a blue background:

output.debug('debug message')

output.error(message | Error)

Prints the given error message or message from the Error instance to the terminal. Error messages will be prefixed with an “ERROR” tag on a red background:

output.debug('debug message')

When providing an Error instance, the stack trace will be printed dimmed below the error message.

output.success(labelOrMessage, message?)

Prints a success message to the terminal. When providing a single parameter to .success(message), the text will be printed in green. You may provide the label and message parameters to create a success tag for the given label with the related message:

// just a success message
output.success('Green-colored success message')

// success message with label
output.success('SUCCESS', 'success message')

output.hint(labelOrMessage, message?)

Prints a hint message to the terminal. When providing a single parameter to .hint(message), the text will be printed in blue. You may provide the label and message parameters to create a hint tag for the given label with the related message:

// just a hint message
output.hint('Blue-colored hint message')

// hint message with label
output.hint('SKIPPED', 'hint message')

output.fail(labelOrMessage, message?)

Prints a fail message to the terminal. When providing a single parameter to .fail(message), the text will be printed in red. You may provide the label and message parameters to create a fail tag for the given label with the related message:

// just a fail message
output.fail('Red-colored fail message')

// fail message with label
output.fail('FAILED', 'fail message')

Tags

The ConsoleOutput class provides a .tag(label) method creating a pending tag output. The pending tag must be completed with a given message. The tag message relates to one of the available states. The tag states define the colors. At this point, there are three tags availabe: success, info, failed.

Supercharge: Console IO Output

output.tag(label).success(message)

Prints a success tag to the terminal. The tag label is printed with dark text on a green background. The message provides more context data:

output.tag(' FINISHED ').success('Database migrations')

output.tag(label).info(message, reason?)

Prints an info tag to the terminal. The tag label is printed with dark text on a blue background. The message provides more context data:

output.tag(' IGNORED ').info('config/app.js file for this run')
output.tag(' SKIPPED ').info('Copying .env', 'File already exists.')

output.tag(label).failed(message, reason?)

Prints a fail tag to the terminal. The tag label is printed with dark text on a red background. The message provides more context data:

output.tag(' FAILED ').failed('to copy .env file', 'File already exists.')

Spinner

A ConsoleOutput instance provides the .spinner(message) and .withSpinner(message, callback) methods creating and returning a loading spinner with the given message. You can process long-running tasks while showing the loading spinner. You must manually stop the when using the .spinner(message) method. Stopping and starting the spinner is handled for you when using the .withSpinner(message, callback) method.

Supercharge: Console IO Spinner

Spinner Interface

You can change the state (message) of a loading spinner by using one of the following three methods:

  • spinner.update(message): update the previous spinner text to the given message
  • spinner.done(message?): stop the spinner and mark it as “done”. Optionally update the spinner text to the given message
  • spinner.fail(message?): stop the spinner and mark it as “failed”. Optionally update the spinner text to the given message

output.spinner(message)

Creates and returns a started loading spinner for the given message:

const spinner = output.spinner('Installing dependencies')
await installDependencies()

spinner.update('Processing long-running task')
await processOtherLongRunningTask()

spinner.stop('Setup complete')

output.withSpinner(message, callback)

Returns a promise and runs the given callback action. The callback receives a started loading spinner instance. Using this method allows you to group actions of a long-running task into a callback function:

const result = await output.withSpinner('Installing dependencies', async spinner => {
  await installDependencies()

  spinner.update('Completing setup')
  await completeSetup()

  /**
   * You can manually stop the spinner with a custom message. You can also skip
   * stopping the spinner here if you’re fine using the previous message as
   * the "done" message. Stopping the spinner is already handled for you.
   */
  spinner.stop('Setup complete')

  /**
   * You may return a value from this callback and use it later in your code.
   */
  return { done: true }
})

You must handle errors youself in case one of your methods inside the callback throws an error. A common approach is wrapping your code in a try/catch block and handling the error after catching it. Here’s an example on how you may handle errors:

try {
  await output.withSpinner('A failing spinner', async () => {
    throw new Error('Uff, failed!')
  })
} catch (error) {
  output.error(error.message)
}

Contributing

Do you miss a function? We very much appreciate your contribution! Please send in a pull request 😊

  1. Create a fork
  2. Create your feature branch: git checkout -b my-feature
  3. Commit your changes: git commit -am 'Add some feature'
  4. Push to the branch: git push origin my-new-feature
  5. Submit a pull request 🚀

License

MIT © Supercharge


superchargejs.com  ·  GitHub @supercharge  ·  Twitter @superchargejs