Socket
Socket
Sign inDemoInstall

inquirer

Package Overview
Dependencies
Maintainers
2
Versions
179
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

inquirer - npm Package Compare versions

Comparing version 3.3.0 to 4.0.0

13

lib/inquirer.js

@@ -0,1 +1,2 @@

'use strict';
/**

@@ -24,4 +25,4 @@ * Inquirer.js

*/
inquirer.createPromptModule = function (opt) {
var promptModule = function (questions) {
inquirer.createPromptModule = function(opt) {
var promptModule = function(questions) {
var ui = new inquirer.ui.Prompt(promptModule.prompts, opt);

@@ -45,3 +46,3 @@ var promise = ui.run(questions);

promptModule.registerPrompt = function (name, prompt) {
promptModule.registerPrompt = function(name, prompt) {
promptModule.prompts[name] = prompt;

@@ -55,3 +56,3 @@ return this;

promptModule.restoreDefaultPrompts = function () {
promptModule.restoreDefaultPrompts = function() {
this.registerPrompt('list', require('./prompts/list'));

@@ -82,7 +83,7 @@ this.registerPrompt('input', require('./prompts/input'));

// Expose helper functions on the top level for easiest usage by common users
inquirer.registerPrompt = function (name, prompt) {
inquirer.registerPrompt = function(name, prompt) {
inquirer.prompt.registerPrompt(name, prompt);
};
inquirer.restoreDefaultPrompts = function () {
inquirer.restoreDefaultPrompts = function() {
inquirer.prompt.restoreDefaultPrompts();
};

@@ -12,25 +12,27 @@ 'use strict';

var Choice = module.exports = function (val, answers) {
// Don't process Choice and Separator object
if (val instanceof Choice || val.type === 'separator') {
return val;
}
module.exports = class Choice {
constructor(val, answers) {
// Don't process Choice and Separator object
if (val instanceof Choice || val.type === 'separator') {
return val;
}
if (_.isString(val)) {
this.name = val;
this.value = val;
this.short = val;
} else {
_.extend(this, val, {
name: val.name || val.value,
value: 'value' in val ? val.value : val.name,
short: val.short || val.name || val.value
});
}
if (_.isString(val)) {
this.name = val;
this.value = val;
this.short = val;
} else {
_.extend(this, val, {
name: val.name || val.value,
value: 'value' in val ? val.value : val.name,
short: val.short || val.name || val.value
});
}
if (_.isFunction(val.disabled)) {
this.disabled = val.disabled(answers);
} else {
this.disabled = val.disabled;
if (_.isFunction(val.disabled)) {
this.disabled = val.disabled(answers);
} else {
this.disabled = val.disabled;
}
}
};

@@ -14,100 +14,102 @@ 'use strict';

var Choices = module.exports = function (choices, answers) {
this.choices = choices.map(function (val) {
if (val.type === 'separator') {
if (!(val instanceof Separator)) {
val = new Separator(val.line);
module.exports = class Choices {
constructor(choices, answers) {
this.choices = choices.map(val => {
if (val.type === 'separator') {
if (!(val instanceof Separator)) {
val = new Separator(val.line);
}
return val;
}
return val;
}
return new Choice(val, answers);
});
return new Choice(val, answers);
});
this.realChoices = this.choices
.filter(Separator.exclude)
.filter(function (item) {
return !item.disabled;
this.realChoices = this.choices
.filter(Separator.exclude)
.filter(item => !item.disabled);
Object.defineProperty(this, 'length', {
get() {
return this.choices.length;
},
set(val) {
this.choices.length = val;
}
});
Object.defineProperty(this, 'length', {
get: function () {
return this.choices.length;
},
set: function (val) {
this.choices.length = val;
}
});
Object.defineProperty(this, 'realLength', {
get() {
return this.realChoices.length;
},
set() {
throw new Error('Cannot set `realLength` of a Choices collection');
}
});
}
Object.defineProperty(this, 'realLength', {
get: function () {
return this.realChoices.length;
},
set: function () {
throw new Error('Cannot set `realLength` of a Choices collection');
}
});
};
/**
* Get a valid choice from the collection
* @param {Number} selector The selected choice index
* @return {Choice|Undefined} Return the matched choice or undefined
*/
/**
* Get a valid choice from the collection
* @param {Number} selector The selected choice index
* @return {Choice|Undefined} Return the matched choice or undefined
*/
getChoice(selector) {
assert(_.isNumber(selector));
return this.realChoices[selector];
}
Choices.prototype.getChoice = function (selector) {
assert(_.isNumber(selector));
return this.realChoices[selector];
};
/**
* Get a raw element from the collection
* @param {Number} selector The selected index value
* @return {Choice|Undefined} Return the matched choice or undefined
*/
/**
* Get a raw element from the collection
* @param {Number} selector The selected index value
* @return {Choice|Undefined} Return the matched choice or undefined
*/
get(selector) {
assert(_.isNumber(selector));
return this.choices[selector];
}
Choices.prototype.get = function (selector) {
assert(_.isNumber(selector));
return this.choices[selector];
};
/**
* Match the valid choices against a where clause
* @param {Object} whereClause Lodash `where` clause
* @return {Array} Matching choices or empty array
*/
/**
* Match the valid choices against a where clause
* @param {Object} whereClause Lodash `where` clause
* @return {Array} Matching choices or empty array
*/
where(whereClause) {
return _.filter(this.realChoices, whereClause);
}
Choices.prototype.where = function (whereClause) {
return _.filter(this.realChoices, whereClause);
};
/**
* Pluck a particular key from the choices
* @param {String} propertyName Property name to select
* @return {Array} Selected properties
*/
/**
* Pluck a particular key from the choices
* @param {String} propertyName Property name to select
* @return {Array} Selected properties
*/
pluck(propertyName) {
return _.map(this.realChoices, propertyName);
}
Choices.prototype.pluck = function (propertyName) {
return _.map(this.realChoices, propertyName);
};
// Expose usual Array methods
indexOf() {
return this.choices.indexOf.apply(this.choices, arguments);
}
// Expose usual Array methods
Choices.prototype.indexOf = function () {
return this.choices.indexOf.apply(this.choices, arguments);
forEach() {
return this.choices.forEach.apply(this.choices, arguments);
}
filter() {
return this.choices.filter.apply(this.choices, arguments);
}
find(func) {
return _.find(this.choices, func);
}
push() {
var objs = _.map(arguments, val => new Choice(val));
this.choices.push.apply(this.choices, objs);
this.realChoices = this.choices.filter(Separator.exclude);
return this.choices;
}
};
Choices.prototype.forEach = function () {
return this.choices.forEach.apply(this.choices, arguments);
};
Choices.prototype.filter = function () {
return this.choices.filter.apply(this.choices, arguments);
};
Choices.prototype.find = function (func) {
return _.find(this.choices, func);
};
Choices.prototype.push = function () {
var objs = _.map(arguments, function (val) {
return new Choice(val);
});
this.choices.push.apply(this.choices, objs);
this.realChoices = this.choices.filter(Separator.exclude);
return this.choices;
};

@@ -12,7 +12,17 @@ 'use strict';

var Separator = module.exports = function (line) {
this.type = 'separator';
this.line = chalk.dim(line || new Array(15).join(figures.line));
};
class Separator {
constructor(line) {
this.type = 'separator';
this.line = chalk.dim(line || new Array(15).join(figures.line));
}
/**
* Stringify separator
* @return {String} the separator display string
*/
toString() {
return this.line;
}
}
/**

@@ -24,13 +34,6 @@ * Helper function returning false if object is a separator

Separator.exclude = function (obj) {
Separator.exclude = function(obj) {
return obj.type !== 'separator';
};
/**
* Stringify separator
* @return {String} the separator display string
*/
Separator.prototype.toString = function () {
return this.line;
};
module.exports = Separator;

@@ -0,1 +1,2 @@

'use strict';
/**

@@ -12,129 +13,122 @@ * Base prompt implementation

var Prompt = module.exports = function (question, rl, answers) {
// Setup instance defaults property
_.assign(this, {
answers: answers,
status: 'pending'
});
class Prompt {
constructor(question, rl, answers) {
// Setup instance defaults property
_.assign(this, {
answers: answers,
status: 'pending'
});
// Set defaults prompt options
this.opt = _.defaults(_.clone(question), {
validate: function () {
return true;
},
filter: function (val) {
return val;
},
when: function () {
return true;
},
suffix: '',
prefix: chalk.green('?')
});
// Set defaults prompt options
this.opt = _.defaults(_.clone(question), {
validate: () => true,
filter: val => val,
when: () => true,
suffix: '',
prefix: chalk.green('?')
});
// Check to make sure prompt requirements are there
if (!this.opt.message) {
this.throwParamError('message');
}
if (!this.opt.name) {
this.throwParamError('name');
}
// Check to make sure prompt requirements are there
if (!this.opt.message) {
this.throwParamError('message');
}
if (!this.opt.name) {
this.throwParamError('name');
}
// Normalize choices
if (Array.isArray(this.opt.choices)) {
this.opt.choices = new Choices(this.opt.choices, answers);
// Normalize choices
if (Array.isArray(this.opt.choices)) {
this.opt.choices = new Choices(this.opt.choices, answers);
}
this.rl = rl;
this.screen = new ScreenManager(this.rl);
}
this.rl = rl;
this.screen = new ScreenManager(this.rl);
};
/**
* Start the Inquiry session and manage output value filtering
* @return {Promise}
*/
/**
* Start the Inquiry session and manage output value filtering
* @return {Promise}
*/
Prompt.prototype.run = function () {
return new Promise(function (resolve) {
this._run(function (value) {
resolve(value);
run() {
return new Promise(resolve => {
this._run(value => resolve(value));
});
}.bind(this));
};
}
// default noop (this one should be overwritten in prompts)
Prompt.prototype._run = function (cb) {
cb();
};
// Default noop (this one should be overwritten in prompts)
_run(cb) {
cb();
}
/**
* Throw an error telling a required parameter is missing
* @param {String} name Name of the missing param
* @return {Throw Error}
*/
/**
* Throw an error telling a required parameter is missing
* @param {String} name Name of the missing param
* @return {Throw Error}
*/
Prompt.prototype.throwParamError = function (name) {
throw new Error('You must provide a `' + name + '` parameter');
};
throwParamError(name) {
throw new Error('You must provide a `' + name + '` parameter');
}
/**
* Called when the UI closes. Override to do any specific cleanup necessary
*/
Prompt.prototype.close = function () {
this.screen.releaseCursor();
};
/**
* Called when the UI closes. Override to do any specific cleanup necessary
*/
close() {
this.screen.releaseCursor();
}
/**
* 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 self = this;
var validate = runAsync(this.opt.validate);
var filter = runAsync(this.opt.filter);
var validation = submit.flatMap(function (value) {
return filter(value, self.answers).then(function (filteredValue) {
return validate(filteredValue, self.answers).then(function (isValid) {
return {isValid: isValid, value: filteredValue};
}, function (err) {
return {isValid: err};
});
}, function (err) {
return {isValid: err};
});
}).share();
/**
* 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`
*/
handleSubmitEvents(submit) {
var self = this;
var validate = runAsync(this.opt.validate);
var filter = runAsync(this.opt.filter);
var validation = submit
.flatMap(value =>
filter(value, self.answers).then(
filteredValue =>
validate(filteredValue, self.answers).then(
isValid => ({ isValid: isValid, value: filteredValue }),
err => ({ isValid: err })
),
err => ({ isValid: err })
)
)
.share();
var success = validation
.filter(function (state) {
return state.isValid === true;
})
.take(1);
var success = validation.filter(state => state.isValid === true).take(1);
var error = validation.filter(state => state.isValid !== true).takeUntil(success);
var error = validation
.filter(function (state) {
return state.isValid !== true;
})
.takeUntil(success);
return {
success: success,
error: error
};
}
return {
success: success,
error: error
};
};
/**
* Generate the prompt question string
* @return {String} prompt question string
*/
/**
* Generate the prompt question string
* @return {String} prompt question string
*/
getQuestion() {
var message =
this.opt.prefix +
' ' +
chalk.bold(this.opt.message) +
this.opt.suffix +
chalk.reset(' ');
Prompt.prototype.getQuestion = function () {
var message = this.opt.prefix + ' ' + chalk.bold(this.opt.message) + this.opt.suffix + chalk.reset(' ');
// 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 + ') ');
}
// 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 + ') ');
return message;
}
}
return message;
};
module.exports = Prompt;

@@ -0,1 +1,2 @@

'use strict';
/**

@@ -6,3 +7,2 @@ * `list` type prompt

var _ = require('lodash');
var util = require('util');
var chalk = require('chalk');

@@ -15,182 +15,187 @@ var cliCursor = require('cli-cursor');

/**
* Module exports
*/
class CheckboxPrompt extends Base {
constructor(questions, rl, answers) {
super(questions, rl, answers);
module.exports = Prompt;
if (!this.opt.choices) {
this.throwParamError('choices');
}
/**
* Constructor
*/
if (_.isArray(this.opt.default)) {
this.opt.choices.forEach(function(choice) {
if (this.opt.default.indexOf(choice.value) >= 0) {
choice.checked = true;
}
}, this);
}
function Prompt() {
Base.apply(this, arguments);
this.pointer = 0;
this.firstRender = true;
if (!this.opt.choices) {
this.throwParamError('choices');
}
// Make sure no default is set (so it won't be printed)
this.opt.default = null;
if (_.isArray(this.opt.default)) {
this.opt.choices.forEach(function (choice) {
if (this.opt.default.indexOf(choice.value) >= 0) {
choice.checked = true;
}
}, this);
this.paginator = new Paginator(this.screen);
}
this.pointer = 0;
this.firstRender = true;
/**
* Start the Inquiry session
* @param {Function} cb Callback when prompt is done
* @return {this}
*/
// Make sure no default is set (so it won't be printed)
this.opt.default = null;
_run(cb) {
this.done = cb;
this.paginator = new Paginator();
}
util.inherits(Prompt, Base);
var events = observe(this.rl);
/**
* Start the Inquiry session
* @param {Function} cb Callback when prompt is done
* @return {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));
Prompt.prototype._run = function (cb) {
this.done = cb;
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.aKey.takeUntil(validation.success).forEach(this.onAllKey.bind(this));
events.iKey.takeUntil(validation.success).forEach(this.onInverseKey.bind(this));
var events = observe(this.rl);
// Init the prompt
cliCursor.hide();
this.render();
this.firstRender = false;
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));
return 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.aKey.takeUntil(validation.success).forEach(this.onAllKey.bind(this));
events.iKey.takeUntil(validation.success).forEach(this.onInverseKey.bind(this));
/**
* Render the prompt to screen
* @return {CheckboxPrompt} self
*/
// Init the prompt
cliCursor.hide();
this.render();
this.firstRender = false;
render(error) {
// Render question
var message = this.getQuestion();
var bottomContent = '';
return this;
};
if (this.firstRender) {
message +=
'(Press ' +
chalk.cyan.bold('<space>') +
' to select, ' +
chalk.cyan.bold('<a>') +
' to toggle all, ' +
chalk.cyan.bold('<i>') +
' to inverse selection)';
}
/**
* Render the prompt to screen
* @return {Prompt} self
*/
// Render choices or answer depending on the state
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);
}
Prompt.prototype.render = function (error) {
// Render question
var message = this.getQuestion();
var bottomContent = '';
if (error) {
bottomContent = chalk.red('>> ') + error;
}
if (this.firstRender) {
message += '(Press ' + chalk.cyan.bold('<space>') + ' to select, ' + chalk.cyan.bold('<a>') + ' to toggle all, ' + chalk.cyan.bold('<i>') + ' to inverse selection)';
this.screen.render(message, bottomContent);
}
// Render choices or answer depending on the state
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);
}
/**
* When user press `enter` key
*/
if (error) {
bottomContent = chalk.red('>> ') + error;
}
onEnd(state) {
this.status = 'answered';
this.screen.render(message, bottomContent);
};
// Rerender prompt (and clean subline error)
this.render();
/**
* When user press `enter` key
*/
this.screen.done();
cliCursor.show();
this.done(state.value);
}
Prompt.prototype.onEnd = function (state) {
this.status = 'answered';
onError(state) {
this.render(state.isValid);
}
// Rerender prompt (and clean subline error)
this.render();
getCurrentValue() {
var choices = this.opt.choices.filter(function(choice) {
return Boolean(choice.checked) && !choice.disabled;
});
this.screen.done();
cliCursor.show();
this.done(state.value);
};
this.selection = _.map(choices, 'short');
return _.map(choices, 'value');
}
Prompt.prototype.onError = function (state) {
this.render(state.isValid);
};
onUpKey() {
var len = this.opt.choices.realLength;
this.pointer = this.pointer > 0 ? this.pointer - 1 : len - 1;
this.render();
}
Prompt.prototype.getCurrentValue = function () {
var choices = this.opt.choices.filter(function (choice) {
return Boolean(choice.checked) && !choice.disabled;
});
onDownKey() {
var len = this.opt.choices.realLength;
this.pointer = this.pointer < len - 1 ? this.pointer + 1 : 0;
this.render();
}
this.selection = _.map(choices, 'short');
return _.map(choices, 'value');
};
onNumberKey(input) {
if (input <= this.opt.choices.realLength) {
this.pointer = input - 1;
this.toggleChoice(this.pointer);
}
this.render();
}
Prompt.prototype.onUpKey = function () {
var len = this.opt.choices.realLength;
this.pointer = (this.pointer > 0) ? this.pointer - 1 : len - 1;
this.render();
};
Prompt.prototype.onDownKey = function () {
var len = this.opt.choices.realLength;
this.pointer = (this.pointer < len - 1) ? this.pointer + 1 : 0;
this.render();
};
Prompt.prototype.onNumberKey = function (input) {
if (input <= this.opt.choices.realLength) {
this.pointer = input - 1;
onSpaceKey() {
this.toggleChoice(this.pointer);
this.render();
}
this.render();
};
Prompt.prototype.onSpaceKey = function () {
this.toggleChoice(this.pointer);
this.render();
};
onAllKey() {
var shouldBeChecked = Boolean(
this.opt.choices.find(function(choice) {
return choice.type !== 'separator' && !choice.checked;
})
);
Prompt.prototype.onAllKey = function () {
var shouldBeChecked = Boolean(this.opt.choices.find(function (choice) {
return choice.type !== 'separator' && !choice.checked;
}));
this.opt.choices.forEach(function(choice) {
if (choice.type !== 'separator') {
choice.checked = shouldBeChecked;
}
});
this.opt.choices.forEach(function (choice) {
if (choice.type !== 'separator') {
choice.checked = shouldBeChecked;
}
});
this.render();
}
this.render();
};
onInverseKey() {
this.opt.choices.forEach(function(choice) {
if (choice.type !== 'separator') {
choice.checked = !choice.checked;
}
});
Prompt.prototype.onInverseKey = function () {
this.opt.choices.forEach(function (choice) {
if (choice.type !== 'separator') {
choice.checked = !choice.checked;
this.render();
}
toggleChoice(index) {
var item = this.opt.choices.getChoice(index);
if (item !== undefined) {
this.opt.choices.getChoice(index).checked = !item.checked;
}
});
this.render();
};
Prompt.prototype.toggleChoice = function (index) {
var item = this.opt.choices.getChoice(index);
if (item !== undefined) {
this.opt.choices.getChoice(index).checked = !item.checked;
}
};
}

@@ -207,3 +212,3 @@ /**

choices.forEach(function (choice, i) {
choices.forEach(function(choice, i) {
if (choice.type === 'separator') {

@@ -220,3 +225,3 @@ separatorOffset++;

} else {
var isSelected = (i - separatorOffset === pointer);
var isSelected = i - separatorOffset === pointer;
output += isSelected ? chalk.cyan(figures.pointer) : ' ';

@@ -241,1 +246,3 @@ output += getCheckbox(choice.checked) + ' ' + choice.name;

}
module.exports = CheckboxPrompt;

@@ -0,1 +1,2 @@

'use strict';
/**

@@ -6,3 +7,2 @@ * `confirm` type prompt

var _ = require('lodash');
var util = require('util');
var chalk = require('chalk');

@@ -12,97 +12,90 @@ var Base = require('./base');

/**
* Module exports
*/
class ConfirmPrompt extends Base {
constructor(questions, rl, answers) {
super(questions, rl, answers);
module.exports = Prompt;
var rawDefault = true;
/**
* Constructor
*/
_.extend(this.opt, {
filter: function(input) {
var value = rawDefault;
if (input != null && input !== '') {
value = /^y(es)?/i.test(input);
}
return value;
}
});
function Prompt() {
Base.apply(this, arguments);
if (_.isBoolean(this.opt.default)) {
rawDefault = this.opt.default;
}
var rawDefault = true;
this.opt.default = rawDefault ? 'Y/n' : 'y/N';
_.extend(this.opt, {
filter: function (input) {
var value = rawDefault;
if (input != null && input !== '') {
value = /^y(es)?/i.test(input);
}
return value;
}
});
if (_.isBoolean(this.opt.default)) {
rawDefault = this.opt.default;
return this;
}
this.opt.default = rawDefault ? 'Y/n' : 'y/N';
/**
* Start the Inquiry session
* @param {Function} cb Callback when prompt is done
* @return {this}
*/
return this;
}
util.inherits(Prompt, Base);
_run(cb) {
this.done = cb;
/**
* Start the Inquiry session
* @param {Function} cb Callback when prompt is done
* @return {this}
*/
// Once user confirm (enter key)
var events = observe(this.rl);
events.keypress.takeUntil(events.line).forEach(this.onKeypress.bind(this));
Prompt.prototype._run = function (cb) {
this.done = cb;
events.line.take(1).forEach(this.onEnd.bind(this));
// Once user confirm (enter key)
var events = observe(this.rl);
events.keypress.takeUntil(events.line).forEach(this.onKeypress.bind(this));
// Init
this.render();
events.line.take(1).forEach(this.onEnd.bind(this));
return this;
}
// Init
this.render();
/**
* Render the prompt to screen
* @return {ConfirmPrompt} self
*/
return this;
};
render(answer) {
var message = this.getQuestion();
/**
* Render the prompt to screen
* @return {Prompt} self
*/
if (typeof answer === 'boolean') {
message += chalk.cyan(answer ? 'Yes' : 'No');
} else {
message += this.rl.line;
}
Prompt.prototype.render = function (answer) {
var message = this.getQuestion();
this.screen.render(message);
if (typeof answer === 'boolean') {
message += chalk.cyan(answer ? 'Yes' : 'No');
} else {
message += this.rl.line;
return this;
}
this.screen.render(message);
/**
* When user press `enter` key
*/
return this;
};
onEnd(input) {
this.status = 'answered';
/**
* When user press `enter` key
*/
var output = this.opt.filter(input);
this.render(output);
Prompt.prototype.onEnd = function (input) {
this.status = 'answered';
this.screen.done();
this.done(output);
}
var output = this.opt.filter(input);
this.render(output);
/**
* When user press a key
*/
this.screen.done();
this.done(output);
};
onKeypress() {
this.render();
}
}
/**
* When user press a key
*/
Prompt.prototype.onKeypress = function () {
this.render();
};
module.exports = ConfirmPrompt;

@@ -0,1 +1,2 @@

'use strict';
/**

@@ -5,3 +6,2 @@ * `editor` type prompt

var util = require('util');
var chalk = require('chalk');

@@ -13,101 +13,90 @@ var ExternalEditor = require('external-editor');

/**
* Module exports
*/
class EditorPrompt extends Base {
/**
* Start the Inquiry session
* @param {Function} cb Callback when prompt is done
* @return {this}
*/
module.exports = Prompt;
_run(cb) {
this.done = cb;
/**
* Constructor
*/
this.editorResult = new rx.Subject();
function Prompt() {
return Base.apply(this, arguments);
}
util.inherits(Prompt, Base);
// Open Editor on "line" (Enter Key)
var events = observe(this.rl);
this.lineSubscription = events.line.forEach(this.startExternalEditor.bind(this));
/**
* Start the Inquiry session
* @param {Function} cb Callback when prompt is done
* @return {this}
*/
// Trigger Validation when editor closes
var validation = this.handleSubmitEvents(this.editorResult);
validation.success.forEach(this.onEnd.bind(this));
validation.error.forEach(this.onError.bind(this));
Prompt.prototype._run = function (cb) {
this.done = cb;
// Prevents default from being printed on screen (can look weird with multiple lines)
this.currentText = this.opt.default;
this.opt.default = null;
this.editorResult = new rx.Subject();
// Init
this.render();
// Open Editor on "line" (Enter Key)
var events = observe(this.rl);
this.lineSubscription = events.line.forEach(this.startExternalEditor.bind(this));
return this;
}
// Trigger Validation when editor closes
var validation = this.handleSubmitEvents(this.editorResult);
validation.success.forEach(this.onEnd.bind(this));
validation.error.forEach(this.onError.bind(this));
/**
* Render the prompt to screen
* @return {EditorPrompt} self
*/
// Prevents default from being printed on screen (can look weird with multiple lines)
this.currentText = this.opt.default;
this.opt.default = null;
render(error) {
var bottomContent = '';
var message = this.getQuestion();
// Init
this.render();
if (this.status === 'answered') {
message += chalk.dim('Received');
} else {
message += chalk.dim('Press <enter> to launch your preferred editor.');
}
return this;
};
if (error) {
bottomContent = chalk.red('>> ') + error;
}
/**
* Render the prompt to screen
* @return {Prompt} self
*/
this.screen.render(message, bottomContent);
}
Prompt.prototype.render = function (error) {
var bottomContent = '';
var message = this.getQuestion();
/**
* Launch $EDITOR on user press enter
*/
if (this.status === 'answered') {
message += chalk.dim('Received');
} else {
message += chalk.dim('Press <enter> to launch your preferred editor.');
startExternalEditor() {
// Pause Readline to prevent stdin and stdout from being modified while the editor is showing
this.rl.pause();
ExternalEditor.editAsync(this.currentText, this.endExternalEditor.bind(this));
}
if (error) {
bottomContent = chalk.red('>> ') + error;
endExternalEditor(error, result) {
this.rl.resume();
if (error) {
this.editorResult.onError(error);
} else {
this.editorResult.onNext(result);
}
}
this.screen.render(message, bottomContent);
};
onEnd(state) {
this.editorResult.dispose();
this.lineSubscription.dispose();
this.answer = state.value;
this.status = 'answered';
// Re-render prompt
this.render();
this.screen.done();
this.done(this.answer);
}
/**
* Launch $EDITOR on user press enter
*/
Prompt.prototype.startExternalEditor = function () {
// Pause Readline to prevent stdin and stdout from being modified while the editor is showing
this.rl.pause();
ExternalEditor.editAsync(this.currentText, this.endExternalEditor.bind(this));
};
Prompt.prototype.endExternalEditor = function (error, result) {
this.rl.resume();
if (error) {
this.editorResult.onError(error);
} else {
this.editorResult.onNext(result);
onError(state) {
this.render(state.isValid);
}
};
}
Prompt.prototype.onEnd = function (state) {
this.editorResult.dispose();
this.lineSubscription.dispose();
this.answer = state.value;
this.status = 'answered';
// Re-render prompt
this.render();
this.screen.done();
this.done(this.answer);
};
Prompt.prototype.onError = function (state) {
this.render(state.isValid);
};
module.exports = EditorPrompt;

@@ -0,1 +1,2 @@

'use strict';
/**

@@ -6,3 +7,2 @@ * `rawlist` type prompt

var _ = require('lodash');
var util = require('util');
var chalk = require('chalk');

@@ -14,223 +14,227 @@ var Base = require('./base');

/**
* Module exports
*/
class ExpandPrompt extends Base {
constructor(questions, rl, answers) {
super(questions, rl, answers);
module.exports = Prompt;
if (!this.opt.choices) {
this.throwParamError('choices');
}
/**
* Constructor
*/
this.validateChoices(this.opt.choices);
function Prompt() {
Base.apply(this, arguments);
// Add the default `help` (/expand) option
this.opt.choices.push({
key: 'h',
name: 'Help, list all options',
value: 'help'
});
if (!this.opt.choices) {
this.throwParamError('choices');
}
this.opt.validate = choice => {
if (choice == null) {
return 'Please enter a valid command';
}
this.validateChoices(this.opt.choices);
return choice !== 'help';
};
// Add the default `help` (/expand) option
this.opt.choices.push({
key: 'h',
name: 'Help, list all options',
value: 'help'
});
// Setup the default string (capitalize the default key)
this.opt.default = this.generateChoicesString(this.opt.choices, this.opt.default);
this.opt.validate = function (choice) {
if (choice == null) {
return 'Please enter a valid command';
}
this.paginator = new Paginator(this.screen);
}
return choice !== 'help';
};
/**
* Start the Inquiry session
* @param {Function} cb Callback when prompt is done
* @return {this}
*/
// Setup the default string (capitalize the default key)
this.opt.default = this.generateChoicesString(this.opt.choices, this.opt.default);
_run(cb) {
this.done = cb;
this.paginator = new Paginator();
}
util.inherits(Prompt, Base);
// Save user answer and update prompt to show selected option.
var events = observe(this.rl);
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));
/**
* Start the Inquiry session
* @param {Function} cb Callback when prompt is done
* @return {this}
*/
// Init the prompt
this.render();
Prompt.prototype._run = function (cb) {
this.done = cb;
return this;
}
// Save user answer and update prompt to show selected option.
var events = observe(this.rl);
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));
/**
* Render the prompt to screen
* @return {ExpandPrompt} self
*/
// Init the prompt
this.render();
render(error, hint) {
var message = this.getQuestion();
var bottomContent = '';
return this;
};
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: ';
}
/**
* Render the prompt to screen
* @return {Prompt} self
*/
message += this.rl.line;
Prompt.prototype.render = function (error, hint) {
var message = this.getQuestion();
var bottomContent = '';
if (error) {
bottomContent = chalk.red('>> ') + error;
}
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: ';
if (hint) {
bottomContent = chalk.cyan('>> ') + hint;
}
this.screen.render(message, bottomContent);
}
message += this.rl.line;
getCurrentValue(input) {
if (!input) {
input = this.rawDefault;
}
var selected = this.opt.choices.where({ key: input.toLowerCase().trim() })[0];
if (!selected) {
return null;
}
if (error) {
bottomContent = chalk.red('>> ') + error;
return selected.value;
}
if (hint) {
bottomContent = chalk.cyan('>> ') + hint;
}
/**
* Generate the prompt choices string
* @return {String} Choices string
*/
this.screen.render(message, bottomContent);
};
getChoices() {
var output = '';
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;
}
this.opt.choices.forEach(choice => {
output += '\n ';
return selected.value;
};
if (choice.type === 'separator') {
output += ' ' + choice;
return;
}
/**
* Generate the prompt choices string
* @return {String} Choices string
*/
var choiceStr = choice.key + ') ' + choice.name;
if (this.selectedKey === choice.key) {
choiceStr = chalk.cyan(choiceStr);
}
output += choiceStr;
});
Prompt.prototype.getChoices = function () {
var output = '';
return output;
}
this.opt.choices.forEach(function (choice) {
output += '\n ';
if (choice.type === 'separator') {
output += ' ' + choice;
onError(state) {
if (state.value === 'help') {
this.selectedKey = '';
this.status = 'expanded';
this.render();
return;
}
this.render(state.isValid);
}
var choiceStr = choice.key + ') ' + choice.name;
if (this.selectedKey === choice.key) {
choiceStr = chalk.cyan(choiceStr);
}
output += choiceStr;
}.bind(this));
/**
* When user press `enter` key
*/
return output;
};
onSubmit(state) {
this.status = 'answered';
var choice = this.opt.choices.where({ value: state.value })[0];
this.answer = choice.short || choice.name;
Prompt.prototype.onError = function (state) {
if (state.value === 'help') {
this.selectedKey = '';
this.status = 'expanded';
// Re-render prompt
this.render();
return;
this.screen.done();
this.done(state.value);
}
this.render(state.isValid);
};
/**
* When user press `enter` key
*/
/**
* When user press a key
*/
Prompt.prototype.onSubmit = function (state) {
this.status = 'answered';
var choice = this.opt.choices.where({value: state.value})[0];
this.answer = choice.short || choice.name;
onKeypress() {
this.selectedKey = this.rl.line.toLowerCase();
var selected = this.opt.choices.where({ key: this.selectedKey })[0];
if (this.status === 'expanded') {
this.render();
} else {
this.render(null, selected ? selected.name : null);
}
}
// Re-render prompt
this.render();
this.screen.done();
this.done(state.value);
};
/**
* Validate the choices
* @param {Array} choices
*/
/**
* When user press a key
*/
validateChoices(choices) {
var formatError;
var errors = [];
var keymap = {};
choices.filter(Separator.exclude).forEach(choice => {
if (!choice.key || choice.key.length !== 1) {
formatError = true;
}
if (keymap[choice.key]) {
errors.push(choice.key);
}
keymap[choice.key] = true;
choice.key = String(choice.key).toLowerCase();
});
Prompt.prototype.onKeypress = function () {
this.selectedKey = this.rl.line.toLowerCase();
var selected = this.opt.choices.where({key: this.selectedKey})[0];
if (this.status === 'expanded') {
this.render();
} else {
this.render(null, selected ? selected.name : null);
}
};
/**
* Validate the choices
* @param {Array} choices
*/
Prompt.prototype.validateChoices = function (choices) {
var formatError;
var errors = [];
var keymap = {};
choices.filter(Separator.exclude).forEach(function (choice) {
if (!choice.key || choice.key.length !== 1) {
formatError = true;
if (formatError) {
throw new Error(
'Format error: `key` param must be a single letter and is required.'
);
}
if (keymap[choice.key]) {
errors.push(choice.key);
if (keymap.h) {
throw new Error(
'Reserved key error: `key` param cannot be `h` - this value is reserved.'
);
}
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 (errors.length) {
throw new Error(
'Duplicate key error: `key` param must be unique. Duplicates: ' +
_.uniq(errors).join(', ')
);
}
}
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(', '));
}
};
/**
* 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 = choices.realLength - 1;
if (_.isNumber(defaultIndex) && this.opt.choices.getChoice(defaultIndex)) {
defIndex = defaultIndex;
/**
* Generate a string out of the choices keys
* @param {Array} choices
* @param {Number|String} default - the choice index or name to capitalize
* @return {String} The rendered choices key string
*/
generateChoicesString(choices, defaultChoice) {
var defIndex = choices.realLength - 1;
if (_.isNumber(defaultChoice) && this.opt.choices.getChoice(defaultChoice)) {
defIndex = defaultChoice;
} else if (_.isString(defaultChoice)) {
let index = _.findIndex(
choices.realChoices,
({ value }) => value === defaultChoice
);
defIndex = index === -1 ? defIndex : index;
}
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('');
};
}

@@ -246,3 +250,3 @@ /**

choices.forEach(function (choice) {
choices.forEach(choice => {
output += '\n ';

@@ -264,1 +268,3 @@

}
module.exports = ExpandPrompt;

@@ -0,1 +1,2 @@

'use strict';
/**

@@ -5,3 +6,2 @@ * `input` type prompt

var util = require('util');
var chalk = require('chalk');

@@ -11,96 +11,85 @@ var Base = require('./base');

/**
* Module exports
*/
class InputPrompt extends Base {
/**
* Start the Inquiry session
* @param {Function} cb Callback when prompt is done
* @return {this}
*/
module.exports = Prompt;
_run(cb) {
this.done = cb;
/**
* Constructor
*/
// Once user confirm (enter key)
var events = observe(this.rl);
var submit = events.line.map(this.filterInput.bind(this));
function Prompt() {
return Base.apply(this, arguments);
}
util.inherits(Prompt, Base);
var validation = this.handleSubmitEvents(submit);
validation.success.forEach(this.onEnd.bind(this));
validation.error.forEach(this.onError.bind(this));
/**
* Start the Inquiry session
* @param {Function} cb Callback when prompt is done
* @return {this}
*/
events.keypress.takeUntil(validation.success).forEach(this.onKeypress.bind(this));
Prompt.prototype._run = function (cb) {
this.done = cb;
// Init
this.render();
// Once user confirm (enter key)
var events = observe(this.rl);
var submit = events.line.map(this.filterInput.bind(this));
return this;
}
var validation = this.handleSubmitEvents(submit);
validation.success.forEach(this.onEnd.bind(this));
validation.error.forEach(this.onError.bind(this));
/**
* Render the prompt to screen
* @return {InputPrompt} self
*/
events.keypress.takeUntil(validation.success).forEach(this.onKeypress.bind(this));
render(error) {
var bottomContent = '';
var message = this.getQuestion();
// Init
this.render();
if (this.status === 'answered') {
message += chalk.cyan(this.answer);
} else {
message += this.rl.line;
}
return this;
};
if (error) {
bottomContent = chalk.red('>> ') + error;
}
/**
* Render the prompt to screen
* @return {Prompt} self
*/
this.screen.render(message, bottomContent);
}
Prompt.prototype.render = function (error) {
var bottomContent = '';
var message = this.getQuestion();
/**
* When user press `enter` key
*/
if (this.status === 'answered') {
message += chalk.cyan(this.answer);
} else {
message += this.rl.line;
filterInput(input) {
if (!input) {
return this.opt.default == null ? '' : this.opt.default;
}
return input;
}
if (error) {
bottomContent = chalk.red('>> ') + error;
}
onEnd(state) {
this.answer = state.value;
this.status = 'answered';
this.screen.render(message, bottomContent);
};
// Re-render prompt
this.render();
/**
* When user press `enter` key
*/
this.screen.done();
this.done(state.value);
}
Prompt.prototype.filterInput = function (input) {
if (!input) {
return this.opt.default == null ? '' : this.opt.default;
onError(state) {
this.render(state.isValid);
}
return input;
};
Prompt.prototype.onEnd = function (state) {
this.answer = state.value;
this.status = 'answered';
/**
* When user press a key
*/
// Re-render prompt
this.render();
onKeypress() {
this.render();
}
}
this.screen.done();
this.done(state.value);
};
Prompt.prototype.onError = function (state) {
this.render(state.isValid);
};
/**
* When user press a key
*/
Prompt.prototype.onKeypress = function () {
this.render();
};
module.exports = InputPrompt;

@@ -0,1 +1,2 @@

'use strict';
/**

@@ -6,3 +7,2 @@ * `list` type prompt

var _ = require('lodash');
var util = require('util');
var chalk = require('chalk');

@@ -16,137 +16,128 @@ var figures = require('figures');

/**
* Module exports
*/
class ListPrompt extends Base {
constructor(questions, rl, answers) {
super(questions, rl, answers);
module.exports = Prompt;
if (!this.opt.choices) {
this.throwParamError('choices');
}
/**
* Constructor
*/
this.firstRender = true;
this.selected = 0;
function Prompt() {
Base.apply(this, arguments);
var def = this.opt.default;
if (!this.opt.choices) {
this.throwParamError('choices');
}
// If def is a Number, then use as index. Otherwise, check for value.
if (_.isNumber(def) && def >= 0 && def < this.opt.choices.realLength) {
this.selected = def;
} else if (!_.isNumber(def) && def != null) {
let index = _.findIndex(this.opt.choices.realChoices, ({ value }) => value === def);
this.selected = Math.max(index, 0);
}
this.firstRender = true;
this.selected = 0;
// Make sure no default is set (so it won't be printed)
this.opt.default = null;
var def = this.opt.default;
// If def is a Number, then use as index. Otherwise, check for value.
if (_.isNumber(def) && def >= 0 && def < this.opt.choices.realLength) {
this.selected = def;
} else if (!_.isNumber(def) && def != null) {
this.selected = this.opt.choices.pluck('value').indexOf(def);
this.paginator = new Paginator(this.screen);
}
// Make sure no default is set (so it won't be printed)
this.opt.default = null;
/**
* Start the Inquiry session
* @param {Function} cb Callback when prompt is done
* @return {this}
*/
this.paginator = new Paginator();
}
util.inherits(Prompt, Base);
_run(cb) {
this.done = cb;
/**
* Start the Inquiry session
* @param {Function} cb Callback when prompt is done
* @return {this}
*/
var self = this;
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)
.map(this.getCurrentValue.bind(this))
.flatMap(value => runAsync(self.opt.filter)(value).catch(err => err))
.forEach(this.onSubmit.bind(this));
var self = this;
// Init the prompt
cliCursor.hide();
this.render();
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)
.map(this.getCurrentValue.bind(this))
.flatMap(function (value) {
return runAsync(self.opt.filter)(value).catch(function (err) {
return err;
});
})
.forEach(this.onSubmit.bind(this));
return this;
}
// Init the prompt
cliCursor.hide();
this.render();
/**
* Render the prompt to screen
* @return {ListPrompt} self
*/
return this;
};
render() {
// Render question
var message = this.getQuestion();
/**
* Render the prompt to screen
* @return {Prompt} self
*/
if (this.firstRender) {
message += chalk.dim('(Use arrow keys)');
}
Prompt.prototype.render = function () {
// Render question
var message = this.getQuestion();
// Render choices or answer depending on the state
if (this.status === 'answered') {
message += chalk.cyan(this.opt.choices.getChoice(this.selected).short);
} else {
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);
}
if (this.firstRender) {
message += chalk.dim('(Use arrow keys)');
}
this.firstRender = false;
// Render choices or answer depending on the state
if (this.status === 'answered') {
message += chalk.cyan(this.opt.choices.getChoice(this.selected).short);
} else {
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);
this.screen.render(message);
}
this.firstRender = false;
/**
* When user press `enter` key
*/
this.screen.render(message);
};
onSubmit(value) {
this.status = 'answered';
/**
* When user press `enter` key
*/
// Rerender prompt
this.render();
Prompt.prototype.onSubmit = function (value) {
this.status = 'answered';
this.screen.done();
cliCursor.show();
this.done(value);
}
// Rerender prompt
this.render();
getCurrentValue() {
return this.opt.choices.getChoice(this.selected).value;
}
this.screen.done();
cliCursor.show();
this.done(value);
};
/**
* When user press a key
*/
onUpKey() {
var len = this.opt.choices.realLength;
this.selected = this.selected > 0 ? this.selected - 1 : len - 1;
this.render();
}
Prompt.prototype.getCurrentValue = function () {
return this.opt.choices.getChoice(this.selected).value;
};
onDownKey() {
var len = this.opt.choices.realLength;
this.selected = this.selected < len - 1 ? this.selected + 1 : 0;
this.render();
}
/**
* When user press a key
*/
Prompt.prototype.onUpKey = function () {
var len = this.opt.choices.realLength;
this.selected = (this.selected > 0) ? this.selected - 1 : len - 1;
this.render();
};
Prompt.prototype.onDownKey = function () {
var len = this.opt.choices.realLength;
this.selected = (this.selected < len - 1) ? this.selected + 1 : 0;
this.render();
};
Prompt.prototype.onNumberKey = function (input) {
if (input <= this.opt.choices.realLength) {
this.selected = input - 1;
onNumberKey(input) {
if (input <= this.opt.choices.realLength) {
this.selected = input - 1;
}
this.render();
}
this.render();
};
}

@@ -162,3 +153,3 @@ /**

choices.forEach(function (choice, i) {
choices.forEach((choice, i) => {
if (choice.type === 'separator') {

@@ -178,3 +169,3 @@ separatorOffset++;

var isSelected = (i - separatorOffset === pointer);
var isSelected = i - separatorOffset === pointer;
var line = (isSelected ? figures.pointer + ' ' : ' ') + choice.name;

@@ -189,1 +180,3 @@ if (isSelected) {

}
module.exports = ListPrompt;

@@ -0,1 +1,2 @@

'use strict';
/**

@@ -5,3 +6,2 @@ * `password` type prompt

var util = require('util');
var chalk = require('chalk');

@@ -21,97 +21,88 @@ var Base = require('./base');

/**
* Module exports
*/
class PasswordPrompt extends Base {
/**
* Start the Inquiry session
* @param {Function} cb Callback when prompt is done
* @return {this}
*/
module.exports = Prompt;
_run(cb) {
this.done = cb;
/**
* Constructor
*/
var events = observe(this.rl);
function Prompt() {
return Base.apply(this, arguments);
}
util.inherits(Prompt, Base);
// Once user confirm (enter key)
var submit = events.line.map(this.filterInput.bind(this));
/**
* Start the Inquiry session
* @param {Function} cb Callback when prompt is done
* @return {this}
*/
var validation = this.handleSubmitEvents(submit);
validation.success.forEach(this.onEnd.bind(this));
validation.error.forEach(this.onError.bind(this));
Prompt.prototype._run = function (cb) {
this.done = cb;
if (this.opt.mask) {
events.keypress.takeUntil(validation.success).forEach(this.onKeypress.bind(this));
}
var events = observe(this.rl);
// Init
this.render();
// Once user confirm (enter key)
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));
if (this.opt.mask) {
events.keypress.takeUntil(validation.success).forEach(this.onKeypress.bind(this));
return this;
}
// Init
this.render();
/**
* Render the prompt to screen
* @return {PasswordPrompt} self
*/
return this;
};
render(error) {
var message = this.getQuestion();
var bottomContent = '';
/**
* Render the prompt to screen
* @return {Prompt} self
*/
if (this.status === 'answered') {
message += this.opt.mask
? chalk.cyan(mask(this.answer, this.opt.mask))
: chalk.italic.dim('[hidden]');
} else if (this.opt.mask) {
message += mask(this.rl.line || '', this.opt.mask);
} else {
message += chalk.italic.dim('[input is hidden] ');
}
Prompt.prototype.render = function (error) {
var message = this.getQuestion();
var bottomContent = '';
if (error) {
bottomContent = '\n' + chalk.red('>> ') + error;
}
if (this.status === 'answered') {
message += this.opt.mask ? chalk.cyan(mask(this.answer, this.opt.mask)) : chalk.italic.dim('[hidden]');
} else if (this.opt.mask) {
message += mask(this.rl.line || '', this.opt.mask);
} else {
message += chalk.italic.dim('[input is hidden] ');
this.screen.render(message, bottomContent);
}
if (error) {
bottomContent = '\n' + chalk.red('>> ') + error;
/**
* When user press `enter` key
*/
filterInput(input) {
if (!input) {
return this.opt.default == null ? '' : this.opt.default;
}
return input;
}
this.screen.render(message, bottomContent);
};
onEnd(state) {
this.status = 'answered';
this.answer = state.value;
/**
* When user press `enter` key
*/
// Re-render prompt
this.render();
Prompt.prototype.filterInput = function (input) {
if (!input) {
return this.opt.default == null ? '' : this.opt.default;
this.screen.done();
this.done(state.value);
}
return input;
};
Prompt.prototype.onEnd = function (state) {
this.status = 'answered';
this.answer = state.value;
onError(state) {
this.render(state.isValid);
}
// Re-render prompt
this.render();
onKeypress() {
this.render();
}
}
this.screen.done();
this.done(state.value);
};
Prompt.prototype.onError = function (state) {
this.render(state.isValid);
};
Prompt.prototype.onKeypress = function () {
this.render();
};
module.exports = PasswordPrompt;

@@ -0,1 +1,2 @@

'use strict';
/**

@@ -6,3 +7,2 @@ * `rawlist` type prompt

var _ = require('lodash');
var util = require('util');
var chalk = require('chalk');

@@ -14,140 +14,137 @@ var Base = require('./base');

/**
* Module exports
*/
class RawListPrompt extends Base {
constructor(questions, rl, answers) {
super(questions, rl, answers);
module.exports = Prompt;
if (!this.opt.choices) {
this.throwParamError('choices');
}
/**
* Constructor
*/
this.opt.validChoices = this.opt.choices.filter(Separator.exclude);
function Prompt() {
Base.apply(this, arguments);
this.selected = 0;
this.rawDefault = 0;
if (!this.opt.choices) {
this.throwParamError('choices');
}
_.extend(this.opt, {
validate: function(val) {
return val != null;
}
});
this.opt.validChoices = this.opt.choices.filter(Separator.exclude);
var def = this.opt.default;
if (_.isNumber(def) && def >= 0 && def < this.opt.choices.realLength) {
this.selected = def;
this.rawDefault = def;
} else if (!_.isNumber(def) && def != null) {
let index = _.findIndex(this.opt.choices.realChoices, ({ value }) => value === def);
let safeIndex = Math.max(index, 0);
this.selected = safeIndex;
this.rawDefault = safeIndex;
}
this.selected = 0;
this.rawDefault = 0;
// Make sure no default is set (so it won't be printed)
this.opt.default = null;
_.extend(this.opt, {
validate: function (val) {
return val != null;
}
});
var def = this.opt.default;
if (_.isNumber(def) && def >= 0 && def < this.opt.choices.realLength) {
this.selected = this.rawDefault = def;
this.paginator = new Paginator();
}
// Make sure no default is set (so it won't be printed)
this.opt.default = null;
/**
* Start the Inquiry session
* @param {Function} cb Callback when prompt is done
* @return {this}
*/
this.paginator = new Paginator();
}
util.inherits(Prompt, Base);
_run(cb) {
this.done = cb;
/**
* Start the Inquiry session
* @param {Function} cb Callback when prompt is done
* @return {this}
*/
// Once user confirm (enter key)
var events = observe(this.rl);
var submit = events.line.map(this.getCurrentValue.bind(this));
Prompt.prototype._run = function (cb) {
this.done = cb;
var validation = this.handleSubmitEvents(submit);
validation.success.forEach(this.onEnd.bind(this));
validation.error.forEach(this.onError.bind(this));
// Once user confirm (enter key)
var events = observe(this.rl);
var submit = events.line.map(this.getCurrentValue.bind(this));
events.keypress.takeUntil(validation.success).forEach(this.onKeypress.bind(this));
var validation = this.handleSubmitEvents(submit);
validation.success.forEach(this.onEnd.bind(this));
validation.error.forEach(this.onError.bind(this));
// Init the prompt
this.render();
events.keypress.takeUntil(validation.success).forEach(this.onKeypress.bind(this));
return this;
}
// Init the prompt
this.render();
/**
* Render the prompt to screen
* @return {RawListPrompt} self
*/
return this;
};
render(error) {
// Render question
var message = this.getQuestion();
var bottomContent = '';
/**
* Render the prompt to screen
* @return {Prompt} self
*/
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: ';
}
Prompt.prototype.render = function (error) {
// Render question
var message = this.getQuestion();
var bottomContent = '';
message += this.rl.line;
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: ';
}
if (error) {
bottomContent = '\n' + chalk.red('>> ') + error;
}
message += this.rl.line;
if (error) {
bottomContent = '\n' + chalk.red('>> ') + error;
this.screen.render(message, bottomContent);
}
this.screen.render(message, bottomContent);
};
/**
* When user press `enter` key
*/
/**
* When user press `enter` key
*/
getCurrentValue(index) {
if (index == null || index === '') {
index = this.rawDefault;
} else {
index -= 1;
}
Prompt.prototype.getCurrentValue = function (index) {
if (index == null || index === '') {
index = this.rawDefault;
} else {
index -= 1;
var choice = this.opt.choices.getChoice(index);
return choice ? choice.value : null;
}
var choice = this.opt.choices.getChoice(index);
return choice ? choice.value : null;
};
onEnd(state) {
this.status = 'answered';
this.answer = state.value;
Prompt.prototype.onEnd = function (state) {
this.status = 'answered';
this.answer = state.value;
// Re-render prompt
this.render();
// Re-render prompt
this.render();
this.screen.done();
this.done(state.value);
}
this.screen.done();
this.done(state.value);
};
onError() {
this.render('Please enter a valid index');
}
Prompt.prototype.onError = function () {
this.render('Please enter a valid index');
};
/**
* When user press a key
*/
/**
* When user press a key
*/
onKeypress() {
var index = this.rl.line.length ? Number(this.rl.line) - 1 : 0;
Prompt.prototype.onKeypress = function () {
var index = this.rl.line.length ? Number(this.rl.line) - 1 : 0;
if (this.opt.choices.getChoice(index)) {
this.selected = index;
} else {
this.selected = undefined;
}
if (this.opt.choices.getChoice(index)) {
this.selected = index;
} else {
this.selected = undefined;
this.render();
}
}
this.render();
};
/**

@@ -163,3 +160,3 @@ * Function for rendering list choices

choices.forEach(function (choice, i) {
choices.forEach(function(choice, i) {
output += '\n ';

@@ -174,3 +171,3 @@

var index = i - separatorOffset;
var display = (index + 1) + ') ' + choice.name;
var display = index + 1 + ') ' + choice.name;
if (index === pointer) {

@@ -184,1 +181,3 @@ display = chalk.cyan(display);

}
module.exports = RawListPrompt;

@@ -10,51 +10,53 @@ 'use strict';

var UI = module.exports = function (opt) {
// Instantiate the Readline interface
// @Note: Don't reassign if already present (allow test to override the Stream)
if (!this.rl) {
this.rl = readline.createInterface(setupReadlineOptions(opt));
}
this.rl.resume();
class UI {
constructor(opt) {
// Instantiate the Readline interface
// @Note: Don't reassign if already present (allow test to override the Stream)
if (!this.rl) {
this.rl = readline.createInterface(setupReadlineOptions(opt));
}
this.rl.resume();
this.onForceClose = this.onForceClose.bind(this);
this.onForceClose = this.onForceClose.bind(this);
// Make sure new prompt start on a newline when closing
process.on('exit', this.onForceClose);
// Make sure new prompt start on a newline when closing
process.on('exit', this.onForceClose);
// Terminate process on SIGINT (which will call process.on('exit') in return)
this.rl.on('SIGINT', this.onForceClose);
};
// Terminate process on SIGINT (which will call process.on('exit') in return)
this.rl.on('SIGINT', this.onForceClose);
}
/**
* Handle the ^C exit
* @return {null}
*/
/**
* Handle the ^C exit
* @return {null}
*/
UI.prototype.onForceClose = function () {
this.close();
process.kill(process.pid, 'SIGINT');
console.log('');
};
onForceClose() {
this.close();
process.kill(process.pid, 'SIGINT');
console.log('');
}
/**
* Close the interface and cleanup listeners
*/
/**
* Close the interface and cleanup listeners
*/
UI.prototype.close = function () {
// Remove events listeners
this.rl.removeListener('SIGINT', this.onForceClose);
process.removeListener('exit', this.onForceClose);
close() {
// Remove events listeners
this.rl.removeListener('SIGINT', this.onForceClose);
process.removeListener('exit', this.onForceClose);
this.rl.output.unmute();
this.rl.output.unmute();
if (this.activePrompt && typeof this.activePrompt.close === 'function') {
this.activePrompt.close();
if (this.activePrompt && typeof this.activePrompt.close === 'function') {
this.activePrompt.close();
}
// Close the readline
this.rl.output.end();
this.rl.pause();
this.rl.close();
}
}
// Close the readline
this.rl.output.end();
this.rl.pause();
this.rl.close();
};
function setupReadlineOptions(opt) {

@@ -71,7 +73,12 @@ opt = opt || {};

return _.extend({
terminal: true,
input: input,
output: output
}, _.omit(opt, ['input', 'output']));
return _.extend(
{
terminal: true,
input: input,
output: output
},
_.omit(opt, ['input', 'output'])
);
}
module.exports = UI;

@@ -0,1 +1,2 @@

'use strict';
/**

@@ -5,3 +6,2 @@ * Sticky bottom bar user interface

var util = require('util');
var through = require('through');

@@ -12,97 +12,90 @@ var Base = require('./baseUI');

/**
* Module exports
*/
class BottomBar extends Base {
constructor(opt) {
opt = opt || {};
module.exports = Prompt;
super(opt);
/**
* Constructor
*/
this.log = through(this.writeLog.bind(this));
this.bottomBar = opt.bottomBar || '';
this.render();
}
function Prompt(opt) {
opt || (opt = {});
/**
* Render the prompt to screen
* @return {BottomBar} self
*/
Base.apply(this, arguments);
render() {
this.write(this.bottomBar);
return this;
}
this.log = through(this.writeLog.bind(this));
this.bottomBar = opt.bottomBar || '';
this.render();
}
util.inherits(Prompt, Base);
clean() {
rlUtils.clearLine(this.rl, this.bottomBar.split('\n').length);
return this;
}
/**
* Render the prompt to screen
* @return {Prompt} self
*/
/**
* Update the bottom bar content and rerender
* @param {String} bottomBar Bottom bar content
* @return {BottomBar} self
*/
Prompt.prototype.render = function () {
this.write(this.bottomBar);
return this;
};
updateBottomBar(bottomBar) {
rlUtils.clearLine(this.rl, 1);
this.rl.output.unmute();
this.clean();
this.bottomBar = bottomBar;
this.render();
this.rl.output.mute();
return this;
}
Prompt.prototype.clean = function () {
rlUtils.clearLine(this.rl, this.bottomBar.split('\n').length);
return this;
};
/**
* Write out log data
* @param {String} data - The log data to be output
* @return {BottomBar} self
*/
/**
* Update the bottom bar content and rerender
* @param {String} bottomBar Bottom bar content
* @return {Prompt} self
*/
writeLog(data) {
this.rl.output.unmute();
this.clean();
this.rl.output.write(this.enforceLF(data.toString()));
this.render();
this.rl.output.mute();
return this;
}
Prompt.prototype.updateBottomBar = function (bottomBar) {
rlUtils.clearLine(this.rl, 1);
this.rl.output.unmute();
this.clean();
this.bottomBar = bottomBar;
this.render();
this.rl.output.mute();
return this;
};
/**
* Make sure line end on a line feed
* @param {String} str Input string
* @return {String} The input string with a final line feed
*/
/**
* Write out log data
* @param {String} data - The log data to be output
* @return {Prompt} self
*/
enforceLF(str) {
return str.match(/[\r\n]$/) ? str : str + '\n';
}
Prompt.prototype.writeLog = function (data) {
this.rl.output.unmute();
this.clean();
this.rl.output.write(this.enforceLF(data.toString()));
this.render();
this.rl.output.mute();
return this;
};
/**
* Helper for writing message in Prompt
* @param {BottomBar} prompt - The Prompt object that extends tty
* @param {String} message - The message to be output
*/
write(message) {
var msgLines = message.split(/\n/);
this.height = msgLines.length;
/**
* Make sure line end on a line feed
* @param {String} str Input string
* @return {String} The input string with a final line feed
*/
// Write message to screen and setPrompt to control backspace
this.rl.setPrompt(_.last(msgLines));
Prompt.prototype.enforceLF = function (str) {
return str.match(/[\r\n]$/) ? str : str + '\n';
};
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);
}
this.rl.output.write(message);
}
}
/**
* Helper for writing message in Prompt
* @param {Prompt} prompt - The Prompt object that extends tty
* @param {String} message - The message to be output
*/
Prompt.prototype.write = function (message) {
var msgLines = message.split(/\n/);
this.height = msgLines.length;
// Write message to screen and setPrompt to control backspace
this.rl.setPrompt(_.last(msgLines));
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);
}
this.rl.output.write(message);
};
module.exports = BottomBar;
'use strict';
var _ = require('lodash');
var rx = require('rx-lite-aggregates');
var util = require('util');
var runAsync = require('run-async');

@@ -13,104 +12,109 @@ var utils = require('../utils/utils');

var PromptUI = module.exports = function (prompts, opt) {
Base.call(this, opt);
this.prompts = prompts;
};
util.inherits(PromptUI, Base);
PromptUI.prototype.run = function (questions) {
// Keep global reference to the answers
this.answers = {};
// Make sure questions is an array.
if (_.isPlainObject(questions)) {
questions = [questions];
class PromptUI extends Base {
constructor(prompts, opt) {
super(opt);
this.prompts = prompts;
}
// 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.from(questions) : questions;
run(questions) {
// Keep global reference to the answers
this.answers = {};
this.process = obs
.concatMap(this.processQuestion.bind(this))
// `publish` creates a hot Observable. It prevents duplicating prompts.
.publish();
// Make sure questions is an array.
if (_.isPlainObject(questions)) {
questions = [questions];
}
this.process.connect();
// 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.from(questions) : questions;
return this.process
.reduce(function (answers, answer) {
_.set(this.answers, answer.name, answer.answer);
return this.answers;
}.bind(this), {})
.toPromise(Promise)
.then(this.onCompletion.bind(this));
};
this.process = obs
.concatMap(this.processQuestion.bind(this))
// `publish` creates a hot Observable. It prevents duplicating prompts.
.publish();
/**
* Once all prompt are over
*/
this.process.connect();
PromptUI.prototype.onCompletion = function (answers) {
this.close();
return this.process
.reduce((answers, answer) => {
_.set(this.answers, answer.name, answer.answer);
return this.answers;
}, {})
.toPromise(Promise)
.then(this.onCompletion.bind(this));
}
return answers;
};
/**
* Once all prompt are over
*/
PromptUI.prototype.processQuestion = function (question) {
question = _.clone(question);
return rx.Observable.defer(function () {
var obs = rx.Observable.of(question);
onCompletion(answers) {
this.close();
return obs
.concatMap(this.setDefaultType.bind(this))
.concatMap(this.filterIfRunnable.bind(this))
.concatMap(utils.fetchAsyncQuestionProperty.bind(null, question, 'message', this.answers))
.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));
};
return answers;
}
PromptUI.prototype.fetchAnswer = function (question) {
var Prompt = this.prompts[question.type];
this.activePrompt = new Prompt(question, this.rl, this.answers);
return rx.Observable.defer(function () {
return rx.Observable.fromPromise(this.activePrompt.run().then(function (answer) {
return {name: question.name, answer: answer};
}));
}.bind(this));
};
processQuestion(question) {
question = _.clone(question);
return rx.Observable.defer(() => {
var obs = rx.Observable.of(question);
PromptUI.prototype.setDefaultType = function (question) {
// Default type to input
if (!this.prompts[question.type]) {
question.type = 'input';
return obs
.concatMap(this.setDefaultType.bind(this))
.concatMap(this.filterIfRunnable.bind(this))
.concatMap(() =>
utils.fetchAsyncQuestionProperty(question, 'message', this.answers)
)
.concatMap(() =>
utils.fetchAsyncQuestionProperty(question, 'default', this.answers)
)
.concatMap(() =>
utils.fetchAsyncQuestionProperty(question, 'choices', this.answers)
)
.concatMap(this.fetchAnswer.bind(this));
});
}
return rx.Observable.defer(function () {
return rx.Observable.return(question);
});
};
PromptUI.prototype.filterIfRunnable = function (question) {
if (question.when === false) {
return rx.Observable.empty();
fetchAnswer(question) {
var Prompt = this.prompts[question.type];
this.activePrompt = new Prompt(question, this.rl, this.answers);
return rx.Observable.defer(() =>
rx.Observable.fromPromise(
this.activePrompt.run().then(answer => ({ name: question.name, answer: answer }))
)
);
}
if (!_.isFunction(question.when)) {
return rx.Observable.return(question);
setDefaultType(question) {
// Default type to input
if (!this.prompts[question.type]) {
question.type = 'input';
}
return rx.Observable.defer(() => rx.Observable.return(question));
}
var answers = this.answers;
return rx.Observable.defer(function () {
return rx.Observable.fromPromise(
runAsync(question.when)(answers).then(function (shouldRun) {
if (shouldRun) {
return question;
}
})
).filter(function (val) {
return val != null;
});
});
};
filterIfRunnable(question) {
if (question.when === false) {
return rx.Observable.empty();
}
if (!_.isFunction(question.when)) {
return rx.Observable.return(question);
}
var answers = this.answers;
return rx.Observable.defer(() =>
rx.Observable.fromPromise(
runAsync(question.when)(answers).then(shouldRun => {
if (shouldRun) {
return question;
}
})
).filter(val => val != null)
);
}
}
module.exports = PromptUI;

@@ -5,11 +5,9 @@ 'use strict';

function normalizeKeypressEvents(value, key) {
return {value: value, key: key || {}};
return { value: value, key: key || {} };
}
module.exports = function (rl) {
module.exports = function(rl) {
var keypress = rx.Observable.fromEvent(rl.input, 'keypress', normalizeKeypressEvents)
.filter(function (e) {
// Ignore `enter` key. On the readline, we only care about the `line` event.
return e.key.name !== 'enter' && e.key.name !== 'return';
});
// Ignore `enter` key. On the readline, we only care about the `line` event.
.filter(({ key }) => key.name !== 'enter' && key.name !== 'return');

@@ -20,28 +18,25 @@ return {

normalizedUpKey: keypress.filter(function (e) {
return e.key.name === 'up' || e.key.name === 'k' || (e.key.name === 'p' && e.key.ctrl);
}).share(),
normalizedUpKey: keypress
.filter(
({ key }) =>
key.name === 'up' || key.name === 'k' || (key.name === 'p' && key.ctrl)
)
.share(),
normalizedDownKey: keypress.filter(function (e) {
return e.key.name === 'down' || e.key.name === 'j' || (e.key.name === 'n' && e.key.ctrl);
}).share(),
normalizedDownKey: keypress
.filter(
({ key }) =>
key.name === 'down' || key.name === 'j' || (key.name === 'n' && key.ctrl)
)
.share(),
numberKey: keypress.filter(function (e) {
return e.value && '123456789'.indexOf(e.value) >= 0;
}).map(function (e) {
return Number(e.value);
}).share(),
numberKey: keypress
.filter(e => e.value && '123456789'.indexOf(e.value) >= 0)
.map(e => Number(e.value))
.share(),
spaceKey: keypress.filter(function (e) {
return e.key && e.key.name === 'space';
}).share(),
aKey: keypress.filter(function (e) {
return e.key && e.key.name === 'a';
}).share(),
iKey: keypress.filter(function (e) {
return e.key && e.key.name === 'i';
}).share()
spaceKey: keypress.filter(({ key }) => key && key.name === 'space').share(),
aKey: keypress.filter(({ key }) => key && key.name === 'a').share(),
iKey: keypress.filter(({ key }) => key && key.name === 'i').share()
};
};

@@ -11,29 +11,44 @@ 'use strict';

var Paginator = module.exports = function () {
this.pointer = 0;
this.lastIndex = 0;
};
class Paginator {
constructor(screen) {
this.pointer = 0;
this.lastIndex = 0;
this.screen = screen;
}
Paginator.prototype.paginate = function (output, active, pageSize) {
pageSize = pageSize || 7;
var middleOfList = Math.floor(pageSize / 2);
var lines = output.split('\n');
paginate(output, active, pageSize) {
pageSize = pageSize || 7;
var middleOfList = Math.floor(pageSize / 2);
var lines = output.split('\n');
// Make sure there's enough lines to paginate
if (lines.length <= pageSize) {
return output;
}
if (this.screen) {
lines = this.screen.breakLines(lines);
active = _.sum(lines.map(lineParts => lineParts.length).splice(0, active));
lines = _.flatten(lines);
}
// Move the pointer only when the user go down and limit it to the middle of the list
if (this.pointer < middleOfList && this.lastIndex < active && active - this.lastIndex < pageSize) {
this.pointer = Math.min(middleOfList, this.pointer + active - this.lastIndex);
// Make sure there's enough lines to paginate
if (lines.length <= pageSize) {
return output;
}
// Move the pointer only when the user go down and limit it to the middle of the list
if (
this.pointer < middleOfList &&
this.lastIndex < active &&
active - this.lastIndex < pageSize
) {
this.pointer = Math.min(middleOfList, this.pointer + active - this.lastIndex);
}
this.lastIndex = active;
// Duplicate the lines so it give an infinite list look
var infinite = _.flatten([lines, lines, lines]);
var topIndex = Math.max(0, active + lines.length - this.pointer);
var section = infinite.splice(topIndex, pageSize).join('\n');
return section + '\n' + chalk.dim('(Move up and down to reveal more choices)');
}
this.lastIndex = active;
}
// Duplicate the lines so it give an infinite list look
var infinite = _.flatten([lines, lines, lines]);
var topIndex = Math.max(0, active + lines.length - this.pointer);
var section = infinite.splice(topIndex, pageSize).join('\n');
return section + '\n' + chalk.dim('(Move up and down to reveal more choices)');
};
module.exports = Paginator;

@@ -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));

@@ -32,3 +32,3 @@ };

exports.up = function (rl, x) {
exports.up = function(rl, x) {
rl.output.write(ansiEscapes.cursorUp(x));

@@ -43,3 +43,3 @@ };

exports.down = function (rl, x) {
exports.down = function(rl, x) {
rl.output.write(ansiEscapes.cursorDown(x));

@@ -53,4 +53,4 @@ };

*/
exports.clearLine = function (rl, len) {
exports.clearLine = function(rl, len) {
rl.output.write(ansiEscapes.eraseLines(len));
};

@@ -16,121 +16,125 @@ 'use strict';

var ScreenManager = module.exports = function (rl) {
// These variables are keeping information to allow correct prompt re-rendering
this.height = 0;
this.extraLinesUnderPrompt = 0;
class ScreenManager {
constructor(rl) {
// These variables are keeping information to allow correct prompt re-rendering
this.height = 0;
this.extraLinesUnderPrompt = 0;
this.rl = rl;
};
this.rl = rl;
}
ScreenManager.prototype.render = function (content, bottomContent) {
this.rl.output.unmute();
this.clean(this.extraLinesUnderPrompt);
render(content, bottomContent) {
this.rl.output.unmute();
this.clean(this.extraLinesUnderPrompt);
/**
* Write message to screen and setPrompt to control backspace
*/
/**
* Write message to screen and setPrompt to control backspace
*/
var promptLine = lastLine(content);
var rawPromptLine = stripAnsi(promptLine);
var promptLine = lastLine(content);
var rawPromptLine = stripAnsi(promptLine);
// Remove the rl.line from our prompt. We can't rely on the content of
// rl.line (mainly because of the password prompt), so just rely on it's
// length.
var prompt = rawPromptLine;
if (this.rl.line.length) {
prompt = prompt.slice(0, -this.rl.line.length);
}
this.rl.setPrompt(prompt);
// Remove the rl.line from our prompt. We can't rely on the content of
// rl.line (mainly because of the password prompt), so just rely on it's
// length.
var prompt = rawPromptLine;
if (this.rl.line.length) {
prompt = prompt.slice(0, -this.rl.line.length);
}
this.rl.setPrompt(prompt);
// setPrompt will change cursor position, now we can get correct value
var cursorPos = this.rl._getCursorPos();
var width = this.normalizedCliWidth();
// SetPrompt will change cursor position, now we can get correct value
var cursorPos = this.rl._getCursorPos();
var width = this.normalizedCliWidth();
content = forceLineReturn(content, width);
if (bottomContent) {
bottomContent = forceLineReturn(bottomContent, width);
}
// Manually insert an extra line if we're at the end of the line.
// This prevent the cursor from appearing at the beginning of the
// current line.
if (rawPromptLine.length % width === 0) {
content += '\n';
}
var fullContent = content + (bottomContent ? '\n' + bottomContent : '');
this.rl.output.write(fullContent);
content = this.forceLineReturn(content, width);
if (bottomContent) {
bottomContent = this.forceLineReturn(bottomContent, width);
}
// Manually insert an extra line if we're at the end of the line.
// This prevent the cursor from appearing at the beginning of the
// current line.
if (rawPromptLine.length % width === 0) {
content += '\n';
}
var fullContent = content + (bottomContent ? '\n' + bottomContent : '');
this.rl.output.write(fullContent);
/**
* Re-adjust the cursor at the correct position.
*/
/**
* Re-adjust the cursor at the correct position.
*/
// We need to consider parts of the prompt under the cursor as part of the bottom
// content in order to correctly cleanup and re-render.
var promptLineUpDiff = Math.floor(rawPromptLine.length / width) - cursorPos.rows;
var bottomContentHeight = promptLineUpDiff + (bottomContent ? height(bottomContent) : 0);
if (bottomContentHeight > 0) {
util.up(this.rl, bottomContentHeight);
}
// We need to consider parts of the prompt under the cursor as part of the bottom
// content in order to correctly cleanup and re-render.
var promptLineUpDiff = Math.floor(rawPromptLine.length / width) - cursorPos.rows;
var bottomContentHeight =
promptLineUpDiff + (bottomContent ? height(bottomContent) : 0);
if (bottomContentHeight > 0) {
util.up(this.rl, bottomContentHeight);
}
// Reset cursor at the beginning of the line
util.left(this.rl, stringWidth(lastLine(fullContent)));
// Reset cursor at the beginning of the line
util.left(this.rl, stringWidth(lastLine(fullContent)));
// Adjust cursor on the right
util.right(this.rl, cursorPos.cols);
// Adjust cursor on the right
util.right(this.rl, cursorPos.cols);
/**
* Set up state for next re-rendering
*/
this.extraLinesUnderPrompt = bottomContentHeight;
this.height = height(fullContent);
/**
* Set up state for next re-rendering
*/
this.extraLinesUnderPrompt = bottomContentHeight;
this.height = height(fullContent);
this.rl.output.mute();
};
this.rl.output.mute();
}
ScreenManager.prototype.clean = function (extraLines) {
if (extraLines > 0) {
util.down(this.rl, extraLines);
clean(extraLines) {
if (extraLines > 0) {
util.down(this.rl, extraLines);
}
util.clearLine(this.rl, this.height);
}
util.clearLine(this.rl, this.height);
};
ScreenManager.prototype.done = function () {
this.rl.setPrompt('');
this.rl.output.unmute();
this.rl.output.write('\n');
};
done() {
this.rl.setPrompt('');
this.rl.output.unmute();
this.rl.output.write('\n');
}
ScreenManager.prototype.releaseCursor = function () {
if (this.extraLinesUnderPrompt > 0) {
util.down(this.rl, this.extraLinesUnderPrompt);
releaseCursor() {
if (this.extraLinesUnderPrompt > 0) {
util.down(this.rl, this.extraLinesUnderPrompt);
}
}
};
ScreenManager.prototype.normalizedCliWidth = function () {
var width = cliWidth({
defaultWidth: 80,
output: this.rl.output
});
if (process.platform === 'win32') {
return width - 1;
normalizedCliWidth() {
var width = cliWidth({
defaultWidth: 80,
output: this.rl.output
});
if (process.platform === 'win32') {
return width - 1;
}
return width;
}
return width;
};
function breakLines(lines, width) {
// Break lines who're longuer than the cli width so we can normalize the natural line
// returns behavior accross terminals.
var regex = new RegExp(
'(?:(?:\\033[[0-9;]*m)*.?){1,' + width + '}',
'g'
);
return lines.map(function (line) {
var chunk = line.match(regex);
// last match is always empty
chunk.pop();
return chunk || '';
});
breakLines(lines, width) {
// Break lines who're longer than the cli width so we can normalize the natural line
// returns behavior across terminals.
width = width || this.normalizedCliWidth();
var regex = new RegExp('(?:(?:\\033[[0-9;]*m)*.?){1,' + width + '}', 'g');
return lines.map(line => {
var chunk = line.match(regex);
// Last match is always empty
chunk.pop();
return chunk || '';
});
}
forceLineReturn(content, width) {
width = width || this.normalizedCliWidth();
return _.flatten(this.breakLines(content.split('\n'), width)).join('\n');
}
}
function forceLineReturn(content, width) {
return _.flatten(breakLines(content.split('\n'), width)).join('\n');
}
module.exports = ScreenManager;

@@ -15,3 +15,3 @@ 'use strict';

exports.fetchAsyncQuestionProperty = function (question, prop, answers) {
exports.fetchAsyncQuestionProperty = function(question, prop, answers) {
if (!_.isFunction(question[prop])) {

@@ -21,4 +21,4 @@ return rx.Observable.return(question);

return rx.Observable.fromPromise(runAsync(question[prop])(answers)
.then(function (value) {
return rx.Observable.fromPromise(
runAsync(question[prop])(answers).then(value => {
question[prop] = value;

@@ -25,0 +25,0 @@ return question;

{
"name": "inquirer",
"version": "3.3.0",
"description": "A collection of common interactive command line user interfaces.",
"version": "4.0.0",
"description":
"A collection of common interactive command line user interfaces.",
"author": "Simon Boudrias <admin@simonboudrias.com>",
"files": [
"lib"
],
"files": ["lib"],
"main": "lib/inquirer.js",
"keywords": [
"command",
"prompt",
"stdin",
"cli",
"tty",
"menu"
],
"keywords": ["command", "prompt", "stdin", "cli", "tty", "menu"],
"engines": {
"node": ">=6.0.0"
},
"devDependencies": {
"chai": "^4.0.1",
"cmdify": "^0.0.4",
"coveralls": "^3.0.0",
"eslint": "^4.1.0",
"eslint-config-prettier": "^2.4.0",
"eslint-config-xo": "^0.19.0",
"eslint-plugin-prettier": "^2.2.0",
"husky": "^0.14.3",
"lint-staged": "^5.0.0",
"mocha": "^4.0.0",
"mockery": "^2.1.0",
"nsp": "^3.0.0",
"nyc": "^11.3.0",
"prettier": "^1.7.0",
"sinon": "^4.0.0"
},
"scripts": {
"test": "gulp",
"prepublish": "gulp prepublish"
"test": "nyc mocha test/** -r ./test/before",
"prepublish": "nsp check",
"pretest": "eslint .",
"precommit": "lint-staged",
"coverage": "nyc report --reporter=text-lcov | coveralls"
},

@@ -40,21 +55,31 @@ "repository": "SBoudrias/Inquirer.js",

},
"devDependencies": {
"chai": "^4.0.1",
"cmdify": "^0.0.4",
"eslint": "^4.2.0",
"eslint-config-xo-space": "^0.16.0",
"gulp": "^3.9.0",
"gulp-codacy": "^1.0.0",
"gulp-coveralls": "^0.1.0",
"gulp-eslint": "^4.0.0",
"gulp-exclude-gitignore": "^1.0.0",
"gulp-istanbul": "^1.1.2",
"gulp-line-ending-corrector": "^1.0.1",
"gulp-mocha": "^3.0.0",
"gulp-nsp": "^2.1.0",
"gulp-plumber": "^1.0.0",
"mocha": "^3.4.2",
"mockery": "^2.1.0",
"sinon": "^3.0.0"
"lint-staged": {
"*.js": ["eslint --fix", "git add"],
"*.json": ["prettier --write", "git add"]
},
"eslintConfig": {
"extends": ["xo", "prettier"],
"env": {
"mocha": true,
"node": true
},
"rules": {
"no-eq-null": "off",
"eqeqeq": [
"error",
"always",
{
"null": "ignore"
}
],
"prettier/prettier": [
"error",
{
"singleQuote": true,
"printWidth": 90
}
]
},
"plugins": ["prettier"]
}
}

@@ -8,2 +8,4 @@ Inquirer.js

**Version 4.x** only supports Node 6 and over. For Node 4 support please use [version 3.x](https://github.com/SBoudrias/Inquirer.js/tree/v3.3.0).
## Table of Contents

@@ -57,3 +59,3 @@

var inquirer = require('inquirer');
inquirer.prompt([/* Pass your questions in here */]).then(function (answers) {
inquirer.prompt([/* Pass your questions in here */]).then(answers => {
// Use user feedback for... whatever!!

@@ -126,3 +128,3 @@ });

/* Preferred way: with promise */
filter: function () {
filter() {
return new Promise(/* etc... */);

@@ -137,3 +139,3 @@ },

// Do async stuff
setTimeout(function () {
setTimeout(function() {
if (typeof input !== 'number') {

@@ -140,0 +142,0 @@ // Pass the return value in the done callback

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc