Comparing version 0.12.0 to 1.0.0
@@ -8,3 +8,2 @@ /** | ||
/** | ||
@@ -27,6 +26,11 @@ * Client interfaces | ||
inquirer.createPromptModule = function (opt) { | ||
var promptModule = function (questions, allDone) { | ||
var promptModule = function (questions) { | ||
var ui = new inquirer.ui.Prompt(promptModule.prompts, opt); | ||
ui.run(questions, allDone); | ||
return ui; | ||
var promise = ui.run(questions); | ||
// Monkey patch the UI on the promise object so | ||
// that it remains publicly accessible. | ||
promise.ui = ui; | ||
return promise; | ||
}; | ||
@@ -33,0 +37,0 @@ promptModule.prompts = {}; |
'use strict'; | ||
var _ = require('lodash'); | ||
/** | ||
@@ -6,0 +5,0 @@ * Choice object |
@@ -7,3 +7,2 @@ 'use strict'; | ||
/** | ||
@@ -52,3 +51,2 @@ * Choices collection | ||
/** | ||
@@ -65,3 +63,2 @@ * Get a valid choice from the collection | ||
/** | ||
@@ -78,3 +75,2 @@ * Get a raw element from the collection | ||
/** | ||
@@ -90,3 +86,2 @@ * Match the valid choices against a where clause | ||
/** | ||
@@ -102,3 +97,2 @@ * Pluck a particular key from the choices | ||
// Expose usual Array methods | ||
@@ -115,3 +109,5 @@ Choices.prototype.indexOf = function () { | ||
Choices.prototype.push = function () { | ||
var objs = _.map(arguments, function (val) { return new Choice(val); }); | ||
var objs = _.map(arguments, function (val) { | ||
return new Choice(val); | ||
}); | ||
this.choices.push.apply(this.choices, objs); | ||
@@ -118,0 +114,0 @@ this.realChoices = this.choices.filter(Separator.exclude); |
@@ -5,3 +5,2 @@ 'use strict'; | ||
/** | ||
@@ -8,0 +7,0 @@ * Separator object |
@@ -6,17 +6,14 @@ /** | ||
var rx = require('rx-lite'); | ||
var _ = require('lodash'); | ||
var chalk = require('chalk'); | ||
var ansiRegex = require('ansi-regex'); | ||
var runAsync = require('run-async'); | ||
var Choices = require('../objects/choices'); | ||
var ScreenManager = require('../utils/screen-manager'); | ||
var Promise = require('pinkie-promise'); | ||
var Prompt = module.exports = function (question, rl, answers) { | ||
// Setup instance defaults property | ||
_.assign(this, { | ||
answers: answers, | ||
status : 'pending' | ||
status: 'pending' | ||
}); | ||
@@ -26,5 +23,11 @@ | ||
this.opt = _.defaults(_.clone(question), { | ||
validate: function () { return true; }, | ||
filter: function (val) { return val; }, | ||
when: function () { return true; } | ||
validate: function () { | ||
return true; | ||
}, | ||
filter: function (val) { | ||
return val; | ||
}, | ||
when: function () { | ||
return true; | ||
} | ||
}); | ||
@@ -49,12 +52,12 @@ | ||
/** | ||
* Start the Inquiry session and manage output value filtering | ||
* @param {Function} cb Callback when prompt is done | ||
* @return {this} | ||
* @return {Promise} | ||
*/ | ||
Prompt.prototype.run = function( cb ) { | ||
this._run(function (value) { | ||
this.filter(value, cb); | ||
Prompt.prototype.run = function () { | ||
return new Promise(function (resolve) { | ||
this._run(function (value) { | ||
resolve(value); | ||
}); | ||
}.bind(this)); | ||
@@ -64,5 +67,6 @@ }; | ||
// default noop (this one should be overwritten in prompts) | ||
Prompt.prototype._run = function (cb) { cb(); }; | ||
Prompt.prototype._run = function (cb) { | ||
cb(); | ||
}; | ||
/** | ||
@@ -79,14 +83,2 @@ * Throw an error telling a required parameter is missing | ||
/** | ||
* Validate a given input | ||
* @param {String} value Input string | ||
* @param {Function} callback Pass `true` (if input is valid) or an error message as | ||
* parameter. | ||
* @return {null} | ||
*/ | ||
Prompt.prototype.validate = function (input, cb) { | ||
runAsync(this.opt.validate, cb, input); | ||
}; | ||
/** | ||
* Run the provided validation method each time a submit event occur. | ||
@@ -98,8 +90,9 @@ * @param {Rx.Observable} submit - submit event flow | ||
var self = this; | ||
var validate = runAsync(this.opt.validate); | ||
var filter = runAsync(this.opt.filter); | ||
var validation = submit.flatMap(function (value) { | ||
return rx.Observable.create(function (observer) { | ||
runAsync(self.opt.validate, function (isValid) { | ||
observer.onNext({ isValid: isValid, value: self.getCurrentValue(value) }); | ||
observer.onCompleted(); | ||
}, self.getCurrentValue(value), self.answers); | ||
return filter(value).then(function (filteredValue) { | ||
return validate(filteredValue, self.answers).then(function (isValid) { | ||
return {isValid: isValid, value: filteredValue}; | ||
}); | ||
}); | ||
@@ -109,7 +102,11 @@ }).share(); | ||
var success = validation | ||
.filter(function (state) { return state.isValid === true; }) | ||
.filter(function (state) { | ||
return state.isValid === true; | ||
}) | ||
.take(1); | ||
var error = validation | ||
.filter(function (state) { return state.isValid !== true; }) | ||
.filter(function (state) { | ||
return state.isValid !== true; | ||
}) | ||
.takeUntil(success); | ||
@@ -123,48 +120,3 @@ | ||
Prompt.prototype.getCurrentValue = function (value) { | ||
return value; | ||
}; | ||
/** | ||
* Filter a given input before sending back | ||
* @param {String} value Input string | ||
* @param {Function} callback Pass the filtered input as parameter. | ||
* @return {null} | ||
*/ | ||
Prompt.prototype.filter = function (input, cb) { | ||
runAsync(this.opt.filter, cb, input); | ||
}; | ||
/** | ||
* Return the prompt line prefix | ||
* @param {String} [optionnal] String to concatenate to the prefix | ||
* @return {String} prompt prefix | ||
*/ | ||
Prompt.prototype.prefix = function (str) { | ||
str || (str = ''); | ||
return chalk.green('?') + ' ' + str; | ||
}; | ||
/** | ||
* Return the prompt line suffix | ||
* @param {String} [optionnal] String to concatenate to the suffix | ||
* @return {String} prompt suffix | ||
*/ | ||
var reStrEnd = new RegExp('(?:' + ansiRegex().source + ')$|$'); | ||
Prompt.prototype.suffix = function (str) { | ||
str || (str = ''); | ||
// make sure we get the `:` inside the styles | ||
if (str.length < 1 || /[a-z1-9]$/i.test(chalk.stripColor(str))) { | ||
str = str.replace(reStrEnd, ':$&'); | ||
} | ||
return str.trim() + ' '; | ||
}; | ||
/** | ||
* Generate the prompt question string | ||
@@ -178,4 +130,4 @@ * @return {String} prompt question string | ||
// Append the default if available, and if question isn't answered | ||
if ( this.opt.default != null && this.status !== 'answered' ) { | ||
message += chalk.dim('('+ this.opt.default + ') '); | ||
if (this.opt.default != null && this.status !== 'answered') { | ||
message += chalk.dim('(' + this.opt.default + ') '); | ||
} | ||
@@ -182,0 +134,0 @@ |
@@ -5,12 +5,11 @@ /** | ||
var _ = require("lodash"); | ||
var util = require("util"); | ||
var chalk = require("chalk"); | ||
var cliCursor = require("cli-cursor"); | ||
var figures = require("figures"); | ||
var Base = require("./base"); | ||
var observe = require("../utils/events"); | ||
var Paginator = require("../utils/paginator"); | ||
var _ = require('lodash'); | ||
var util = require('util'); | ||
var chalk = require('chalk'); | ||
var cliCursor = require('cli-cursor'); | ||
var figures = require('figures'); | ||
var Base = require('./base'); | ||
var observe = require('../utils/events'); | ||
var Paginator = require('../utils/paginator'); | ||
/** | ||
@@ -22,3 +21,2 @@ * Module exports | ||
/** | ||
@@ -29,11 +27,11 @@ * Constructor | ||
function Prompt() { | ||
Base.apply( this, arguments ); | ||
Base.apply(this, arguments); | ||
if (!this.opt.choices) { | ||
this.throwParamError("choices"); | ||
this.throwParamError('choices'); | ||
} | ||
if ( _.isArray(this.opt.default) ) { | ||
this.opt.choices.forEach(function( choice ) { | ||
if ( this.opt.default.indexOf(choice.value) >= 0 ) { | ||
if (_.isArray(this.opt.default)) { | ||
this.opt.choices.forEach(function (choice) { | ||
if (this.opt.default.indexOf(choice.value) >= 0) { | ||
choice.checked = true; | ||
@@ -52,5 +50,4 @@ } | ||
} | ||
util.inherits( Prompt, Base ); | ||
util.inherits(Prompt, Base); | ||
/** | ||
@@ -62,3 +59,3 @@ * Start the Inquiry session | ||
Prompt.prototype._run = function( cb ) { | ||
Prompt.prototype._run = function (cb) { | ||
this.done = cb; | ||
@@ -68,10 +65,12 @@ | ||
var validation = this.handleSubmitEvents( events.line ); | ||
validation.success.forEach( this.onEnd.bind(this) ); | ||
validation.error.forEach( this.onError.bind(this) ); | ||
var validation = this.handleSubmitEvents( | ||
events.line.map(this.getCurrentValue.bind(this)) | ||
); | ||
validation.success.forEach(this.onEnd.bind(this)); | ||
validation.error.forEach(this.onError.bind(this)); | ||
events.normalizedUpKey.takeUntil( validation.success ).forEach( this.onUpKey.bind(this) ); | ||
events.normalizedDownKey.takeUntil( validation.success ).forEach( this.onDownKey.bind(this) ); | ||
events.numberKey.takeUntil( validation.success ).forEach( this.onNumberKey.bind(this) ); | ||
events.spaceKey.takeUntil( validation.success ).forEach( this.onSpaceKey.bind(this) ); | ||
events.normalizedUpKey.takeUntil(validation.success).forEach(this.onUpKey.bind(this)); | ||
events.normalizedDownKey.takeUntil(validation.success).forEach(this.onDownKey.bind(this)); | ||
events.numberKey.takeUntil(validation.success).forEach(this.onNumberKey.bind(this)); | ||
events.spaceKey.takeUntil(validation.success).forEach(this.onSpaceKey.bind(this)); | ||
@@ -85,3 +84,2 @@ // Init the prompt | ||
/** | ||
@@ -97,13 +95,13 @@ * Render the prompt to screen | ||
if ( this.firstRender ) { | ||
message += "(Press <space> to select)"; | ||
if (this.firstRender) { | ||
message += '(Press <space> to select)'; | ||
} | ||
// Render choices or answer depending on the state | ||
if ( this.status === "answered" ) { | ||
message += chalk.cyan( this.selection.join(", ") ); | ||
if (this.status === 'answered') { | ||
message += chalk.cyan(this.selection.join(', ')); | ||
} else { | ||
var choicesStr = renderChoices(this.opt.choices, this.pointer); | ||
var indexPosition = this.opt.choices.indexOf(this.opt.choices.getChoice(this.pointer)); | ||
message += "\n" + this.paginator.paginate(choicesStr, indexPosition, this.opt.pageSize); | ||
message += '\n' + this.paginator.paginate(choicesStr, indexPosition, this.opt.pageSize); | ||
} | ||
@@ -120,3 +118,2 @@ | ||
/** | ||
@@ -126,6 +123,5 @@ * When user press `enter` key | ||
Prompt.prototype.onEnd = function( state ) { | ||
Prompt.prototype.onEnd = function (state) { | ||
this.status = 'answered'; | ||
this.status = "answered"; | ||
// Rerender prompt (and clean subline error) | ||
@@ -136,6 +132,6 @@ this.render(); | ||
cliCursor.show(); | ||
this.done( state.value ); | ||
this.done(state.value); | ||
}; | ||
Prompt.prototype.onError = function ( state ) { | ||
Prompt.prototype.onError = function (state) { | ||
this.render(state.isValid); | ||
@@ -145,11 +141,11 @@ }; | ||
Prompt.prototype.getCurrentValue = function () { | ||
var choices = this.opt.choices.filter(function( choice ) { | ||
return !!choice.checked && !choice.disabled; | ||
var choices = this.opt.choices.filter(function (choice) { | ||
return Boolean(choice.checked) && !choice.disabled; | ||
}); | ||
this.selection = _.map(choices, "short"); | ||
return _.map(choices, "value"); | ||
this.selection = _.map(choices, 'short'); | ||
return _.map(choices, 'value'); | ||
}; | ||
Prompt.prototype.onUpKey = function() { | ||
Prompt.prototype.onUpKey = function () { | ||
var len = this.opt.choices.realLength; | ||
@@ -160,3 +156,3 @@ this.pointer = (this.pointer > 0) ? this.pointer - 1 : len - 1; | ||
Prompt.prototype.onDownKey = function() { | ||
Prompt.prototype.onDownKey = function () { | ||
var len = this.opt.choices.realLength; | ||
@@ -167,6 +163,6 @@ this.pointer = (this.pointer < len - 1) ? this.pointer + 1 : 0; | ||
Prompt.prototype.onNumberKey = function( input ) { | ||
if ( input <= this.opt.choices.realLength ) { | ||
Prompt.prototype.onNumberKey = function (input) { | ||
if (input <= this.opt.choices.realLength) { | ||
this.pointer = input - 1; | ||
this.toggleChoice( this.pointer ); | ||
this.toggleChoice(this.pointer); | ||
} | ||
@@ -176,3 +172,3 @@ this.render(); | ||
Prompt.prototype.onSpaceKey = function( input ) { | ||
Prompt.prototype.onSpaceKey = function () { | ||
this.toggleChoice(this.pointer); | ||
@@ -182,3 +178,3 @@ this.render(); | ||
Prompt.prototype.toggleChoice = function( index ) { | ||
Prompt.prototype.toggleChoice = function (index) { | ||
var checked = this.opt.choices.getChoice(index).checked; | ||
@@ -185,0 +181,0 @@ this.opt.choices.getChoice(index).checked = !checked; |
@@ -5,9 +5,8 @@ /** | ||
var _ = require("lodash"); | ||
var util = require("util"); | ||
var chalk = require("chalk"); | ||
var Base = require("./base"); | ||
var observe = require("../utils/events"); | ||
var _ = require('lodash'); | ||
var util = require('util'); | ||
var chalk = require('chalk'); | ||
var Base = require('./base'); | ||
var observe = require('../utils/events'); | ||
/** | ||
@@ -19,3 +18,2 @@ * Module exports | ||
/** | ||
@@ -26,27 +24,26 @@ * Constructor | ||
function Prompt() { | ||
Base.apply( this, arguments ); | ||
Base.apply(this, arguments); | ||
var rawDefault = true; | ||
_.extend( this.opt, { | ||
filter: function( input ) { | ||
_.extend(this.opt, { | ||
filter: function (input) { | ||
var value = rawDefault; | ||
if ( input != null && input !== "" ) { | ||
if (input != null && input !== '') { | ||
value = /^y(es)?/i.test(input); | ||
} | ||
return value; | ||
}.bind(this) | ||
} | ||
}); | ||
if ( _.isBoolean(this.opt.default) ) { | ||
if (_.isBoolean(this.opt.default)) { | ||
rawDefault = this.opt.default; | ||
} | ||
this.opt.default = rawDefault ? "Y/n" : "y/N"; | ||
this.opt.default = rawDefault ? 'Y/n' : 'y/N'; | ||
return this; | ||
} | ||
util.inherits( Prompt, Base ); | ||
util.inherits(Prompt, Base); | ||
/** | ||
@@ -58,3 +55,3 @@ * Start the Inquiry session | ||
Prompt.prototype._run = function( cb ) { | ||
Prompt.prototype._run = function (cb) { | ||
this.done = cb; | ||
@@ -64,5 +61,5 @@ | ||
var events = observe(this.rl); | ||
events.keypress.takeUntil( events.line ).forEach( this.onKeypress.bind(this) ); | ||
events.keypress.takeUntil(events.line).forEach(this.onKeypress.bind(this)); | ||
events.line.take(1).forEach( this.onEnd.bind(this) ); | ||
events.line.take(1).forEach(this.onEnd.bind(this)); | ||
@@ -75,3 +72,2 @@ // Init | ||
/** | ||
@@ -85,4 +81,4 @@ * Render the prompt to screen | ||
if (typeof answer === "boolean") { | ||
message += chalk.cyan(answer ? "Yes" : "No"); | ||
if (typeof answer === 'boolean') { | ||
message += chalk.cyan(answer ? 'Yes' : 'No'); | ||
} else { | ||
@@ -101,10 +97,10 @@ message += this.rl.line; | ||
Prompt.prototype.onEnd = function( input ) { | ||
this.status = "answered"; | ||
Prompt.prototype.onEnd = function (input) { | ||
this.status = 'answered'; | ||
var output = this.opt.filter( input ); | ||
this.render( output ); | ||
var output = this.opt.filter(input); | ||
this.render(output); | ||
this.screen.done(); | ||
this.done( input ); // send "input" because the master class will refilter | ||
this.done(output); | ||
}; | ||
@@ -116,4 +112,4 @@ | ||
Prompt.prototype.onKeypress = function() { | ||
this.render(); | ||
Prompt.prototype.onKeypress = function () { | ||
this.render(); | ||
}; |
@@ -5,10 +5,11 @@ /** | ||
var _ = require("lodash"); | ||
var util = require("util"); | ||
var chalk = require("chalk"); | ||
var Base = require("./base"); | ||
var Separator = require("../objects/separator"); | ||
var observe = require("../utils/events"); | ||
var Paginator = require("../utils/paginator"); | ||
var _ = require('lodash'); | ||
var util = require('util'); | ||
var chalk = require('chalk'); | ||
var Base = require('./base'); | ||
var Separator = require('../objects/separator'); | ||
var observe = require('../utils/events'); | ||
var Paginator = require('../utils/paginator'); | ||
var HELP_CHOICE = 'Help, list all options'; | ||
@@ -21,3 +22,2 @@ /** | ||
/** | ||
@@ -28,25 +28,32 @@ * Constructor | ||
function Prompt() { | ||
Base.apply( this, arguments ); | ||
Base.apply(this, arguments); | ||
if ( !this.opt.choices ) { | ||
this.throwParamError("choices"); | ||
if (!this.opt.choices) { | ||
this.throwParamError('choices'); | ||
} | ||
this.validateChoices( this.opt.choices ); | ||
this.validateChoices(this.opt.choices); | ||
// Add the default `help` (/expand) option | ||
this.opt.choices.push({ | ||
key : "h", | ||
name : "Help, list all options", | ||
value : "help" | ||
key: 'h', | ||
name: HELP_CHOICE, | ||
value: 'help' | ||
}); | ||
this.opt.validate = function (choice) { | ||
if (choice == null) { | ||
return 'Please enter a valid command'; | ||
} | ||
return choice !== HELP_CHOICE; | ||
}; | ||
// Setup the default string (capitalize the default key) | ||
this.opt.default = this.generateChoicesString( this.opt.choices, this.opt.default ); | ||
this.opt.default = this.generateChoicesString(this.opt.choices, this.opt.default); | ||
this.paginator = new Paginator(); | ||
} | ||
util.inherits( Prompt, Base ); | ||
util.inherits(Prompt, Base); | ||
/** | ||
@@ -58,3 +65,3 @@ * Start the Inquiry session | ||
Prompt.prototype._run = function( cb ) { | ||
Prompt.prototype._run = function (cb) { | ||
this.done = cb; | ||
@@ -64,4 +71,9 @@ | ||
var events = observe(this.rl); | ||
this.lineObs = events.line.forEach( this.onSubmit.bind(this) ); | ||
this.keypressObs = events.keypress.forEach( this.onKeypress.bind(this) ); | ||
var validation = this.handleSubmitEvents( | ||
events.line.map(this.getCurrentValue.bind(this)) | ||
); | ||
validation.success.forEach(this.onSubmit.bind(this)); | ||
validation.error.forEach(this.onError.bind(this)); | ||
this.keypressObs = events.keypress.takeUntil(validation.success) | ||
.forEach(this.onKeypress.bind(this)); | ||
@@ -74,3 +86,2 @@ // Init the prompt | ||
/** | ||
@@ -85,8 +96,8 @@ * Render the prompt to screen | ||
if ( this.status === "answered" ) { | ||
message += chalk.cyan( this.selected.short || this.selected.name ); | ||
} else if ( this.status === "expanded" ) { | ||
if (this.status === 'answered') { | ||
message += chalk.cyan(this.answer); | ||
} else if (this.status === 'expanded') { | ||
var choicesStr = renderChoices(this.opt.choices, this.selectedKey); | ||
message += this.paginator.paginate(choicesStr, this.selectedKey, this.opt.pageSize); | ||
message += "\n Answer: "; | ||
message += '\n Answer: '; | ||
} | ||
@@ -107,3 +118,14 @@ | ||
Prompt.prototype.getCurrentValue = function (input) { | ||
if (!input) { | ||
input = this.rawDefault; | ||
} | ||
var selected = this.opt.choices.where({key: input.toLowerCase().trim()})[0]; | ||
if (!selected) { | ||
return null; | ||
} | ||
return selected.short || selected.name; | ||
}; | ||
/** | ||
@@ -114,16 +136,16 @@ * Generate the prompt choices string | ||
Prompt.prototype.getChoices = function() { | ||
var output = ""; | ||
Prompt.prototype.getChoices = function () { | ||
var output = ''; | ||
this.opt.choices.forEach(function( choice, i ) { | ||
output += "\n "; | ||
this.opt.choices.forEach(function (choice) { | ||
output += '\n '; | ||
if ( choice.type === "separator" ) { | ||
output += " " + choice; | ||
if (choice.type === 'separator') { | ||
output += ' ' + choice; | ||
return; | ||
} | ||
var choiceStr = choice.key + ") " + choice.name; | ||
if ( this.selectedKey === choice.key ) { | ||
choiceStr = chalk.cyan( choiceStr ); | ||
var choiceStr = choice.key + ') ' + choice.name; | ||
if (this.selectedKey === choice.key) { | ||
choiceStr = chalk.cyan(choiceStr); | ||
} | ||
@@ -136,2 +158,11 @@ output += choiceStr; | ||
Prompt.prototype.onError = function (state) { | ||
if (state.value === HELP_CHOICE) { | ||
this.selectedKey = ''; | ||
this.status = 'expanded'; | ||
this.render(); | ||
return; | ||
} | ||
this.render(state.isValid); | ||
}; | ||
@@ -142,35 +173,12 @@ /** | ||
Prompt.prototype.onSubmit = function( input ) { | ||
if ( input == null || input === "" ) { | ||
input = this.rawDefault; | ||
} | ||
Prompt.prototype.onSubmit = function (state) { | ||
this.status = 'answered'; | ||
this.answer = state.value; | ||
var selected = this.opt.choices.where({ key : input.toLowerCase().trim() })[0]; | ||
if ( selected != null && selected.key === "h" ) { | ||
this.selectedKey = ""; | ||
this.status = "expanded"; | ||
this.render(); | ||
return; | ||
} | ||
if ( selected != null ) { | ||
this.status = "answered"; | ||
this.selected = selected; | ||
// Re-render prompt | ||
this.render(); | ||
this.lineObs.dispose(); | ||
this.keypressObs.dispose(); | ||
this.screen.done(); | ||
this.done( this.selected.value ); | ||
return; | ||
} | ||
// Input is invalid | ||
this.render("Please enter a valid command"); | ||
// Re-render prompt | ||
this.render(); | ||
this.screen.done(); | ||
this.done(state.value); | ||
}; | ||
/** | ||
@@ -180,6 +188,6 @@ * When user press a key | ||
Prompt.prototype.onKeypress = function( s, key ) { | ||
Prompt.prototype.onKeypress = function () { | ||
this.selectedKey = this.rl.line.toLowerCase(); | ||
var selected = this.opt.choices.where({ key : this.selectedKey })[0]; | ||
if ( this.status === "expanded" ) { | ||
var selected = this.opt.choices.where({key: this.selectedKey})[0]; | ||
if (this.status === 'expanded') { | ||
this.render(); | ||
@@ -191,3 +199,2 @@ } else { | ||
/** | ||
@@ -198,26 +205,26 @@ * Validate the choices | ||
Prompt.prototype.validateChoices = function( choices ) { | ||
Prompt.prototype.validateChoices = function (choices) { | ||
var formatError; | ||
var errors = []; | ||
var keymap = {}; | ||
choices.filter(Separator.exclude).map(function( choice ) { | ||
if ( !choice.key || choice.key.length !== 1 ) { | ||
choices.filter(Separator.exclude).forEach(function (choice) { | ||
if (!choice.key || choice.key.length !== 1) { | ||
formatError = true; | ||
} | ||
if ( keymap[choice.key] ) { | ||
if (keymap[choice.key]) { | ||
errors.push(choice.key); | ||
} | ||
keymap[ choice.key ] = true; | ||
choice.key = String( choice.key ).toLowerCase(); | ||
keymap[choice.key] = true; | ||
choice.key = String(choice.key).toLowerCase(); | ||
}); | ||
if ( formatError ) { | ||
throw new Error("Format error: `key` param must be a single letter and is required."); | ||
if (formatError) { | ||
throw new Error('Format error: `key` param must be a single letter and is required.'); | ||
} | ||
if ( keymap.h ) { | ||
throw new Error("Reserved key error: `key` param cannot be `h` - this value is reserved."); | ||
if (keymap.h) { | ||
throw new Error('Reserved key error: `key` param cannot be `h` - this value is reserved.'); | ||
} | ||
if ( errors.length ) { | ||
throw new Error( "Duplicate key error: `key` param must be unique. Duplicates: " + | ||
_.uniq(errors).join(", ") ); | ||
if (errors.length) { | ||
throw new Error('Duplicate key error: `key` param must be unique. Duplicates: ' + | ||
_.uniq(errors).join(', ')); | ||
} | ||
@@ -232,14 +239,13 @@ }; | ||
*/ | ||
Prompt.prototype.generateChoicesString = function( choices, defaultIndex ) { | ||
Prompt.prototype.generateChoicesString = function (choices, defaultIndex) { | ||
var defIndex = 0; | ||
if ( _.isNumber(defaultIndex) && this.opt.choices.getChoice(defaultIndex) ) { | ||
if (_.isNumber(defaultIndex) && this.opt.choices.getChoice(defaultIndex)) { | ||
defIndex = defaultIndex; | ||
} | ||
var defStr = this.opt.choices.pluck("key"); | ||
this.rawDefault = defStr[ defIndex ]; | ||
defStr[ defIndex ] = String( defStr[defIndex] ).toUpperCase(); | ||
return defStr.join(""); | ||
var defStr = this.opt.choices.pluck('key'); | ||
this.rawDefault = defStr[defIndex]; | ||
defStr[defIndex] = String(defStr[defIndex]).toUpperCase(); | ||
return defStr.join(''); | ||
}; | ||
/** | ||
@@ -251,6 +257,6 @@ * Function for rendering checkbox choices | ||
function renderChoices (choices, pointer) { | ||
function renderChoices(choices, pointer) { | ||
var output = ''; | ||
choices.forEach(function (choice, i) { | ||
choices.forEach(function (choice) { | ||
output += '\n '; | ||
@@ -257,0 +263,0 @@ |
@@ -5,8 +5,7 @@ /** | ||
var util = require("util"); | ||
var chalk = require("chalk"); | ||
var Base = require("./base"); | ||
var observe = require("../utils/events"); | ||
var util = require('util'); | ||
var chalk = require('chalk'); | ||
var Base = require('./base'); | ||
var observe = require('../utils/events'); | ||
/** | ||
@@ -18,3 +17,2 @@ * Module exports | ||
/** | ||
@@ -25,7 +23,6 @@ * Constructor | ||
function Prompt() { | ||
return Base.apply( this, arguments ); | ||
return Base.apply(this, arguments); | ||
} | ||
util.inherits( Prompt, Base ); | ||
util.inherits(Prompt, Base); | ||
/** | ||
@@ -37,3 +34,3 @@ * Start the Inquiry session | ||
Prompt.prototype._run = function( cb ) { | ||
Prompt.prototype._run = function (cb) { | ||
this.done = cb; | ||
@@ -43,9 +40,9 @@ | ||
var events = observe(this.rl); | ||
var submit = events.line.map( this.filterInput.bind(this) ); | ||
var submit = events.line.map(this.filterInput.bind(this)); | ||
var validation = this.handleSubmitEvents( submit ); | ||
validation.success.forEach( this.onEnd.bind(this) ); | ||
validation.error.forEach( this.onError.bind(this) ); | ||
var validation = this.handleSubmitEvents(submit); | ||
validation.success.forEach(this.onEnd.bind(this)); | ||
validation.error.forEach(this.onError.bind(this)); | ||
events.keypress.takeUntil( validation.success ).forEach( this.onKeypress.bind(this) ); | ||
events.keypress.takeUntil(validation.success).forEach(this.onKeypress.bind(this)); | ||
@@ -58,3 +55,2 @@ // Init | ||
/** | ||
@@ -82,3 +78,2 @@ * Render the prompt to screen | ||
/** | ||
@@ -88,5 +83,5 @@ * When user press `enter` key | ||
Prompt.prototype.filterInput = function( input ) { | ||
if ( !input ) { | ||
return this.opt.default != null ? this.opt.default : ""; | ||
Prompt.prototype.filterInput = function (input) { | ||
if (!input) { | ||
return this.opt.default == null ? '' : this.opt.default; | ||
} | ||
@@ -96,16 +91,14 @@ return input; | ||
Prompt.prototype.onEnd = function( state ) { | ||
this.filter( state.value, function( filteredValue ) { | ||
this.answer = filteredValue; | ||
this.status = "answered"; | ||
Prompt.prototype.onEnd = function (state) { | ||
this.answer = state.value; | ||
this.status = 'answered'; | ||
// Re-render prompt | ||
this.render(); | ||
// Re-render prompt | ||
this.render(); | ||
this.screen.done(); | ||
this.done( state.value ); | ||
}.bind(this)); | ||
this.screen.done(); | ||
this.done(state.value); | ||
}; | ||
Prompt.prototype.onError = function( state ) { | ||
Prompt.prototype.onError = function (state) { | ||
this.render(state.isValid); | ||
@@ -118,4 +111,4 @@ }; | ||
Prompt.prototype.onKeypress = function() { | ||
Prompt.prototype.onKeypress = function () { | ||
this.render(); | ||
}; |
@@ -5,12 +5,11 @@ /** | ||
var _ = require("lodash"); | ||
var util = require("util"); | ||
var chalk = require("chalk"); | ||
var figures = require("figures"); | ||
var cliCursor = require("cli-cursor"); | ||
var Base = require("./base"); | ||
var observe = require("../utils/events"); | ||
var Paginator = require("../utils/paginator"); | ||
var _ = require('lodash'); | ||
var util = require('util'); | ||
var chalk = require('chalk'); | ||
var figures = require('figures'); | ||
var cliCursor = require('cli-cursor'); | ||
var Base = require('./base'); | ||
var observe = require('../utils/events'); | ||
var Paginator = require('../utils/paginator'); | ||
/** | ||
@@ -22,3 +21,2 @@ * Module exports | ||
/** | ||
@@ -29,6 +27,6 @@ * Constructor | ||
function Prompt() { | ||
Base.apply( this, arguments ); | ||
Base.apply(this, arguments); | ||
if (!this.opt.choices) { | ||
this.throwParamError("choices"); | ||
this.throwParamError('choices'); | ||
} | ||
@@ -42,3 +40,3 @@ | ||
// Default being a Number | ||
if ( _.isNumber(def) && def >= 0 && def < this.opt.choices.realLength ) { | ||
if (_.isNumber(def) && def >= 0 && def < this.opt.choices.realLength) { | ||
this.selected = def; | ||
@@ -48,4 +46,4 @@ } | ||
// Default being a String | ||
if ( _.isString(def) ) { | ||
this.selected = this.opt.choices.pluck("value").indexOf( def ); | ||
if (_.isString(def)) { | ||
this.selected = this.opt.choices.pluck('value').indexOf(def); | ||
} | ||
@@ -58,5 +56,4 @@ | ||
} | ||
util.inherits( Prompt, Base ); | ||
util.inherits(Prompt, Base); | ||
/** | ||
@@ -68,10 +65,13 @@ * Start the Inquiry session | ||
Prompt.prototype._run = function( cb ) { | ||
Prompt.prototype._run = function (cb) { | ||
this.done = cb; | ||
var events = observe(this.rl); | ||
events.normalizedUpKey.takeUntil( events.line ).forEach( this.onUpKey.bind(this) ); | ||
events.normalizedDownKey.takeUntil( events.line ).forEach( this.onDownKey.bind(this) ); | ||
events.numberKey.takeUntil( events.line ).forEach( this.onNumberKey.bind(this) ); | ||
events.line.take(1).forEach( this.onSubmit.bind(this) ); | ||
events.normalizedUpKey.takeUntil(events.line).forEach(this.onUpKey.bind(this)); | ||
events.normalizedDownKey.takeUntil(events.line).forEach(this.onDownKey.bind(this)); | ||
events.numberKey.takeUntil(events.line).forEach(this.onNumberKey.bind(this)); | ||
var validation = this.handleSubmitEvents( | ||
events.line.map(this.getCurrentValue.bind(this)) | ||
); | ||
validation.success.forEach(this.onSubmit.bind(this)); | ||
@@ -85,3 +85,2 @@ // Init the prompt | ||
/** | ||
@@ -92,17 +91,17 @@ * Render the prompt to screen | ||
Prompt.prototype.render = function() { | ||
Prompt.prototype.render = function () { | ||
// Render question | ||
var message = this.getQuestion(); | ||
if ( this.firstRender ) { | ||
message += chalk.dim( "(Use arrow keys)" ); | ||
if (this.firstRender) { | ||
message += chalk.dim('(Use arrow keys)'); | ||
} | ||
// Render choices or answer depending on the state | ||
if ( this.status === "answered" ) { | ||
message += chalk.cyan( this.opt.choices.getChoice(this.selected).short ); | ||
if (this.status === 'answered') { | ||
message += chalk.cyan(this.opt.choices.getChoice(this.selected).short); | ||
} else { | ||
var choicesStr = listRender(this.opt.choices, this.selected ); | ||
var choicesStr = listRender(this.opt.choices, this.selected); | ||
var indexPosition = this.opt.choices.indexOf(this.opt.choices.getChoice(this.selected)); | ||
message += "\n" + this.paginator.paginate(choicesStr, indexPosition, this.opt.pageSize); | ||
message += '\n' + this.paginator.paginate(choicesStr, indexPosition, this.opt.pageSize); | ||
} | ||
@@ -115,3 +114,2 @@ | ||
/** | ||
@@ -121,5 +119,4 @@ * When user press `enter` key | ||
Prompt.prototype.onSubmit = function() { | ||
var choice = this.opt.choices.getChoice( this.selected ); | ||
this.status = "answered"; | ||
Prompt.prototype.onSubmit = function (state) { | ||
this.status = 'answered'; | ||
@@ -131,5 +128,8 @@ // Rerender prompt | ||
cliCursor.show(); | ||
this.done( choice.value ); | ||
this.done(state.value); | ||
}; | ||
Prompt.prototype.getCurrentValue = function () { | ||
return this.opt.choices.getChoice(this.selected).value; | ||
}; | ||
@@ -139,3 +139,3 @@ /** | ||
*/ | ||
Prompt.prototype.onUpKey = function() { | ||
Prompt.prototype.onUpKey = function () { | ||
var len = this.opt.choices.realLength; | ||
@@ -146,3 +146,3 @@ this.selected = (this.selected > 0) ? this.selected - 1 : len - 1; | ||
Prompt.prototype.onDownKey = function() { | ||
Prompt.prototype.onDownKey = function () { | ||
var len = this.opt.choices.realLength; | ||
@@ -153,4 +153,4 @@ this.selected = (this.selected < len - 1) ? this.selected + 1 : 0; | ||
Prompt.prototype.onNumberKey = function( input ) { | ||
if ( input <= this.opt.choices.realLength ) { | ||
Prompt.prototype.onNumberKey = function (input) { | ||
if (input <= this.opt.choices.realLength) { | ||
this.selected = input - 1; | ||
@@ -161,3 +161,2 @@ } | ||
/** | ||
@@ -168,3 +167,3 @@ * Function for rendering list choices | ||
*/ | ||
function listRender(choices, pointer) { | ||
function listRender(choices, pointer) { | ||
var output = ''; | ||
@@ -180,2 +179,10 @@ var separatorOffset = 0; | ||
if (choice.disabled) { | ||
separatorOffset++; | ||
output += ' - ' + choice.name; | ||
output += ' (' + (_.isString(choice.disabled) ? choice.disabled : 'Disabled') + ')'; | ||
output += '\n'; | ||
return; | ||
} | ||
var isSelected = (i - separatorOffset === pointer); | ||
@@ -182,0 +189,0 @@ var line = (isSelected ? figures.pointer + ' ' : ' ') + choice.name; |
@@ -5,6 +5,6 @@ /** | ||
var util = require("util"); | ||
var chalk = require("chalk"); | ||
var Base = require("./base"); | ||
var observe = require("../utils/events"); | ||
var util = require('util'); | ||
var chalk = require('chalk'); | ||
var Base = require('./base'); | ||
var observe = require('../utils/events'); | ||
@@ -26,3 +26,2 @@ function mask(input) { | ||
/** | ||
@@ -33,7 +32,6 @@ * Constructor | ||
function Prompt() { | ||
return Base.apply( this, arguments ); | ||
return Base.apply(this, arguments); | ||
} | ||
util.inherits( Prompt, Base ); | ||
util.inherits(Prompt, Base); | ||
/** | ||
@@ -45,3 +43,3 @@ * Start the Inquiry session | ||
Prompt.prototype._run = function( cb ) { | ||
Prompt.prototype._run = function (cb) { | ||
this.done = cb; | ||
@@ -52,9 +50,9 @@ | ||
// Once user confirm (enter key) | ||
var submit = events.line.map( this.filterInput.bind(this) ); | ||
var submit = events.line.map(this.filterInput.bind(this)); | ||
var validation = this.handleSubmitEvents( submit ); | ||
validation.success.forEach( this.onEnd.bind(this) ); | ||
validation.error.forEach( this.onError.bind(this) ); | ||
var validation = this.handleSubmitEvents(submit); | ||
validation.success.forEach(this.onEnd.bind(this)); | ||
validation.error.forEach(this.onError.bind(this)); | ||
events.keypress.takeUntil( validation.success ).forEach( this.onKeypress.bind(this) ); | ||
events.keypress.takeUntil(validation.success).forEach(this.onKeypress.bind(this)); | ||
@@ -67,3 +65,2 @@ // Init | ||
/** | ||
@@ -95,5 +92,5 @@ * Render the prompt to screen | ||
Prompt.prototype.filterInput = function( input ) { | ||
if ( !input ) { | ||
return this.opt.default != null ? this.opt.default : ""; | ||
Prompt.prototype.filterInput = function (input) { | ||
if (!input) { | ||
return this.opt.default == null ? '' : this.opt.default; | ||
} | ||
@@ -103,4 +100,4 @@ return input; | ||
Prompt.prototype.onEnd = function( state ) { | ||
this.status = "answered"; | ||
Prompt.prototype.onEnd = function (state) { | ||
this.status = 'answered'; | ||
this.answer = state.value; | ||
@@ -112,6 +109,6 @@ | ||
this.screen.done(); | ||
this.done( state.value ); | ||
this.done(state.value); | ||
}; | ||
Prompt.prototype.onError = function( state ) { | ||
Prompt.prototype.onError = function (state) { | ||
this.render(state.isValid); | ||
@@ -125,4 +122,4 @@ this.rl.output.unmute(); | ||
Prompt.prototype.onKeypress = function() { | ||
Prompt.prototype.onKeypress = function () { | ||
this.render(); | ||
}; |
@@ -5,11 +5,10 @@ /** | ||
var _ = require("lodash"); | ||
var util = require("util"); | ||
var chalk = require("chalk"); | ||
var Base = require("./base"); | ||
var Separator = require("../objects/separator"); | ||
var observe = require("../utils/events"); | ||
var Paginator = require("../utils/paginator"); | ||
var _ = require('lodash'); | ||
var util = require('util'); | ||
var chalk = require('chalk'); | ||
var Base = require('./base'); | ||
var Separator = require('../objects/separator'); | ||
var observe = require('../utils/events'); | ||
var Paginator = require('../utils/paginator'); | ||
/** | ||
@@ -21,3 +20,2 @@ * Module exports | ||
/** | ||
@@ -28,6 +26,6 @@ * Constructor | ||
function Prompt() { | ||
Base.apply( this, arguments ); | ||
Base.apply(this, arguments); | ||
if (!this.opt.choices) { | ||
this.throwParamError("choices"); | ||
this.throwParamError('choices'); | ||
} | ||
@@ -41,9 +39,9 @@ | ||
_.extend(this.opt, { | ||
validate: function( index ) { | ||
return this.opt.choices.getChoice( index ) != null; | ||
}.bind(this) | ||
validate: function (val) { | ||
return val != null; | ||
} | ||
}); | ||
var def = this.opt.default; | ||
if ( _.isNumber(def) && def >= 0 && def < this.opt.choices.realLength ) { | ||
if (_.isNumber(def) && def >= 0 && def < this.opt.choices.realLength) { | ||
this.selected = this.rawDefault = def; | ||
@@ -57,5 +55,4 @@ } | ||
} | ||
util.inherits( Prompt, Base ); | ||
util.inherits(Prompt, Base); | ||
/** | ||
@@ -67,3 +64,3 @@ * Start the Inquiry session | ||
Prompt.prototype._run = function( cb ) { | ||
Prompt.prototype._run = function (cb) { | ||
this.done = cb; | ||
@@ -73,9 +70,9 @@ | ||
var events = observe(this.rl); | ||
var submit = events.line.map( this.filterInput.bind(this) ); | ||
var submit = events.line.map(this.getCurrentValue.bind(this)); | ||
var validation = this.handleSubmitEvents( submit ); | ||
validation.success.forEach( this.onEnd.bind(this) ); | ||
validation.error.forEach( this.onError.bind(this) ); | ||
var validation = this.handleSubmitEvents(submit); | ||
validation.success.forEach(this.onEnd.bind(this)); | ||
validation.error.forEach(this.onError.bind(this)); | ||
events.keypress.takeUntil( validation.success ).forEach( this.onKeypress.bind(this) ); | ||
events.keypress.takeUntil(validation.success).forEach(this.onKeypress.bind(this)); | ||
@@ -88,3 +85,2 @@ // Init the prompt | ||
/** | ||
@@ -100,8 +96,8 @@ * Render the prompt to screen | ||
if ( this.status === "answered" ) { | ||
message += chalk.cyan(this.opt.choices.getChoice(this.selected).name); | ||
if (this.status === 'answered') { | ||
message += chalk.cyan(this.answer); | ||
} else { | ||
var choicesStr = renderChoices(this.opt.choices, this.selected); | ||
message += this.paginator.paginate(choicesStr, this.selected, this.opt.pageSize); | ||
message += "\n Answer: "; | ||
message += '\n Answer: '; | ||
} | ||
@@ -122,16 +118,17 @@ | ||
Prompt.prototype.filterInput = function( input ) { | ||
if ( input == null || input === "" ) { | ||
return this.rawDefault; | ||
Prompt.prototype.getCurrentValue = function (index) { | ||
if (index == null || index === '') { | ||
index = this.rawDefault; | ||
} else { | ||
return input - 1; | ||
index -= 1; | ||
} | ||
var choice = this.opt.choices.getChoice(index); | ||
return choice ? choice.value : null; | ||
}; | ||
Prompt.prototype.onEnd = function( state ) { | ||
this.status = "answered"; | ||
this.selected = state.value; | ||
Prompt.prototype.onEnd = function (state) { | ||
this.status = 'answered'; | ||
this.answer = state.value; | ||
var selectedChoice = this.opt.choices.getChoice( this.selected ); | ||
// Re-render prompt | ||
@@ -141,7 +138,7 @@ this.render(); | ||
this.screen.done(); | ||
this.done( selectedChoice.value ); | ||
this.done(state.value); | ||
}; | ||
Prompt.prototype.onError = function() { | ||
this.render("Please enter a valid index"); | ||
Prompt.prototype.onError = function () { | ||
this.render('Please enter a valid index'); | ||
}; | ||
@@ -153,6 +150,6 @@ | ||
Prompt.prototype.onKeypress = function() { | ||
Prompt.prototype.onKeypress = function () { | ||
var index = this.rl.line.length ? Number(this.rl.line) - 1 : 0; | ||
if ( this.opt.choices.getChoice(index) ) { | ||
if (this.opt.choices.getChoice(index)) { | ||
this.selected = index; | ||
@@ -166,3 +163,2 @@ } else { | ||
/** | ||
@@ -190,3 +186,3 @@ * Function for rendering list choices | ||
if (index === pointer) { | ||
display = chalk.cyan( display ); | ||
display = chalk.cyan(display); | ||
} | ||
@@ -193,0 +189,0 @@ output += display; |
@@ -5,3 +5,2 @@ 'use strict'; | ||
/** | ||
@@ -28,3 +27,2 @@ * Base interface class other can inherits from | ||
/** | ||
@@ -40,3 +38,2 @@ * Handle the ^C exit | ||
/** | ||
@@ -58,3 +55,2 @@ * Close the interface and cleanup listeners | ||
this.rl.close(); | ||
this.rl = null; | ||
}; |
@@ -5,9 +5,8 @@ /** | ||
var util = require("util"); | ||
var through = require("through"); | ||
var Base = require("./baseUI"); | ||
var rlUtils = require("../utils/readline"); | ||
var _ = require("lodash"); | ||
var util = require('util'); | ||
var through = require('through'); | ||
var Base = require('./baseUI'); | ||
var rlUtils = require('../utils/readline'); | ||
var _ = require('lodash'); | ||
/** | ||
@@ -23,14 +22,13 @@ * Module exports | ||
function Prompt( opt ) { | ||
function Prompt(opt) { | ||
opt || (opt = {}); | ||
Base.apply( this, arguments ); | ||
Base.apply(this, arguments); | ||
this.log = through( this.writeLog.bind(this) ); | ||
this.bottomBar = opt.bottomBar || ""; | ||
this.log = through(this.writeLog.bind(this)); | ||
this.bottomBar = opt.bottomBar || ''; | ||
this.render(); | ||
} | ||
util.inherits( Prompt, Base ); | ||
util.inherits(Prompt, Base); | ||
/** | ||
@@ -41,3 +39,3 @@ * Render the prompt to screen | ||
Prompt.prototype.render = function() { | ||
Prompt.prototype.render = function () { | ||
this.write(this.bottomBar); | ||
@@ -47,2 +45,6 @@ return this; | ||
Prompt.prototype.clean = function () { | ||
rlUtils.clearLine(this.rl, this.bottomBar.split('\n').length); | ||
return this; | ||
}; | ||
@@ -55,9 +57,11 @@ /** | ||
Prompt.prototype.updateBottomBar = function( bottomBar ) { | ||
Prompt.prototype.updateBottomBar = function (bottomBar) { | ||
this.bottomBar = bottomBar; | ||
rlUtils.clearLine(this.rl, 1); | ||
return this.render(); | ||
this.rl.output.unmute(); | ||
this.clean().render(); | ||
this.rl.output.mute(); | ||
return this; | ||
}; | ||
/** | ||
@@ -68,3 +72,3 @@ * Rerender the prompt | ||
Prompt.prototype.writeLog = function( data ) { | ||
Prompt.prototype.writeLog = function (data) { | ||
rlUtils.clearLine(this.rl, 1); | ||
@@ -75,3 +79,2 @@ this.rl.output.write(this.enforceLF(data.toString())); | ||
/** | ||
@@ -83,4 +86,4 @@ * Make sure line end on a line feed | ||
Prompt.prototype.enforceLF = function( str ) { | ||
return str.match(/[\r\n]$/) ? str : str + "\n"; | ||
Prompt.prototype.enforceLF = function (str) { | ||
return str.match(/[\r\n]$/) ? str : str + '\n'; | ||
}; | ||
@@ -98,10 +101,10 @@ | ||
// Write message to screen and setPrompt to control backspace | ||
this.rl.setPrompt( _.last(msgLines) ); | ||
this.rl.setPrompt(_.last(msgLines)); | ||
if ( this.rl.output.rows === 0 && this.rl.output.columns === 0 ) { | ||
if (this.rl.output.rows === 0 && this.rl.output.columns === 0) { | ||
/* When it's a tty through serial port there's no terminal info and the render will malfunction, | ||
so we need enforce the cursor to locate to the leftmost position for rendering. */ | ||
rlUtils.left( this.rl, message.length + this.rl.line.length ); | ||
rlUtils.left(this.rl, message.length + this.rl.line.length); | ||
} | ||
this.rl.output.write( message ); | ||
this.rl.output.write(message); | ||
}; |
'use strict'; | ||
var _ = require('lodash'); | ||
var rx = require('rx-lite'); | ||
var rx = require('rx'); | ||
var util = require('util'); | ||
@@ -8,4 +8,4 @@ var runAsync = require('run-async'); | ||
var Base = require('./baseUI'); | ||
var Promise = require('pinkie-promise'); | ||
/** | ||
@@ -21,6 +21,5 @@ * Base interface class other can inherits from | ||
PromptUI.prototype.run = function (questions, allDone) { | ||
PromptUI.prototype.run = function (questions) { | ||
// Keep global reference to the answers | ||
this.answers = {}; | ||
this.completed = allDone; | ||
@@ -39,14 +38,16 @@ // Make sure questions is an array. | ||
.concatMap(this.processQuestion.bind(this)) | ||
.publish(); // `publish` creates a hot Observable. It prevents duplicating prompts. | ||
// `publish` creates a hot Observable. It prevents duplicating prompts. | ||
.publish(); | ||
this.process.subscribe( | ||
_.noop, | ||
function (err) { throw err; }, | ||
this.onCompletion.bind(this) | ||
); | ||
this.process.connect(); | ||
return this.process.connect(); | ||
return this.process | ||
.reduce(function (answers, answer) { | ||
this.answers[answer.name] = answer.answer; | ||
return this.answers; | ||
}.bind(this), {}) | ||
.toPromise(Promise) | ||
.then(this.onCompletion.bind(this)); | ||
}; | ||
/** | ||
@@ -56,8 +57,6 @@ * Once all prompt are over | ||
PromptUI.prototype.onCompletion = function () { | ||
PromptUI.prototype.onCompletion = function (answers) { | ||
this.close(); | ||
if (_.isFunction(this.completed)) { | ||
this.completed(this.answers); | ||
} | ||
return answers; | ||
}; | ||
@@ -67,6 +66,3 @@ | ||
return rx.Observable.defer(function () { | ||
var obs = rx.Observable.create(function (obs) { | ||
obs.onNext(question); | ||
obs.onCompleted(); | ||
}); | ||
var obs = rx.Observable.of(question); | ||
@@ -86,9 +82,6 @@ return obs | ||
var prompt = new Prompt(question, this.rl, this.answers); | ||
var answers = this.answers; | ||
return utils.createObservableFromAsync(function () { | ||
var done = this.async(); | ||
prompt.run(function (answer) { | ||
answers[question.name] = answer; | ||
done({ name: question.name, answer: answer }); | ||
}); | ||
return rx.Observable.defer(function () { | ||
return rx.Observable.fromPromise(prompt.run().then(function (answer) { | ||
return {name: question.name, answer: answer}; | ||
})); | ||
}); | ||
@@ -108,26 +101,22 @@ }; | ||
PromptUI.prototype.filterIfRunnable = function (question) { | ||
if (question.when == null) { | ||
if (question.when === false) { | ||
return rx.Observable.empty(); | ||
} | ||
if (!_.isFunction(question.when)) { | ||
return rx.Observable.return(question); | ||
} | ||
var handleResult = function (obs, shouldRun) { | ||
if (shouldRun) { | ||
obs.onNext(question); | ||
} | ||
obs.onCompleted(); | ||
}; | ||
var answers = this.answers; | ||
return rx.Observable.defer(function () { | ||
return rx.Observable.create(function (obs) { | ||
if (_.isBoolean(question.when)) { | ||
handleResult(obs, question.when); | ||
return; | ||
} | ||
runAsync(question.when, function (shouldRun) { | ||
handleResult(obs, shouldRun); | ||
}, answers); | ||
return rx.Observable.fromPromise( | ||
runAsync(question.when)(answers).then(function (shouldRun) { | ||
if (shouldRun) { | ||
return question; | ||
} | ||
}) | ||
).filter(function (val) { | ||
return val != null; | ||
}); | ||
}); | ||
}; |
'use strict'; | ||
var rx = require('rx-lite'); | ||
var rx = require('rx'); | ||
function normalizeKeypressEvents(value, key) { | ||
return { value: value, key: key || {} }; | ||
return {value: value, key: key || {}}; | ||
} | ||
@@ -7,0 +7,0 @@ |
@@ -6,3 +6,2 @@ 'use strict'; | ||
/** | ||
@@ -19,3 +18,3 @@ * The paginator keep trakcs of a pointer index in a list and return | ||
Paginator.prototype.paginate = function (output, active, pageSize) { | ||
var pageSize = pageSize || 7; | ||
pageSize = pageSize || 7; | ||
var lines = output.split('\n'); | ||
@@ -22,0 +21,0 @@ |
@@ -10,3 +10,3 @@ 'use strict'; | ||
exports.left = function(rl, x) { | ||
exports.left = function (rl, x) { | ||
rl.output.write(ansiEscapes.cursorBackward(x)); | ||
@@ -21,3 +21,3 @@ }; | ||
exports.right = function(rl, x) { | ||
exports.right = function (rl, x) { | ||
rl.output.write(ansiEscapes.cursorForward(x)); | ||
@@ -24,0 +24,0 @@ }; |
@@ -56,3 +56,3 @@ 'use strict'; | ||
if (rawPromptLine.length % width === 0) { | ||
content = content + '\n'; | ||
content += '\n'; | ||
} | ||
@@ -59,0 +59,0 @@ var fullContent = content + (bottomContent ? '\n' + bottomContent : ''); |
'use strict'; | ||
var _ = require('lodash'); | ||
var rx = require('rx-lite'); | ||
var rx = require('rx'); | ||
var runAsync = require('run-async'); | ||
/** | ||
* Create an oversable returning the result of a function runned in sync or async mode. | ||
* @param {Function} func Function to run | ||
* @return {rx.Observable} Observable emitting when value is known | ||
*/ | ||
exports.createObservableFromAsync = function (func) { | ||
return rx.Observable.defer(function () { | ||
return rx.Observable.create(function (obs) { | ||
runAsync(func, function (value) { | ||
obs.onNext(value); | ||
obs.onCompleted(); | ||
}); | ||
}); | ||
}); | ||
}; | ||
/** | ||
* Resolve a question property value if it is passed as a function. | ||
@@ -31,3 +12,2 @@ * This method will overwrite the property on the question object with the received value. | ||
* @param {Object} answers - Answers object | ||
* @...rest {Mixed} rest - Arguments to pass to `func` | ||
* @return {rx.Obsersable} - Observable emitting once value is known | ||
@@ -41,9 +21,8 @@ */ | ||
return exports.createObservableFromAsync(function () { | ||
var done = this.async(); | ||
runAsync(question[prop], function (value) { | ||
return rx.Observable.fromPromise(runAsync(question[prop])(answers) | ||
.then(function (value) { | ||
question[prop] = value; | ||
done(question); | ||
}, answers); | ||
}); | ||
return question; | ||
}) | ||
); | ||
}; |
{ | ||
"name": "inquirer", | ||
"version": "0.12.0", | ||
"version": "1.0.0", | ||
"description": "A collection of common interactive command line user interfaces.", | ||
"author": "Simon Boudrias <admin@simonboudrias.com>", | ||
"files": [ | ||
"lib" | ||
], | ||
"main": "lib/inquirer.js", | ||
"scripts": { | ||
"test": "grunt --verbose" | ||
}, | ||
"repository": "SBoudrias/Inquirer.js", | ||
"keywords": [ | ||
@@ -18,10 +18,10 @@ "command", | ||
], | ||
"author": "Simon Boudrias <admin@simonboudrias.com>", | ||
"scripts": { | ||
"test": "gulp", | ||
"prepublish": "gulp prepublish" | ||
}, | ||
"repository": "SBoudrias/Inquirer.js", | ||
"license": "MIT", | ||
"files": [ | ||
"lib" | ||
], | ||
"dependencies": { | ||
"ansi-escapes": "^1.1.0", | ||
"ansi-regex": "^2.0.0", | ||
"chalk": "^1.0.0", | ||
@@ -32,5 +32,6 @@ "cli-cursor": "^1.0.1", | ||
"lodash": "^4.3.0", | ||
"pinkie-promise": "^2.0.0", | ||
"readline2": "^1.0.1", | ||
"run-async": "^0.1.0", | ||
"rx-lite": "^3.1.2", | ||
"run-async": "^2.2.0", | ||
"rx": "^4.1.0", | ||
"string-width": "^1.0.1", | ||
@@ -43,10 +44,36 @@ "strip-ansi": "^3.0.0", | ||
"cmdify": "^0.0.4", | ||
"grunt": "^0.4.1", | ||
"grunt-cli": "^0.1.8", | ||
"grunt-contrib-jshint": "^0.11.1", | ||
"grunt-mocha-test": "^0.12.7", | ||
"eslint": "^2.1.0", | ||
"eslint-config-xo-space": "^0.11.0", | ||
"gulp": "^3.9.0", | ||
"gulp-coveralls": "^0.1.0", | ||
"gulp-eslint": "^2.0.0", | ||
"gulp-exclude-gitignore": "^1.0.0", | ||
"gulp-istanbul": "^0.10.3", | ||
"gulp-line-ending-corrector": "^1.0.1", | ||
"gulp-mocha": "^2.0.0", | ||
"gulp-nsp": "^2.1.0", | ||
"gulp-plumber": "^1.0.0", | ||
"mocha": "^2.2.1", | ||
"mockery": "^1.4.0", | ||
"sinon": "^1.12.1" | ||
}, | ||
"eslintConfig": { | ||
"extends": "xo-space", | ||
"env": { | ||
"mocha": true | ||
}, | ||
"rules": { | ||
"quotes": [ | ||
"error", | ||
"single" | ||
], | ||
"no-unused-expressions": "off", | ||
"handle-callback-err": "off", | ||
"no-eq-null": "off", | ||
"eqeqeq": [ | ||
"error", | ||
"allow-null" | ||
] | ||
} | ||
} | ||
} |
106
README.md
Inquirer.js | ||
=========== | ||
[![npm](https://badge.fury.io/js/inquirer.svg)](http://badge.fury.io/js/inquirer) [![tests](https://travis-ci.org/SBoudrias/Inquirer.js.svg?branch=master)](http://travis-ci.org/SBoudrias/Inquirer.js) [![dependencies](https://david-dm.org/SBoudrias/Inquirer.js.svg?theme=shields.io)](https://david-dm.org/SBoudrias/Inquirer.js) | ||
[![npm](https://badge.fury.io/js/inquirer.svg)](http://badge.fury.io/js/inquirer) [![tests](https://travis-ci.org/SBoudrias/Inquirer.js.svg?branch=master)](http://travis-ci.org/SBoudrias/Inquirer.js) [![Coverage Status](https://coveralls.io/repos/yeoman/generator/badge.svg)](https://coveralls.io/r/SBoudrias/Inquirer.js) [![dependencies](https://david-dm.org/SBoudrias/Inquirer.js.svg?theme=shields.io)](https://david-dm.org/SBoudrias/Inquirer.js) | ||
@@ -13,3 +13,3 @@ A collection of common interactive command line user interfaces. | ||
**`Inquirer.js`** strives to be an easily embeddable and beautiful command line interface for [Node.js](https://nodejs.org/) (and perhaps the "CLI [Xanadu](https://en.wikipedia.org/wiki/Xanadu_(Citizen_Kane))"). | ||
**`Inquirer.js`** strives to be an easily embeddable and beautiful command line interface for [Node.js](https://nodejs.org/) (and perhaps the "CLI [Xanadu](https://en.wikipedia.org/wiki/Citizen_Kane)"). | ||
@@ -35,4 +35,4 @@ **`Inquirer.js`** should ease the process of | ||
```javascript | ||
var inquirer = require("inquirer"); | ||
inquirer.prompt([/* Pass your questions in here */], function( answers ) { | ||
var inquirer = require('inquirer'); | ||
inquirer.prompt([/* Pass your questions in here */]).then(function (answers) { | ||
// Use user feedback for... whatever!! | ||
@@ -55,3 +55,3 @@ }); | ||
`inquirer.prompt( questions, callback )` | ||
#### `inquirer.prompt(questions) -> promise` | ||
@@ -61,5 +61,21 @@ Launch the prompt interface (inquiry session) | ||
- **questions** (Array) containing [Question Object](#question) (using the [reactive interface](#reactive-interface), you can also pass a `Rx.Observable` instance) | ||
- **callback** (Function) first parameter is the [Answers Object](#answers) | ||
- returns a **Promise** | ||
#### `inquirer.registerPrompt(name, prompt)` | ||
Register prompt plugins under `name`. | ||
- **name** (string) name of the this new prompt. (used for question `type`) | ||
- **prompt** (object) the prompt object itself (the plugin) | ||
#### `inquirer.createPromptModule() -> prompt function` | ||
Create a self contained inquirer module. If don't want to affect other libraries that also rely on inquirer when you overwrite or add new prompt types. | ||
```js | ||
var prompt = inquirer.createPromptModule(); | ||
prompt(questions).then(/* ... */); | ||
``` | ||
### Objects | ||
@@ -81,8 +97,13 @@ | ||
`default`, `choices`(if defined as functions), `validate`, `filter` and `when` functions can be called asynchronously using `this.async()`. You just have to pass the value you'd normally return to the callback option. | ||
`default`, `choices`(if defined as functions), `validate`, `filter` and `when` functions can be called asynchronous. Either return a promise or use `this.async()` to get a callback you'll call with the final value. | ||
``` javascript | ||
{ | ||
validate: function(input) { | ||
/* Preferred way: with promise */ | ||
filter: function () { | ||
return new Promise(/* etc... */); | ||
}, | ||
/* Legacy way: with this.async */ | ||
validate: function (input) { | ||
// Declare function as asynchronous, and save the done callback | ||
@@ -92,10 +113,10 @@ var done = this.async(); | ||
// Do async stuff | ||
setTimeout(function() { | ||
if (typeof input !== "number") { | ||
setTimeout(function () { | ||
if (typeof input !== 'number') { | ||
// Pass the return value in the done callback | ||
done("You need to provide a number"); | ||
done('You need to provide a number'); | ||
return; | ||
} | ||
// Pass the return value in the done callback | ||
done(true); | ||
done(null, true); | ||
}, 3000); | ||
@@ -140,3 +161,3 @@ } | ||
#### List - `{ type: "list" }` | ||
#### List - `{type: 'list'}` | ||
@@ -150,3 +171,3 @@ Take `type`, `name`, `message`, `choices`[, `default`, `filter`] properties. (Note that | ||
#### Raw List - `{ type: "rawlist" }` | ||
#### Raw List - `{type: 'rawlist'}` | ||
@@ -160,3 +181,3 @@ Take `type`, `name`, `message`, `choices`[, `default`, `filter`] properties. (Note that | ||
#### Expand - `{ type: "expand" }` | ||
#### Expand - `{type: 'expand'}` | ||
@@ -175,7 +196,7 @@ Take `type`, `name`, `message`, `choices`[, `default`, `filter`] properties. (Note that | ||
#### Checkbox - `{ type: "checkbox" }` | ||
#### Checkbox - `{type: 'checkbox'}` | ||
Take `type`, `name`, `message`, `choices`[, `filter`, `validate`, `default`] properties. `default` is expected to be an Array of the checked choices value. | ||
Choices marked as `{ checked: true }` will be checked by default. | ||
Choices marked as `{checked: true}` will be checked by default. | ||
@@ -188,3 +209,3 @@ Choices whose property `disabled` is truthy will be unselectable. If `disabled` is a string, then the string will be outputted next to the disabled choice, otherwise it'll default to `"Disabled"`. The `disabled` property can also be a synchronous function receiving the current answers as argument and returning a boolean or a string. | ||
#### Confirm - `{ type: "confirm" }` | ||
#### Confirm - `{type: 'confirm'}` | ||
@@ -197,3 +218,3 @@ Take `type`, `name`, `message`[, `default`] properties. `default` is expected to be a boolean if used. | ||
#### Input - `{ type: "input" }` | ||
#### Input - `{type: 'input'}` | ||
@@ -206,3 +227,3 @@ Take `type`, `name`, `message`[, `default`, `filter`, `validate`] properties. | ||
#### Password - `{ type: "password" }` | ||
#### Password - `{type: 'password'}` | ||
@@ -225,18 +246,13 @@ Take `type`, `name`, `message`[, `default`, `filter`, `validate`] properties. | ||
// pipe a Stream to the log zone | ||
outputStream.pipe( ui.log ); | ||
outputStream.pipe(ui.log); | ||
// Or simply write output | ||
ui.log.write("something just happened."); | ||
ui.log.write("Almost over, standby!"); | ||
ui.log.write('something just happened.'); | ||
ui.log.write('Almost over, standby!'); | ||
// During processing, update the bottom bar content to display a loader | ||
// or output a progress bar, etc | ||
ui.updateBottomBar("new bottom bar content"); | ||
ui.updateBottomBar('new bottom bar content'); | ||
``` | ||
#### Prompt - `inquirer.ui.Prompt` | ||
This is UI layout used to run prompt. This layout is returned by `inquirer.prompt` and you should probably always use `inquirer.prompt` to interface with this UI. | ||
## Reactive interface | ||
@@ -249,14 +265,14 @@ | ||
```js | ||
var prompts = Rx.Observable.create(function( obs ) { | ||
obs.onNext({ /* question... */ }); | ||
setTimeout(function () { | ||
obs.onNext({ /* question... */ }); | ||
obs.onCompleted(); | ||
}); | ||
}); | ||
var prompts = new Rx.Subject(); | ||
inquirer.prompt(prompts); | ||
inquirer.prompt(prompts); | ||
// At some point in the future, push new questions | ||
prompts.onNext({ /* question... */ }); | ||
prompts.onNext({ /* question... */ }); | ||
// When you're done | ||
prompts.onCompleted(); | ||
``` | ||
And using the `process` property, you have access to more fine grained callbacks: | ||
And using the return value `process` property, you can access more fine grained callbacks: | ||
@@ -280,2 +296,3 @@ ```js | ||
- **Windows**: | ||
- [ConEmu](https://conemu.github.io/) | ||
- cmd.exe | ||
@@ -296,5 +313,2 @@ - Powershell | ||
**Style Guide** | ||
Please brief yourself on [Idiomatic.js](https://github.com/rwldrn/idiomatic.js) style guide with two space indent | ||
**Unit test** | ||
@@ -304,16 +318,12 @@ Unit test are written in [Mocha](https://mochajs.org/). Please add a unit test for every new feature or bug fix. `npm test` to run the test suite. | ||
**Documentation** | ||
Add documentation for every API change. Feel free to send corrections | ||
or better docs! | ||
Add documentation for every API change. Feel free to send typo fixes and better docs! | ||
**Pull Requests** | ||
Send _fixes_ PR on the `master` branch. Any new features should be send on the `wip`branch. | ||
We're looking to offer good support for multiple prompts and environments. If you want to | ||
help, we'd like to keep a list of testers for each terminal/OS so we can contact you and | ||
get feedback before release. Let us know if you want to be added to the list (just tweet | ||
to @vaxilart) or just add your name to [the wiki](https://github.com/SBoudrias/Inquirer.js/wiki/Testers) | ||
to [@vaxilart](https://twitter.com/Vaxilart)) or just add your name to [the wiki](https://github.com/SBoudrias/Inquirer.js/wiki/Testers) | ||
## License | ||
Copyright (c) 2015 Simon Boudrias (twitter: @vaxilart) | ||
Copyright (c) 2016 Simon Boudrias (twitter: [@vaxilart](https://twitter.com/Vaxilart)) | ||
Licensed under the MIT license. |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
1
312
62463
16
1683
+ Addedpinkie-promise@^2.0.0
+ Addedrx@^4.1.0
+ Addedpinkie@2.0.4(transitive)
+ Addedpinkie-promise@2.0.1(transitive)
+ Addedrun-async@2.4.1(transitive)
+ Addedrx@4.1.0(transitive)
- Removedansi-regex@^2.0.0
- Removedrx-lite@^3.1.2
- Removedonce@1.4.0(transitive)
- Removedrun-async@0.1.0(transitive)
- Removedrx-lite@3.1.2(transitive)
- Removedwrappy@1.0.2(transitive)
Updatedrun-async@^2.2.0