promptly
Advanced tools
Comparing version 2.2.0 to 3.0.0
207
index.js
'use strict'; | ||
var read = require('read'); | ||
var promptly = module.exports; | ||
const prompt = require('./lib/prompt'); | ||
const getOptions = require('./lib/getOptions'); | ||
function prompt(message, opts, fn) { | ||
// Setup read's options | ||
var readOpts = { | ||
prompt: message, | ||
input: opts.input || process.stdin, | ||
output: opts.output || process.stdout, | ||
silent: opts.silent, | ||
replace: opts.replace || '' | ||
}; | ||
const promptly = module.exports; | ||
// Use readline question | ||
read(readOpts, function (err, data) { | ||
// Ignore the error attribute | ||
// It is set on SIGINT or if timeout reached (we are not using timeout) | ||
if (err) { | ||
return; | ||
} | ||
promptly.prompt = (message, options) => { | ||
options = getOptions(options); | ||
// Trim? | ||
if (opts.trim) { | ||
data = data.trim(); | ||
} | ||
return prompt(message, options); | ||
}; | ||
// Mandatory? | ||
if (opts['default'] == null && !data) { | ||
return promptly.prompt(message, opts, fn); | ||
} else { | ||
data = data || opts['default']; | ||
} | ||
// Validator verification | ||
if (opts.validator) { | ||
if (!Array.isArray(opts.validator)) { | ||
opts.validator = [opts.validator]; | ||
} | ||
var x; | ||
var length = opts.validator.length; | ||
for (x = 0; x < length; x += 1) { | ||
try { | ||
data = opts.validator[x](data); | ||
} catch (e) { | ||
// Retry automatically if the retry option is enabled | ||
if (opts.retry) { | ||
if (e.message) { | ||
readOpts.output.write(e.message + '\n'); | ||
} | ||
return promptly.prompt(message, opts, fn); | ||
} | ||
e.retry = promptly.prompt.bind(promptly, message, opts, fn); | ||
return fn(e); | ||
} | ||
} | ||
} | ||
// Everything ok | ||
fn(null, data); | ||
promptly.password = (message, options) => { | ||
options = getOptions({ | ||
silent: true, // Hide password chars | ||
trim: false, // Do not trim so that spaces can be part of the password | ||
default: '', // Allow empty passwords | ||
...options, | ||
}); | ||
} | ||
promptly.prompt = function (message, opts, fn) { | ||
// Arguments parsing | ||
if (typeof opts === 'function') { | ||
fn = opts; | ||
opts = {}; | ||
} else if (!opts) { | ||
opts = {}; | ||
} | ||
return prompt(message, options); | ||
}; | ||
if (opts.trim === undefined) { | ||
opts.trim = true; | ||
} | ||
if (opts.retry === undefined) { | ||
opts.retry = true; | ||
} | ||
if (fn) { | ||
return prompt(message, opts, fn); | ||
} | ||
return new Promise(function (resolve, reject) { | ||
prompt(message, opts, function (err, result) { | ||
if (err) { | ||
return reject(err); | ||
} | ||
resolve(result); | ||
}); | ||
promptly.confirm = (message, options) => { | ||
options = getOptions({ | ||
trim: false, // Do not trim so that only exact matches pass the validator | ||
...options, | ||
}); | ||
}; | ||
promptly.password = function (message, opts, fn) { | ||
// Arguments parsing | ||
if (typeof opts === 'function') { | ||
fn = opts; | ||
opts = {}; | ||
} else { | ||
opts = opts || {}; | ||
} | ||
// Unshift the validator that will coerse boolean values | ||
options.validator.unshift((value) => { | ||
value = value.toLowerCase(); | ||
// Set default options | ||
if (opts.silent === undefined) { | ||
opts.silent = true; | ||
} | ||
if (opts.trim === undefined) { | ||
opts.trim = false; | ||
} | ||
if (opts['default'] === undefined) { | ||
opts['default'] = ''; | ||
} | ||
// Use prompt() | ||
return promptly.prompt(message, opts, fn); | ||
}; | ||
promptly.confirm = function (message, opts, fn) { | ||
// Arguments parsing | ||
if (typeof opts === 'function') { | ||
fn = opts; | ||
opts = {}; | ||
} else if (!opts) { | ||
opts = {}; | ||
} | ||
opts.validator = opts.validator || []; | ||
if (!Array.isArray(opts.validator)) { | ||
opts.validator = [opts.validator]; | ||
} | ||
// Push the validator that will coerse boolean values | ||
var validator = function (value) { | ||
if (typeof value === 'string') { | ||
value = value.toLowerCase(); | ||
} | ||
switch (value) { | ||
@@ -149,3 +39,2 @@ case 'y': | ||
case '1': | ||
case true: | ||
return true; | ||
@@ -155,45 +44,31 @@ case 'n': | ||
case '0': | ||
case false: | ||
return false; | ||
default: | ||
throw new Error(`Invalid choice: ${value}`); | ||
} | ||
}); | ||
throw new Error(); | ||
}; | ||
opts.validator.push(validator); | ||
// Use choose() with true, false | ||
return promptly.choose(message, [true, false], opts, fn); | ||
return prompt(message, options); | ||
}; | ||
promptly.choose = function (message, choices, opts, fn) { | ||
// Arguments parsing | ||
if (typeof opts === 'function') { | ||
fn = opts; | ||
opts = {}; | ||
} else if (!opts) { | ||
opts = {}; | ||
} | ||
promptly.choose = (message, choices, options) => { | ||
options = getOptions({ | ||
trim: false, // Do not trim so that only exact matches pass the validator | ||
...options, | ||
}); | ||
opts.validator = opts.validator || []; | ||
if (!Array.isArray(opts.validator)) { | ||
opts.validator = [opts.validator]; | ||
} | ||
// Unshift the validator that will validate the data against the choices | ||
options.validator.unshift((value) => { | ||
// Check if the value exists by comparing values loosely | ||
// Additionally, use the coorced value | ||
const index = choices.findIndex((choice) => value == choice); // eslint-disable-line eqeqeq | ||
// Push the choice validator | ||
var nrChoices = choices.length; | ||
var validator = function (value) { | ||
var x; | ||
for (x = 0; x < nrChoices; x++) { | ||
if (choices[x] == value) { | ||
return choices[x]; | ||
} | ||
if (index === -1) { | ||
throw new Error(`Invalid choice: ${value}`); | ||
} | ||
throw new Error('Invalid choice: ' + value); | ||
}; | ||
opts.validator.push(validator); | ||
return choices[index]; | ||
}); | ||
// Use prompt() | ||
return promptly.prompt(message, opts, fn); | ||
return prompt(message, options); | ||
}; |
{ | ||
"name": "promptly", | ||
"version": "2.2.0", | ||
"version": "3.0.0", | ||
"description": "Simple command line prompting utility", | ||
"main": "index.js", | ||
"dependencies": { | ||
"read": "^1.0.4" | ||
}, | ||
"devDependencies": { | ||
"async": "^2.0.0", | ||
"expect.js": "^0.3.1", | ||
"mocha": "^3.0.2" | ||
}, | ||
"scripts": { | ||
"test": "mocha --bail" | ||
}, | ||
"repository": { | ||
"type": "git", | ||
"url": "git://github.com/IndigoUnited/node-promptly" | ||
}, | ||
"bugs": { | ||
"url": "http://github.com/IndigoUnited/node-promptly/issues" | ||
}, | ||
"keywords": [ | ||
@@ -32,4 +13,52 @@ "prompt", | ||
], | ||
"author": "IndigoUnited <hello@indigounited.com> (http://indigounited.com)", | ||
"license": "MIT" | ||
"author": "André Cruz <andre@moxy.studio>", | ||
"homepage": "https://github.com/moxystudio/node-promptly", | ||
"repository": { | ||
"type": "git", | ||
"url": "git@github.com:moxystudio/node-promptly.git" | ||
}, | ||
"license": "MIT", | ||
"main": "index.js", | ||
"files": [ | ||
"lib" | ||
], | ||
"scripts": { | ||
"lint": "eslint .", | ||
"test": "jest --env node --coverage --runInBand", | ||
"prerelease": "npm t && npm run lint", | ||
"release": "standard-version", | ||
"precommit": "lint-staged", | ||
"commitmsg": "commitlint -e $GIT_PARAMS" | ||
}, | ||
"standard-version": { | ||
"scripts": { | ||
"posttag": "git push --follow-tags origin master && npm publish" | ||
} | ||
}, | ||
"lint-staged": { | ||
"*.js": [ | ||
"eslint --fix", | ||
"git add" | ||
] | ||
}, | ||
"commitlint": { | ||
"extends": [ | ||
"@commitlint/config-conventional" | ||
] | ||
}, | ||
"dependencies": { | ||
"read": "^1.0.4", | ||
"strip-ansi": "^4.0.0" | ||
}, | ||
"devDependencies": { | ||
"@commitlint/cli": "^6.0.0", | ||
"@commitlint/config-conventional": "^6.0.2", | ||
"eslint": "^4.3.0", | ||
"eslint-config-moxy": "^4.1.0", | ||
"husky": "^0.14.3", | ||
"jest": "^22.0.0", | ||
"lint-staged": "^6.0.0", | ||
"p-series": "^1.0.0", | ||
"standard-version": "^4.2.0" | ||
} | ||
} |
229
README.md
# promptly | ||
[![NPM version][npm-image]][npm-url] [![Downloads][downloads-image]][npm-url] [![Build Status][travis-image]][travis-url] [![Dependency status][david-dm-image]][david-dm-url] [![Dev Dependency status][david-dm-dev-image]][david-dm-dev-url] | ||
[![NPM version][npm-image]][npm-url] [![Downloads][downloads-image]][npm-url] [![Build Status][travis-image]][travis-url] [![Coverage Status][codecov-image]][codecov-url] [![Dependency status][david-dm-image]][david-dm-url] [![Dev Dependency status][david-dm-dev-image]][david-dm-dev-url] [![Greenkeeper badge][greenkeeper-image]][greenkeeper-url] | ||
@@ -8,8 +8,12 @@ [npm-url]:https://npmjs.org/package/promptly | ||
[npm-image]:http://img.shields.io/npm/v/promptly.svg | ||
[travis-url]:https://travis-ci.org/IndigoUnited/node-promptly | ||
[travis-image]:http://img.shields.io/travis/IndigoUnited/node-promptly/master.svg | ||
[david-dm-url]:https://david-dm.org/IndigoUnited/node-promptly | ||
[david-dm-image]:https://img.shields.io/david/IndigoUnited/node-promptly.svg | ||
[david-dm-dev-url]:https://david-dm.org/IndigoUnited/node-promptly#info=devDependencies | ||
[david-dm-dev-image]:https://img.shields.io/david/dev/IndigoUnited/node-promptly.svg | ||
[travis-url]:https://travis-ci.org/moxystudio/node-promptly | ||
[travis-image]:http://img.shields.io/travis/moxystudio/node-promptly/master.svg | ||
[codecov-url]:https://codecov.io/gh/moxystudio/node-promptly | ||
[codecov-image]:https://img.shields.io/codecov/c/github/moxystudio/node-ppromptly/master.svg | ||
[david-dm-url]:https://david-dm.org/moxystudio/node-promptly | ||
[david-dm-image]:https://img.shields.io/david/moxystudio/node-promptly.svg | ||
[david-dm-dev-url]:https://david-dm.org/moxystudio/node-promptly?type=dev | ||
[david-dm-dev-image]:https://img.shields.io/david/dev/moxystudio/node-promptly.svg | ||
[greenkeeper-image]:https://badges.greenkeeper.io/moxystudio/node-promptly.svg | ||
[greenkeeper-url]:https://greenkeeper.io/ | ||
@@ -26,155 +30,152 @@ Simple command line prompting utility. | ||
### .prompt(message, [opts], [fn]) | ||
### .prompt(message, [options]) | ||
Prompts for a value, printing the `message` and waiting for the input. | ||
When done, calls `fn` with `error` and `value` or returns a `Promise` if no `fn` is provided. | ||
Returns a promise that resolves with the input. | ||
Default options: | ||
```js | ||
{ | ||
// The default value. If not supplied, the input is mandatory | ||
'default': null, | ||
// Automatically trim the input | ||
'trim': true, | ||
// A validator or an array of validators. | ||
'validator': null, | ||
// Automatically retry if a validator fails | ||
'retry': true, | ||
// Do not print what the user types | ||
'silent': false, | ||
// Replace each character with the specified string when 'silent' is true | ||
'replace': '', | ||
// Input and output streams to read and write to | ||
'input': process.stdin, | ||
'output': process.stdout | ||
} | ||
``` | ||
Available options: | ||
The validators have two purposes: | ||
| Name | Description | Type | Default | | ||
| ------ | ------------- | -------- | ------- | | ||
| default | The default value to use if the user provided an empty input | string | undefined | | ||
| trim | Trims the user input | boolean | true | | ||
| validator | A validator or an array of validators | Function/Array | undefined | | ||
| retry | Retry if any of the validators fail | boolean | true | | ||
| silent | Do not print what the user types | boolean | false | | ||
| replace | Replace each character with the specified string when `silent` is true | string | '' | | ||
| input | Input stream to read from | [Stream](https://nodejs.org/api/process.html#process_process_stdin) | process.stdin | | ||
| output | Output stream to write to | [Stream](https://nodejs.org/api/process.html#process_process_stdout) | process.stdout | | ||
```js | ||
function (value) { | ||
// Validation example, throwing an error when invalid | ||
if (value.length !== 2) { | ||
throw new Error('Length must be 2'); | ||
} | ||
The same **options** are available to **all functions** but with different default values. | ||
// Parse the value, modifying it | ||
return value.replace('aa', 'bb'); | ||
} | ||
``` | ||
#### Examples | ||
Example usages | ||
- Ask for a name: | ||
Ask for a name: | ||
```js | ||
const name = await promptly.prompt('Name: '); | ||
```js | ||
promptly.prompt('Name: ', function (err, value) { | ||
// err is always null in this case, because no validators are set | ||
console.log(value); | ||
}); | ||
``` | ||
console.log(name); | ||
``` | ||
Using Promise: | ||
- Ask for a name with a constraint (non-empty value and length > 2): | ||
```js | ||
promptly.prompt('Name: ') | ||
.then(function (value) { | ||
// no need for catch in this case, because no validators are set | ||
console.log(value); | ||
}); | ||
``` | ||
```js | ||
const validator = function (value) { | ||
if (value.length < 2) { | ||
throw new Error('Min length of 2'); | ||
} | ||
Ask for a name with a constraint (non-empty value and length > 2): | ||
return value; | ||
}; | ||
```js | ||
var validator = function (value) { | ||
if (value.length < 2) { | ||
throw new Error('Min length of 2'); | ||
} | ||
const name = await promptly.prompt('Name: ', { validator }); | ||
return value; | ||
}; | ||
promptly.prompt('Name: ', { validator: validator }, function (err, value) { | ||
// Since retry is true by default, err is always null | ||
// because promptly will be prompting for a name until it validates | ||
// Since retry is true by default, promptly will keep asking for a name until it is valid | ||
// Between each prompt, the error message from the validator will be printed | ||
console.log('Name is:', value); | ||
}); | ||
``` | ||
``` | ||
Same as above but do not retry automatically: | ||
- Same as above but do not retry automatically: | ||
```js | ||
var validator = function (value) { | ||
if (value.length < 2) { | ||
throw new Error('Min length of 2'); | ||
```js | ||
var validator = function (value) { | ||
if (value.length < 2) { | ||
throw new Error('Min length of 2'); | ||
} | ||
return value; | ||
}; | ||
try { | ||
const name = promptly.prompt('Name: ', { validator, retry: false }); | ||
console.log('Name is:', value); | ||
} catch (err) { | ||
console.error('Invalid name:') | ||
console.error(`- ${err.message}`); | ||
} | ||
``` | ||
return value; | ||
}; | ||
#### Validators | ||
promptly.prompt('Name: ', { validator: validator, retry: false }, function (err, value) { | ||
if (err) { | ||
console.error('Invalid name:', err.message); | ||
// Manually call retry | ||
// The passed error has a retry method to easily prompt again. | ||
return err.retry(); | ||
The validators have two purposes: to validate and to transform input. | ||
```js | ||
(value) => { | ||
// Validation example, throwing an error when invalid | ||
if (value.length !== 2) { | ||
throw new Error('Length must be 2'); | ||
} | ||
console.log('Name is:', value); | ||
}); | ||
// Parse the value, modifying it | ||
return value.replace('aa', 'bb'); | ||
} | ||
``` | ||
### .confirm(message, [opts], fn) | ||
### .confirm(message, [options]) | ||
Ask the user to confirm something. | ||
Calls `fn` with `error` and `value` (true or false). | ||
Ask the user for confirmation, printing the `message` and waiting for the input. | ||
Returns a promise that resolves with the answer. | ||
Truthy values are: `y`, `yes` and `1`. | ||
Falsy values are `n`, `no`, and `0`. | ||
Truthy values are: `y`, `yes` and `1`. Falsy values are `n`, `no`, and `0`. | ||
Comparison is made in a case insensitive way. | ||
Example usage: | ||
The same options from [prompt](#promptmessage-options) are available. | ||
```js | ||
promptly.confirm('Are you sure? ', function (err, value) { | ||
console.log('Answer:', value); | ||
}); | ||
``` | ||
#### Examples | ||
- Ask to confirm something important: | ||
### .choose(message, choices, [opts], fn) | ||
```js | ||
const answer = await promptly.confirm('Are you really sure? '); | ||
Ask the user to choose between multiple `choices` (array of choices). | ||
Calls `fn` with `error` and `value`. | ||
console.log('Answer:', answer); | ||
``` | ||
Example usage: | ||
### .choose(message, choices, [options]) | ||
```js | ||
promptly.choose('Do you want an apple or an orange? ', ['apple', 'orange'], function (err, value) { | ||
console.log('Answer:', value); | ||
}); | ||
``` | ||
Ask the user to choose between multiple `choices` (array of choices), printing the `message` and waiting for the input. | ||
Returns a promise that resolves with the choice. | ||
The same options from [prompt](#promptmessage-options) are available. | ||
### .password(message, [opts], fn) | ||
#### Examples | ||
- Ask to choose between: | ||
```js | ||
const choice = promptly.choose('Do you want an apple or an orange? ', ['apple', 'orange']); | ||
console.log('Choice:', choice); | ||
``` | ||
### .password(message, [options]) | ||
Prompts for a password, printing the `message` and waiting for the input. | ||
When available, calls `fn` with `error` and `value`. | ||
Returns a promise that resolves with the password. | ||
The available options are the same, except that `trim` and `silent` default to `false` and `default` is an empty string (to allow empty passwords). | ||
The same options from [prompt](#promptmessage-options) are available, except that `trim` and `silent` default to `false` and `default` is an empty string to allow empty passwords. | ||
Example usage: | ||
#### Examples | ||
```js | ||
promptly.password('Type a password: ', { replace: '*' }, function (err, value) { | ||
console.log('Password is:', value); | ||
}); | ||
``` | ||
- Ask for a password: | ||
```js | ||
const password = promptly.password('Type a password: '); | ||
console.log('Password:', password); | ||
``` | ||
- Ask for a password but mask the input with `*`: | ||
```js | ||
const password = promptly.password('Type a password: ', { replace: '*' }); | ||
console.log('Password:', password); | ||
``` | ||
## License | ||
Released under the [MIT License](http://www.opensource.org/licenses/mit-license.php). |
Sorry, the diff of this file is not supported yet
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
180
12643
2
9
7
121
1
+ Addedstrip-ansi@^4.0.0
+ Addedansi-regex@3.0.1(transitive)
+ Addedstrip-ansi@4.0.0(transitive)