Comparing version 0.1.4 to 0.1.5
@@ -9,2 +9,4 @@ /** | ||
console.log("Hi, welcome to Node Pizza"); | ||
var questions = [ | ||
@@ -14,4 +16,4 @@ { | ||
name: "toBeDelivered", | ||
message: "Hi, welcome to Node Pizza plaza. \n" + | ||
"Is it for a delivery?" | ||
message: "Is it for a delivery", | ||
default: false | ||
}, | ||
@@ -21,4 +23,4 @@ { | ||
name: "phone", | ||
message: "What's your phone number?", | ||
validate: function(value) { | ||
message: "What's your phone number", | ||
validate: function( value ) { | ||
var pass = value.match(/^([01]{1})?[\-\.\s]?\(?(\d{3})\)?[\-\.\s]?(\d{3})[\-\.\s]?(\d{4})\s?((?:#|ext\.?\s?|x\.?\s?){1}(?:\d+)?)?$/i); | ||
@@ -35,11 +37,11 @@ if (pass) { | ||
name: "size", | ||
message: "What size do you need?", | ||
message: "What size do you need", | ||
choices: [ "Large", "Medium", "Small" ], | ||
filter: function(val) { return val.toLowerCase(); } | ||
filter: function( val ) { return val.toLowerCase(); } | ||
}, | ||
{ | ||
type: "input", | ||
name: "quanity", | ||
message: "How many do you need?", | ||
validate: function(value) { | ||
name: "quantity", | ||
message: "How many do you need", | ||
validate: function( value ) { | ||
var valid = !isNaN(parseFloat(value)); | ||
@@ -53,3 +55,3 @@ return valid || "Please enter a number"; | ||
name: "toppings", | ||
message: "What about the toping?", | ||
message: "What about the toping", | ||
choices: [ | ||
@@ -73,3 +75,3 @@ { | ||
name: "liquor", | ||
message: "You also get a free 2L liquor! Which one?", | ||
message: "You also get a free 2L liquor", | ||
choices: [ "Pepsi", "7up", "Coke" ] | ||
@@ -80,3 +82,3 @@ }, | ||
name: "comments", | ||
message: "Before leaving, any comments on your purchase experience?", | ||
message: "Any comments on your purchase experience", | ||
default: "Nope, all good!" | ||
@@ -86,6 +88,5 @@ } | ||
inquirer.prompt(questions, function(answers) { | ||
console.log(); | ||
console.log("Order receipt:"); | ||
console.log(JSON.stringify(answers, null, " ")); | ||
inquirer.prompt( questions, function( answers ) { | ||
console.log("\nOrder receipt:"); | ||
console.log( JSON.stringify(answers, null, " ") ); | ||
}); |
@@ -8,7 +8,7 @@ /** | ||
var _ = require("lodash"); | ||
var readline = require("readline"); | ||
var async = require("async"); | ||
var EventEmitter = require("events").EventEmitter; | ||
var charm = require("charm")(); | ||
var clc = require("cli-color"); | ||
var readlineFacade = require("./utils/readline"); | ||
/** | ||
@@ -20,2 +20,3 @@ * Module exports | ||
/** | ||
@@ -32,2 +33,3 @@ * Client interfaces | ||
/** | ||
@@ -40,71 +42,59 @@ * Public CLI helper interface | ||
cli.prompt = function(questions, allDone) { | ||
cli.prompt = function( questions, allDone ) { | ||
var self = this; | ||
var rlVent = new EventEmitter(); | ||
var rl = readline.createInterface(process.stdin, process.stdout); | ||
// Instantiate the Readline interface | ||
var rl = readlineFacade.createInterface(); | ||
// Keep global reference to the answers | ||
var answers = {}; | ||
// Make Charm global while running Inquirer | ||
charm.pipe(process.stdout); | ||
var oldCharm = process.charm; | ||
process.charm = charm; | ||
// Make sure questions is an array. | ||
if (!_.isArray(questions)) { | ||
if ( !_.isArray(questions) ) { | ||
questions = [questions]; | ||
} | ||
// Propagate current readline events to the global façade | ||
process.stdin | ||
.on("keypress", function(s, key) { | ||
rlVent.emit("keypress", s, key); | ||
}); | ||
rl | ||
.on("line", function() { | ||
var args = Array.prototype.slice.call(arguments, 0); | ||
rlVent.emit.apply(rlVent, ["line"].concat(args)); | ||
}) | ||
.on("close", function() { | ||
var args = Array.prototype.slice.call(arguments, 0); | ||
rlVent.emit.apply(rlVent, ["close"].concat(args)); | ||
console.log("\n"); // Line return | ||
}); | ||
// Propagate keypress events directly on the readline | ||
process.stdin.on("keypress", function( s, key ) { | ||
rl.emit( "keypress", s, key ); | ||
}); | ||
async.mapSeries(questions, | ||
// Each question | ||
function(question, done) { | ||
// Callback to save answer and continu to next question | ||
function after(answer) { | ||
answers[question.name] = answer; | ||
done(null, answer); | ||
} | ||
// Make sure new prompt start on a newline when closing | ||
rl.on("close", function() { | ||
console.log("\n"); // Line return | ||
}); | ||
charm.write("\n"); // line return; | ||
// Start running the questions | ||
async.mapSeries( questions, onEach, onCompletion ); | ||
if (!self.prompts[question.type]) { | ||
question.type = "input"; | ||
} | ||
function onEach( question, done ) { | ||
// Callback to save answer and continu to next question | ||
function after( answer ) { | ||
answers[question.name] = answer; | ||
done( null, answer ); | ||
} | ||
var Prompt = require(self.prompts[question.type]); | ||
var prompt = new Prompt(question, rlVent); | ||
prompt.run(after); | ||
}, | ||
// After all questions | ||
function() { | ||
// Remove events | ||
rlVent.removeAllListeners(); | ||
rl.removeAllListeners(); | ||
process.stdin.removeAllListeners("keypress"); | ||
rl.close(); | ||
charm.end(); | ||
// Default type to input | ||
if ( !self.prompts[question.type] ) { | ||
question.type = "input"; | ||
} | ||
// Restore global charm | ||
process.charm = oldCharm; | ||
var Prompt = require( self.prompts[question.type] ); | ||
var prompt = new Prompt( question, rl ); | ||
prompt.run( after ); | ||
} | ||
if (_.isFunction(allDone)) { | ||
allDone(answers); | ||
} | ||
function onCompletion() { | ||
// Remove events listeners | ||
rl.removeAllListeners(); | ||
process.stdin.removeAllListeners("keypress"); | ||
// Close the readline | ||
rl.close(); | ||
if ( _.isFunction(allDone) ) { | ||
allDone(answers); | ||
} | ||
); | ||
} | ||
}; |
/** | ||
* Base prompt implementation | ||
* Should only be extended by prompt types. | ||
* Should be extended by prompt types. | ||
*/ | ||
@@ -8,4 +8,7 @@ | ||
var utils = require("../utils/utils"); | ||
var charm = process.charm; | ||
var clc = require("cli-color"); | ||
var ansiTrim = require("cli-color/lib/trim"); | ||
var readline = require("readline"); | ||
/** | ||
@@ -17,2 +20,3 @@ * Module exports | ||
/** | ||
@@ -22,11 +26,19 @@ * Prompt constructor | ||
function Prompt(question, rl) { | ||
_.assign(this, { height : 0 }); | ||
this.opt = _.defaults(question, { | ||
function Prompt( question, rl ) { | ||
// Setup instance defaults property | ||
_.assign( this, { | ||
height : 0, | ||
answered : false | ||
}); | ||
// Set defaults prompt options | ||
this.opt = _.defaults( question, { | ||
validate: function() { return true; }, | ||
filter: function(val) { return val; } | ||
filter: function( val ) { return val; } | ||
}); | ||
if (_.isArray(this.opt.choices)) { | ||
this.opt.choices = utils.normalizeChoices(this.opt.choices); | ||
// Normalize choices | ||
if ( _.isArray(this.opt.choices) ) { | ||
this.opt.choices = utils.normalizeChoices( this.opt.choices ); | ||
} | ||
@@ -41,3 +53,3 @@ | ||
/** | ||
* Start the Inquiry session | ||
* Start the Inquiry session and manage output value filtering | ||
* @param {Function} cb Callback when prompt is done | ||
@@ -47,6 +59,6 @@ * @return {this} | ||
Prompt.prototype.run = function(cb) { | ||
Prompt.prototype.run = function( cb ) { | ||
var self = this; | ||
this._run(function(value) { | ||
self.filter(value, cb); | ||
this._run(function( value ) { | ||
self.filter( value, cb ); | ||
}); | ||
@@ -56,9 +68,8 @@ return this; | ||
// noop | ||
Prompt.prototype._run = function(cb) { | ||
cb(); | ||
}; | ||
// default noop (this one should be overwritten in prompts) | ||
Prompt.prototype._run = function( cb ) { cb(); }; | ||
/** | ||
* Remove the prompt to screen | ||
* Remove the prompt from screen | ||
* @param {Number} Extra lines to remove (probably to compensate the "enter" key line | ||
@@ -69,13 +80,47 @@ * return) | ||
Prompt.prototype.clean = function(extra) { | ||
if (!_.isNumber(extra)) { | ||
extra = 0; | ||
Prompt.prototype.clean = function( extra ) { | ||
_.isNumber(extra) || (extra = 0); | ||
var len = this.height + extra; | ||
while ( len-- ) { | ||
readline.moveCursor(this.rl.output, +Infinity, 0); | ||
this.rl.output.write( | ||
clc.bol(0, true) + | ||
(new Array(clc.width)).join(" ") + // write a full line of space to force cleanup | ||
clc.bol(0, true) | ||
); | ||
if ( len ) readline.moveCursor(this.rl.output, 0, -1); | ||
} | ||
utils.cleanLine(this.height + extra); | ||
charm.left(300).display("reset"); | ||
return this; | ||
}; | ||
/** | ||
* Write error message | ||
* Move cursor down by `x` | ||
* @param {Number} x How far to go down (default to 1) | ||
* @return {Prompt} self | ||
*/ | ||
Prompt.prototype.down = function( x ) { | ||
_.isNumber(x) || (x = 1); | ||
readline.moveCursor( this.rl.output, 0, x ); | ||
return this; | ||
}; | ||
/** | ||
* Move cursor up by `x` | ||
* @param {Number} x How far to go up (default to 1) | ||
* @return {Prompt} self | ||
*/ | ||
Prompt.prototype.up = function( x ) { | ||
_.isNumber(x) || (x = 1); | ||
readline.moveCursor( this.rl.output, 0, -x ); | ||
return this; | ||
}; | ||
/** | ||
* Write error message under the current line | ||
* @param {String} Error Error message | ||
@@ -85,17 +130,22 @@ * @return {Prompt} Self | ||
Prompt.prototype.error = function(error) { | ||
charm.erase("line"); | ||
charm.foreground("red").write(">> ").display("reset") | ||
.write(error || "Please enter a valid value"); | ||
charm.up(1); | ||
Prompt.prototype.error = function( error ) { | ||
var errMsg = clc.red(">> ") + | ||
(error || "Please enter a valid value"); | ||
this.write( errMsg ); | ||
this.up(); | ||
return this; | ||
}; | ||
/** | ||
* Valid a given input | ||
* @param {String} value Input string | ||
* @return {Boolean|String} Return `true` if input is valid or return error message | ||
* if no error message is provided, a default one will be used. | ||
* 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) { | ||
Prompt.prototype.validate = function( input, cb ) { | ||
var async = false; | ||
@@ -107,5 +157,5 @@ var isValid = this.opt.validate.call({ | ||
} | ||
}, input); | ||
}, input ); | ||
if (!async) { | ||
if ( !async ) { | ||
cb(isValid); | ||
@@ -115,8 +165,11 @@ } | ||
/** | ||
* Filter a given input before sending back | ||
* @param {String} value Input string | ||
* @return {mixed} Return filtered input | ||
* @param {String} value Input string | ||
* @param {Function} callback Pass the filtered input as parameter. | ||
* @return {null} | ||
*/ | ||
Prompt.prototype.filter = function(input, cb) { | ||
Prompt.prototype.filter = function( input, cb ) { | ||
var async = false; | ||
@@ -130,5 +183,58 @@ var output = this.opt.filter.call({ | ||
if (!async) { | ||
if ( !async ) { | ||
cb(output); | ||
} | ||
}; | ||
/** | ||
* 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 "[" + clc.green("?") + "] " + str; | ||
}; | ||
/** | ||
* Return the prompt line suffix | ||
* @param {String} [optionnal] String to concatenate to the suffix | ||
* @return {String} prompt suffix | ||
*/ | ||
Prompt.prototype.suffix = function( str ) { | ||
str || (str = ""); | ||
return str + ": "; | ||
}; | ||
/** | ||
* Generate the prompt question string | ||
* @return {String} prompt question string | ||
*/ | ||
Prompt.prototype.getQuestion = function() { | ||
var message = this.prefix() + this.opt.message + this.suffix(); | ||
// Append the default if available, and if question isn't answered | ||
if ( this.opt.default && !this.answered ) { | ||
message += "("+ this.opt.default + ") "; | ||
} | ||
return message; | ||
}; | ||
/** | ||
* Write a string to the stdout | ||
* @return {Self} | ||
*/ | ||
Prompt.prototype.write = function( str ) { | ||
process.stdout.write( str ); | ||
return this; | ||
}; |
@@ -7,5 +7,4 @@ /** | ||
var util = require("util"); | ||
var utils = require("../utils/utils"); | ||
var clc = require("cli-color"); | ||
var Base = require("./base"); | ||
var charm = process.charm; | ||
@@ -25,17 +24,25 @@ | ||
function Prompt() { | ||
Base.apply(this, arguments); | ||
Base.apply( this, arguments ); | ||
_.extend(this.opt, { | ||
filter: function(input) { | ||
var value = true; | ||
if (input != null && input !== "") { | ||
var rawDefault = true; | ||
_.extend( this.opt, { | ||
filter: function( input ) { | ||
var value = rawDefault; | ||
if ( input != null && input !== "" ) { | ||
value = /^y(es)?/i.test(input); | ||
} | ||
return value; | ||
} | ||
}.bind(this) | ||
}); | ||
if ( _.isBoolean(this.opt.default) ) { | ||
rawDefault = this.opt.default; | ||
} | ||
this.opt.default = rawDefault ? "Y/n" : "y/N"; | ||
return this; | ||
} | ||
util.inherits(Prompt, Base); | ||
util.inherits( Prompt, Base ); | ||
@@ -45,11 +52,11 @@ | ||
* Start the Inquiry session | ||
* @param {Function} cb Callback when prompt is done | ||
* @param {Function} cb Callback when prompt is done | ||
* @return {this} | ||
*/ | ||
Prompt.prototype._run = function(cb) { | ||
Prompt.prototype._run = function( cb ) { | ||
this.done = cb; | ||
// Once user confirm (enter key) | ||
this.rl.once("line", this.onSubmit.bind(this)); | ||
this.rl.once( "line", this.onSubmit.bind(this) ); | ||
@@ -69,5 +76,7 @@ // Init | ||
Prompt.prototype.render = function() { | ||
var message = this.opt.message + " (Y/n) "; | ||
charm.write(message); | ||
var message = this.getQuestion(); | ||
this.write( message ); | ||
this.rl.setPrompt( message ); | ||
this.height = message.split(/\n/).length; | ||
@@ -83,8 +92,12 @@ | ||
Prompt.prototype.onSubmit = function(input) { | ||
this.clean(1).render(); | ||
this.filter(input, function(output) { | ||
charm.foreground("cyan").write(output ? "Yes" : "No").display("reset").write("\n"); | ||
this.done(input); | ||
Prompt.prototype.onSubmit = function( input ) { | ||
this.answered = true; | ||
// Filter value to write final answer to screen | ||
this.filter( input, function( output ) { | ||
this.clean(1).render(); | ||
this.write( clc.cyan(output ? "Yes" : "No") + "\n" ); | ||
this.done( input ); // send "input" because the master class will refilter | ||
}.bind(this)); | ||
}; |
@@ -7,5 +7,4 @@ /** | ||
var util = require("util"); | ||
var utils = require("../utils/utils"); | ||
var clc = require("cli-color"); | ||
var Base = require("./base"); | ||
var charm = process.charm; | ||
@@ -24,5 +23,5 @@ /** | ||
function Prompt() { | ||
return Base.apply(this, arguments); | ||
return Base.apply( this, arguments ); | ||
} | ||
util.inherits(Prompt, Base); | ||
util.inherits( Prompt, Base ); | ||
@@ -32,3 +31,2 @@ | ||
* Start the Inquiry session | ||
* @param {array} question Questions object array | ||
* @param {Function} cb Callback when prompt is done | ||
@@ -38,7 +36,7 @@ * @return {this} | ||
Prompt.prototype._run = function(cb) { | ||
Prompt.prototype._run = function( cb ) { | ||
this.done = cb; | ||
// Once user confirm (enter key) | ||
this.rl.on("line", this.onSubmit.bind(this)); | ||
this.rl.on( "line", this.onSubmit.bind(this) ); | ||
@@ -58,10 +56,6 @@ // Init | ||
Prompt.prototype.render = function() { | ||
var message = ""; | ||
message += this.opt.message; | ||
if (this.opt.default) { | ||
message += " (default \"" + this.opt.default + "\")"; | ||
} | ||
message += ": "; | ||
var message = this.getQuestion(); | ||
charm.write(message); | ||
this.write( message ); | ||
this.rl.setPrompt( message ); | ||
@@ -78,10 +72,18 @@ this.height = message.split(/\n/).length; | ||
Prompt.prototype.onSubmit = function(input) { | ||
Prompt.prototype.onSubmit = function( input ) { | ||
var value = input || this.opt.default || ""; | ||
this.validate(value, function(isValid) { | ||
if (isValid === true) { | ||
this.clean(1).render(); | ||
charm.foreground("cyan").write(value).display("reset").write("\n"); | ||
this.validate( value, function( isValid ) { | ||
if ( isValid === true ) { | ||
this.answered = true; | ||
// Re-render prompt | ||
this.down().clean(2).render(); | ||
// Render answer | ||
this.write( clc.cyan(value) + "\n" ); | ||
this.rl.removeAllListeners("line"); | ||
this.done(value); | ||
this.done( value ); | ||
} else { | ||
@@ -88,0 +90,0 @@ this.error(isValid).clean().render(); |
@@ -7,5 +7,4 @@ /** | ||
var util = require("util"); | ||
var utils = require("../utils/utils"); | ||
var clc = require("cli-color"); | ||
var Base = require("./base"); | ||
var charm = process.charm; | ||
@@ -25,3 +24,3 @@ | ||
function Prompt() { | ||
Base.apply(this, arguments); | ||
Base.apply( this, arguments ); | ||
@@ -31,5 +30,8 @@ this.firstRender = true; | ||
// Make sure no default is set | ||
this.opt.default = null; | ||
return this; | ||
} | ||
util.inherits(Prompt, Base); | ||
util.inherits( Prompt, Base ); | ||
@@ -43,10 +45,10 @@ | ||
Prompt.prototype._run = function(cb) { | ||
Prompt.prototype._run = function( cb ) { | ||
this.done = cb; | ||
// Move the selected marker on keypress | ||
this.rl.on("keypress", this.onKeypress.bind(this)); | ||
this.rl.on( "keypress", this.onKeypress.bind(this) ); | ||
// Once user confirm (enter key) | ||
this.rl.once("line", this.onSubmit.bind(this)); | ||
this.rl.once( "line", this.onSubmit.bind(this) ); | ||
@@ -56,2 +58,5 @@ // Init the prompt | ||
// Prevent user from writing | ||
this.rl.output.mute(); | ||
return this; | ||
@@ -67,24 +72,27 @@ }; | ||
Prompt.prototype.render = function() { | ||
var self = this; | ||
this.height = 0; | ||
// Render question | ||
var message = this.opt.message + "\n"; | ||
charm.write(message); | ||
var message = this.getQuestion(); | ||
var choicesStr = this.getChoices(); | ||
// Render choices | ||
this.opt.choices.forEach(function(choice, i) { | ||
charm.foreground("cyan").write(" [" + (i === self.selected ? "X" : " ") + "] "); | ||
(i !== self.selected) && charm.display("reset"); | ||
charm.write(choice.name + "\n").display("reset"); | ||
self.height++; | ||
}); | ||
if ( this.firstRender ) { | ||
message += clc.blackBright("(Use arrow keys)"); | ||
} | ||
if (this.firstRender) { | ||
charm.display("dim").write("(Use arrow key)").display("reset"); | ||
// Render choices or answer depending on the state | ||
if ( this.answered ) { | ||
message += clc.cyan(this.opt.choices[this.selected].name) + "\n"; | ||
} else { | ||
message += choicesStr; | ||
} | ||
this.firstRender = false; | ||
this.height += message.split(/\n/).length; | ||
var msgLines = message.split(/\n/); | ||
this.height = msgLines.length; | ||
// Write message to screen and setPrompt to control backspace | ||
this.rl.setPrompt( _.last(msgLines) ); | ||
this.write( message ); | ||
return this; | ||
@@ -95,2 +103,24 @@ }; | ||
/** | ||
* Generate the prompt choices string | ||
* @return {String} Choices string | ||
*/ | ||
Prompt.prototype.getChoices = function() { | ||
var output = ""; | ||
this.opt.choices.forEach(function( choice, i ) { | ||
output += "\n "; | ||
if ( i === this.selected ) { | ||
output += clc.cyan("[X] " + choice.name); | ||
} else { | ||
output += "[ ] " + choice.name; | ||
} | ||
output += " "; | ||
}.bind(this)); | ||
return output; | ||
}; | ||
/** | ||
* When user press `enter` key | ||
@@ -101,5 +131,10 @@ */ | ||
var choice = this.opt.choices[this.selected]; | ||
utils.cleanLine(2); | ||
this.answered = true; | ||
// Rerender prompt | ||
this.rl.output.unmute(); | ||
this.clean().render(); | ||
this.rl.removeAllListeners("keypress"); | ||
this.done(choice.value); | ||
this.done( choice.value ); | ||
}; | ||
@@ -112,11 +147,18 @@ | ||
Prompt.prototype.onKeypress = function(s, key) { | ||
if (key.name === "up" && (this.selected - 1) >= 0) { | ||
Prompt.prototype.onKeypress = function( s, key ) { | ||
// Only process up and down key | ||
if ( !key || (key.name !== "up" && key.name !== "down") ) return; | ||
this.rl.output.unmute(); | ||
if ( key.name === "up" && (this.selected - 1) >= 0 ) { | ||
this.selected--; | ||
} else if (key.name === "down" && (this.selected + 1) < this.opt.choices.length) { | ||
} else if ( key.name === "down" && (this.selected + 1) < this.opt.choices.length ) { | ||
this.selected++; | ||
} else { | ||
return; // don't render if nothing changed | ||
} | ||
// Rerender | ||
this.clean().render(); | ||
this.rl.output.mute(); | ||
}; |
@@ -7,5 +7,4 @@ /** | ||
var util = require("util"); | ||
var utils = require("../utils/utils"); | ||
var clc = require("cli-color"); | ||
var Base = require("./base"); | ||
var charm = process.charm; | ||
@@ -25,9 +24,12 @@ | ||
function Prompt() { | ||
Base.apply(this, arguments); | ||
Base.apply( this, arguments ); | ||
this.selected = 0; | ||
// Make sure no default is set | ||
this.opt.default = null; | ||
return this; | ||
} | ||
util.inherits(Prompt, Base); | ||
util.inherits( Prompt, Base ); | ||
@@ -41,7 +43,7 @@ | ||
Prompt.prototype._run = function(cb) { | ||
Prompt.prototype._run = function( cb ) { | ||
this.done = cb; | ||
// Save user answer and update prompt to show selected option. | ||
this.rl.on("line", this.onSubmit.bind(this)); | ||
this.rl.on( "line", this.onSubmit.bind(this) ); | ||
@@ -61,19 +63,20 @@ // Init the prompt | ||
Prompt.prototype.render = function() { | ||
var self = this; | ||
this.height = 0; | ||
// Render question | ||
var message = this.opt.message + "\n"; | ||
charm.write(message); | ||
var message = this.getQuestion(); | ||
var choicesStr = this.getChoices(); | ||
// Render choices | ||
this.opt.choices.forEach(function(choice, i) { | ||
(i === self.selected) && charm.foreground("cyan"); | ||
charm.write(" " + (i + 1) + ") " + choice.name + "\n").display("reset"); | ||
self.height++; | ||
}); | ||
charm.write(" Default (1) "); | ||
if ( this.answered ) { | ||
message += clc.cyan(this.opt.choices[this.selected].name) + "\n"; | ||
} else { | ||
message += choicesStr; | ||
message += "\n Answer: "; | ||
} | ||
this.height += message.split(/\n/).length; | ||
var msgLines = message.split(/\n/); | ||
this.height = msgLines.length; | ||
this.rl.setPrompt( _.last(msgLines) ); | ||
this.write( message ); | ||
return this; | ||
@@ -84,18 +87,49 @@ }; | ||
/** | ||
* Generate the prompt choices string | ||
* @return {String} Choices string | ||
*/ | ||
Prompt.prototype.getChoices = function() { | ||
var output = ""; | ||
this.opt.choices.forEach(function( choice, i ) { | ||
var display = "\n " + (i + 1) + ") " + choice.name; | ||
if ( i === this.selected ) { | ||
display = clc.cyan( display ); | ||
} | ||
output += display; | ||
}.bind(this)); | ||
return output; | ||
}; | ||
/** | ||
* When user press `enter` key | ||
*/ | ||
Prompt.prototype.onSubmit = function(input) { | ||
if (input == null || input === "") { | ||
Prompt.prototype.onSubmit = function( input ) { | ||
if ( input == null || input === "" ) { | ||
input = 1; | ||
} | ||
if (this.opt.choices[input - 1] != null) { | ||
// Input is valid | ||
if ( this.opt.choices[input - 1] != null ) { | ||
this.answered = true; | ||
this.selected = input - 1; | ||
this.clean(1).render(); | ||
charm.write( input + "\n"); | ||
// Re-render prompt | ||
this.down().clean(2).render(); | ||
this.rl.removeAllListeners("line"); | ||
this.done(this.opt.choices[this.selected].value); | ||
this.done( this.opt.choices[this.selected].value ); | ||
return; | ||
} | ||
this.error("Please enter a valid index").clean().render(); | ||
// Input is invalid | ||
this | ||
.error("Please enter a valid index") | ||
.write( clc.bol(0, true) ) | ||
.clean() | ||
.render(); | ||
}; |
@@ -7,3 +7,2 @@ /** | ||
var _ = require("lodash"); | ||
var charm = process.charm; | ||
@@ -16,6 +15,10 @@ /** | ||
// Normalize choices object keys | ||
utils.normalizeChoices = function normalizeChoices(choices) { | ||
return _.map(choices, function(val) { | ||
if (_.isString(val)) { | ||
/** | ||
* Normalize choices array | ||
* @param {Array} choices The avaiable prompt choices | ||
* @return {Array} Normalized choices containing `name`/`value` hash | ||
*/ | ||
utils.normalizeChoices = function normalizeChoices( choices ) { | ||
return _.map( choices, function( val ) { | ||
if ( _.isString(val) ) { | ||
return { name : val, value: val }; | ||
@@ -30,14 +33,1 @@ } | ||
}; | ||
// Delete `n` number of lines | ||
utils.cleanLine = function cleanLine(n) { | ||
n || (n = 1); | ||
// Erase current line | ||
charm.erase("line"); | ||
// Go up and erase | ||
_.each(_.range(n - 1), function() { | ||
charm.up(1).erase("line"); | ||
}); | ||
}; |
{ | ||
"name": "inquirer", | ||
"version": "0.1.4", | ||
"version": "0.1.5", | ||
"description": "A collection of common interactive command line user interfaces.", | ||
@@ -24,3 +24,4 @@ "main": "lib/inquirer.js", | ||
"async": "~0.2.8", | ||
"charm": "~0.1.2" | ||
"cli-color": "~0.2.2", | ||
"mute-stream": "0.0.3" | ||
}, | ||
@@ -27,0 +28,0 @@ "devDependencies": { |
@@ -6,2 +6,3 @@ Inquirer.js [![Build Status](https://travis-ci.org/SBoudrias/Inquirer.js.png?branch=master)](http://travis-ci.org/SBoudrias/Inquirer.js) | ||
Goal and philosophy | ||
@@ -15,7 +16,9 @@ --------------------- | ||
_Inquirer_ provide the user interface, and the inquiry session flow. If you're searching for a full blown command line program utility, then check out [Commander.js](https://github.com/visionmedia/commander.js) (inspired by) or [Charm](https://github.com/substack/node-charm) (used internally). | ||
_Inquirer_ provide the user interface, and the inquiry session flow. If you're searching for a full blown command line program utility, then check out [Commander.js](https://github.com/visionmedia/commander.js) (inspired by) or [Cli-color](https://github.com/medikoo/cli-color) (used internally). | ||
Documentation | ||
===================== | ||
Installation | ||
@@ -35,2 +38,3 @@ --------------------- | ||
Examples (Run it and see it) | ||
@@ -46,2 +50,3 @@ --------------------- | ||
Methods | ||
@@ -57,2 +62,3 @@ --------------------- | ||
Objects | ||
@@ -100,2 +106,3 @@ --------------------- | ||
Prompts | ||
@@ -130,3 +137,3 @@ --------------------- | ||
Take `type`, `name`, `message` properties. | ||
Take `type`, `name`, `message`[, `default`] properties. | ||
@@ -153,2 +160,13 @@ ``` prompt | ||
Contributing | ||
===================== | ||
**Style Guide**: Please base yourself on [Idiomatic.js](https://github.com/rwldrn/idiomatic.js) style guide with two space indent | ||
**Unit test**: Unit test are wrote in Mocha. 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! | ||
License | ||
@@ -155,0 +173,0 @@ ===================== |
var expect = require("chai").expect; | ||
var sinon = require("sinon"); | ||
var EventEmitter = require("events").EventEmitter; | ||
var ReadlineStub = require("../../helpers/readline"); | ||
process.charm = require("../../helpers/charm"); | ||
var Confirm = require("../../../lib/prompts/confirm"); | ||
// Prevent prompt from writing to screen | ||
Confirm.prototype.write = function() { return this; }; | ||
describe("`confirm` prompt", function() { | ||
beforeEach(function() { | ||
this.rl = new EventEmitter(); | ||
this.rl = new ReadlineStub(); | ||
this.confirm = new Confirm({ | ||
@@ -27,2 +29,32 @@ message: "foo bar" | ||
it("should allow a default `false` value", function(done) { | ||
var falseConfirm = new Confirm({ | ||
message: "foo bar", | ||
default: false | ||
}, this.rl); | ||
falseConfirm.run(function(answer) { | ||
expect(answer).to.be.false; | ||
done(); | ||
}); | ||
this.rl.emit("line", ""); | ||
}); | ||
it("should allow a default `true` value", function(done) { | ||
var falseConfirm = new Confirm({ | ||
message: "foo bar", | ||
default: true | ||
}, this.rl); | ||
falseConfirm.run(function(answer) { | ||
expect(answer).to.be.true; | ||
done(); | ||
}); | ||
this.rl.emit("line", ""); | ||
}); | ||
it("should parse 'Y' value to boolean", function(done) { | ||
@@ -29,0 +61,0 @@ |
var expect = require("chai").expect; | ||
var sinon = require("sinon"); | ||
var EventEmitter = require("events").EventEmitter; | ||
var ReadlineStub = require("../../helpers/readline"); | ||
process.charm = require("../../helpers/charm"); | ||
var Input = require("../../../lib/prompts/input"); | ||
// Prevent prompt from writing to screen | ||
Input.prototype.write = function() { return this; }; | ||
describe("`input` prompt", function() { | ||
beforeEach(function() { | ||
this.rl = new EventEmitter(); | ||
this.rl = new ReadlineStub(); | ||
}); | ||
@@ -13,0 +15,0 @@ |
var expect = require("chai").expect; | ||
var sinon = require("sinon"); | ||
var EventEmitter = require("events").EventEmitter; | ||
var ReadlineStub = require("../../helpers/readline"); | ||
process.charm = require("../../helpers/charm"); | ||
var List = require("../../../lib/prompts/list"); | ||
// Prevent prompt from writing to screen | ||
List.prototype.write = function() { return this; }; | ||
@@ -12,3 +13,3 @@ describe("`list` prompt", function() { | ||
beforeEach(function() { | ||
this.rl = new EventEmitter(); | ||
this.rl = new ReadlineStub(); | ||
this.list = new List({ | ||
@@ -15,0 +16,0 @@ message: "", |
var expect = require("chai").expect; | ||
var sinon = require("sinon"); | ||
var EventEmitter = require("events").EventEmitter; | ||
var ReadlineStub = require("../../helpers/readline"); | ||
process.charm = require("../../helpers/charm"); | ||
var Rawlist = require("../../../lib/prompts/rawlist"); | ||
// Prevent prompt from writing to screen | ||
Rawlist.prototype.write = function() { return this; }; | ||
describe("`rawlist` prompt", function() { | ||
beforeEach(function() { | ||
this.rl = new EventEmitter(); | ||
this.rl = new ReadlineStub(); | ||
this.rawlist = new Rawlist({ | ||
@@ -13,0 +15,0 @@ message: "", |
var expect = require("chai").expect; | ||
var sinon = require("sinon"); | ||
var proxyquire = require("proxyquire"); | ||
var utils = require("../../lib/utils/utils"); | ||
function makeCharmStub() { | ||
var stub = {}; | ||
stub.up = sinon.stub().returns(stub); | ||
stub.erase = sinon.stub().returns(stub); | ||
return function() { | ||
return stub; | ||
}; | ||
} | ||
describe("cleanLine", function() { | ||
beforeEach(function() { | ||
this.charmStub = makeCharmStub(); | ||
process.charm = this.charmStub(); | ||
this.utils = proxyquire("../../lib/utils/utils", {}); | ||
}); | ||
it("should clean `n` number of line", function() { | ||
this.utils.cleanLine(10); | ||
expect(this.charmStub().erase.callCount).to.equal(10); | ||
expect(this.charmStub().up.callCount).to.equal(9); | ||
}); | ||
it("should clean 1 line by default", function() { | ||
this.utils.cleanLine(); | ||
expect(this.charmStub().erase.callCount).to.equal(1); | ||
}); | ||
}); | ||
describe("normalizeChoices", function() { | ||
var utils = proxyquire("../../lib/utils/utils", {}); | ||
it("should normalize array containing strings", function() { | ||
@@ -41,0 +8,0 @@ var initial = [ "foo", "bar" ]; |
Sorry, the diff of this file is not supported yet
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
87184
26
1143
169
4
+ Addedcli-color@~0.2.2
+ Addedmute-stream@0.0.3
+ Addedcli-color@0.2.3(transitive)
+ Addedes5-ext@0.9.2(transitive)
+ Addedevent-emitter@0.2.2(transitive)
+ Addedmemoizee@0.2.6(transitive)
+ Addedmute-stream@0.0.3(transitive)
+ Addednext-tick@0.1.0(transitive)
- Removedcharm@~0.1.2
- Removedcharm@0.1.2(transitive)