prompt-sync-plus
v1.0.2
Published
Yet another synchronous prompt for Node.js
Downloads
88
Maintainers
Readme
prompt-sync-plus
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}'`);
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
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:
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}'`);
Handling sensitive input
For password entry, etc. character input can be obscured via the echo
field:
const result = prompt("Password: ", { echo: "*" });
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: ");
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,
});
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,
});
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.
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,
},
});
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.
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.
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
.
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,
},
});
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();
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 ornpm run test-coverage
to run tests and output a test coverage report
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.