Separator = function (line) { this.type = 'separator'; this.line = chalk.dim(line || new Array(15).join(figures.line)); }
...
### Separator
<a name="separator"></a>
A separator can be added to any `choices` array:
```
// In the question object
choices: [ "Choice A", new inquirer.Separator(), "choice B" ]
// Which'll be displayed this way
[?] What do you want to do?
> Order a pizza
Make a reservation
--------
Ask opening hours
...
createPromptModule = function (opt) {
var promptModule = function (questions) {
var ui = new inquirer.ui.Prompt(promptModule.prompts, opt);
var promise = ui.run(questions);
// Monkey patch the UI on the promise object so
// that it remains publicly accessible.
promise.ui = ui;
return promise;
};
promptModule.prompts = {};
/**
* Register a prompt type
* @param {String} name Prompt type name
* @param {Function} prompt Prompt constructor
* @return {inquirer}
*/
promptModule.registerPrompt = function (name, prompt) {
promptModule.prompts[name] = prompt;
return this;
};
/**
* Register the defaults provider prompts
*/
promptModule.restoreDefaultPrompts = function () {
this.registerPrompt('list', require('./prompts/list'));
this.registerPrompt('input', require('./prompts/input'));
this.registerPrompt('confirm', require('./prompts/confirm'));
this.registerPrompt('rawlist', require('./prompts/rawlist'));
this.registerPrompt('expand', require('./prompts/expand'));
this.registerPrompt('checkbox', require('./prompts/checkbox'));
this.registerPrompt('password', require('./prompts/password'));
this.registerPrompt('editor', require('./prompts/editor'));
};
promptModule.restoreDefaultPrompts();
return promptModule;
}
...
#### `inquirer.registerPrompt(name, prompt)`
Register prompt plugins under `name`.
- **name** (string) name of the this new prompt. (used for question `type`)
- **prompt** (object) the prompt object itself (the plugin)
#### `inquirer.createPromptModule() -> prompt function`
Create a self contained inquirer module. If don't want to affect other libraries that also rely on inquirer when you overwrite
or add new prompt types.
```js
var prompt = inquirer.createPromptModule();
prompt(questions).then(/* ... */);
...
prompt = function (questions) { var ui = new inquirer.ui.Prompt(promptModule.prompts, opt); var promise = ui.run(questions); // Monkey patch the UI on the promise object so // that it remains publicly accessible. promise.ui = ui; return promise; }
...
``` shell
npm install inquirer
```
```javascript
var inquirer = require('inquirer');
inquirer.prompt([/* Pass your questions in here */]).then(function (answers) {
// Use user feedback for... whatever!!
});
```
<a name="examples"></a>
### Examples (Run it and see it)
Check out the `examples/` folder for code and interface examples.
...
registerPrompt = function (name, prompt) { inquirer.prompt.registerPrompt(name, prompt); }
...
#### `inquirer.prompt(questions) -> promise`
Launch the prompt interface (inquiry session)
- **questions** (Array) containing [Question Object](#question) (using the [reactive interface](#reactive-interface), you can also
pass a `Rx.Observable` instance)
- returns a **Promise**
#### `inquirer.registerPrompt(name, prompt)`
Register prompt plugins under `name`.
- **name** (string) name of the this new prompt. (used for question `type`)
- **prompt** (object) the prompt object itself (the plugin)
#### `inquirer.createPromptModule() -> prompt function`
...
restoreDefaultPrompts = function () { inquirer.prompt.restoreDefaultPrompts(); }
n/a
function Prompt(opt) { opt || (opt = {}); Base.apply(this, arguments); this.log = through(this.writeLog.bind(this)); this.bottomBar = opt.bottomBar || ''; this.render(); }
...
Along with the prompts, Inquirer offers some basic text UI.
#### Bottom Bar - `inquirer.ui.BottomBar`
This UI present a fixed text at the bottom of a free text zone. This is useful to keep a message to the bottom of the screen while
outputting command outputs on the higher section.
```javascript
var ui = new inquirer.ui.BottomBar();
// pipe a Stream to the log zone
outputStream.pipe(ui.log);
// Or simply write output
ui.log.write('something just happened.');
ui.log.write('Almost over, standby!');
...
ui.Prompt = function (prompts, opt) { Base.call(this, opt); this.prompts = prompts; }
n/a
Separator = function (line) { this.type = 'separator'; this.line = chalk.dim(line || new Array(15).join(figures.line)); }
...
### Separator
<a name="separator"></a>
A separator can be added to any `choices` array:
```
// In the question object
choices: [ "Choice A", new inquirer.Separator(), "choice B" ]
// Which'll be displayed this way
[?] What do you want to do?
> Order a pizza
Make a reservation
--------
Ask opening hours
...
exclude = function (obj) { return obj.type !== 'separator'; }
n/a
toString = function () { return this.line; }
n/a
prompt = function (questions) { var ui = new inquirer.ui.Prompt(promptModule.prompts, opt); var promise = ui.run(questions); // Monkey patch the UI on the promise object so // that it remains publicly accessible. promise.ui = ui; return promise; }
...
``` shell
npm install inquirer
```
```javascript
var inquirer = require('inquirer');
inquirer.prompt([/* Pass your questions in here */]).then(function (answers) {
// Use user feedback for... whatever!!
});
```
<a name="examples"></a>
### Examples (Run it and see it)
Check out the `examples/` folder for code and interface examples.
...
registerPrompt = function (name, prompt) { promptModule.prompts[name] = prompt; return this; }
...
#### `inquirer.prompt(questions) -> promise`
Launch the prompt interface (inquiry session)
- **questions** (Array) containing [Question Object](#question) (using the [reactive interface](#reactive-interface), you can also
pass a `Rx.Observable` instance)
- returns a **Promise**
#### `inquirer.registerPrompt(name, prompt)`
Register prompt plugins under `name`.
- **name** (string) name of the this new prompt. (used for question `type`)
- **prompt** (object) the prompt object itself (the plugin)
#### `inquirer.createPromptModule() -> prompt function`
...
restoreDefaultPrompts = function () { this.registerPrompt('list', require('./prompts/list')); this.registerPrompt('input', require('./prompts/input')); this.registerPrompt('confirm', require('./prompts/confirm')); this.registerPrompt('rawlist', require('./prompts/rawlist')); this.registerPrompt('expand', require('./prompts/expand')); this.registerPrompt('checkbox', require('./prompts/checkbox')); this.registerPrompt('password', require('./prompts/password')); this.registerPrompt('editor', require('./prompts/editor')); }
n/a
function Prompt() { Base.apply(this, arguments); if (!this.opt.choices) { this.throwParamError('choices'); } if (_.isArray(this.opt.default)) { this.opt.choices.forEach(function (choice) { if (this.opt.default.indexOf(choice.value) >= 0) { choice.checked = true; } }, this); } this.pointer = 0; this.firstRender = true; // Make sure no default is set (so it won't be printed) this.opt.default = null; this.paginator = new Paginator(); }
n/a
function Prompt() { Base.apply(this, arguments); var rawDefault = true; _.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; } this.opt.default = rawDefault ? 'Y/n' : 'y/N'; return this; }
n/a
function Prompt() { return Base.apply(this, arguments); }
n/a
function Prompt() { Base.apply(this, arguments); if (!this.opt.choices) { this.throwParamError('choices'); } this.validateChoices(this.opt.choices); // Add the default `help` (/expand) option this.opt.choices.push({ key: 'h', name: 'Help, list all options', value: 'help' }); this.opt.validate = function (choice) { if (choice == null) { return 'Please enter a valid command'; } return choice !== 'help'; }; // Setup the default string (capitalize the default key) this.opt.default = this.generateChoicesString(this.opt.choices, this.opt.default); this.paginator = new Paginator(); }
n/a
function Prompt() { return Base.apply(this, arguments); }
n/a
function Prompt() { Base.apply(this, arguments); if (!this.opt.choices) { this.throwParamError('choices'); } this.firstRender = true; this.selected = 0; var def = this.opt.default; // Default being a Number if (_.isNumber(def) && def >= 0 && def < this.opt.choices.realLength) { this.selected = def; } // Default being a String if (_.isString(def)) { this.selected = this.opt.choices.pluck('value').indexOf(def); } // Make sure no default is set (so it won't be printed) this.opt.default = null; this.paginator = new Paginator(); }
n/a
function Prompt() { return Base.apply(this, arguments); }
n/a
function Prompt() { Base.apply(this, arguments); if (!this.opt.choices) { this.throwParamError('choices'); } this.opt.validChoices = this.opt.choices.filter(Separator.exclude); this.selected = 0; this.rawDefault = 0; _.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; } // Make sure no default is set (so it won't be printed) this.opt.default = null; this.paginator = new Paginator(); }
n/a
_run = function (cb) { this.done = cb; var events = observe(this.rl); var validation = this.handleSubmitEvents( events.line.map(this.getCurrentValue.bind(this)) ); validation.success.forEach(this.onEnd.bind(this)); validation.error.forEach(this.onError.bind(this)); events.normalizedUpKey.takeUntil(validation.success).forEach(this.onUpKey.bind(this)); events.normalizedDownKey.takeUntil(validation.success).forEach(this.onDownKey.bind(this)); events.numberKey.takeUntil(validation.success).forEach(this.onNumberKey.bind(this)); events.spaceKey.takeUntil(validation.success).forEach(this.onSpaceKey.bind(this)); events.aKey.takeUntil(validation.success).forEach(this.onAllKey.bind(this)); events.iKey.takeUntil(validation.success).forEach(this.onInverseKey.bind(this)); // Init the prompt cliCursor.hide(); this.render(); this.firstRender = false; return this; }
n/a
getCurrentValue = function () { var choices = this.opt.choices.filter(function (choice) { return Boolean(choice.checked) && !choice.disabled; }); this.selection = _.map(choices, 'short'); return _.map(choices, 'value'); }
n/a
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.render(); }
n/a
onDownKey = function () { var len = this.opt.choices.realLength; this.pointer = (this.pointer < len - 1) ? this.pointer + 1 : 0; this.render(); }
n/a
onEnd = function (state) { this.status = 'answered'; // Rerender prompt (and clean subline error) this.render(); this.screen.done(); cliCursor.show(); this.done(state.value); }
n/a
onError = function (state) { this.render(state.isValid); }
n/a
onInverseKey = function () { this.opt.choices.forEach(function (choice) { if (choice.type !== 'separator') { choice.checked = !choice.checked; } }); this.render(); }
n/a
onNumberKey = function (input) { if (input <= this.opt.choices.realLength) { this.pointer = input - 1; this.toggleChoice(this.pointer); } this.render(); }
n/a
onSpaceKey = function () { this.toggleChoice(this.pointer); this.render(); }
n/a
onUpKey = function () { var len = this.opt.choices.realLength; this.pointer = (this.pointer > 0) ? this.pointer - 1 : len - 1; this.render(); }
n/a
render = function (error) { // Render question var message = this.getQuestion(); var bottomContent = ''; 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 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); } if (error) { bottomContent = chalk.red('>> ') + error; } this.screen.render(message, bottomContent); }
n/a
toggleChoice = function (index) { var item = this.opt.choices.getChoice(index); if (item !== undefined) { this.opt.choices.getChoice(index).checked = !item.checked; } }
n/a
_run = function (cb) { this.done = cb; // Once user confirm (enter key) var events = observe(this.rl); events.keypress.takeUntil(events.line).forEach(this.onKeypress.bind(this)); events.line.take(1).forEach(this.onEnd.bind(this)); // Init this.render(); return this; }
n/a
onEnd = function (input) { this.status = 'answered'; var output = this.opt.filter(input); this.render(output); this.screen.done(); this.done(output); }
n/a
onKeypress = function () { this.render(); }
n/a
render = function (answer) { var message = this.getQuestion(); if (typeof answer === 'boolean') { message += chalk.cyan(answer ? 'Yes' : 'No'); } else { message += this.rl.line; } this.screen.render(message); return this; }
n/a
_run = function (cb) { this.done = cb; this.editorResult = new rx.Subject(); // Open Editor on "line" (Enter Key) var events = observe(this.rl); this.lineSubscription = events.line.forEach(this.startExternalEditor.bind(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)); // Prevents default from being printed on screen (can look weird with multiple lines) this.currentText = this.opt.default; this.opt.default = null; // Init this.render(); return this; }
n/a
endExternalEditor = function (error, result) { this.rl.resume(); if (error) { this.editorResult.onError(error); } else { this.editorResult.onNext(result); } }
n/a
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); }
n/a
onError = function (state) { this.render(state.isValid); }
n/a
render = function (error) { var bottomContent = ''; var message = this.getQuestion(); if (this.status === 'answered') { message += chalk.dim('Received'); } else { message += chalk.dim('Press <enter> to launch your preferred editor.'); } if (error) { bottomContent = chalk.red('>> ') + error; } this.screen.render(message, bottomContent); }
n/a
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)); }
n/a
_run = function (cb) { this.done = cb; // 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)); // Init the prompt this.render(); return this; }
n/a
generateChoicesString = function (choices, defaultIndex) { var defIndex = choices.realLength - 1; if (_.isNumber(defaultIndex) && this.opt.choices.getChoice(defaultIndex)) { defIndex = defaultIndex; } var defStr = this.opt.choices.pluck('key'); this.rawDefault = defStr[defIndex]; defStr[defIndex] = String(defStr[defIndex]).toUpperCase(); return defStr.join(''); }
n/a
getChoices = function () { var output = ''; this.opt.choices.forEach(function (choice) { output += '\n '; if (choice.type === 'separator') { output += ' ' + choice; return; } var choiceStr = choice.key + ') ' + choice.name; if (this.selectedKey === choice.key) { choiceStr = chalk.cyan(choiceStr); } output += choiceStr; }.bind(this)); return output; }
n/a
getCurrentValue = function (input) { if (!input) { input = this.rawDefault; } var selected = this.opt.choices.where({key: input.toLowerCase().trim()})[0]; if (!selected) { return null; } return selected.value; }
n/a
onError = function (state) { if (state.value === 'help') { this.selectedKey = ''; this.status = 'expanded'; this.render(); return; } this.render(state.isValid); }
n/a
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); } }
n/a
onSubmit = function (state) { this.status = 'answered'; var choice = this.opt.choices.where({value: state.value})[0]; this.answer = choice.short || choice.name; // Re-render prompt this.render(); this.screen.done(); this.done(state.value); }
n/a
render = function (error, hint) { var message = this.getQuestion(); var bottomContent = ''; if (this.status === 'answered') { message += chalk.cyan(this.answer); } else if (this.status === 'expanded') { var choicesStr = renderChoices(this.opt.choices, this.selectedKey); message += this.paginator.paginate(choicesStr, this.selectedKey, this.opt.pageSize); message += '\n Answer: '; } message += this.rl.line; if (error) { bottomContent = chalk.red('>> ') + error; } if (hint) { bottomContent = chalk.cyan('>> ') + hint; } this.screen.render(message, bottomContent); }
n/a
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 (keymap[choice.key]) { errors.push(choice.key); } 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 (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(', ')); } }
n/a
_run = function (cb) { this.done = cb; // Once user confirm (enter key) var events = observe(this.rl); var submit = events.line.map(this.filterInput.bind(this)); var validation = this.handleSubmitEvents(submit); validation.success.forEach(this.onEnd.bind(this)); validation.error.forEach(this.onError.bind(this)); events.keypress.takeUntil(validation.success).forEach(this.onKeypress.bind(this)); // Init this.render(); return this; }
n/a
filterInput = function (input) { if (!input) { return this.opt.default == null ? '' : this.opt.default; } return input; }
n/a
onEnd = function (state) { this.answer = state.value; this.status = 'answered'; // Re-render prompt this.render(); this.screen.done(); this.done(state.value); }
n/a
onError = function (state) { this.render(state.isValid); }
n/a
onKeypress = function () { this.render(); }
n/a
render = function (error) { var bottomContent = ''; var message = this.getQuestion(); if (this.status === 'answered') { message += chalk.cyan(this.answer); } else { message += this.rl.line; } if (error) { bottomContent = chalk.red('>> ') + error; } this.screen.render(message, bottomContent); }
n/a
_run = function (cb) { this.done = cb; var self = this; var events = observe(this.rl); events.normalizedUpKey.takeUntil(events.line).forEach(this.onUpKey.bind(this)); events.normalizedDownKey.takeUntil(events.line).forEach(this.onDownKey.bind(this)); events.numberKey.takeUntil(events.line).forEach(this.onNumberKey.bind(this)); events.line .take(1) .map(this.getCurrentValue.bind(this)) .flatMap(function (value) { return runAsync(self.opt.filter)(value).catch(function (err) { return err; }); }) .forEach(this.onSubmit.bind(this)); // Init the prompt cliCursor.hide(); this.render(); return this; }
n/a
getCurrentValue = function () { return this.opt.choices.getChoice(this.selected).value; }
n/a
onDownKey = function () { var len = this.opt.choices.realLength; this.selected = (this.selected < len - 1) ? this.selected + 1 : 0; this.render(); }
n/a
onNumberKey = function (input) { if (input <= this.opt.choices.realLength) { this.selected = input - 1; } this.render(); }
n/a
onSubmit = function (value) { this.status = 'answered'; // Rerender prompt this.render(); this.screen.done(); cliCursor.show(); this.done(value); }
n/a
onUpKey = function () { var len = this.opt.choices.realLength; this.selected = (this.selected > 0) ? this.selected - 1 : len - 1; this.render(); }
n/a
render = function () { // Render question var message = this.getQuestion(); if (this.firstRender) { message += chalk.dim('(Use arrow keys)'); } // Render choices or answer depending on the state if (this.status === 'answered') { message += chalk.cyan(this.opt.choices.getChoice(this.selected).short); } 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.firstRender = false; this.screen.render(message); }
n/a
_run = function (cb) { this.done = cb; var events = observe(this.rl); // 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)); events.keypress.takeUntil(validation.success).forEach(this.onKeypress.bind(this)); // Init this.render(); return this; }
n/a
filterInput = function (input) { if (!input) { return this.opt.default == null ? '' : this.opt.default; } return input; }
n/a
onEnd = function (state) { this.status = 'answered'; this.answer = state.value; // Re-render prompt this.render(); this.screen.done(); this.done(state.value); }
n/a
onError = function (state) { this.render(state.isValid); this.rl.output.unmute(); }
n/a
onKeypress = function () { this.render(); }
n/a
render = function (error) { var message = this.getQuestion(); var bottomContent = ''; if (this.status === 'answered') { message += chalk.cyan(mask(this.answer)); } else { message += mask(this.rl.line || ''); } if (error) { bottomContent = '\n' + chalk.red('>> ') + error; } this.screen.render(message, bottomContent); }
n/a
_run = function (cb) { this.done = cb; // Once user confirm (enter key) var events = observe(this.rl); var submit = events.line.map(this.getCurrentValue.bind(this)); var validation = this.handleSubmitEvents(submit); validation.success.forEach(this.onEnd.bind(this)); validation.error.forEach(this.onError.bind(this)); events.keypress.takeUntil(validation.success).forEach(this.onKeypress.bind(this)); // Init the prompt this.render(); return this; }
n/a
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; }
n/a
onEnd = function (state) { this.status = 'answered'; this.answer = state.value; // Re-render prompt this.render(); this.screen.done(); this.done(state.value); }
n/a
onError = function () { this.render('Please enter a valid index'); }
n/a
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; } this.render(); }
n/a
render = function (error) { // Render question var message = this.getQuestion(); var bottomContent = ''; if (this.status === 'answered') { message += chalk.cyan(this.answer); } else { var choicesStr = renderChoices(this.opt.choices, this.selected); message += this.paginator.paginate(choicesStr, this.selected, this.opt.pageSize); message += '\n Answer: '; } message += this.rl.line; if (error) { bottomContent = '\n' + chalk.red('>> ') + error; } this.screen.render(message, bottomContent); }
n/a
function Prompt(opt) { opt || (opt = {}); Base.apply(this, arguments); this.log = through(this.writeLog.bind(this)); this.bottomBar = opt.bottomBar || ''; this.render(); }
...
Along with the prompts, Inquirer offers some basic text UI.
#### Bottom Bar - `inquirer.ui.BottomBar`
This UI present a fixed text at the bottom of a free text zone. This is useful to keep a message to the bottom of the screen while
outputting command outputs on the higher section.
```javascript
var ui = new inquirer.ui.BottomBar();
// pipe a Stream to the log zone
outputStream.pipe(ui.log);
// Or simply write output
ui.log.write('something just happened.');
ui.log.write('Almost over, standby!');
...
Prompt = function (prompts, opt) { Base.call(this, opt); this.prompts = prompts; }
n/a
function Prompt(opt) { opt || (opt = {}); Base.apply(this, arguments); this.log = through(this.writeLog.bind(this)); this.bottomBar = opt.bottomBar || ''; this.render(); }
...
Along with the prompts, Inquirer offers some basic text UI.
#### Bottom Bar - `inquirer.ui.BottomBar`
This UI present a fixed text at the bottom of a free text zone. This is useful to keep a message to the bottom of the screen while
outputting command outputs on the higher section.
```javascript
var ui = new inquirer.ui.BottomBar();
// pipe a Stream to the log zone
outputStream.pipe(ui.log);
// Or simply write output
ui.log.write('something just happened.');
ui.log.write('Almost over, standby!');
...
super_ = 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(); this.onForceClose = this.onForceClose.bind(this); // Make sure new prompt start on a newline when closing this.rl.on('SIGINT', this.onForceClose); process.on('exit', this.onForceClose); }
n/a
clean = function () { rlUtils.clearLine(this.rl, this.bottomBar.split('\n').length); return this; }
n/a
enforceLF = function (str) { return str.match(/[\r\n]$/) ? str : str + '\n'; }
n/a
render = function () { this.write(this.bottomBar); return this; }
n/a
updateBottomBar = function (bottomBar) { this.bottomBar = bottomBar; rlUtils.clearLine(this.rl, 1); this.rl.output.unmute(); this.clean().render(); this.rl.output.mute(); return this; }
...
// Or simply write output
ui.log.write('something just happened.');
ui.log.write('Almost over, standby!');
// During processing, update the bottom bar content to display a loader
// or output a progress bar, etc
ui.updateBottomBar('new bottom bar content');
```
<a name="reactive"></a>
## Reactive interface
Internally, Inquirer uses the [JS reactive extension](https://github.com/Reactive-Extensions/RxJS) to handle events and async flows
.
...
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);
}
...
```javascript
var ui = new inquirer.ui.BottomBar();
// pipe a Stream to the log zone
outputStream.pipe(ui.log);
// Or simply write output
ui.log.write('something just happened.');
ui.log.write('Almost over, standby!');
// During processing, update the bottom bar content to display a loader
// or output a progress bar, etc
ui.updateBottomBar('new bottom bar content');
```
...
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; }
n/a
close = function () { // Remove events listeners this.rl.removeListener('SIGINT', this.onForceClose); process.removeListener('exit', this.onForceClose); this.rl.output.unmute(); if (this.activePrompt && typeof this.activePrompt.close === 'function') { this.activePrompt.close(); } // Close the readline this.rl.output.end(); this.rl.pause(); this.rl.close(); }
n/a
onForceClose = function () { this.close(); console.log(''); }
n/a
Prompt = function (prompts, opt) { Base.call(this, opt); this.prompts = prompts; }
n/a
super_ = 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(); this.onForceClose = this.onForceClose.bind(this); // Make sure new prompt start on a newline when closing this.rl.on('SIGINT', this.onForceClose); process.on('exit', this.onForceClose); }
n/a
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)); }
n/a
filterIfRunnable = function (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(function () { return rx.Observable.fromPromise( runAsync(question.when)(answers).then(function (shouldRun) { if (shouldRun) { return question; } }) ).filter(function (val) { return val != null; }); }); }
n/a
onCompletion = function (answers) { this.close(); return answers; }
n/a
processQuestion = function (question) { question = _.clone(question); return rx.Observable.defer(function () { var obs = rx.Observable.of(question); 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)); }
n/a
run = function (questions) { // Keep global reference to the answers this.answers = {}; // Make sure questions is an array. if (_.isPlainObject(questions)) { questions = [questions]; } // Create an observable, unless we received one as parameter. // Note: As this is a public interface, we cannot do an instanceof check as we won't // be using the exact same object in memory. var obs = _.isArray(questions) ? rx.Observable.from(questions) : questions; this.process = obs .concatMap(this.processQuestion.bind(this)) // `publish` creates a hot Observable. It prevents duplicating prompts. .publish(); this.process.connect(); 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)); }
n/a
setDefaultType = function (question) { // Default type to input if (!this.prompts[question.type]) { question.type = 'input'; } return rx.Observable.defer(function () { return rx.Observable.return(question); }); }
n/a