1.0.2 • Published 1 year ago

prompt-sync-plus v1.0.2

Weekly downloads
-
License
MIT
Repository
github
Last release
1 year ago

prompt-sync-plus

license build test results coverage

An easy-to-use, synchronous prompt for Node.js based on the widely-adopted prompt-sync. The intent behind this project is to expand upon the original work of heapwolf, et. al., clean up, modernize, and patch the library to fix several existing issues and add some additional features. This library should be a 1:1 drop-in for the original and should at minimum require only an install and replacement of existing imports.

Changes include:

  • A port to ES6 and TypeScript
  • Addition of unit tests with ~90%+ code coverage
  • Some fixes to existing autocomplete behavior
  • Addition of new autocomplete behaviors and configuration options
  • Support for multi-line inputs and editing
  • Improved documentation + examples

Installation

Install via NPM:

npm install --save prompt-sync-plus

Features & Examples

Basic Example

At minimum, you need to import the library and instantiate the prompt. The entered text is returned directly:

import psp from "prompt-sync-plus";

const prompt = psp();
const result = prompt("How are you? ");

console.log(`You responded with: '${result}'`);

Kapture 2022-09-23 at 16 34 38

Configuration

Prompt settings can be supplied via JSON object globally and/or on a prompt-by-prompt basis. Global settings are supplied when the prompt is instantiated:

import psp from "prompt-sync-plus";

const prompt = psp({
  /* your settings here */
});

Prompt-specific settings can be supplied either as the second or third argument to the prompt invocation.

const result = prompt("How are you?", {
  /* your settings here */
});

Or with a default response supplied:

const result = prompt("How are you?", "Good", {
  /* your settings here */
});

Prompt-specific settings override global settings wherever there is overlap:

import psp from "prompt-sync-plus";

const prompt = psp({ sigint: true });

// overrides sigint behavior established by global setting above
const result = prompt("How are you?", { sigint: false });

Both methods of configuration take the same JSON schema. See API for a full listing of available settings, or keep scrolling to see examples of various settings in action.

Supply a default value

A global default value can be supplied via the defaultResponse field:

import psp from "prompt-sync-plus";

const prompt = psp({ defaultResponse: "No response" });
const result = prompt("Some question");

console.log(result); // No response

A prompt-specific default value can be supplied as a second argument to the prompt:

const result = prompt("How are you? ", "Good");

console.log(`You responded with: '${result}'`); // You responded with Good

Kapture 2022-09-23 at 16 38 18

Handling multi-line input

Prompt input that spans across multiple lines is supported in every interaction within prompt-sync-plus, including simple prompts, autocomplete suggestions, history scrolling, etc. Use arrow keys to position the cursor anywhere within the given multi-line input and edit it from the middle:

multi-line-editing

Note: prompt history supercedes up and down arrow key behavior. To use up and down arrow keys for positioning, the current prompt call must not have prompt history enabled.

Handling multi-line asks

The above support for multi-line inputs applies to the prompts themselves, for example you can use a multi-line JavaScript template literal:

import psp from "prompt-sync-plus";

const prompt = psp();

const result = prompt(`Enter
Something
Here: `);

console.log(`You entered: '${result}'`);

Kapture 2022-12-02 at 17 09 49

Handling sensitive input

For password entry, etc. character input can be obscured via the echo field:

const result = prompt("Password: ", { echo: "*" });

Kapture 2022-09-23 at 16 40 27

To omit output entirely, supply the empty string to echo:

const result = prompt("Sensitive info: ", { echo: "" });

Prompt-sync-plus exposes a helpful shorthand for the syntax above:

const result = prompt.hide("Sensitive info: ");

Kapture 2022-09-23 at 16 41 36

Handling SIGINT

Handling of SIGINT (Ctrl+C) is configured via the sigint boolean field. It determines whether to kill the process and return code 130 (true) or gobble up the signal and immediately return null from the prompt (false). The latter is the default.

const result = prompt("Enter something or CTRL+C to quit: ", {
  sigint: true,
});

Kapture 2022-09-23 at 16 45 23

Handling end-of-transmission

Handling end-of-transmission (CTRL+D) is configured via the eot boolean field. It determines whether to kill the process and return code 0 (true) or gobble up the signal and continue prompting (false). The latter is the default behavior.

const result = prompt("Enter something CTRL+D: ", {
  eot: true,
});

Kapture 2022-09-23 at 16 51 12

Autocompletion

Prompt-sync-plus supports a few different methods for providing users with autocomplete output. Note that this is an area where major changes were made to the original approach of prompt-sync, so your code will likely need some adjustments to use prompt-sync-plus.

At minimum, a search function needs to be passed in to the configuration to enable autocomplete - prompt-sync-plus handles the display and selection of results, but it is up to the caller to decide selection criteria with their own code:

const listOfWords = [
  "interspecies",
  "interstelar",
  "interstate",
  "interesting",
  "interoperating",
  "intolerant",
  "introversion",
  "introspection",
  "interrogation",
];

const findWordStart = (str) =>
  listOfWords.filter((word) => word.indexOf(str) === 0);

// a simple autocomplete algorithm - find word starts
const result = prompt("Enter a word: ", {
  autocomplete: {
    searchFn: findWordStart,
  },
});

In the example above, autocomplete can be initiated from the prompt with a TAB key press. There are a few different prompting behaviors outlined below:

Cycle

This is the default autocompletion behavior, but can also be configured explicitly via the behavior field:

const result = prompt("Enter a word: ", {
  autocomplete: {
    searchFn: findWordStart,
    behavior: AutocompleteBehavior.CYCLE,
  },
});

This behavior cycles through each of the autocomplete results and replaces the input string at the cursor location. At the end of the autocomplete result list, cycle loops around to the start of the list.

Kapture 2022-09-23 at 16 54 12

Suggest

This behavior leaves input intact but outputs columns of suggested words made from the list of autocomplete results. When these is only one matching autocomplete result, the rest of the word is filled in automatically.

const result = prompt("Enter a word: ", {
  autocomplete: {
    searchFn: findWordStart,
    behavior: AutocompleteBehavior.SUGGEST,
  },
});

Kapture 2022-09-23 at 16 56 40

Autocomplete SUGGEST supports some additional configuration:

Resulting column count

Determine how many columns are displayed in the resulting output with the suggestColCount field:

const result = prompt("Enter a word: ", {
  autocomplete: {
    searchFn: findWordStart,
    behavior: AutocompleteBehavior.SUGGEST,
    suggestColCount: 5,
  },
});

The default value is 3. This setting has no impact on the CYCLE behavior.

Kapture 2022-09-23 at 17 00 06

Fill

Determine whether prompt-sync-plus fills user input up to the common starting substring of the results with the fill field:

const result = prompt("Enter a word: ", {
  autocomplete: {
    searchFn: findWordStart,
    behavior: AutocompleteBehavior.SUGGEST,
    fill: true,
  },
});

The default value is false. This setting has no impact on other autocomplete behaviors.

Kapture 2022-09-23 at 17 04 31

Sticky

Determine whether, for the duration of the current prompt execution, autocomplete executes on every key stroke, or only on the configured key (TAB, by default) via the sticky field:

const result = prompt("Enter a word: ", {
  autocomplete: {
    searchFn: findWordStart,
    behavior: AutocompleteBehavior.SUGGEST,
    sticky: true,
  },
});

The default value is false - i.e. autocomplete only triggers on TAB (or whichever key is configured to trigger autocomplete; see additional settings). Note that sticky is only configurable with AutocompleteBehavior.SUGGEST.

Kapture 2022-09-23 at 17 06 35

Hybrid

This behavior is a hybrid of CYCLE and SUGGEST. Prompt-sync-plus will output columns of suggested words based on the autocomplete search results, in addition to filling the input line with each successive word in the list.

const result = prompt("Enter a word: ", {
  autocomplete: {
    searchFn: findWordStart,
    behavior: AutocompleteBehavior.HYBRID,
  },
});

Kapture 2022-09-23 at 17 07 36

Autocomplete trigger

By default, autocomplete triggers on the TAB key, but this is configurable with the triggerKey field:

const result = prompt("Enter a word: ", {
  autocomplete: {
    searchFn: findWordStart,
    triggerKey: 96, // back tick
  },
});

Trigger keys are just defined by the ascii character you want. This library also provides a helpful utility for specifying keys by name to keep your code more self-documenting:

import psp, { Key } from "prompt-sync-plus";

const findWordStart = /* etc */

const prompt = psp({
  autocomplete: {
    searchFn: findWordStart,
    triggerKey: Key.BACK_TICK
  }
});

History

The line history interface hasn't changed from prompt-sync and can be used in the same way:

import psp from "prompt-sync-plus";
import promptSyncHistory from "prompt-sync-history";

const prompt = psp({
  history: promptSyncHistory(),
});

let result = prompt("Prompt 1: ");
result = prompt("Prompt 2: ");
result = prompt("Prompt 3: ");
result = prompt("Prompt 4: ");
result = prompt("Prompt 5: ");

/* user can choose to up or down arrow to scroll through past responses */

// or persist responses to disk
prompt.history.save();

Kapture 2022-09-23 at 17 13 23

See prompt-sync-history to learn more about the expected interface and the resulting API.

Contributing

Contributions are welcome and encouraged! Feel free to:

  • Open an issue to report bugs and request features/enhancements
  • Open a Pull Request - for major changes, please open an issue first to discuss what you would like to change
  • Spread the word - ⭐️ this repo and let others know about prompt-sync-plus

Development

To work on prompt-sync-plus:

  • Clone the repo locally
  • Run npm install to pull down dependencies
  • Run npm run build to compile the TypeScript
    • This generates JavaScript files and sourcemap files in the build directory, as well as TypeScript type definitions in the build/dts directory
    • The second step in the build runs rollup.js to bundle everything into a single source file called index.js in the dist folder
  • Run npm run test to run unit tests or npm run test-coverage to run tests and output a test coverage report
    • This project uses the mocha test framework, the chai assertion library, and c8 tool to generate test coverage reports

In general: prompt-sync-plus development follows the Git Feature Branch workflow - new feature work or bug fixes should be done in a dedicated branch.

Attempt to add tests to the test suite whenever possible.

This project uses husky to run pre- and post-commit git hooks to run code formatting via prettier, build, run tests, and update status badges.

This process will fail if tests fail, code coverage dips below minimum, or the build fails. Github actions run in response to pull requests to build and run tests.

Roadmap

Like any open source project, this one's a work in progress. Additional work includes, but is not limited to:

  • Improving the infrastructure of this project including
    • Git hooks to run code linter, formatter (Prettier), and unit tests prior to push
    • Github actions for automated building, testing, commit squashing, etc.
    • Add a script to run and check npm audit, and generate a badge based on the result. Add badge to readme.
  • Major cleanup and refactoring to remove reliance on global state
    • Maybe a re-write, using existing tests and the same user-facing API
  • Workflow standardization - branch names, PR and issue formatting, etc.
  • Unit test organization, cleanup, and adding new/more tests
  • Continued development to address other pain points of prompt-sync
  • Adding support for pasting text
  • Adding support for SHIFT+ENTER to insert new-line characters and continue writing
  • Development to expand the concept to add more utility:
    • prompt.yesno()
    • prompt.yesnomaybe()
    • prompt.choose(list)
    • etc.
  • JSON object as prompt ask

License

This project is licensed under the MIT license. In general, behave in the spirit of the DBaD license.