npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@inquirer/core

v8.1.0

Published

Core Inquirer prompt API

Downloads

7,852,204

Readme

@inquirer/core

The @inquirer/core package is the library enabling the creation of Inquirer prompts.

It aims to implements a lightweight API similar to React hooks - but without JSX.

Installation

npm install @inquirer/core

yarn add @inquirer/core

Usage

import chalk from 'chalk';
import {
  createPrompt,
  useState,
  useKeypress,
  isEnterKey,
  usePrefix,
} from '@inquirer/core';

const confirm = createPrompt<boolean, { message: string; default?: boolean }>(
  (config, done) => {
    const [status, setStatus] = useState('pending');
    const [value, setValue] = useState('');
    const prefix = usePrefix({});

    useKeypress((key, rl) => {
      if (isEnterKey(key)) {
        const answer = value ? /^y(es)?/i.test(value) : config.default !== false;
        setValue(answer ? 'yes' : 'no');
        setStatus('done');
        done(answer);
      } else {
        setValue(rl.line);
      }
    });

    let formattedValue = value;
    let defaultValue = '';
    if (status === 'done') {
      formattedValue = chalk.cyan(value);
    } else {
      defaultValue = chalk.dim(config.default === false ? ' (y/N)' : ' (Y/n)');
    }

    const message = chalk.bold(config.message);
    return `${prefix} ${message}${defaultValue} ${formattedValue}`;
  },
);

/**
 *  Which then can be used like this:
 */
const answer = await confirm({ message: 'Do you want to continue?' });

See more examples:

API

createPrompt(viewFn)

The createPrompt function returns an asynchronous function that returns a cancelable promise resolving to the valid answer a user submit. This prompt function takes the prompt configuration as its first argument (this is defined by each prompt), and the context options as a second argument.

The prompt configuration is unique to each prompt. The context options are:

| Property | Type | Required | Description | | ----------------- | ----------------------- | -------- | ------------------------------------------------------------ | | input | NodeJS.ReadableStream | no | The stdin stream (defaults to process.stdin) | | output | NodeJS.WritableStream | no | The stdout stream (defaults to process.stdout) | | clearPromptOnDone | boolean | no | If true, we'll clear the screen after the prompt is answered |

The cancelable promise exposes a cancel method that'll exit the prompt and reject the promise.

Typescript

If using typescript, createPrompt takes 2 generic arguments (ex createPrompt<string, { message: string }>())

The first one is the type of the resolved value; function createPrompt<Value>(): Promise<Value> {}

The second one is the type of the prompt config; in other words the interface the created prompt will provide to users.

Hooks

Hooks can only be called within the prompt function and are used to handle state and events.

Those hooks are matching the React hooks API:

  • useState
  • useRef
  • useEffect
  • useMemo

And those are custom utilities from Inquirer:

  • useKeypress
  • usePagination
  • usePrefix

Key utilities

Listening for keypress events inside an inquirer prompt is a very common pattern. To ease this, we export a few utility functions taking in the keypress event object and return a boolean:

  • isEnterKey()
  • isBackspaceKey()
  • isSpaceKey()
  • isUpKey() - Note: this utility will handle vim and emacs keybindings (up, k, and ctrl+p)
  • isDownKey() - Note: this utility will handle vim and emacs keybindings (down, j, and ctrl+n)
  • isNumberKey() one of 1, 2, 3, 4, 5, 6, 7, 8, 9, 0

usePagination

When looping through a long list of options (like in the select prompt), paginating the results appearing on the screen at once can be necessary. The usePagination hook is the utility used within the select and checkbox prompts to cycle through the list of options.

Pagination works by taking in the list of options and returning a subset of the rendered items that fit within the page. The hook takes in a few options. It needs a list of options (items), and a pageSize which is the number of lines to be rendered. The active index is the index of the currently selected/selectable item. The loop option is a boolean that indicates if the list should loop around when reaching the end: this is the default behavior. The pagination hook renders items only as necessary, so it takes a function that can render an item at an index, including an active state, called renderItem.

export default createPrompt((config, done) => {
  const [active, setActive] = useState(0);

  const allChoices = config.choices.map((choice) => choice.name);

  const page = usePagination({
    items: allChoices,
    active: active,
    renderItem: ({ item, index, isActive }) => `${isActive ? ">" : " "}${index}. ${item.toString()}`
    pageSize: config.pageSize,
    loop: config.loop,
  });

  return `... ${page}`;
});

Theming

Theming utilities will allow you to expose customization of the prompt style. Inquirer also has a few standard theme values shared across all the official prompts.

To allow standard customization:

import { createPrompt, usePrefix, makeTheme, type Theme } from '@inquirer/core';
import type { PartialDeep } from '@inquirer/type';

type PromptConfig = {
  theme?: PartialDeep<Theme>;
};

export default createPrompt<string, PromptConfig>((config, done) => {
  const theme = makeTheme(config.theme);

  const prefix = usePrefix({ isLoading, theme });

  return `${prefix} ${theme.style.highlight('hello')}`;
});

To setup a custom theme:

import { createPrompt, makeTheme, type Theme } from '@inquirer/core';
import type { PartialDeep } from '@inquirer/type';

type PromptTheme = {};

const promptTheme: PromptTheme = {
  icon: '!',
};

type PromptConfig = {
  theme?: PartialDeep<Theme<PromptTheme>>;
};

export default createPrompt<string, PromptConfig>((config, done) => {
  const theme = makeTheme(promptTheme, config.theme);

  const prefix = usePrefix({ isLoading, theme });

  return `${prefix} ${theme.icon}`;
});

The default theme keys cover:

type DefaultTheme = {
  prefix: string;
  spinner: {
    interval: number;
    frames: string[];
  };
  style: {
    answer: (text: string) => string;
    message: (text: string) => string;
    error: (text: string) => string;
    defaultAnswer: (text: string) => string;
    help: (text: string) => string;
    highlight: (text: string) => string;
    key: (text: string) => string;
  };
};

License

Copyright (c) 2023 Simon Boudrias (twitter: @vaxilart) Licensed under the MIT license.