diff options
Diffstat (limited to 'sandbox/testAppNevena/Front/node_modules/inquirer/lib')
23 files changed, 2703 insertions, 0 deletions
diff --git a/sandbox/testAppNevena/Front/node_modules/inquirer/lib/inquirer.js b/sandbox/testAppNevena/Front/node_modules/inquirer/lib/inquirer.js new file mode 100644 index 00000000..0f91e4be --- /dev/null +++ b/sandbox/testAppNevena/Front/node_modules/inquirer/lib/inquirer.js @@ -0,0 +1,93 @@ +'use strict'; +/** + * Inquirer.js + * A collection of common interactive command line user interfaces. + */ + +const inquirer = module.exports; + +/** + * Client interfaces + */ + +inquirer.prompts = {}; + +inquirer.Separator = require('./objects/separator'); + +inquirer.ui = { + BottomBar: require('./ui/bottom-bar'), + Prompt: require('./ui/prompt'), +}; + +/** + * Create a new self-contained prompt module. + */ +inquirer.createPromptModule = function (opt) { + const promptModule = function (questions, answers) { + let ui; + try { + ui = new inquirer.ui.Prompt(promptModule.prompts, opt); + } catch (error) { + return Promise.reject(error); + } + const promise = ui.run(questions, answers); + + // 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('number', require('./prompts/number')); + 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; +}; + +/** + * Public CLI helper interface + * @param {Array|Object|Rx.Observable} questions - Questions settings array + * @param {Function} cb - Callback being passed the user answers + * @return {inquirer.ui.Prompt} + */ + +inquirer.prompt = inquirer.createPromptModule(); + +// Expose helper functions on the top level for easiest usage by common users +inquirer.registerPrompt = function (name, prompt) { + inquirer.prompt.registerPrompt(name, prompt); +}; + +inquirer.restoreDefaultPrompts = function () { + inquirer.prompt.restoreDefaultPrompts(); +}; diff --git a/sandbox/testAppNevena/Front/node_modules/inquirer/lib/objects/choice.js b/sandbox/testAppNevena/Front/node_modules/inquirer/lib/objects/choice.js new file mode 100644 index 00000000..7dc88d67 --- /dev/null +++ b/sandbox/testAppNevena/Front/node_modules/inquirer/lib/objects/choice.js @@ -0,0 +1,43 @@ +'use strict'; +const _ = { + isString: require('lodash/isString'), + isNumber: require('lodash/isNumber'), + extend: require('lodash/extend'), + isFunction: require('lodash/isFunction'), +}; + +/** + * Choice object + * Normalize input as choice object + * @constructor + * @param {Number|String|Object} val Choice value. If an object is passed, it should contains + * at least one of `value` or `name` property + */ + +module.exports = class Choice { + constructor(val, answers) { + // Don't process Choice and Separator object + if (val instanceof Choice || val.type === 'separator') { + // eslint-disable-next-line no-constructor-return + return val; + } + + if (_.isString(val) || _.isNumber(val)) { + this.name = String(val); + this.value = val; + this.short = String(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; + } + } +}; diff --git a/sandbox/testAppNevena/Front/node_modules/inquirer/lib/objects/choices.js b/sandbox/testAppNevena/Front/node_modules/inquirer/lib/objects/choices.js new file mode 100644 index 00000000..917b9196 --- /dev/null +++ b/sandbox/testAppNevena/Front/node_modules/inquirer/lib/objects/choices.js @@ -0,0 +1,127 @@ +'use strict'; +const assert = require('assert'); +const _ = { + isNumber: require('lodash/isNumber'), + filter: require('lodash/filter'), + map: require('lodash/map'), + find: require('lodash/find'), +}; +const Separator = require('./separator'); +const Choice = require('./choice'); + +/** + * Choices collection + * Collection of multiple `choice` object + * @constructor + * @param {Array} choices All `choice` to keep in the collection + */ + +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 new Choice(val, answers); + }); + + 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, 'realLength', { + get() { + return this.realChoices.length; + }, + set() { + 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 + */ + + getChoice(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(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 + */ + + where(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(propertyName) { + return _.map(this.realChoices, propertyName); + } + + // Expose usual Array methods + indexOf(...args) { + return this.choices.indexOf(...args); + } + + forEach(...args) { + return this.choices.forEach(...args); + } + + filter(...args) { + return this.choices.filter(...args); + } + + reduce(...args) { + return this.choices.reduce(...args); + } + + find(func) { + return _.find(this.choices, func); + } + + push(...args) { + const objs = _.map(args, (val) => new Choice(val)); + this.choices.push(...objs); + this.realChoices = this.choices + .filter(Separator.exclude) + .filter((item) => !item.disabled); + return this.choices; + } +}; diff --git a/sandbox/testAppNevena/Front/node_modules/inquirer/lib/objects/separator.js b/sandbox/testAppNevena/Front/node_modules/inquirer/lib/objects/separator.js new file mode 100644 index 00000000..c7708628 --- /dev/null +++ b/sandbox/testAppNevena/Front/node_modules/inquirer/lib/objects/separator.js @@ -0,0 +1,37 @@ +'use strict'; +const chalk = require('chalk'); +const figures = require('figures'); + +/** + * Separator object + * Used to space/separate choices group + * @constructor + * @param {String} line Separation line content (facultative) + */ + +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; + } +} + +/** + * Helper function returning false if object is a separator + * @param {Object} obj object to test against + * @return {Boolean} `false` if object is a separator + */ + +Separator.exclude = function (obj) { + return obj.type !== 'separator'; +}; + +module.exports = Separator; diff --git a/sandbox/testAppNevena/Front/node_modules/inquirer/lib/prompts/base.js b/sandbox/testAppNevena/Front/node_modules/inquirer/lib/prompts/base.js new file mode 100644 index 00000000..ec503eec --- /dev/null +++ b/sandbox/testAppNevena/Front/node_modules/inquirer/lib/prompts/base.js @@ -0,0 +1,181 @@ +'use strict'; +/** + * Base prompt implementation + * Should be extended by prompt types. + */ +const _ = { + assign: require('lodash/assign'), + defaults: require('lodash/defaults'), + clone: require('lodash/clone'), +}; +const chalk = require('chalk'); +const runAsync = require('run-async'); +const { filter, flatMap, share, take, takeUntil } = require('rxjs/operators'); +const Choices = require('../objects/choices'); +const ScreenManager = require('../utils/screen-manager'); + +class Prompt { + constructor(question, rl, answers) { + // Setup instance defaults property + _.assign(this, { + answers, + status: 'pending', + }); + + // Set defaults prompt options + this.opt = _.defaults(_.clone(question), { + validate: () => true, + validatingText: '', + filter: (val) => val, + filteringText: '', + when: () => true, + suffix: '', + prefix: chalk.green('?'), + }); + + // Make sure name is present + if (!this.opt.name) { + this.throwParamError('name'); + } + + // Set default message if no message defined + if (!this.opt.message) { + this.opt.message = this.opt.name + ':'; + } + + // 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); + } + + /** + * Start the Inquiry session and manage output value filtering + * @return {Promise} + */ + + run() { + return new Promise((resolve, reject) => { + this._run( + (value) => resolve(value), + (error) => reject(error) + ); + }); + } + + // 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} + */ + + throwParamError(name) { + throw new Error('You must provide a `' + name + '` parameter'); + } + + /** + * 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` + */ + handleSubmitEvents(submit) { + const self = this; + const validate = runAsync(this.opt.validate); + const asyncFilter = runAsync(this.opt.filter); + const validation = submit.pipe( + flatMap((value) => { + this.startSpinner(value, this.opt.filteringText); + return asyncFilter(value, self.answers).then( + (filteredValue) => { + this.startSpinner(filteredValue, this.opt.validatingText); + return validate(filteredValue, self.answers).then( + (isValid) => ({ isValid, value: filteredValue }), + (err) => ({ isValid: err, value: filteredValue }) + ); + }, + (err) => ({ isValid: err }) + ); + }), + share() + ); + + const success = validation.pipe( + filter((state) => state.isValid === true), + take(1) + ); + const error = validation.pipe( + filter((state) => state.isValid !== true), + takeUntil(success) + ); + + return { + success, + error, + }; + } + + startSpinner(value, bottomContent) { + value = this.getSpinningValue(value); + // If the question will spin, cut off the prefix (for layout purposes) + const content = bottomContent + ? this.getQuestion() + value + : this.getQuestion().slice(this.opt.prefix.length + 1) + value; + + this.screen.renderWithSpinner(content, bottomContent); + } + + /** + * Allow override, e.g. for password prompts + * See: https://github.com/SBoudrias/Inquirer.js/issues/1022 + * + * @return {String} value to display while spinning + */ + getSpinningValue(value) { + return value; + } + + /** + * Generate the prompt question string + * @return {String} prompt question string + */ + getQuestion() { + let message = + (this.opt.prefix ? this.opt.prefix + ' ' : '') + + chalk.bold(this.opt.message) + + this.opt.suffix + + chalk.reset(' '); + + // Append the default if available, and if question isn't touched/answered + if ( + this.opt.default != null && + this.status !== 'touched' && + this.status !== 'answered' + ) { + // If default password is supplied, hide it + if (this.opt.type === 'password') { + message += chalk.italic.dim('[hidden] '); + } else { + message += chalk.dim('(' + this.opt.default + ') '); + } + } + + return message; + } +} + +module.exports = Prompt; diff --git a/sandbox/testAppNevena/Front/node_modules/inquirer/lib/prompts/checkbox.js b/sandbox/testAppNevena/Front/node_modules/inquirer/lib/prompts/checkbox.js new file mode 100644 index 00000000..4f60b77e --- /dev/null +++ b/sandbox/testAppNevena/Front/node_modules/inquirer/lib/prompts/checkbox.js @@ -0,0 +1,278 @@ +'use strict'; +/** + * `list` type prompt + */ + +const _ = { + isArray: require('lodash/isArray'), + map: require('lodash/map'), + isString: require('lodash/isString'), +}; +const chalk = require('chalk'); +const cliCursor = require('cli-cursor'); +const figures = require('figures'); +const { map, takeUntil } = require('rxjs/operators'); +const Base = require('./base'); +const observe = require('../utils/events'); +const Paginator = require('../utils/paginator'); +const incrementListIndex = require('../utils/incrementListIndex'); + +class CheckboxPrompt extends Base { + constructor(questions, rl, answers) { + super(questions, rl, answers); + + 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; + + // Make sure no default is set (so it won't be printed) + this.opt.default = null; + + const shouldLoop = this.opt.loop === undefined ? true : this.opt.loop; + this.paginator = new Paginator(this.screen, { isInfinite: shouldLoop }); + } + + /** + * Start the Inquiry session + * @param {Function} cb Callback when prompt is done + * @return {this} + */ + + _run(cb) { + this.done = cb; + + const events = observe(this.rl); + + const validation = this.handleSubmitEvents( + events.line.pipe(map(this.getCurrentValue.bind(this))) + ); + validation.success.forEach(this.onEnd.bind(this)); + validation.error.forEach(this.onError.bind(this)); + + events.normalizedUpKey + .pipe(takeUntil(validation.success)) + .forEach(this.onUpKey.bind(this)); + events.normalizedDownKey + .pipe(takeUntil(validation.success)) + .forEach(this.onDownKey.bind(this)); + events.numberKey + .pipe(takeUntil(validation.success)) + .forEach(this.onNumberKey.bind(this)); + events.spaceKey + .pipe(takeUntil(validation.success)) + .forEach(this.onSpaceKey.bind(this)); + events.aKey.pipe(takeUntil(validation.success)).forEach(this.onAllKey.bind(this)); + events.iKey.pipe(takeUntil(validation.success)).forEach(this.onInverseKey.bind(this)); + + // Init the prompt + cliCursor.hide(); + this.render(); + this.firstRender = false; + + return this; + } + + /** + * Render the prompt to screen + * @return {CheckboxPrompt} self + */ + + render(error) { + // Render question + let message = this.getQuestion(); + let bottomContent = ''; + + if (!this.dontShowHints) { + message += + '(Press ' + + chalk.cyan.bold('<space>') + + ' to select, ' + + chalk.cyan.bold('<a>') + + ' to toggle all, ' + + chalk.cyan.bold('<i>') + + ' to invert selection, and ' + + chalk.cyan.bold('<enter>') + + ' to proceed)'; + } + + // Render choices or answer depending on the state + if (this.status === 'answered') { + message += chalk.cyan(this.selection.join(', ')); + } else { + const choicesStr = renderChoices(this.opt.choices, this.pointer); + const indexPosition = this.opt.choices.indexOf( + this.opt.choices.getChoice(this.pointer) + ); + const realIndexPosition = + this.opt.choices.reduce((acc, value, i) => { + // Dont count lines past the choice we are looking at + if (i > indexPosition) { + return acc; + } + // Add line if it's a separator + if (value.type === 'separator') { + return acc + 1; + } + + let l = value.name; + // Non-strings take up one line + if (typeof l !== 'string') { + return acc + 1; + } + + // Calculate lines taken up by string + l = l.split('\n'); + return acc + l.length; + }, 0) - 1; + message += + '\n' + this.paginator.paginate(choicesStr, realIndexPosition, this.opt.pageSize); + } + + if (error) { + bottomContent = chalk.red('>> ') + error; + } + + this.screen.render(message, bottomContent); + } + + /** + * When user press `enter` key + */ + + onEnd(state) { + this.status = 'answered'; + this.dontShowHints = true; + // Rerender prompt (and clean subline error) + this.render(); + + this.screen.done(); + cliCursor.show(); + this.done(state.value); + } + + onError(state) { + this.render(state.isValid); + } + + getCurrentValue() { + const choices = this.opt.choices.filter( + (choice) => Boolean(choice.checked) && !choice.disabled + ); + + this.selection = _.map(choices, 'short'); + return _.map(choices, 'value'); + } + + onUpKey() { + this.pointer = incrementListIndex(this.pointer, 'up', this.opt); + this.render(); + } + + onDownKey() { + this.pointer = incrementListIndex(this.pointer, 'down', this.opt); + this.render(); + } + + onNumberKey(input) { + if (input <= this.opt.choices.realLength) { + this.pointer = input - 1; + this.toggleChoice(this.pointer); + } + + this.render(); + } + + onSpaceKey() { + this.toggleChoice(this.pointer); + this.render(); + } + + onAllKey() { + const shouldBeChecked = Boolean( + this.opt.choices.find((choice) => choice.type !== 'separator' && !choice.checked) + ); + + this.opt.choices.forEach((choice) => { + if (choice.type !== 'separator') { + choice.checked = shouldBeChecked; + } + }); + + this.render(); + } + + onInverseKey() { + this.opt.choices.forEach((choice) => { + if (choice.type !== 'separator') { + choice.checked = !choice.checked; + } + }); + + this.render(); + } + + toggleChoice(index) { + const item = this.opt.choices.getChoice(index); + if (item !== undefined) { + this.opt.choices.getChoice(index).checked = !item.checked; + } + } +} + +/** + * Function for rendering checkbox choices + * @param {Number} pointer Position of the pointer + * @return {String} Rendered content + */ + +function renderChoices(choices, pointer) { + let output = ''; + let separatorOffset = 0; + + choices.forEach((choice, i) => { + if (choice.type === 'separator') { + separatorOffset++; + output += ' ' + choice + '\n'; + return; + } + + if (choice.disabled) { + separatorOffset++; + output += ' - ' + choice.name; + output += ' (' + (_.isString(choice.disabled) ? choice.disabled : 'Disabled') + ')'; + } else { + const line = getCheckbox(choice.checked) + ' ' + choice.name; + if (i - separatorOffset === pointer) { + output += chalk.cyan(figures.pointer + line); + } else { + output += ' ' + line; + } + } + + output += '\n'; + }); + + return output.replace(/\n$/, ''); +} + +/** + * Get the checkbox + * @param {Boolean} checked - add a X or not to the checkbox + * @return {String} Composited checkbox string + */ + +function getCheckbox(checked) { + return checked ? chalk.green(figures.radioOn) : figures.radioOff; +} + +module.exports = CheckboxPrompt; diff --git a/sandbox/testAppNevena/Front/node_modules/inquirer/lib/prompts/confirm.js b/sandbox/testAppNevena/Front/node_modules/inquirer/lib/prompts/confirm.js new file mode 100644 index 00000000..8171aab5 --- /dev/null +++ b/sandbox/testAppNevena/Front/node_modules/inquirer/lib/prompts/confirm.js @@ -0,0 +1,102 @@ +'use strict'; +/** + * `confirm` type prompt + */ + +const _ = { + extend: require('lodash/extend'), + isBoolean: require('lodash/isBoolean'), +}; +const chalk = require('chalk'); +const { take, takeUntil } = require('rxjs/operators'); +const Base = require('./base'); +const observe = require('../utils/events'); + +class ConfirmPrompt extends Base { + constructor(questions, rl, answers) { + super(questions, rl, answers); + + let rawDefault = true; + + _.extend(this.opt, { + filter(input) { + let 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'; + } + + /** + * Start the Inquiry session + * @param {Function} cb Callback when prompt is done + * @return {this} + */ + + _run(cb) { + this.done = cb; + + // Once user confirm (enter key) + const events = observe(this.rl); + events.keypress.pipe(takeUntil(events.line)).forEach(this.onKeypress.bind(this)); + + events.line.pipe(take(1)).forEach(this.onEnd.bind(this)); + + // Init + this.render(); + + return this; + } + + /** + * Render the prompt to screen + * @return {ConfirmPrompt} self + */ + + render(answer) { + let message = this.getQuestion(); + + if (typeof answer === 'boolean') { + message += chalk.cyan(answer ? 'Yes' : 'No'); + } else { + message += this.rl.line; + } + + this.screen.render(message); + + return this; + } + + /** + * When user press `enter` key + */ + + onEnd(input) { + this.status = 'answered'; + + const output = this.opt.filter(input); + this.render(output); + + this.screen.done(); + this.done(output); + } + + /** + * When user press a key + */ + + onKeypress() { + this.render(); + } +} + +module.exports = ConfirmPrompt; diff --git a/sandbox/testAppNevena/Front/node_modules/inquirer/lib/prompts/editor.js b/sandbox/testAppNevena/Front/node_modules/inquirer/lib/prompts/editor.js new file mode 100644 index 00000000..600e0745 --- /dev/null +++ b/sandbox/testAppNevena/Front/node_modules/inquirer/lib/prompts/editor.js @@ -0,0 +1,100 @@ +'use strict'; +/** + * `editor` type prompt + */ + +const chalk = require('chalk'); +const { editAsync } = require('external-editor'); +const Base = require('./base'); +const observe = require('../utils/events'); +const { Subject } = require('rxjs'); + +class EditorPrompt extends Base { + /** + * Start the Inquiry session + * @param {Function} cb Callback when prompt is done + * @return {this} + */ + + _run(cb) { + this.done = cb; + + this.editorResult = new Subject(); + + // Open Editor on "line" (Enter Key) + const events = observe(this.rl); + this.lineSubscription = events.line.subscribe(this.startExternalEditor.bind(this)); + + // Trigger Validation when editor closes + const 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; + } + + /** + * Render the prompt to screen + * @return {EditorPrompt} self + */ + + render(error) { + let bottomContent = ''; + let 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); + } + + /** + * Launch $EDITOR on user press enter + */ + + startExternalEditor() { + // Pause Readline to prevent stdin and stdout from being modified while the editor is showing + this.rl.pause(); + editAsync(this.currentText, this.endExternalEditor.bind(this)); + } + + endExternalEditor(error, result) { + this.rl.resume(); + if (error) { + this.editorResult.error(error); + } else { + this.editorResult.next(result); + } + } + + onEnd(state) { + this.editorResult.unsubscribe(); + this.lineSubscription.unsubscribe(); + this.answer = state.value; + this.status = 'answered'; + // Re-render prompt + this.render(); + this.screen.done(); + this.done(this.answer); + } + + onError(state) { + this.render(state.isValid); + } +} + +module.exports = EditorPrompt; diff --git a/sandbox/testAppNevena/Front/node_modules/inquirer/lib/prompts/expand.js b/sandbox/testAppNevena/Front/node_modules/inquirer/lib/prompts/expand.js new file mode 100644 index 00000000..af1ad810 --- /dev/null +++ b/sandbox/testAppNevena/Front/node_modules/inquirer/lib/prompts/expand.js @@ -0,0 +1,282 @@ +'use strict'; +/** + * `rawlist` type prompt + */ + +const _ = { + uniq: require('lodash/uniq'), + isString: require('lodash/isString'), + isNumber: require('lodash/isNumber'), + findIndex: require('lodash/findIndex'), +}; +const chalk = require('chalk'); +const { map, takeUntil } = require('rxjs/operators'); +const Base = require('./base'); +const Separator = require('../objects/separator'); +const observe = require('../utils/events'); +const Paginator = require('../utils/paginator'); + +class ExpandPrompt extends Base { + constructor(questions, rl, answers) { + super(questions, rl, answers); + + 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 = (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(this.screen); + } + + /** + * Start the Inquiry session + * @param {Function} cb Callback when prompt is done + * @return {this} + */ + + _run(cb) { + this.done = cb; + + // Save user answer and update prompt to show selected option. + const events = observe(this.rl); + const validation = this.handleSubmitEvents( + events.line.pipe(map(this.getCurrentValue.bind(this))) + ); + validation.success.forEach(this.onSubmit.bind(this)); + validation.error.forEach(this.onError.bind(this)); + this.keypressObs = events.keypress + .pipe(takeUntil(validation.success)) + .forEach(this.onKeypress.bind(this)); + + // Init the prompt + this.render(); + + return this; + } + + /** + * Render the prompt to screen + * @return {ExpandPrompt} self + */ + + render(error, hint) { + let message = this.getQuestion(); + let bottomContent = ''; + + if (this.status === 'answered') { + message += chalk.cyan(this.answer); + } else if (this.status === 'expanded') { + const 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); + } + + getCurrentValue(input) { + if (!input) { + input = this.rawDefault; + } + + const selected = this.opt.choices.where({ key: input.toLowerCase().trim() })[0]; + if (!selected) { + return null; + } + + return selected.value; + } + + /** + * Generate the prompt choices string + * @return {String} Choices string + */ + + getChoices() { + let output = ''; + + this.opt.choices.forEach((choice) => { + output += '\n '; + + if (choice.type === 'separator') { + output += ' ' + choice; + return; + } + + let choiceStr = choice.key + ') ' + choice.name; + if (this.selectedKey === choice.key) { + choiceStr = chalk.cyan(choiceStr); + } + + output += choiceStr; + }); + + return output; + } + + onError(state) { + if (state.value === 'help') { + this.selectedKey = ''; + this.status = 'expanded'; + this.render(); + return; + } + + this.render(state.isValid); + } + + /** + * When user press `enter` key + */ + + onSubmit(state) { + this.status = 'answered'; + const 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); + } + + /** + * When user press a key + */ + + onKeypress() { + this.selectedKey = this.rl.line.toLowerCase(); + const 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 + */ + + validateChoices(choices) { + let formatError; + const errors = []; + const keymap = {}; + choices.filter(Separator.exclude).forEach((choice) => { + if (!choice.key || choice.key.length !== 1) { + formatError = true; + } + + choice.key = String(choice.key).toLowerCase(); + + if (keymap[choice.key]) { + errors.push(choice.key); + } + + keymap[choice.key] = true; + }); + + 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(', ') + ); + } + } + + /** + * 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) { + let defIndex = choices.realLength - 1; + if (_.isNumber(defaultChoice) && this.opt.choices.getChoice(defaultChoice)) { + defIndex = defaultChoice; + } else if (_.isString(defaultChoice)) { + const index = _.findIndex( + choices.realChoices, + ({ value }) => value === defaultChoice + ); + defIndex = index === -1 ? defIndex : index; + } + + const defStr = this.opt.choices.pluck('key'); + this.rawDefault = defStr[defIndex]; + defStr[defIndex] = String(defStr[defIndex]).toUpperCase(); + return defStr.join(''); + } +} + +/** + * Function for rendering checkbox choices + * @param {String} pointer Selected key + * @return {String} Rendered content + */ + +function renderChoices(choices, pointer) { + let output = ''; + + choices.forEach((choice) => { + output += '\n '; + + if (choice.type === 'separator') { + output += ' ' + choice; + return; + } + + let choiceStr = choice.key + ') ' + choice.name; + if (pointer === choice.key) { + choiceStr = chalk.cyan(choiceStr); + } + + output += choiceStr; + }); + + return output; +} + +module.exports = ExpandPrompt; diff --git a/sandbox/testAppNevena/Front/node_modules/inquirer/lib/prompts/input.js b/sandbox/testAppNevena/Front/node_modules/inquirer/lib/prompts/input.js new file mode 100644 index 00000000..87f68b92 --- /dev/null +++ b/sandbox/testAppNevena/Front/node_modules/inquirer/lib/prompts/input.js @@ -0,0 +1,110 @@ +'use strict'; +/** + * `input` type prompt + */ + +const chalk = require('chalk'); +const { map, takeUntil } = require('rxjs/operators'); +const Base = require('./base'); +const observe = require('../utils/events'); + +class InputPrompt extends Base { + /** + * Start the Inquiry session + * @param {Function} cb Callback when prompt is done + * @return {this} + */ + + _run(cb) { + this.done = cb; + + // Once user confirm (enter key) + const events = observe(this.rl); + const submit = events.line.pipe(map(this.filterInput.bind(this))); + + const validation = this.handleSubmitEvents(submit); + validation.success.forEach(this.onEnd.bind(this)); + validation.error.forEach(this.onError.bind(this)); + + events.keypress + .pipe(takeUntil(validation.success)) + .forEach(this.onKeypress.bind(this)); + + // Init + this.render(); + + return this; + } + + /** + * Render the prompt to screen + * @return {InputPrompt} self + */ + + render(error) { + let bottomContent = ''; + let appendContent = ''; + let message = this.getQuestion(); + const { transformer } = this.opt; + const isFinal = this.status === 'answered'; + + if (isFinal) { + appendContent = this.answer; + } else { + appendContent = this.rl.line; + } + + if (transformer) { + message += transformer(appendContent, this.answers, { isFinal }); + } else { + message += isFinal ? chalk.cyan(appendContent) : appendContent; + } + + if (error) { + bottomContent = chalk.red('>> ') + error; + } + + this.screen.render(message, bottomContent); + } + + /** + * When user press `enter` key + */ + + filterInput(input) { + if (!input) { + return this.opt.default == null ? '' : this.opt.default; + } + + return input; + } + + onEnd(state) { + this.answer = state.value; + this.status = 'answered'; + + // Re-render prompt + this.render(); + + this.screen.done(); + this.done(state.value); + } + + onError({ value = '', isValid }) { + this.rl.line += value; + this.rl.cursor += value.length; + this.render(isValid); + } + + /** + * When user press a key + */ + + onKeypress() { + this.state = 'touched'; + + this.render(); + } +} + +module.exports = InputPrompt; diff --git a/sandbox/testAppNevena/Front/node_modules/inquirer/lib/prompts/list.js b/sandbox/testAppNevena/Front/node_modules/inquirer/lib/prompts/list.js new file mode 100644 index 00000000..486c030e --- /dev/null +++ b/sandbox/testAppNevena/Front/node_modules/inquirer/lib/prompts/list.js @@ -0,0 +1,214 @@ +'use strict'; +/** + * `list` type prompt + */ + +const _ = { + isNumber: require('lodash/isNumber'), + findIndex: require('lodash/findIndex'), + isString: require('lodash/isString'), +}; +const chalk = require('chalk'); +const figures = require('figures'); +const cliCursor = require('cli-cursor'); +const runAsync = require('run-async'); +const { flatMap, map, take, takeUntil } = require('rxjs/operators'); +const Base = require('./base'); +const observe = require('../utils/events'); +const Paginator = require('../utils/paginator'); +const incrementListIndex = require('../utils/incrementListIndex'); + +class ListPrompt extends Base { + constructor(questions, rl, answers) { + super(questions, rl, answers); + + if (!this.opt.choices) { + this.throwParamError('choices'); + } + + this.firstRender = true; + this.selected = 0; + + const 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) { + const index = _.findIndex( + this.opt.choices.realChoices, + ({ value }) => value === def + ); + this.selected = Math.max(index, 0); + } + + // Make sure no default is set (so it won't be printed) + this.opt.default = null; + + const shouldLoop = this.opt.loop === undefined ? true : this.opt.loop; + this.paginator = new Paginator(this.screen, { isInfinite: shouldLoop }); + } + + /** + * Start the Inquiry session + * @param {Function} cb Callback when prompt is done + * @return {this} + */ + + _run(cb) { + this.done = cb; + + const self = this; + + const events = observe(this.rl); + events.normalizedUpKey.pipe(takeUntil(events.line)).forEach(this.onUpKey.bind(this)); + events.normalizedDownKey + .pipe(takeUntil(events.line)) + .forEach(this.onDownKey.bind(this)); + events.numberKey.pipe(takeUntil(events.line)).forEach(this.onNumberKey.bind(this)); + events.line + .pipe( + take(1), + map(this.getCurrentValue.bind(this)), + flatMap((value) => + runAsync(self.opt.filter)(value, self.answers).catch((err) => err) + ) + ) + .forEach(this.onSubmit.bind(this)); + + // Init the prompt + cliCursor.hide(); + this.render(); + + return this; + } + + /** + * Render the prompt to screen + * @return {ListPrompt} self + */ + + render() { + // Render question + let 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 { + const choicesStr = listRender(this.opt.choices, this.selected); + const indexPosition = this.opt.choices.indexOf( + this.opt.choices.getChoice(this.selected) + ); + const realIndexPosition = + this.opt.choices.reduce((acc, value, i) => { + // Dont count lines past the choice we are looking at + if (i > indexPosition) { + return acc; + } + // Add line if it's a separator + if (value.type === 'separator') { + return acc + 1; + } + + let l = value.name; + // Non-strings take up one line + if (typeof l !== 'string') { + return acc + 1; + } + + // Calculate lines taken up by string + l = l.split('\n'); + return acc + l.length; + }, 0) - 1; + message += + '\n' + this.paginator.paginate(choicesStr, realIndexPosition, this.opt.pageSize); + } + + this.firstRender = false; + + this.screen.render(message); + } + + /** + * When user press `enter` key + */ + + onSubmit(value) { + this.status = 'answered'; + + // Rerender prompt + this.render(); + + this.screen.done(); + cliCursor.show(); + this.done(value); + } + + getCurrentValue() { + return this.opt.choices.getChoice(this.selected).value; + } + + /** + * When user press a key + */ + onUpKey() { + this.selected = incrementListIndex(this.selected, 'up', this.opt); + this.render(); + } + + onDownKey() { + this.selected = incrementListIndex(this.selected, 'down', this.opt); + this.render(); + } + + onNumberKey(input) { + if (input <= this.opt.choices.realLength) { + this.selected = input - 1; + } + + this.render(); + } +} + +/** + * Function for rendering list choices + * @param {Number} pointer Position of the pointer + * @return {String} Rendered content + */ +function listRender(choices, pointer) { + let output = ''; + let separatorOffset = 0; + + choices.forEach((choice, i) => { + if (choice.type === 'separator') { + separatorOffset++; + output += ' ' + choice + '\n'; + return; + } + + if (choice.disabled) { + separatorOffset++; + output += ' - ' + choice.name; + output += ' (' + (_.isString(choice.disabled) ? choice.disabled : 'Disabled') + ')'; + output += '\n'; + return; + } + + const isSelected = i - separatorOffset === pointer; + let line = (isSelected ? figures.pointer + ' ' : ' ') + choice.name; + if (isSelected) { + line = chalk.cyan(line); + } + + output += line + ' \n'; + }); + + return output.replace(/\n$/, ''); +} + +module.exports = ListPrompt; diff --git a/sandbox/testAppNevena/Front/node_modules/inquirer/lib/prompts/number.js b/sandbox/testAppNevena/Front/node_modules/inquirer/lib/prompts/number.js new file mode 100644 index 00000000..ccabc018 --- /dev/null +++ b/sandbox/testAppNevena/Front/node_modules/inquirer/lib/prompts/number.js @@ -0,0 +1,29 @@ +'use strict'; +/** + * `input` type prompt + */ + +const Input = require('./input'); + +/** + * Extention of the Input prompt specifically for use with number inputs. + */ + +class NumberPrompt extends Input { + filterInput(input) { + if (input && typeof input === 'string') { + input = input.trim(); + // Match a number in the input + const numberMatch = input.match(/(^-?\d+|^\d+\.\d*|^\d*\.\d+)(e\d+)?$/); + // If a number is found, return that input. + if (numberMatch) { + return Number(numberMatch[0]); + } + } + + // If the input was invalid return the default value. + return this.opt.default == null ? NaN : this.opt.default; + } +} + +module.exports = NumberPrompt; diff --git a/sandbox/testAppNevena/Front/node_modules/inquirer/lib/prompts/password.js b/sandbox/testAppNevena/Front/node_modules/inquirer/lib/prompts/password.js new file mode 100644 index 00000000..840249d3 --- /dev/null +++ b/sandbox/testAppNevena/Front/node_modules/inquirer/lib/prompts/password.js @@ -0,0 +1,127 @@ +'use strict'; +/** + * `password` type prompt + */ + +const chalk = require('chalk'); +const { map, takeUntil } = require('rxjs/operators'); +const Base = require('./base'); +const observe = require('../utils/events'); + +function mask(input, maskChar) { + input = String(input); + maskChar = typeof maskChar === 'string' ? maskChar : '*'; + if (input.length === 0) { + return ''; + } + + return new Array(input.length + 1).join(maskChar); +} + +class PasswordPrompt extends Base { + /** + * Start the Inquiry session + * @param {Function} cb Callback when prompt is done + * @return {this} + */ + + _run(cb) { + this.done = cb; + + const events = observe(this.rl); + + // Once user confirm (enter key) + const submit = events.line.pipe(map(this.filterInput.bind(this))); + + const validation = this.handleSubmitEvents(submit); + validation.success.forEach(this.onEnd.bind(this)); + validation.error.forEach(this.onError.bind(this)); + + events.keypress + .pipe(takeUntil(validation.success)) + .forEach(this.onKeypress.bind(this)); + + // Init + this.render(); + + return this; + } + + /** + * Render the prompt to screen + * @return {PasswordPrompt} self + */ + + render(error) { + let message = this.getQuestion(); + let bottomContent = ''; + + if (this.status === 'answered') { + message += this.getMaskedValue(this.answer); + } else { + message += this.getMaskedValue(this.rl.line || ''); + } + + if (error) { + bottomContent = '\n' + chalk.red('>> ') + error; + } + + this.screen.render(message, bottomContent); + } + + getMaskedValue(value) { + if (this.status === 'answered') { + return this.opt.mask + ? chalk.cyan(mask(value, this.opt.mask)) + : chalk.italic.dim('[hidden]'); + } + return this.opt.mask + ? mask(value, this.opt.mask) + : chalk.italic.dim('[input is hidden] '); + } + + /** + * Mask value during async filter/validation. + */ + getSpinningValue(value) { + return this.getMaskedValue(value); + } + + /** + * When user press `enter` key + */ + + filterInput(input) { + if (!input) { + return this.opt.default == null ? '' : this.opt.default; + } + + return input; + } + + onEnd(state) { + this.status = 'answered'; + this.answer = state.value; + + // Re-render prompt + this.render(); + + this.screen.done(); + this.done(state.value); + } + + onError(state) { + this.render(state.isValid); + } + + onKeypress() { + // If user press a key, just clear the default value + if (this.opt.default) { + this.opt.default = undefined; + } + + this.render(); + } +} + +module.exports = PasswordPrompt; diff --git a/sandbox/testAppNevena/Front/node_modules/inquirer/lib/prompts/rawlist.js b/sandbox/testAppNevena/Front/node_modules/inquirer/lib/prompts/rawlist.js new file mode 100644 index 00000000..06036eaf --- /dev/null +++ b/sandbox/testAppNevena/Front/node_modules/inquirer/lib/prompts/rawlist.js @@ -0,0 +1,224 @@ +'use strict'; +/** + * `rawlist` type prompt + */ + +const _ = { + extend: require('lodash/extend'), + isNumber: require('lodash/isNumber'), + findIndex: require('lodash/findIndex'), +}; +const chalk = require('chalk'); +const { map, takeUntil } = require('rxjs/operators'); +const Base = require('./base'); +const Separator = require('../objects/separator'); +const observe = require('../utils/events'); +const Paginator = require('../utils/paginator'); +const incrementListIndex = require('../utils/incrementListIndex'); + +class RawListPrompt extends Base { + constructor(questions, rl, answers) { + super(questions, rl, answers); + + 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(val) { + return val != null; + }, + }); + + const 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) { + const index = _.findIndex( + this.opt.choices.realChoices, + ({ value }) => value === def + ); + const safeIndex = Math.max(index, 0); + this.selected = safeIndex; + this.rawDefault = safeIndex; + } + + // Make sure no default is set (so it won't be printed) + this.opt.default = null; + + const shouldLoop = this.opt.loop === undefined ? true : this.opt.loop; + this.paginator = new Paginator(undefined, { isInfinite: shouldLoop }); + } + + /** + * Start the Inquiry session + * @param {Function} cb Callback when prompt is done + * @return {this} + */ + + _run(cb) { + this.done = cb; + + // Once user confirm (enter key) + const events = observe(this.rl); + const submit = events.line.pipe(map(this.getCurrentValue.bind(this))); + + const validation = this.handleSubmitEvents(submit); + validation.success.forEach(this.onEnd.bind(this)); + validation.error.forEach(this.onError.bind(this)); + + events.normalizedUpKey + .pipe(takeUntil(validation.success)) + .forEach(this.onUpKey.bind(this)); + events.normalizedDownKey + .pipe(takeUntil(validation.success)) + .forEach(this.onDownKey.bind(this)); + events.keypress + .pipe(takeUntil(validation.success)) + .forEach(this.onKeypress.bind(this)); + // Init the prompt + this.render(); + + return this; + } + + /** + * Render the prompt to screen + * @return {RawListPrompt} self + */ + + render(error) { + // Render question + let message = this.getQuestion(); + let bottomContent = ''; + + if (this.status === 'answered') { + message += chalk.cyan(this.opt.choices.getChoice(this.selected).short); + } else { + const choicesStr = renderChoices(this.opt.choices, this.selected); + message += + '\n' + 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); + } + + /** + * When user press `enter` key + */ + + getCurrentValue(index) { + if (index == null) { + index = this.rawDefault; + } else if (index === '') { + this.selected = this.selected === undefined ? -1 : this.selected; + index = this.selected; + } else { + index -= 1; + } + + const choice = this.opt.choices.getChoice(index); + return choice ? choice.value : null; + } + + onEnd(state) { + this.status = 'answered'; + this.answer = state.value; + + // Re-render prompt + this.render(); + + this.screen.done(); + this.done(state.value); + } + + onError() { + this.render('Please enter a valid index'); + } + + /** + * When user press a key + */ + + onKeypress() { + const 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(); + } + + /** + * When user press up key + */ + + onUpKey() { + this.onArrowKey('up'); + } + + /** + * When user press down key + */ + + onDownKey() { + this.onArrowKey('down'); + } + + /** + * When user press up or down key + * @param {String} type Arrow type: up or down + */ + + onArrowKey(type) { + this.selected = incrementListIndex(this.selected, type, this.opt); + this.rl.line = String(this.selected + 1); + } +} + +/** + * Function for rendering list choices + * @param {Number} pointer Position of the pointer + * @return {String} Rendered content + */ + +function renderChoices(choices, pointer) { + let output = ''; + let separatorOffset = 0; + + choices.forEach((choice, i) => { + output += '\n '; + + if (choice.type === 'separator') { + separatorOffset++; + output += ' ' + choice; + return; + } + + const index = i - separatorOffset; + let display = index + 1 + ') ' + choice.name; + if (index === pointer) { + display = chalk.cyan(display); + } + + output += display; + }); + + return output; +} + +module.exports = RawListPrompt; diff --git a/sandbox/testAppNevena/Front/node_modules/inquirer/lib/ui/baseUI.js b/sandbox/testAppNevena/Front/node_modules/inquirer/lib/ui/baseUI.js new file mode 100644 index 00000000..547e5658 --- /dev/null +++ b/sandbox/testAppNevena/Front/node_modules/inquirer/lib/ui/baseUI.js @@ -0,0 +1,99 @@ +'use strict'; +const _ = { + extend: require('lodash/extend'), + omit: require('lodash/omit'), +}; +const MuteStream = require('mute-stream'); +const readline = require('readline'); + +/** + * Base interface class other can inherits from + */ + +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); + + // 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); + } + + /** + * Handle the ^C exit + * @return {null} + */ + + onForceClose() { + this.close(); + process.kill(process.pid, 'SIGINT'); + console.log(''); + } + + /** + * Close the interface and cleanup listeners + */ + + close() { + // 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(); + } +} + +function setupReadlineOptions(opt) { + opt = opt || {}; + // Inquirer 8.x: + // opt.skipTTYChecks = opt.skipTTYChecks === undefined ? opt.input !== undefined : opt.skipTTYChecks; + opt.skipTTYChecks = opt.skipTTYChecks === undefined ? true : opt.skipTTYChecks; + + // Default `input` to stdin + const input = opt.input || process.stdin; + + // Check if prompt is being called in TTY environment + // If it isn't return a failed promise + if (!opt.skipTTYChecks && !input.isTTY) { + const nonTtyError = new Error( + 'Prompts can not be meaningfully rendered in non-TTY environments' + ); + nonTtyError.isTtyError = true; + throw nonTtyError; + } + + // Add mute capabilities to the output + const ms = new MuteStream(); + ms.pipe(opt.output || process.stdout); + const output = ms; + + return _.extend( + { + terminal: true, + input, + output, + }, + _.omit(opt, ['input', 'output']) + ); +} + +module.exports = UI; diff --git a/sandbox/testAppNevena/Front/node_modules/inquirer/lib/ui/bottom-bar.js b/sandbox/testAppNevena/Front/node_modules/inquirer/lib/ui/bottom-bar.js new file mode 100644 index 00000000..9235273e --- /dev/null +++ b/sandbox/testAppNevena/Front/node_modules/inquirer/lib/ui/bottom-bar.js @@ -0,0 +1,102 @@ +'use strict'; +/** + * Sticky bottom bar user interface + */ + +const through = require('through'); +const Base = require('./baseUI'); +const rlUtils = require('../utils/readline'); +const _ = { + last: require('lodash/last'), +}; + +class BottomBar extends Base { + constructor(opt) { + opt = opt || {}; + + super(opt); + + this.log = through(this.writeLog.bind(this)); + this.bottomBar = opt.bottomBar || ''; + this.render(); + } + + /** + * Render the prompt to screen + * @return {BottomBar} self + */ + + render() { + this.write(this.bottomBar); + return this; + } + + clean() { + rlUtils.clearLine(this.rl, this.bottomBar.split('\n').length); + return this; + } + + /** + * Update the bottom bar content and rerender + * @param {String} bottomBar Bottom bar content + * @return {BottomBar} self + */ + + updateBottomBar(bottomBar) { + rlUtils.clearLine(this.rl, 1); + this.rl.output.unmute(); + this.clean(); + this.bottomBar = bottomBar; + this.render(); + this.rl.output.mute(); + return this; + } + + /** + * Write out log data + * @param {String} data - The log data to be output + * @return {BottomBar} 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; + } + + /** + * Make sure line end on a line feed + * @param {String} str Input string + * @return {String} The input string with a final line feed + */ + + enforceLF(str) { + return str.match(/[\r\n]$/) ? str : str + '\n'; + } + + /** + * 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) { + const 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; diff --git a/sandbox/testAppNevena/Front/node_modules/inquirer/lib/ui/prompt.js b/sandbox/testAppNevena/Front/node_modules/inquirer/lib/ui/prompt.js new file mode 100644 index 00000000..d9383879 --- /dev/null +++ b/sandbox/testAppNevena/Front/node_modules/inquirer/lib/ui/prompt.js @@ -0,0 +1,150 @@ +'use strict'; +const _ = { + isPlainObject: require('lodash/isPlainObject'), + clone: require('lodash/clone'), + isArray: require('lodash/isArray'), + get: require('lodash/get'), + set: require('lodash/set'), + isFunction: require('lodash/isFunction'), +}; +const { defer, empty, from, of } = require('rxjs'); +const { concatMap, filter, publish, reduce } = require('rxjs/operators'); +const runAsync = require('run-async'); +const utils = require('../utils/utils'); +const Base = require('./baseUI'); + +/** + * Base interface class other can inherits from + */ + +class PromptUI extends Base { + constructor(prompts, opt) { + super(opt); + this.prompts = prompts; + } + + run(questions, answers) { + // Keep global reference to the answers + if (_.isPlainObject(answers)) { + this.answers = _.clone(answers); + } else { + this.answers = {}; + } + + // Make sure questions is an array. + if (_.isPlainObject(questions)) { + // It's either an object of questions or a single question + questions = Object.values(questions).every( + (v) => _.isPlainObject(v) && v.name === undefined + ) + ? Object.entries(questions).map(([name, question]) => ({ name, ...question })) + : [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. + const obs = _.isArray(questions) ? from(questions) : questions; + + this.process = obs.pipe( + concatMap(this.processQuestion.bind(this)), + publish() // Creates a hot Observable. It prevents duplicating prompts. + ); + + this.process.connect(); + + return this.process + .pipe( + reduce((answers, answer) => { + _.set(answers, answer.name, answer.answer); + return answers; + }, this.answers) + ) + .toPromise(Promise) + .then(this.onCompletion.bind(this), this.onError.bind(this)); + } + + /** + * Once all prompt are over + */ + + onCompletion() { + this.close(); + + return this.answers; + } + + onError(error) { + this.close(); + return Promise.reject(error); + } + + processQuestion(question) { + question = _.clone(question); + return defer(() => { + const obs = of(question); + + return obs.pipe( + 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)) + ); + }); + } + + fetchAnswer(question) { + const Prompt = this.prompts[question.type]; + this.activePrompt = new Prompt(question, this.rl, this.answers); + return defer(() => + from(this.activePrompt.run().then((answer) => ({ name: question.name, answer }))) + ); + } + + setDefaultType(question) { + // Default type to input + if (!this.prompts[question.type]) { + question.type = 'input'; + } + + return defer(() => of(question)); + } + + filterIfRunnable(question) { + if ( + question.askAnswered !== true && + _.get(this.answers, question.name) !== undefined + ) { + return empty(); + } + + if (question.when === false) { + return empty(); + } + + if (!_.isFunction(question.when)) { + return of(question); + } + + const { answers } = this; + return defer(() => + from( + runAsync(question.when)(answers).then((shouldRun) => { + if (shouldRun) { + return question; + } + }) + ).pipe(filter((val) => val != null)) + ); + } +} + +module.exports = PromptUI; diff --git a/sandbox/testAppNevena/Front/node_modules/inquirer/lib/utils/events.js b/sandbox/testAppNevena/Front/node_modules/inquirer/lib/utils/events.js new file mode 100644 index 00000000..b4b16bb1 --- /dev/null +++ b/sandbox/testAppNevena/Front/node_modules/inquirer/lib/utils/events.js @@ -0,0 +1,54 @@ +'use strict'; +const { fromEvent } = require('rxjs'); +const { filter, map, share, takeUntil } = require('rxjs/operators'); + +function normalizeKeypressEvents(value, key) { + return { value, key: key || {} }; +} + +module.exports = function (rl) { + const keypress = fromEvent(rl.input, 'keypress', normalizeKeypressEvents) + .pipe(takeUntil(fromEvent(rl, 'close'))) + // Ignore `enter` key. On the readline, we only care about the `line` event. + .pipe(filter(({ key }) => key.name !== 'enter' && key.name !== 'return')); + + return { + line: fromEvent(rl, 'line'), + keypress, + + normalizedUpKey: keypress.pipe( + filter( + ({ key }) => + key.name === 'up' || key.name === 'k' || (key.name === 'p' && key.ctrl) + ), + share() + ), + + normalizedDownKey: keypress.pipe( + filter( + ({ key }) => + key.name === 'down' || key.name === 'j' || (key.name === 'n' && key.ctrl) + ), + share() + ), + + numberKey: keypress.pipe( + filter((e) => e.value && '123456789'.indexOf(e.value) >= 0), + map((e) => Number(e.value)), + share() + ), + + spaceKey: keypress.pipe( + filter(({ key }) => key && key.name === 'space'), + share() + ), + aKey: keypress.pipe( + filter(({ key }) => key && key.name === 'a'), + share() + ), + iKey: keypress.pipe( + filter(({ key }) => key && key.name === 'i'), + share() + ), + }; +}; diff --git a/sandbox/testAppNevena/Front/node_modules/inquirer/lib/utils/incrementListIndex.js b/sandbox/testAppNevena/Front/node_modules/inquirer/lib/utils/incrementListIndex.js new file mode 100644 index 00000000..49aa2011 --- /dev/null +++ b/sandbox/testAppNevena/Front/node_modules/inquirer/lib/utils/incrementListIndex.js @@ -0,0 +1,19 @@ +function incrementListIndex(current, dir, opt) { + const len = opt.choices.realLength; + const shouldLoop = 'loop' in opt ? Boolean(opt.loop) : true; + if (dir === 'up') { + if (current > 0) { + return current - 1; + } + return shouldLoop ? len - 1 : current; + } + if (dir === 'down') { + if (current < len - 1) { + return current + 1; + } + return shouldLoop ? 0 : current; + } + throw new Error('dir must be up or down'); +} + +module.exports = incrementListIndex; diff --git a/sandbox/testAppNevena/Front/node_modules/inquirer/lib/utils/paginator.js b/sandbox/testAppNevena/Front/node_modules/inquirer/lib/utils/paginator.js new file mode 100644 index 00000000..767bf672 --- /dev/null +++ b/sandbox/testAppNevena/Front/node_modules/inquirer/lib/utils/paginator.js @@ -0,0 +1,78 @@ +'use strict'; + +const _ = { + sum: require('lodash/sum'), + flatten: require('lodash/flatten'), +}; +const chalk = require('chalk'); + +/** + * The paginator returns a subset of the choices if the list is too long. + */ + +class Paginator { + constructor(screen, options = {}) { + const { isInfinite = true } = options; + this.lastIndex = 0; + this.screen = screen; + this.isInfinite = isInfinite; + } + + paginate(output, active, pageSize) { + pageSize = pageSize || 7; + let lines = output.split('\n'); + + if (this.screen) { + lines = this.screen.breakLines(lines); + active = _.sum(lines.map((lineParts) => lineParts.length).splice(0, active)); + lines = _.flatten(lines); + } + + // Make sure there's enough lines to paginate + if (lines.length <= pageSize) { + return output; + } + const visibleLines = this.isInfinite + ? this.getInfiniteLines(lines, active, pageSize) + : this.getFiniteLines(lines, active, pageSize); + this.lastIndex = active; + return ( + visibleLines.join('\n') + + '\n' + + chalk.dim('(Move up and down to reveal more choices)') + ); + } + + getInfiniteLines(lines, active, pageSize) { + if (this.pointer === undefined) { + this.pointer = 0; + } + const middleOfList = Math.floor(pageSize / 2); + // 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); + } + + // Duplicate the lines so it give an infinite list look + const infinite = _.flatten([lines, lines, lines]); + const topIndex = Math.max(0, active + lines.length - this.pointer); + + return infinite.splice(topIndex, pageSize); + } + + getFiniteLines(lines, active, pageSize) { + let topIndex = active - pageSize / 2; + if (topIndex < 0) { + topIndex = 0; + } else if (topIndex + pageSize > lines.length) { + topIndex = lines.length - pageSize; + } + return lines.splice(topIndex, pageSize); + } +} + +module.exports = Paginator; diff --git a/sandbox/testAppNevena/Front/node_modules/inquirer/lib/utils/readline.js b/sandbox/testAppNevena/Front/node_modules/inquirer/lib/utils/readline.js new file mode 100644 index 00000000..769f5d7f --- /dev/null +++ b/sandbox/testAppNevena/Front/node_modules/inquirer/lib/utils/readline.js @@ -0,0 +1,51 @@ +'use strict'; +const ansiEscapes = require('ansi-escapes'); + +/** + * Move cursor left by `x` + * @param {Readline} rl - Readline instance + * @param {Number} x - How far to go left (default to 1) + */ + +exports.left = function (rl, x) { + rl.output.write(ansiEscapes.cursorBackward(x)); +}; + +/** + * Move cursor right by `x` + * @param {Readline} rl - Readline instance + * @param {Number} x - How far to go left (default to 1) + */ + +exports.right = function (rl, x) { + rl.output.write(ansiEscapes.cursorForward(x)); +}; + +/** + * Move cursor up by `x` + * @param {Readline} rl - Readline instance + * @param {Number} x - How far to go up (default to 1) + */ + +exports.up = function (rl, x) { + rl.output.write(ansiEscapes.cursorUp(x)); +}; + +/** + * Move cursor down by `x` + * @param {Readline} rl - Readline instance + * @param {Number} x - How far to go down (default to 1) + */ + +exports.down = function (rl, x) { + rl.output.write(ansiEscapes.cursorDown(x)); +}; + +/** + * Clear current line + * @param {Readline} rl - Readline instance + * @param {Number} len - number of line to delete + */ +exports.clearLine = function (rl, len) { + rl.output.write(ansiEscapes.eraseLines(len)); +}; diff --git a/sandbox/testAppNevena/Front/node_modules/inquirer/lib/utils/screen-manager.js b/sandbox/testAppNevena/Front/node_modules/inquirer/lib/utils/screen-manager.js new file mode 100644 index 00000000..12412c64 --- /dev/null +++ b/sandbox/testAppNevena/Front/node_modules/inquirer/lib/utils/screen-manager.js @@ -0,0 +1,175 @@ +'use strict'; +const _ = { + last: require('lodash/last'), + flatten: require('lodash/flatten'), +}; +const util = require('./readline'); +const cliWidth = require('cli-width'); +const stripAnsi = require('strip-ansi'); +const stringWidth = require('string-width'); +const ora = require('ora'); + +function height(content) { + return content.split('\n').length; +} + +function lastLine(content) { + return _.last(content.split('\n')); +} + +class ScreenManager { + constructor(rl) { + // These variables are keeping information to allow correct prompt re-rendering + this.height = 0; + this.extraLinesUnderPrompt = 0; + + this.rl = rl; + } + + renderWithSpinner(content, bottomContent) { + if (this.spinnerId) { + clearInterval(this.spinnerId); + } + + let spinner; + let contentFunc; + let bottomContentFunc; + + if (bottomContent) { + spinner = ora(bottomContent); + contentFunc = () => content; + bottomContentFunc = () => spinner.frame(); + } else { + spinner = ora(content); + contentFunc = () => spinner.frame(); + bottomContentFunc = () => ''; + } + + this.spinnerId = setInterval( + () => this.render(contentFunc(), bottomContentFunc(), true), + spinner.interval + ); + } + + render(content, bottomContent, spinning = false) { + if (this.spinnerId && !spinning) { + clearInterval(this.spinnerId); + } + + this.rl.output.unmute(); + this.clean(this.extraLinesUnderPrompt); + + /** + * Write message to screen and setPrompt to control backspace + */ + + const promptLine = lastLine(content); + const 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. + let 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 + const cursorPos = this.rl._getCursorPos(); + const width = this.normalizedCliWidth(); + + 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'; + } + + const fullContent = content + (bottomContent ? '\n' + bottomContent : ''); + this.rl.output.write(fullContent); + + /** + * 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. + const promptLineUpDiff = Math.floor(rawPromptLine.length / width) - cursorPos.rows; + const 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))); + + // Adjust cursor on the right + if (cursorPos.cols > 0) { + util.right(this.rl, cursorPos.cols); + } + + /** + * Set up state for next re-rendering + */ + this.extraLinesUnderPrompt = bottomContentHeight; + this.height = height(fullContent); + + this.rl.output.mute(); + } + + clean(extraLines) { + if (extraLines > 0) { + util.down(this.rl, extraLines); + } + + util.clearLine(this.rl, this.height); + } + + done() { + this.rl.setPrompt(''); + this.rl.output.unmute(); + this.rl.output.write('\n'); + } + + releaseCursor() { + if (this.extraLinesUnderPrompt > 0) { + util.down(this.rl, this.extraLinesUnderPrompt); + } + } + + normalizedCliWidth() { + const width = cliWidth({ + defaultWidth: 80, + output: this.rl.output, + }); + return width; + } + + 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(); + const regex = new RegExp('(?:(?:\\033[[0-9;]*m)*.?){1,' + width + '}', 'g'); + return lines.map((line) => { + const 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'); + } +} + +module.exports = ScreenManager; diff --git a/sandbox/testAppNevena/Front/node_modules/inquirer/lib/utils/utils.js b/sandbox/testAppNevena/Front/node_modules/inquirer/lib/utils/utils.js new file mode 100644 index 00000000..92117510 --- /dev/null +++ b/sandbox/testAppNevena/Front/node_modules/inquirer/lib/utils/utils.js @@ -0,0 +1,28 @@ +'use strict'; +const _ = { + isFunction: require('lodash/isFunction'), +}; +const { from, of } = require('rxjs'); +const runAsync = require('run-async'); + +/** + * Resolve a question property value if it is passed as a function. + * This method will overwrite the property on the question object with the received value. + * @param {Object} question - Question object + * @param {String} prop - Property to fetch name + * @param {Object} answers - Answers object + * @return {Rx.Observable} - Observable emitting once value is known + */ + +exports.fetchAsyncQuestionProperty = function (question, prop, answers) { + if (!_.isFunction(question[prop])) { + return of(question); + } + + return from( + runAsync(question[prop])(answers).then((value) => { + question[prop] = value; + return question; + }) + ); +}; |