Comparing version 0.5.1 to 0.6.0
@@ -33,9 +33,11 @@ /** | ||
* Public CLI helper interface | ||
* @param {Array} questions Questions settings array | ||
* @param {Function} cb Callback being passed the user answers | ||
* @return {null} | ||
* @param {Array|Object|rx.Observable} questions - Questions settings array | ||
* @param {Function} cb - Callback being passed the user answers | ||
* @return {inquirer.ui.Prompt} | ||
*/ | ||
inquirer.prompt = function( questions, allDone ) { | ||
return new inquirer.ui.Prompt( questions, allDone ); | ||
var prompt = new inquirer.ui.Prompt(); | ||
prompt.run( questions, allDone ); | ||
return prompt; | ||
}; |
@@ -13,4 +13,4 @@ /** | ||
var tty = require("../utils/tty"); | ||
var rx = require("rx"); | ||
/** | ||
@@ -141,3 +141,33 @@ * Module exports | ||
/** | ||
* Run the provided validation method each time a submit event occur. | ||
* @param {Rx.Observable} submit - submit event flow | ||
* @return {Object} Object containing two observables: `success` and `error` | ||
*/ | ||
Prompt.prototype.handleSubmitEvents = function( submit ) { | ||
var opt = this.opt; | ||
var validation = submit.flatMap(function( value ) { | ||
return rx.Observable.create(function( observer ) { | ||
utils.runAsync( opt.validate, function( isValid ) { | ||
observer.onNext({ isValid: isValid, value: value }); | ||
observer.onCompleted(); | ||
}, value ); | ||
}); | ||
}).share(); | ||
var success = validation | ||
.filter(function( state ) { return state.isValid === true; }) | ||
.take(1); | ||
var error = validation | ||
.filter(function( state ) { return state.isValid !== true; }) | ||
.takeUntil(success); | ||
return { | ||
success: success, | ||
error: error | ||
}; | ||
}; | ||
/** | ||
@@ -144,0 +174,0 @@ * Filter a given input before sending back |
@@ -10,2 +10,3 @@ /** | ||
var utils = require("../utils/utils"); | ||
var observe = require("../utils/events"); | ||
@@ -61,8 +62,9 @@ | ||
// Move the selected marker on keypress | ||
this.rl.on( "keypress", this.onKeypress.bind(this) ); | ||
var events = observe(this.rl); | ||
this.lineObs = events.line.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) ); | ||
events.spaceKey.takeUntil( events.line ).forEach( this.onSpaceKey.bind(this) ); | ||
// Once user confirm (enter key) | ||
this.rl.on( "line", this.onSubmit.bind(this) ); | ||
// Init the prompt | ||
@@ -134,4 +136,3 @@ this.render(); | ||
this.rl.removeAllListeners("keypress"); | ||
this.rl.removeAllListeners("line"); | ||
this.lineObs.dispose(); | ||
this.done( answer ); | ||
@@ -182,3 +183,48 @@ } else { | ||
Prompt.prototype.handleKeypress = function(action) { | ||
this.rl.output.unmute(); | ||
action(); | ||
// Rerender | ||
this.clean().render(); | ||
this.rl.output.mute(); | ||
}; | ||
Prompt.prototype.onUpKey = function() { | ||
this.handleKeypress(function() { | ||
var len = this.opt.choices.realLength; | ||
this.pointer = (this.pointer > 0) ? this.pointer - 1 : len - 1; | ||
}.bind(this)); | ||
}; | ||
Prompt.prototype.onDownKey = function() { | ||
this.handleKeypress(function() { | ||
var len = this.opt.choices.realLength; | ||
this.pointer = (this.pointer < len - 1) ? this.pointer + 1 : 0; | ||
}.bind(this)); | ||
}; | ||
Prompt.prototype.onNumberKey = function( input ) { | ||
this.handleKeypress(function() { | ||
if ( input <= this.opt.choices.realLength ) { | ||
this.pointer = input - 1; | ||
this.toggleChoice( this.pointer ); | ||
} | ||
}.bind(this)); | ||
}; | ||
Prompt.prototype.onSpaceKey = function( input ) { | ||
this.handleKeypress(function() { | ||
this.toggleChoice(this.pointer); | ||
}.bind(this)); | ||
}; | ||
Prompt.prototype.toggleChoice = function( index ) { | ||
var checked = this.opt.choices.getChoice(index).checked; | ||
this.opt.choices.getChoice(index).checked = !checked; | ||
}; | ||
/** | ||
@@ -185,0 +231,0 @@ * Function for rendering checkbox choices |
@@ -9,2 +9,3 @@ /** | ||
var Base = require("./base"); | ||
var observe = require("../utils/events"); | ||
@@ -59,3 +60,3 @@ | ||
// Once user confirm (enter key) | ||
this.rl.once( "line", this.onSubmit.bind(this) ); | ||
observe(this.rl).line.take(1).forEach( this.onEnd.bind(this) ); | ||
@@ -86,17 +87,15 @@ // Init | ||
/** | ||
* When user press "enter" key | ||
* When user press `enter` key | ||
*/ | ||
Prompt.prototype.onSubmit = function( input ) { | ||
Prompt.prototype.onEnd = function( input ) { | ||
this.status = "answered"; | ||
// Filter value to write final answer to screen | ||
this.filter( input, function( output ) { | ||
this.clean(1).render(); | ||
this.write( chalk.cyan(output ? "Yes" : "No") + "\n" ); | ||
var output = this.opt.filter( input ); | ||
this.done( input ); // send "input" because the master class will refilter | ||
}.bind(this)); | ||
this.clean(1).render(); | ||
this.write( chalk.cyan(output ? "Yes" : "No") + "\n" ); | ||
this.done( input ); // send "input" because the master class will refilter | ||
}; |
@@ -10,2 +10,3 @@ /** | ||
var Separator = require("../objects/separator"); | ||
var observe = require("../utils/events"); | ||
@@ -31,3 +32,3 @@ | ||
this.validate(); | ||
this.validateChoices( this.opt.choices ); | ||
@@ -44,10 +45,3 @@ // Add the default `help` (/expand) option | ||
// Setup the default string (capitalize the default key) | ||
var defIndex = 0; | ||
if ( _.isNumber(this.opt.default) && this.opt.choices.getChoice(this.opt.default) ) { | ||
defIndex = this.opt.default; | ||
} | ||
var defStr = this.opt.choices.pluck("key"); | ||
this.rawDefault = defStr[ defIndex ]; | ||
defStr[ defIndex ] = String( defStr[defIndex] ).toUpperCase(); | ||
this.opt.default = defStr.join(""); | ||
this.opt.default = this.generateChoicesString( this.opt.choices, this.opt.default ); | ||
@@ -69,4 +63,5 @@ return this; | ||
// Save user answer and update prompt to show selected option. | ||
this.rl.on( "line", this.onSubmit.bind(this) ); | ||
this.rl.on( "keypress", this.onKeypress.bind(this) ); | ||
var events = observe(this.rl); | ||
this.lineObs = events.line.forEach( this.onSubmit.bind(this) ); | ||
this.keypressObs = events.keypress.forEach( this.onKeypress.bind(this) ); | ||
@@ -159,4 +154,4 @@ // Init the prompt | ||
this.rl.removeAllListeners("line"); | ||
this.rl.removeAllListeners("keypress"); | ||
this.lineObs.dispose(); | ||
this.keypressObs.dispose(); | ||
this.done( this.selected.value ); | ||
@@ -198,9 +193,10 @@ return; | ||
* Validate the choices | ||
* @param {Array} choices | ||
*/ | ||
Prompt.prototype.validate = function() { | ||
Prompt.prototype.validateChoices = function( choices ) { | ||
var formatError; | ||
var errors = []; | ||
var keymap = {}; | ||
this.opt.choices.filter(Separator.exclude).map(function( choice ) { | ||
choices.filter(Separator.exclude).map(function( choice ) { | ||
if ( !choice.key || choice.key.length !== 1 ) { | ||
@@ -228,3 +224,20 @@ formatError = true; | ||
/** | ||
* Generate a string out of the choices keys | ||
* @param {Array} choices | ||
* @param {Number} defaultIndex - the choice index to capitalize | ||
* @return {String} The rendered choices key string | ||
*/ | ||
Prompt.prototype.generateChoicesString = function( choices, defaultIndex ) { | ||
var defIndex = 0; | ||
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(""); | ||
}; | ||
/** | ||
@@ -231,0 +244,0 @@ * Function for rendering checkbox choices |
@@ -9,3 +9,5 @@ /** | ||
var Base = require("./base"); | ||
var observe = require("../utils/events"); | ||
/** | ||
@@ -38,4 +40,8 @@ * Module exports | ||
// Once user confirm (enter key) | ||
this.rl.on( "line", this.onSubmit.bind(this) ); | ||
var submit = observe(this.rl).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) ); | ||
// Init | ||
@@ -70,26 +76,25 @@ this.render(); | ||
Prompt.prototype.onSubmit = function( input ) { | ||
var value = input; | ||
if ( !value ) { | ||
value = this.opt.default != null ? this.opt.default : ""; | ||
Prompt.prototype.filterInput = function( input ) { | ||
if ( !input ) { | ||
return this.opt.default != null ? this.opt.default : ""; | ||
} | ||
this.validate( value, function( isValid ) { | ||
if ( isValid === true ) { | ||
this.filter( value, function( value ) { | ||
this.status = "answered"; | ||
return input; | ||
}; | ||
// Re-render prompt | ||
this.clean(1).render(); | ||
Prompt.prototype.onEnd = function( state ) { | ||
this.filter( state.value, function( filteredValue ) { | ||
this.status = "answered"; | ||
// Render answer | ||
this.write( chalk.cyan(value) + "\n" ); | ||
// Re-render prompt | ||
this.clean(1).render(); | ||
this.rl.removeAllListeners("line"); | ||
this.done( value ); | ||
}.bind(this)); | ||
} else { | ||
this.error( isValid ).clean().render(); | ||
} | ||
// Render answer | ||
this.write( chalk.cyan(filteredValue) + "\n" ); | ||
this.done( state.value ); | ||
}.bind(this)); | ||
}; | ||
Prompt.prototype.onError = function( state ) { | ||
this.error( state.isValid ).clean().render(); | ||
}; |
@@ -10,2 +10,3 @@ /** | ||
var utils = require("../utils/utils"); | ||
var observe = require("../utils/events"); | ||
@@ -65,8 +66,8 @@ | ||
// Move the selected marker on keypress | ||
this.rl.on( "keypress", this.onKeypress.bind(this) ); | ||
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) ); | ||
// Once user confirm (enter key) | ||
this.rl.once( "line", this.onSubmit.bind(this) ); | ||
// Init the prompt | ||
@@ -132,3 +133,2 @@ this.render(); | ||
this.rl.removeAllListeners("keypress"); | ||
this.done( choice.value ); | ||
@@ -142,19 +142,6 @@ }; | ||
Prompt.prototype.onKeypress = function( s, key ) { | ||
// Only process up, down, j, k and 1-9 keys | ||
var keyWhitelist = [ "up", "down", "j", "k" ]; | ||
if ( key && !_.contains(keyWhitelist, key.name) ) return; | ||
if ( key && (key.name === "j" || key.name === "k") ) s = undefined; | ||
if ( s && "123456789".indexOf(s) < 0 ) return; | ||
Prompt.prototype.handleKeypress = function(action) { | ||
this.rl.output.unmute(); | ||
var len = this.opt.choices.realLength; | ||
if ( key && (key.name === "up" || key.name === "k") ) { | ||
(this.selected > 0) ? this.selected-- : (this.selected = len - 1); | ||
} else if ( key && (key.name === "down" || key.name === "j") ) { | ||
(this.selected < len - 1) ? this.selected++ : (this.selected = 0); | ||
} else if ( Number(s) <= len ) { | ||
this.selected = Number(s) - 1; | ||
} | ||
action(); | ||
@@ -167,3 +154,25 @@ // Rerender | ||
Prompt.prototype.onUpKey = function() { | ||
this.handleKeypress(function() { | ||
var len = this.opt.choices.realLength; | ||
this.selected = (this.selected > 0) ? this.selected - 1 : len - 1; | ||
}.bind(this)); | ||
}; | ||
Prompt.prototype.onDownKey = function() { | ||
this.handleKeypress(function() { | ||
var len = this.opt.choices.realLength; | ||
this.selected = (this.selected < len - 1) ? this.selected + 1 : 0; | ||
}.bind(this)); | ||
}; | ||
Prompt.prototype.onNumberKey = function( input ) { | ||
this.handleKeypress(function() { | ||
if ( input <= this.opt.choices.realLength ) { | ||
this.selected = input - 1; | ||
} | ||
}.bind(this)); | ||
}; | ||
/** | ||
@@ -170,0 +179,0 @@ * Function for rendering list choices |
@@ -9,4 +9,4 @@ /** | ||
var Base = require("./base"); | ||
var observe = require("../utils/events"); | ||
/** | ||
@@ -38,6 +38,13 @@ * Module exports | ||
var events = observe(this.rl); | ||
// Once user confirm (enter key) | ||
this.rl.on( "line", this.onSubmit.bind(this) ); | ||
this.rl.on( "keypress", this.onKeypress.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) ); | ||
events.keypress.takeUntil( validation.success ).forEach( this.onKeypress.bind(this) ); | ||
// Init | ||
@@ -96,4 +103,4 @@ this.render(); | ||
this.rl.removeAllListeners("line"); | ||
this.rl.removeAllListeners("keypress"); | ||
this.lineObs.dispose(); | ||
this.keypressObs.dispose(); | ||
this.done( value ); | ||
@@ -107,3 +114,39 @@ } else { | ||
/** | ||
* When user press `enter` key | ||
*/ | ||
Prompt.prototype.filterInput = function( input ) { | ||
if ( !input ) { | ||
return this.opt.default != null ? this.opt.default : ""; | ||
} | ||
return input; | ||
}; | ||
Prompt.prototype.onEnd = function( state ) { | ||
this.rl.output.unmute(); | ||
this.write("\n"); // manually output the line return as the readline was muted | ||
this.status = "answered"; | ||
// Re-render prompt | ||
this.clean(1).render(); | ||
// Mask answer | ||
var mask = new Array( state.value.toString().length + 1 ).join("*"); | ||
// Render answer | ||
this.write( chalk.cyan(mask) + "\n" ); | ||
this.done( state.value ); | ||
}; | ||
Prompt.prototype.onError = function( state ) { | ||
this.rl.output.unmute(); | ||
this.write("\n"); // manually output the line return as the readline was muted | ||
this.error( state.isValid ).clean().render(); | ||
this.rl.output.mute(); | ||
}; | ||
/** | ||
@@ -110,0 +153,0 @@ * When user type |
@@ -11,2 +11,3 @@ /** | ||
var Separator = require("../objects/separator"); | ||
var observe = require("../utils/events"); | ||
@@ -39,2 +40,8 @@ | ||
_.extend(this.opt, { | ||
validate: function( index ) { | ||
return this.opt.choices.getChoice( index ) != null; | ||
}.bind(this) | ||
}); | ||
var def = this.opt.default; | ||
@@ -62,6 +69,12 @@ if ( _.isNumber(def) && def >= 0 && def < this.opt.choices.realLength ) { | ||
// Save user answer and update prompt to show selected option. | ||
this.rl.on( "line", this.onSubmit.bind(this) ); | ||
this.rl.on( "keypress", this.onKeypress.bind(this) ); | ||
// Once user confirm (enter key) | ||
var events = observe(this.rl); | ||
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) ); | ||
events.keypress.takeUntil( validation.success ).forEach( this.onKeypress.bind(this) ); | ||
// Init the prompt | ||
@@ -100,3 +113,2 @@ this.render(); | ||
/** | ||
@@ -106,26 +118,24 @@ * When user press `enter` key | ||
Prompt.prototype.onSubmit = function( input ) { | ||
Prompt.prototype.filterInput = function( input ) { | ||
if ( input == null || input === "" ) { | ||
input = this.rawDefault; | ||
return this.rawDefault; | ||
} else { | ||
input -= 1; | ||
return input - 1; | ||
} | ||
}; | ||
var selectedChoice = this.opt.choices.getChoice(input); | ||
Prompt.prototype.onEnd = function( state ) { | ||
this.status = "answered"; | ||
this.selected = state.value; | ||
// Input is valid | ||
if ( selectedChoice != null ) { | ||
this.status = "answered"; | ||
this.selected = input; | ||
var selectedChoice = this.opt.choices.getChoice( this.selected ); | ||
// Re-render prompt | ||
this.down().clean(2).render(); | ||
// Re-render prompt | ||
this.down().clean(2).render(); | ||
this.rl.removeAllListeners("line"); | ||
this.rl.removeAllListeners("keypress"); | ||
this.done( selectedChoice.value ); | ||
return; | ||
} | ||
this.done( selectedChoice.value ); | ||
}; | ||
// Input is invalid | ||
Prompt.prototype.onError = function() { | ||
this.hasError = true; | ||
this | ||
@@ -138,3 +148,2 @@ .error("Please enter a valid index") | ||
/** | ||
@@ -144,3 +153,3 @@ * When user press a key | ||
Prompt.prototype.onKeypress = function( s, key ) { | ||
Prompt.prototype.onKeypress = function() { | ||
var index = this.rl.line.length ? Number(this.rl.line) - 1 : 0; | ||
@@ -154,3 +163,11 @@ | ||
this.cacheCursorPos().down().clean(1).render().write( this.rl.line ).restoreCursorPos(); | ||
this.cacheCursorPos(); | ||
if ( this.hasError ) { | ||
this.down().clean(1); | ||
} else { | ||
this.clean(); | ||
} | ||
this.render().write( this.rl.line ).restoreCursorPos(); | ||
}; | ||
@@ -157,0 +174,0 @@ |
@@ -6,3 +6,3 @@ /** | ||
var _ = require("lodash"); | ||
var async = require("async"); | ||
var rx = require("rx"); | ||
var util = require("util"); | ||
@@ -26,5 +26,8 @@ var utils = require("../utils/utils"); | ||
function PromptUI( questions, allDone ) { | ||
function PromptUI() { | ||
Base.call(this); | ||
} | ||
util.inherits( PromptUI, Base ); | ||
PromptUI.prototype.run = function( questions, allDone ) { | ||
// Keep global reference to the answers | ||
@@ -35,12 +38,26 @@ this.answers = {}; | ||
// Make sure questions is an array. | ||
if ( !_.isArray(questions) ) { | ||
if ( _.isPlainObject(questions) ) { | ||
questions = [questions]; | ||
} | ||
// Create an observable, unless we received one as parameter. | ||
// Note: As this is a public interface, we cannot do an instanceof check as we won't | ||
// be using the exact same object in memory. | ||
var obs = _.isArray( questions ) ? rx.Observable.fromArray( questions ) : questions; | ||
// Start running the questions | ||
async.mapSeries( questions, this.onEachPrompt.bind(this), this.onCompletion.bind(this) ); | ||
} | ||
util.inherits( PromptUI, Base ); | ||
this.process = obs.concatMap( this.processQuestion.bind(this) ); | ||
this.process.forEach( | ||
function() {}, | ||
function( err ) { | ||
throw err; | ||
}, | ||
this.onCompletion.bind(this) | ||
); | ||
return this.process; | ||
}; | ||
/** | ||
@@ -58,16 +75,31 @@ * Once all prompt are over | ||
PromptUI.prototype.processQuestion = function( question ) { | ||
return rx.Observable.defer(function() { | ||
var obs = rx.Observable.create(function(obs) { | ||
obs.onNext( question ); | ||
obs.onCompleted(); | ||
}); | ||
/** | ||
* Each prompt rendering | ||
* @param {Object} question Question object | ||
* @param {Function} done Callback | ||
*/ | ||
return obs | ||
.concatMap( this.setDefaultType ) | ||
.concatMap( this.filterIfRunnable.bind(this) ) | ||
.concatMap( utils.fetchAsyncQuestionProperty.bind( null, question, "default", this.answers ) ) | ||
.concatMap( utils.fetchAsyncQuestionProperty.bind( null, question, "choices", this.answers ) ) | ||
.concatMap( this.fetchAnswer.bind(this) ); | ||
}.bind(this)); | ||
}; | ||
PromptUI.prototype.onEachPrompt = function( question, done ) { | ||
// Callback to save answer and continu to next question | ||
var after = function( answer ) { | ||
this.answers[question.name] = answer; | ||
done( null ); | ||
}.bind(this); | ||
PromptUI.prototype.fetchAnswer = function( question ) { | ||
var prompt = new inquirer.prompts[question.type]( 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 }); | ||
}); | ||
}); | ||
}; | ||
PromptUI.prototype.setDefaultType = function( question ) { | ||
// Default type to input | ||
@@ -77,21 +109,21 @@ if ( !inquirer.prompts[question.type] ) { | ||
} | ||
return rx.Observable.defer(function() { | ||
return rx.Observable.return( question ); | ||
}); | ||
}; | ||
if ( _.isFunction(question.default) ) { | ||
question.default = question.default( this.answers ); | ||
} | ||
PromptUI.prototype.filterIfRunnable = function( question ) { | ||
if ( !_.isFunction(question.when) ) return rx.Observable.return(question); | ||
if ( _.isFunction(question.choices) ) { | ||
question.choices = question.choices( this.answers ); | ||
} | ||
var prompt = new inquirer.prompts[question.type]( question, this.rl, this.answers ); | ||
// Check if prompt should be runned (if `when` return true) | ||
utils.runAsync( prompt.opt.when, function( continu ) { | ||
if ( continu ) { | ||
prompt.run( after ); | ||
} else { | ||
done( null ); | ||
} | ||
}, this.answers ); | ||
var answers = this.answers; | ||
return rx.Observable.defer(function() { | ||
return rx.Observable.create(function( obs ) { | ||
utils.runAsync( question.when, function( shouldRun ) { | ||
if ( shouldRun ) { | ||
obs.onNext( question ); | ||
} | ||
obs.onCompleted(); | ||
}, answers ); | ||
}); | ||
}); | ||
}; |
@@ -8,2 +8,3 @@ /** | ||
var chalk = require("chalk"); | ||
var rx = require("rx"); | ||
@@ -27,9 +28,2 @@ | ||
utils.runAsync = function( func, cb ) { | ||
var rest = []; | ||
var len = 1; | ||
while ( len++ < arguments.length ) { | ||
rest.push( arguments[len] ); | ||
} | ||
var async = false; | ||
@@ -41,3 +35,3 @@ var isValid = func.apply({ | ||
} | ||
}, rest ); | ||
}, Array.prototype.slice.call(arguments, 2) ); | ||
@@ -51,2 +45,43 @@ if ( !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 | ||
*/ | ||
utils.createObservableFromAsync = function( func ) { | ||
return rx.Observable.defer(function() { | ||
return rx.Observable.create(function( obs ) { | ||
utils.runAsync( func, function( value ) { | ||
obs.onNext( value ); | ||
obs.onCompleted(); | ||
}); | ||
}); | ||
}); | ||
}; | ||
/** | ||
* Resolve a question property value if it is passed as a function. | ||
* This method will overwrite the property on the question object with the received value. | ||
* @param {Object} question - Question object | ||
* @param {String} prop - Property to fetch name | ||
* @param {Object} answers - Answers object | ||
* @...rest {Mixed} rest - Arguments to pass to `func` | ||
* @return {rx.Obsersable} - Observable emitting once value is known | ||
*/ | ||
utils.fetchAsyncQuestionProperty = function( question, prop, answers ) { | ||
if ( !_.isFunction(question[prop]) ) return rx.Observable.return(question); | ||
return utils.createObservableFromAsync(function() { | ||
var done = this.async(); | ||
utils.runAsync( question[prop], function( value ) { | ||
question[prop] = value; | ||
done( question ); | ||
}, answers ); | ||
}); | ||
}; | ||
/** | ||
* Get the pointer char | ||
@@ -53,0 +88,0 @@ * @return {String} the pointer char |
{ | ||
"name": "inquirer", | ||
"version": "0.5.1", | ||
"version": "0.6.0", | ||
"description": "A collection of common interactive command line user interfaces.", | ||
@@ -22,3 +22,3 @@ "main": "lib/inquirer.js", | ||
"dependencies": { | ||
"async": "~0.8.0", | ||
"chalk": "^0.5.0", | ||
"cli-color": "~0.3.2", | ||
@@ -28,4 +28,4 @@ "lodash": "~2.4.1", | ||
"readline2": "~0.1.0", | ||
"through": "~2.3.4", | ||
"chalk": "~0.4.0" | ||
"rx": "^2.2.27", | ||
"through": "~2.3.4" | ||
}, | ||
@@ -32,0 +32,0 @@ "devDependencies": { |
@@ -50,3 +50,3 @@ Inquirer.js | ||
- **questions** (Array) containing [Question Object](#question) | ||
- **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) | ||
@@ -71,3 +71,3 @@ | ||
`validate`, `filter` and `when` functions can be asynchronously using `this.async()`. You just have to pass the value you'd normally return to the callback option. | ||
`default`(if defined as a function), `validate`, `filter` and `when` functions can be asynchronously using `this.async()`. You just have to pass the value you'd normally return to the callback option. | ||
@@ -223,2 +223,30 @@ ``` javascript | ||
## Reactive interface | ||
Internally, Inquirer uses the [JS reactive extension](https://github.com/Reactive-Extensions/RxJS) to handle events and async flows. | ||
This mean you can take advantage of this feature to provide more advanced flows. For example, you can dynamically add questions to be asked: | ||
```js | ||
var prompts = Rx.Observable.create(function( obs ) { | ||
obs.onNext({ /* question... */ }); | ||
setTimeout(function () { | ||
obs.onNext({ /* question... */ }); | ||
obs.onCompleted(); | ||
}); | ||
}); | ||
inquirer.prompt(prompts); | ||
``` | ||
And using the `process` property, you have access to more fine grained callbacks: | ||
```js | ||
inquirer.prompts(prompts).process.subscribe( | ||
onEachAnswer, | ||
onError, | ||
onComplete | ||
); | ||
``` | ||
## Support (OS Terminals) | ||
@@ -225,0 +253,0 @@ |
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
64035
22
1772
294
+ Addedrx@^2.2.27
+ Addedansi-regex@0.2.1(transitive)
+ Addedansi-styles@1.1.0(transitive)
+ Addedchalk@0.5.1(transitive)
+ Addedescape-string-regexp@1.0.5(transitive)
+ Addedhas-ansi@0.1.0(transitive)
+ Addedrx@2.5.3(transitive)
+ Addedstrip-ansi@0.3.0(transitive)
+ Addedsupports-color@0.2.0(transitive)
- Removedasync@~0.8.0
- Removedansi-styles@1.0.0(transitive)
- Removedasync@0.8.0(transitive)
- Removedchalk@0.4.0(transitive)
- Removedhas-color@0.1.7(transitive)
- Removedstrip-ansi@0.1.1(transitive)
Updatedchalk@^0.5.0